Branch data Line data Source code
1 : : /**
2 : : * @file json_dom.c
3 : : * @brief DOM-style access: open, close, path lookup, iteration.
4 : : *
5 : : * Copyright (c) 2026 M. A. Chatterjee <deftio at deftio dot com>
6 : : * BSD-2-Clause — see LICENSE.txt
7 : : */
8 : :
9 : : #include "json_internal.h"
10 : :
11 : : #include <stdlib.h>
12 : : #include <string.h>
13 : :
14 : : /* ── Open / Close ────────────────────────────────────────────────────── */
15 : :
16 : 32 : tp_result tp_json_open(tp_json **out, const uint8_t *buf, size_t buf_len)
17 : : {
18 [ + + - + ]: 32 : if (!out || !buf)
19 : 1 : return TP_ERR_INVALID_PARAM;
20 : :
21 : : /* Allocation failure paths are excluded from coverage (LCOV_EXCL). */
22 : 31 : tp_json *j = calloc(1, sizeof(*j));
23 : 31 : if (!j) /* LCOV_EXCL_BR_LINE */
24 : : return TP_ERR_ALLOC; /* LCOV_EXCL_LINE */
25 : :
26 : : /* Make our own copy of the buffer so the caller doesn't need to keep it alive */
27 : 31 : j->buf_owned = malloc(buf_len);
28 : 31 : if (!j->buf_owned) { /* LCOV_EXCL_BR_LINE */
29 : : /* LCOV_EXCL_START */
30 : : free(j);
31 : : return TP_ERR_ALLOC;
32 : : /* LCOV_EXCL_STOP */
33 : : }
34 : 31 : memcpy(j->buf_owned, buf, buf_len);
35 : 31 : j->buf_len = buf_len;
36 : :
37 : : /* Open as dict */
38 : 31 : tp_result rc = tp_dict_open(&j->dict, j->buf_owned, buf_len);
39 [ + + ]: 31 : if (rc != TP_OK) {
40 : 1 : free(j->buf_owned);
41 : 1 : free(j);
42 : 1 : return rc;
43 : : }
44 : :
45 : : /* Determine root type */
46 : : tp_value root_val;
47 [ + + ]: 30 : if (tp_dict_lookup(j->dict, TP_JSON_META_ROOT, &root_val) == TP_OK) {
48 : 29 : uint32_t rt = 0;
49 [ + + ]: 29 : if (root_val.type == TP_UINT)
50 : 28 : rt = (uint32_t)root_val.data.uint_val;
51 [ + - ]: 1 : else if (root_val.type == TP_INT)
52 : 1 : rt = (uint32_t)root_val.data.int_val;
53 [ + + ]: 29 : j->root = (rt == TP_JSON_ROOT_ARRAY) ? TP_ARRAY : TP_DICT;
54 : : } else {
55 : 1 : j->root = TP_DICT; /* default to object */
56 : : }
57 : :
58 : : /* Count top-level keys (keys without dots, excluding metadata and array indices) */
59 : : /* For objects: keys without '.' separator
60 : : For arrays: count [0], [1], ... indices */
61 : : /* Since we can't iterate yet, we use dict count minus metadata */
62 : 30 : j->count = tp_dict_count(j->dict);
63 [ + - ]: 30 : if (j->count > 0)
64 : 30 : j->count--; /* subtract the metadata key */
65 : :
66 : 30 : *out = j;
67 : 30 : return TP_OK;
68 : : }
69 : :
70 : 41 : tp_result tp_json_close(tp_json **json)
71 : : {
72 [ + + ]: 41 : if (!json)
73 : 1 : return TP_ERR_INVALID_PARAM;
74 [ + + ]: 40 : if (*json) {
75 : : /* open() fails and cleans up unless the dict was created, so a live
76 : : tp_json always has one; close tolerates NULL anyway. */
77 : 30 : tp_dict_close(&(*json)->dict);
78 : 30 : free((*json)->buf_owned);
79 : 30 : free(*json);
80 : 30 : *json = NULL;
81 : : }
82 : 40 : return TP_OK;
83 : : }
84 : :
85 : : /* ── Path lookup ─────────────────────────────────────────────────────── */
86 : :
87 : 45 : tp_result tp_json_lookup_path(const tp_json *j, const char *path, tp_value *val)
88 : : {
89 [ + + + - : 45 : if (!j || !path || !val)
- + ]
90 : 1 : return TP_ERR_INVALID_PARAM;
91 : :
92 : : /* The path is already in dot/bracket notation, which matches our
93 : : flattened key format. Just look it up directly. */
94 : 44 : return tp_dict_lookup(j->dict, path, val);
95 : : }
96 : :
97 : : /* ── Root type ───────────────────────────────────────────────────────── */
98 : :
99 : 5 : tp_result tp_json_root_type(const tp_json *j, tp_value_type *type)
100 : : {
101 [ + + - + ]: 5 : if (!j || !type)
102 : 1 : return TP_ERR_INVALID_PARAM;
103 : :
104 : 4 : *type = j->root;
105 : 4 : return TP_OK;
106 : : }
107 : :
108 : : /* ── Iteration ───────────────────────────────────────────────────────── */
109 : :
110 : 2 : tp_result tp_json_iterate(const tp_json *j, tp_iterator **out)
111 : : {
112 [ + + - + ]: 2 : if (!j || !out)
113 : 1 : return TP_ERR_INVALID_PARAM;
114 : :
115 : 1 : return tp_dict_iterate(j->dict, out);
116 : : }
117 : :
118 : : /* ── Count ───────────────────────────────────────────────────────────── */
119 : :
120 : 2 : uint32_t tp_json_count(const tp_json *j)
121 : : {
122 [ + + ]: 2 : if (!j)
123 : 1 : return 0;
124 : :
125 : 1 : return j->count;
126 : : }
|