Skip to content

Commit adf662d

Browse files
committed
Add json_extract_value to extract a bit of a DOM.
Fixes #64.
1 parent 51154d7 commit adf662d

File tree

4 files changed

+401
-4
lines changed

4 files changed

+401
-4
lines changed

README.md

+27
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,33 @@ free(root);
295295

296296
As you can see it makes iterating through the DOM a little more pleasant.
297297

298+
### Extracting a Value from a DOM
299+
300+
If you want to extract a value from a DOM into a new allocation then
301+
`json_extract_value` and `json_extract_value_ex` are you friends. These
302+
functions let you take any value and its subtree from a DOM and clone it
303+
into a new allocation - either a single `malloc` or a user-provided
304+
allocation region.
305+
306+
```c
307+
const char json[] = "{\"foo\" : { \"bar\" : [123, false, null, true], \"haz\" : \"haha\" }}";
308+
struct json_value_s* root = json_parse(json, strlen(json));
309+
assert(root);
310+
311+
struct json_value_s* foo = json_value_as_object(root)->start->value;
312+
assert(foo);
313+
314+
struct json_value_s* extracted = json_extract_value(foo);
315+
316+
/* We can free root now because we've got a new allocation for extracted! */
317+
free(root);
318+
319+
assert(json_value_as_object(extracted));
320+
321+
/* Don't forget to free the one allocation! */
322+
free(extracted);
323+
```
324+
298325
## Design
299326
300327
The json_parse function calls malloc once, and then slices up this single

json.h

+276-4
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
#endif
4848

4949
#include <stddef.h>
50+
#include <string.h>
5051

5152
#if defined(_MSC_VER)
5253
#define json_weak __inline
@@ -143,15 +144,29 @@ enum json_parse_flags_e {
143144
json_weak struct json_value_s *json_parse(const void *src, size_t src_size);
144145

145146
/* Parse a JSON text file, returning a pointer to the root of the JSON
146-
* structure. json_parse performs 1 call to malloc for the entire encoding.
147-
* Returns 0 if an error occurred (malformed JSON input, or malloc failed). If
148-
* an error occurred, the result struct (if not NULL) will explain the type of
149-
* error, and the location in the input it occurred. */
147+
* structure. json_parse performs 1 call to alloc_func_ptr for the entire
148+
* encoding. Returns 0 if an error occurred (malformed JSON input, or malloc
149+
* failed). If an error occurred, the result struct (if not NULL) will explain
150+
* the type of error, and the location in the input it occurred. If
151+
* alloc_func_ptr is null then malloc is used. */
150152
json_weak struct json_value_s *
151153
json_parse_ex(const void *src, size_t src_size, size_t flags_bitset,
152154
void *(*alloc_func_ptr)(void *, size_t), void *user_data,
153155
struct json_parse_result_s *result);
154156

157+
/* Extracts a value and all the data that makes it up into a newly created
158+
* value. json_extract_value performs 1 call to malloc for the entire encoding.
159+
*/
160+
json_weak struct json_value_s *
161+
json_extract_value(const struct json_value_s *value);
162+
163+
/* Extracts a value and all the data that makes it up into a newly created
164+
* value. json_extract_value performs 1 call to alloc_func_ptr for the entire
165+
* encoding. If alloc_func_ptr is null then malloc is used. */
166+
json_weak struct json_value_s *
167+
json_extract_value_ex(const struct json_value_s *value,
168+
void *(*alloc_func_ptr)(void *, size_t), void *user_data);
169+
155170
/* Write out a minified JSON utf-8 string. This string is an encoding of the
156171
* minimal string characters required to still encode the same data.
157172
* json_write_minified performs 1 call to malloc for the entire encoding. Return
@@ -2101,6 +2116,263 @@ struct json_value_s *json_parse(const void *src, size_t src_size) {
21012116
json_null, json_null);
21022117
}
21032118

2119+
struct json_extract_result_s {
2120+
size_t dom_size;
2121+
size_t data_size;
2122+
};
2123+
2124+
struct json_value_s *json_extract_value(const struct json_value_s *value) {
2125+
return json_extract_value_ex(value, json_null, json_null);
2126+
}
2127+
2128+
json_weak struct json_extract_result_s
2129+
json_extract_get_number_size(const struct json_number_s *const number);
2130+
json_weak struct json_extract_result_s
2131+
json_extract_get_string_size(const struct json_string_s *const string);
2132+
json_weak struct json_extract_result_s
2133+
json_extract_get_object_size(const struct json_object_s *const object);
2134+
json_weak struct json_extract_result_s
2135+
json_extract_get_array_size(const struct json_array_s *const array);
2136+
json_weak struct json_extract_result_s
2137+
json_extract_get_value_size(const struct json_value_s *const value);
2138+
2139+
struct json_extract_result_s
2140+
json_extract_get_number_size(const struct json_number_s *const number) {
2141+
struct json_extract_result_s result;
2142+
result.dom_size = sizeof(struct json_number_s);
2143+
result.data_size = number->number_size;
2144+
return result;
2145+
}
2146+
2147+
struct json_extract_result_s
2148+
json_extract_get_string_size(const struct json_string_s *const string) {
2149+
struct json_extract_result_s result;
2150+
result.dom_size = sizeof(struct json_string_s);
2151+
result.data_size = string->string_size + 1;
2152+
return result;
2153+
}
2154+
2155+
struct json_extract_result_s
2156+
json_extract_get_object_size(const struct json_object_s *const object) {
2157+
struct json_extract_result_s result;
2158+
size_t i;
2159+
const struct json_object_element_s *element = object->start;
2160+
2161+
result.dom_size = sizeof(struct json_object_s) +
2162+
(sizeof(struct json_object_element_s) * object->length);
2163+
result.data_size = 0;
2164+
2165+
for (i = 0; i < object->length; i++) {
2166+
const struct json_extract_result_s string_result =
2167+
json_extract_get_string_size(element->name);
2168+
const struct json_extract_result_s value_result =
2169+
json_extract_get_value_size(element->value);
2170+
2171+
result.dom_size += string_result.dom_size;
2172+
result.data_size += string_result.data_size;
2173+
2174+
result.dom_size += value_result.dom_size;
2175+
result.data_size += value_result.data_size;
2176+
2177+
element = element->next;
2178+
}
2179+
2180+
return result;
2181+
}
2182+
2183+
struct json_extract_result_s
2184+
json_extract_get_array_size(const struct json_array_s *const array) {
2185+
struct json_extract_result_s result;
2186+
size_t i;
2187+
const struct json_array_element_s *element = array->start;
2188+
2189+
result.dom_size = sizeof(struct json_array_s) +
2190+
(sizeof(struct json_array_element_s) * array->length);
2191+
result.data_size = 0;
2192+
2193+
for (i = 0; i < array->length; i++) {
2194+
const struct json_extract_result_s value_result =
2195+
json_extract_get_value_size(element->value);
2196+
2197+
result.dom_size += value_result.dom_size;
2198+
result.data_size += value_result.data_size;
2199+
2200+
element = element->next;
2201+
}
2202+
2203+
return result;
2204+
}
2205+
2206+
struct json_extract_result_s
2207+
json_extract_get_value_size(const struct json_value_s *const value) {
2208+
struct json_extract_result_s result = {0, 0};
2209+
2210+
switch (value->type) {
2211+
default:
2212+
break;
2213+
case json_type_object:
2214+
result = json_extract_get_object_size(
2215+
(const struct json_object_s *)value->payload);
2216+
break;
2217+
case json_type_array:
2218+
result = json_extract_get_array_size(
2219+
(const struct json_array_s *)value->payload);
2220+
break;
2221+
case json_type_number:
2222+
result = json_extract_get_number_size(
2223+
(const struct json_number_s *)value->payload);
2224+
break;
2225+
case json_type_string:
2226+
result = json_extract_get_string_size(
2227+
(const struct json_string_s *)value->payload);
2228+
break;
2229+
}
2230+
2231+
result.dom_size += sizeof(struct json_value_s);
2232+
2233+
return result;
2234+
}
2235+
2236+
struct json_extract_state_s {
2237+
char *dom;
2238+
char *data;
2239+
};
2240+
2241+
json_weak void json_extract_copy_value(struct json_extract_state_s *const state,
2242+
const struct json_value_s *const value);
2243+
void json_extract_copy_value(struct json_extract_state_s *const state,
2244+
const struct json_value_s *const value) {
2245+
struct json_string_s *string;
2246+
struct json_number_s *number;
2247+
struct json_object_s *object;
2248+
struct json_array_s *array;
2249+
struct json_value_s *new_value;
2250+
2251+
memcpy(state->dom, value, sizeof(struct json_value_s));
2252+
new_value = (struct json_value_s *)state->dom;
2253+
state->dom += sizeof(struct json_value_s);
2254+
new_value->payload = state->dom;
2255+
2256+
if (json_type_string == value->type) {
2257+
memcpy(state->dom, value->payload, sizeof(struct json_string_s));
2258+
string = (struct json_string_s *)state->dom;
2259+
state->dom += sizeof(struct json_string_s);
2260+
2261+
memcpy(state->data, string->string, string->string_size + 1);
2262+
string->string = state->data;
2263+
state->data += string->string_size + 1;
2264+
} else if (json_type_number == value->type) {
2265+
memcpy(state->dom, value->payload, sizeof(struct json_number_s));
2266+
number = (struct json_number_s *)state->dom;
2267+
state->dom += sizeof(struct json_number_s);
2268+
2269+
memcpy(state->data, number->number, number->number_size);
2270+
number->number = state->data;
2271+
state->data += number->number_size;
2272+
} else if (json_type_object == value->type) {
2273+
struct json_object_element_s *element;
2274+
size_t i;
2275+
2276+
memcpy(state->dom, value->payload, sizeof(struct json_object_s));
2277+
object = (struct json_object_s *)state->dom;
2278+
state->dom += sizeof(struct json_object_s);
2279+
2280+
element = object->start;
2281+
object->start = (struct json_object_element_s *)state->dom;
2282+
2283+
for (i = 0; i < object->length; i++) {
2284+
struct json_value_s *previous_value;
2285+
struct json_object_element_s *previous_element;
2286+
2287+
memcpy(state->dom, element, sizeof(struct json_object_element_s));
2288+
element = (struct json_object_element_s *)state->dom;
2289+
state->dom += sizeof(struct json_object_element_s);
2290+
2291+
string = element->name;
2292+
memcpy(state->dom, string, sizeof(struct json_string_s));
2293+
string = (struct json_string_s *)state->dom;
2294+
state->dom += sizeof(struct json_string_s);
2295+
element->name = string;
2296+
2297+
memcpy(state->data, string->string, string->string_size + 1);
2298+
string->string = state->data;
2299+
state->data += string->string_size + 1;
2300+
2301+
previous_value = element->value;
2302+
element->value = (struct json_value_s *)state->dom;
2303+
json_extract_copy_value(state, previous_value);
2304+
2305+
previous_element = element;
2306+
element = element->next;
2307+
2308+
if (element) {
2309+
previous_element->next = (struct json_object_element_s *)state->dom;
2310+
}
2311+
}
2312+
} else if (json_type_array == value->type) {
2313+
struct json_array_element_s *element;
2314+
size_t i;
2315+
2316+
memcpy(state->dom, value->payload, sizeof(struct json_array_s));
2317+
array = (struct json_array_s *)state->dom;
2318+
state->dom += sizeof(struct json_array_s);
2319+
2320+
element = array->start;
2321+
array->start = (struct json_array_element_s *)state->dom;
2322+
2323+
for (i = 0; i < array->length; i++) {
2324+
struct json_value_s *previous_value;
2325+
struct json_array_element_s *previous_element;
2326+
2327+
memcpy(state->dom, element, sizeof(struct json_array_element_s));
2328+
element = (struct json_array_element_s *)state->dom;
2329+
state->dom += sizeof(struct json_array_element_s);
2330+
2331+
previous_value = element->value;
2332+
element->value = (struct json_value_s *)state->dom;
2333+
json_extract_copy_value(state, previous_value);
2334+
2335+
previous_element = element;
2336+
element = element->next;
2337+
2338+
if (element) {
2339+
previous_element->next = (struct json_array_element_s *)state->dom;
2340+
}
2341+
}
2342+
}
2343+
}
2344+
2345+
struct json_value_s *json_extract_value_ex(const struct json_value_s *value,
2346+
void *(*alloc_func_ptr)(void *,
2347+
size_t),
2348+
void *user_data) {
2349+
void *allocation;
2350+
struct json_extract_result_s result;
2351+
struct json_extract_state_s state;
2352+
size_t total_size;
2353+
2354+
if (json_null == value) {
2355+
/* invalid value was null! */
2356+
return json_null;
2357+
}
2358+
2359+
result = json_extract_get_value_size(value);
2360+
total_size = result.dom_size + result.data_size;
2361+
2362+
if (json_null == alloc_func_ptr) {
2363+
allocation = malloc(total_size);
2364+
} else {
2365+
allocation = alloc_func_ptr(user_data, total_size);
2366+
}
2367+
2368+
state.dom = (char *)allocation;
2369+
state.data = state.dom + result.dom_size;
2370+
2371+
json_extract_copy_value(&state, value);
2372+
2373+
return (struct json_value_s *)allocation;
2374+
}
2375+
21042376
struct json_string_s *json_value_as_string(struct json_value_s *const value) {
21052377
if (value->type != json_type_string) {
21062378
return json_null;

test/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ add_executable(json_test
5757
allow_single_quoted_strings.c
5858
allow_trailing_comma.cpp
5959
allow_unquoted_keys.c
60+
extract.cpp
6061
main.cpp
6162
test.c
6263
test.cpp

0 commit comments

Comments
 (0)