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)
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) {
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 [ + - ]: 30 : if ((*json)->dict)
76 : 30 : tp_dict_close(&(*json)->dict);
77 : 30 : free((*json)->buf_owned);
78 : 30 : free(*json);
79 : 30 : *json = NULL;
80 : : }
81 : 40 : return TP_OK;
82 : : }
83 : :
84 : : /* ── Path lookup ─────────────────────────────────────────────────────── */
85 : :
86 : 45 : tp_result tp_json_lookup_path(const tp_json *j, const char *path, tp_value *val)
87 : : {
88 [ + + + - : 45 : if (!j || !path || !val)
- + ]
89 : 1 : return TP_ERR_INVALID_PARAM;
90 : :
91 : : /* The path is already in dot/bracket notation, which matches our
92 : : flattened key format. Just look it up directly. */
93 : 44 : return tp_dict_lookup(j->dict, path, val);
94 : : }
95 : :
96 : : /* ── Root type ───────────────────────────────────────────────────────── */
97 : :
98 : 5 : tp_result tp_json_root_type(const tp_json *j, tp_value_type *type)
99 : : {
100 [ + + - + ]: 5 : if (!j || !type)
101 : 1 : return TP_ERR_INVALID_PARAM;
102 : :
103 : 4 : *type = j->root;
104 : 4 : return TP_OK;
105 : : }
106 : :
107 : : /* ── Iteration ───────────────────────────────────────────────────────── */
108 : :
109 : 2 : tp_result tp_json_iterate(const tp_json *j, tp_iterator **out)
110 : : {
111 [ + + - + ]: 2 : if (!j || !out)
112 : 1 : return TP_ERR_INVALID_PARAM;
113 : :
114 : 1 : return tp_dict_iterate(j->dict, out);
115 : : }
116 : :
117 : : /* ── Count ───────────────────────────────────────────────────────────── */
118 : :
119 : 2 : uint32_t tp_json_count(const tp_json *j)
120 : : {
121 [ + + ]: 2 : if (!j)
122 : 1 : return 0;
123 : :
124 : 1 : return j->count;
125 : : }
|