Fossil SCM
latest cson_amalgamation. Fixes an obscure ref-counting discrepancy and cuts memory cost of cloning.
Commit
7830e2cc0f175cfc248d9e6aed82900dc1bf0adb
Parent
69d0dbf2f2cc7c7…
2 files changed
+76
-30
+45
-16
+76
-30
| --- src/cson_amalgamation.c | ||
| +++ src/cson_amalgamation.c | ||
| @@ -1427,11 +1427,11 @@ | ||
| 1427 | 1427 | /** |
| 1428 | 1428 | Type IDs corresponding to JavaScript/JSON types. |
| 1429 | 1429 | */ |
| 1430 | 1430 | enum cson_type_id { |
| 1431 | 1431 | /** |
| 1432 | - The special "null" value constant. | |
| 1432 | + The special "undefined" value constant. | |
| 1433 | 1433 | |
| 1434 | 1434 | Its value must be 0 for internal reasons. |
| 1435 | 1435 | */ |
| 1436 | 1436 | CSON_TYPE_UNDEF = 0, |
| 1437 | 1437 | /** |
| @@ -1597,10 +1597,18 @@ | ||
| 1597 | 1597 | static const cson_value cson_value_double_empty = { &cson_value_api_double, NULL, 0 }; |
| 1598 | 1598 | static const cson_value cson_value_string_empty = { &cson_value_api_string, NULL, 0 }; |
| 1599 | 1599 | static const cson_value cson_value_array_empty = { &cson_value_api_array, NULL, 0 }; |
| 1600 | 1600 | static const cson_value cson_value_object_empty = { &cson_value_api_object, NULL, 0 }; |
| 1601 | 1601 | |
| 1602 | +/** | |
| 1603 | + Strings are allocated as an instances of this class with N+1 | |
| 1604 | + trailing bytes, where N is the length of the string being | |
| 1605 | + allocated. To convert a cson_string to c-string we simply increment | |
| 1606 | + the cson_string pointer. To do the opposite we use (cstr - | |
| 1607 | + sizeof(cson_string)). Zero-length strings are a special case | |
| 1608 | + handled by a couple of the cson_string functions. | |
| 1609 | +*/ | |
| 1602 | 1610 | struct cson_string |
| 1603 | 1611 | { |
| 1604 | 1612 | unsigned int length; |
| 1605 | 1613 | }; |
| 1606 | 1614 | #define cson_string_empty_m {0/*length*/} |
| @@ -1668,11 +1676,11 @@ | ||
| 1668 | 1676 | { &cson_value_api_bool, &CSON_EMPTY_HOLDER.trueValue, 0 }, /* TRUE */ |
| 1669 | 1677 | { &cson_value_api_bool, NULL, 0 }, /* FALSE */ |
| 1670 | 1678 | { &cson_value_api_integer, NULL, 0 }, /* INT_0 */ |
| 1671 | 1679 | { &cson_value_api_double, NULL, 0 }, /* DBL_0 */ |
| 1672 | 1680 | { &cson_value_api_string, &CSON_EMPTY_HOLDER.stringValue, 0 }, /* STR_EMPTY */ |
| 1673 | -{ 0, NULL, 0 } | |
| 1681 | +{ NULL, NULL, 0 } | |
| 1674 | 1682 | }; |
| 1675 | 1683 | |
| 1676 | 1684 | |
| 1677 | 1685 | /** |
| 1678 | 1686 | Returns non-0 (true) if m is one of our special |
| @@ -1916,11 +1924,11 @@ | ||
| 1916 | 1924 | |
| 1917 | 1925 | cson_strings are supposed to be immutable, but this form provides |
| 1918 | 1926 | access to the immutable bits, which are v->length bytes long. A |
| 1919 | 1927 | length-0 string is returned as NULL from here, as opposed to |
| 1920 | 1928 | "". (This is a side-effect of the string allocation mechanism.) |
| 1921 | - Returns NULL if !v. | |
| 1929 | + Returns NULL if !v or if v is the internal empty-string singleton. | |
| 1922 | 1930 | */ |
| 1923 | 1931 | static char * cson_string_str(cson_string *v) |
| 1924 | 1932 | { |
| 1925 | 1933 | /* |
| 1926 | 1934 | See http://groups.google.com/group/comp.lang.c.moderated/browse_thread/thread/2e0c0df5e8a0cd6a |
| @@ -1948,11 +1956,14 @@ | ||
| 1948 | 1956 | See http://groups.google.com/group/comp.lang.c.moderated/browse_thread/thread/2e0c0df5e8a0cd6a |
| 1949 | 1957 | */ |
| 1950 | 1958 | #if 1 |
| 1951 | 1959 | if( ! v ) return NULL; |
| 1952 | 1960 | else if( v == &CSON_EMPTY_HOLDER.stringValue ) return ""; |
| 1953 | - else return (char *)((unsigned char *)(v+1)); | |
| 1961 | + else { | |
| 1962 | + assert((0 < v->length) && "How do we have a non-singleton empty string?"); | |
| 1963 | + return (char *)((unsigned char *)(v+1)); | |
| 1964 | + } | |
| 1954 | 1965 | #else |
| 1955 | 1966 | return (NULL == v) |
| 1956 | 1967 | ? NULL |
| 1957 | 1968 | : (v->length |
| 1958 | 1969 | ? (char const *) ((unsigned char const *)(v+1)) |
| @@ -2222,13 +2233,11 @@ | ||
| 2222 | 2233 | @see cson_value_new_integer() |
| 2223 | 2234 | @see cson_value_new_double() |
| 2224 | 2235 | @see cson_value_new_bool() |
| 2225 | 2236 | @see cson_value_free() |
| 2226 | 2237 | */ |
| 2227 | -static cson_value * cson_value_new(cson_type_id t, size_t extra); | |
| 2228 | - | |
| 2229 | -cson_value * cson_value_new(cson_type_id t, size_t extra) | |
| 2238 | +static cson_value * cson_value_new(cson_type_id t, size_t extra) | |
| 2230 | 2239 | { |
| 2231 | 2240 | static const size_t vsz = sizeof(cson_value); |
| 2232 | 2241 | const size_t sz = vsz + extra; |
| 2233 | 2242 | size_t tx = 0; |
| 2234 | 2243 | cson_value def = cson_value_undef; |
| @@ -3142,11 +3151,11 @@ | ||
| 3142 | 3151 | kvp->value = v; |
| 3143 | 3152 | } |
| 3144 | 3153 | return 0; |
| 3145 | 3154 | } |
| 3146 | 3155 | if( !obj->kvp.alloced || (obj->kvp.count == obj->kvp.alloced-1)) |
| 3147 | - { | |
| 3156 | + { /* reserve space */ | |
| 3148 | 3157 | unsigned int const n = obj->kvp.count ? (obj->kvp.count*2) : 6; |
| 3149 | 3158 | if( n > cson_kvp_list_reserve( &obj->kvp, n ) ) |
| 3150 | 3159 | { |
| 3151 | 3160 | return cson_rc.AllocError; |
| 3152 | 3161 | } |
| @@ -3281,10 +3290,12 @@ | ||
| 3281 | 3290 | { |
| 3282 | 3291 | cson_value_free(val); |
| 3283 | 3292 | return cson_rc.AllocError; |
| 3284 | 3293 | } |
| 3285 | 3294 | kvp->key = cson_string_value(p->ckey)/*transfer ownership*/; |
| 3295 | + assert(0 == kvp->key->refcount); | |
| 3296 | + cson_refcount_incr(kvp->key); | |
| 3286 | 3297 | p->ckey = NULL; |
| 3287 | 3298 | kvp->value = val; |
| 3288 | 3299 | cson_refcount_incr( val ); |
| 3289 | 3300 | rc = cson_kvp_list_append( &obj->kvp, kvp ); |
| 3290 | 3301 | if( 0 != rc ) |
| @@ -3361,11 +3372,11 @@ | ||
| 3361 | 3372 | ? cson_value_new_array() |
| 3362 | 3373 | : cson_value_new_object(); |
| 3363 | 3374 | if( ! obja ) |
| 3364 | 3375 | { |
| 3365 | 3376 | p->errNo = cson_rc.AllocError; |
| 3366 | - return 0; | |
| 3377 | + break; | |
| 3367 | 3378 | } |
| 3368 | 3379 | if( 0 != rc ) break; |
| 3369 | 3380 | if( ! p->root ) |
| 3370 | 3381 | { |
| 3371 | 3382 | p->root = p->node = obja; |
| @@ -3384,12 +3395,16 @@ | ||
| 3384 | 3395 | } |
| 3385 | 3396 | } |
| 3386 | 3397 | else |
| 3387 | 3398 | { |
| 3388 | 3399 | rc = cson_array_append( &p->stack, obja ); |
| 3389 | - if( 0 == rc ) rc = cson_parser_push_value( p, obja ); | |
| 3390 | - if( 0 == rc ) p->node = obja; | |
| 3400 | + if(rc) cson_value_free( obja ); | |
| 3401 | + else | |
| 3402 | + { | |
| 3403 | + rc = cson_parser_push_value( p, obja ); | |
| 3404 | + if( 0 == rc ) p->node = obja; | |
| 3405 | + } | |
| 3391 | 3406 | } |
| 3392 | 3407 | break; |
| 3393 | 3408 | } |
| 3394 | 3409 | case JSON_T_ARRAY_END: |
| 3395 | 3410 | case JSON_T_OBJECT_END: { |
| @@ -4578,10 +4593,39 @@ | ||
| 4578 | 4593 | cson_value * v = NULL; |
| 4579 | 4594 | cson_object_fetch_sub2( obj, &v, path ); |
| 4580 | 4595 | return v; |
| 4581 | 4596 | } |
| 4582 | 4597 | |
| 4598 | + | |
| 4599 | +/** | |
| 4600 | + If v is-a Object or Array then this function returns a deep | |
| 4601 | + clone, otherwise it returns v. In either case, the refcount | |
| 4602 | + of the returned value is increased by 1. | |
| 4603 | +*/ | |
| 4604 | +static cson_value * cson_value_clone_shared( cson_value * v ) | |
| 4605 | +{ | |
| 4606 | + cson_value * rc = NULL; | |
| 4607 | +#define TRY_SHARING 1 | |
| 4608 | +#if TRY_SHARING | |
| 4609 | + if(!v ) return rc; | |
| 4610 | + else if( cson_value_is_object(v) | |
| 4611 | + || cson_value_is_array(v)) | |
| 4612 | + { | |
| 4613 | + rc = cson_value_clone( v ); | |
| 4614 | + } | |
| 4615 | + else | |
| 4616 | + { | |
| 4617 | + rc = v; | |
| 4618 | + } | |
| 4619 | +#else | |
| 4620 | + rc = cson_value_clone(v); | |
| 4621 | +#endif | |
| 4622 | +#undef TRY_SHARING | |
| 4623 | + cson_value_add_reference(rc); | |
| 4624 | + return rc; | |
| 4625 | +} | |
| 4626 | + | |
| 4583 | 4627 | static cson_value * cson_value_clone_array( cson_value const * orig ) |
| 4584 | 4628 | { |
| 4585 | 4629 | unsigned int i = 0; |
| 4586 | 4630 | cson_array const * asrc = cson_value_get_array( orig ); |
| 4587 | 4631 | unsigned int alen = cson_array_length_get( asrc ); |
| @@ -4600,11 +4644,11 @@ | ||
| 4600 | 4644 | for( ; i < alen; ++i ) |
| 4601 | 4645 | { |
| 4602 | 4646 | cson_value * ch = cson_array_get( asrc, i ); |
| 4603 | 4647 | if( NULL != ch ) |
| 4604 | 4648 | { |
| 4605 | - cson_value * cl = cson_value_clone( ch ); | |
| 4649 | + cson_value * cl = cson_value_clone_shared( ch ); | |
| 4606 | 4650 | if( NULL == cl ) |
| 4607 | 4651 | { |
| 4608 | 4652 | cson_value_free( destV ); |
| 4609 | 4653 | return NULL; |
| 4610 | 4654 | } |
| @@ -4612,15 +4656,16 @@ | ||
| 4612 | 4656 | { |
| 4613 | 4657 | cson_value_free( cl ); |
| 4614 | 4658 | cson_value_free( destV ); |
| 4615 | 4659 | return NULL; |
| 4616 | 4660 | } |
| 4661 | + cson_value_free(cl)/*remove our artificial reference */; | |
| 4617 | 4662 | } |
| 4618 | 4663 | } |
| 4619 | 4664 | return destV; |
| 4620 | 4665 | } |
| 4621 | - | |
| 4666 | + | |
| 4622 | 4667 | static cson_value * cson_value_clone_object( cson_value const * orig ) |
| 4623 | 4668 | { |
| 4624 | 4669 | cson_object const * src = cson_value_get_object( orig ); |
| 4625 | 4670 | cson_value * destV = NULL; |
| 4626 | 4671 | cson_object * dest = NULL; |
| @@ -4639,32 +4684,33 @@ | ||
| 4639 | 4684 | cson_value_free( destV ); |
| 4640 | 4685 | return NULL; |
| 4641 | 4686 | } |
| 4642 | 4687 | while( (kvp = cson_object_iter_next( &iter )) ) |
| 4643 | 4688 | { |
| 4644 | - /* | |
| 4645 | - FIXME: refcount the keys! We first need a setter which takes | |
| 4646 | - a cson_string or cson_value key type. | |
| 4647 | - */ | |
| 4648 | 4689 | cson_value * key = NULL; |
| 4649 | 4690 | cson_value * val = NULL; |
| 4650 | - key = cson_value_clone(kvp->key); | |
| 4651 | - val = key ? cson_value_clone( kvp->value ) : NULL; | |
| 4691 | + assert( kvp->key && (kvp->key->refcount>0) ); | |
| 4692 | + key = cson_value_clone_shared(kvp->key); | |
| 4693 | + val = key ? cson_value_clone_shared(kvp->value) : NULL; | |
| 4652 | 4694 | if( ! key || !val ){ |
| 4653 | - cson_value_free(key); | |
| 4654 | - cson_value_free(val); | |
| 4655 | - cson_value_free(destV); | |
| 4656 | - return NULL; | |
| 4695 | + goto error; | |
| 4657 | 4696 | } |
| 4658 | 4697 | assert( CSON_STR(key) ); |
| 4659 | 4698 | if( 0 != cson_object_set_s( dest, CSON_STR(key), val ) ) |
| 4660 | 4699 | { |
| 4661 | - cson_value_free(key); | |
| 4662 | - cson_value_free(val); | |
| 4663 | - cson_value_free(destV); | |
| 4664 | - return NULL; | |
| 4700 | + goto error; | |
| 4665 | 4701 | } |
| 4702 | + /* remove our references */ | |
| 4703 | + cson_value_free(key); | |
| 4704 | + cson_value_free(val); | |
| 4705 | + continue; | |
| 4706 | + error: | |
| 4707 | + cson_value_free(key); | |
| 4708 | + cson_value_free(val); | |
| 4709 | + cson_value_free(destV); | |
| 4710 | + destV = NULL; | |
| 4711 | + break; | |
| 4666 | 4712 | } |
| 4667 | 4713 | return destV; |
| 4668 | 4714 | } |
| 4669 | 4715 | |
| 4670 | 4716 | cson_value * cson_value_clone( cson_value const * orig ) |
| @@ -4733,17 +4779,17 @@ | ||
| 4733 | 4779 | void cson_free_array(cson_array *x) |
| 4734 | 4780 | { |
| 4735 | 4781 | if(x) cson_value_free(cson_array_value(x)); |
| 4736 | 4782 | } |
| 4737 | 4783 | |
| 4738 | -void cson_free_string(cson_string const *x) | |
| 4784 | +void cson_free_string(cson_string *x) | |
| 4739 | 4785 | { |
| 4740 | 4786 | if(x) cson_value_free(cson_string_value(x)); |
| 4741 | 4787 | } |
| 4742 | 4788 | void cson_free_value(cson_value *x) |
| 4743 | 4789 | { |
| 4744 | - cson_value_free(x); | |
| 4790 | + if(x) cson_value_free(x); | |
| 4745 | 4791 | } |
| 4746 | 4792 | |
| 4747 | 4793 | |
| 4748 | 4794 | #if 0 |
| 4749 | 4795 | /* i'm not happy with this... */ |
| @@ -4907,11 +4953,11 @@ | ||
| 4907 | 4953 | assert( 0 && "Should have been caught by is-builtin check!" ); |
| 4908 | 4954 | break; |
| 4909 | 4955 | default: |
| 4910 | 4956 | assert(0 && "Invalid typeID!"); |
| 4911 | 4957 | return 0; |
| 4912 | - | |
| 4958 | +#undef RCCHECK | |
| 4913 | 4959 | } |
| 4914 | 4960 | return rc; |
| 4915 | 4961 | } |
| 4916 | 4962 | } |
| 4917 | 4963 | |
| 4918 | 4964 |
| --- src/cson_amalgamation.c | |
| +++ src/cson_amalgamation.c | |
| @@ -1427,11 +1427,11 @@ | |
| 1427 | /** |
| 1428 | Type IDs corresponding to JavaScript/JSON types. |
| 1429 | */ |
| 1430 | enum cson_type_id { |
| 1431 | /** |
| 1432 | The special "null" value constant. |
| 1433 | |
| 1434 | Its value must be 0 for internal reasons. |
| 1435 | */ |
| 1436 | CSON_TYPE_UNDEF = 0, |
| 1437 | /** |
| @@ -1597,10 +1597,18 @@ | |
| 1597 | static const cson_value cson_value_double_empty = { &cson_value_api_double, NULL, 0 }; |
| 1598 | static const cson_value cson_value_string_empty = { &cson_value_api_string, NULL, 0 }; |
| 1599 | static const cson_value cson_value_array_empty = { &cson_value_api_array, NULL, 0 }; |
| 1600 | static const cson_value cson_value_object_empty = { &cson_value_api_object, NULL, 0 }; |
| 1601 | |
| 1602 | struct cson_string |
| 1603 | { |
| 1604 | unsigned int length; |
| 1605 | }; |
| 1606 | #define cson_string_empty_m {0/*length*/} |
| @@ -1668,11 +1676,11 @@ | |
| 1668 | { &cson_value_api_bool, &CSON_EMPTY_HOLDER.trueValue, 0 }, /* TRUE */ |
| 1669 | { &cson_value_api_bool, NULL, 0 }, /* FALSE */ |
| 1670 | { &cson_value_api_integer, NULL, 0 }, /* INT_0 */ |
| 1671 | { &cson_value_api_double, NULL, 0 }, /* DBL_0 */ |
| 1672 | { &cson_value_api_string, &CSON_EMPTY_HOLDER.stringValue, 0 }, /* STR_EMPTY */ |
| 1673 | { 0, NULL, 0 } |
| 1674 | }; |
| 1675 | |
| 1676 | |
| 1677 | /** |
| 1678 | Returns non-0 (true) if m is one of our special |
| @@ -1916,11 +1924,11 @@ | |
| 1916 | |
| 1917 | cson_strings are supposed to be immutable, but this form provides |
| 1918 | access to the immutable bits, which are v->length bytes long. A |
| 1919 | length-0 string is returned as NULL from here, as opposed to |
| 1920 | "". (This is a side-effect of the string allocation mechanism.) |
| 1921 | Returns NULL if !v. |
| 1922 | */ |
| 1923 | static char * cson_string_str(cson_string *v) |
| 1924 | { |
| 1925 | /* |
| 1926 | See http://groups.google.com/group/comp.lang.c.moderated/browse_thread/thread/2e0c0df5e8a0cd6a |
| @@ -1948,11 +1956,14 @@ | |
| 1948 | See http://groups.google.com/group/comp.lang.c.moderated/browse_thread/thread/2e0c0df5e8a0cd6a |
| 1949 | */ |
| 1950 | #if 1 |
| 1951 | if( ! v ) return NULL; |
| 1952 | else if( v == &CSON_EMPTY_HOLDER.stringValue ) return ""; |
| 1953 | else return (char *)((unsigned char *)(v+1)); |
| 1954 | #else |
| 1955 | return (NULL == v) |
| 1956 | ? NULL |
| 1957 | : (v->length |
| 1958 | ? (char const *) ((unsigned char const *)(v+1)) |
| @@ -2222,13 +2233,11 @@ | |
| 2222 | @see cson_value_new_integer() |
| 2223 | @see cson_value_new_double() |
| 2224 | @see cson_value_new_bool() |
| 2225 | @see cson_value_free() |
| 2226 | */ |
| 2227 | static cson_value * cson_value_new(cson_type_id t, size_t extra); |
| 2228 | |
| 2229 | cson_value * cson_value_new(cson_type_id t, size_t extra) |
| 2230 | { |
| 2231 | static const size_t vsz = sizeof(cson_value); |
| 2232 | const size_t sz = vsz + extra; |
| 2233 | size_t tx = 0; |
| 2234 | cson_value def = cson_value_undef; |
| @@ -3142,11 +3151,11 @@ | |
| 3142 | kvp->value = v; |
| 3143 | } |
| 3144 | return 0; |
| 3145 | } |
| 3146 | if( !obj->kvp.alloced || (obj->kvp.count == obj->kvp.alloced-1)) |
| 3147 | { |
| 3148 | unsigned int const n = obj->kvp.count ? (obj->kvp.count*2) : 6; |
| 3149 | if( n > cson_kvp_list_reserve( &obj->kvp, n ) ) |
| 3150 | { |
| 3151 | return cson_rc.AllocError; |
| 3152 | } |
| @@ -3281,10 +3290,12 @@ | |
| 3281 | { |
| 3282 | cson_value_free(val); |
| 3283 | return cson_rc.AllocError; |
| 3284 | } |
| 3285 | kvp->key = cson_string_value(p->ckey)/*transfer ownership*/; |
| 3286 | p->ckey = NULL; |
| 3287 | kvp->value = val; |
| 3288 | cson_refcount_incr( val ); |
| 3289 | rc = cson_kvp_list_append( &obj->kvp, kvp ); |
| 3290 | if( 0 != rc ) |
| @@ -3361,11 +3372,11 @@ | |
| 3361 | ? cson_value_new_array() |
| 3362 | : cson_value_new_object(); |
| 3363 | if( ! obja ) |
| 3364 | { |
| 3365 | p->errNo = cson_rc.AllocError; |
| 3366 | return 0; |
| 3367 | } |
| 3368 | if( 0 != rc ) break; |
| 3369 | if( ! p->root ) |
| 3370 | { |
| 3371 | p->root = p->node = obja; |
| @@ -3384,12 +3395,16 @@ | |
| 3384 | } |
| 3385 | } |
| 3386 | else |
| 3387 | { |
| 3388 | rc = cson_array_append( &p->stack, obja ); |
| 3389 | if( 0 == rc ) rc = cson_parser_push_value( p, obja ); |
| 3390 | if( 0 == rc ) p->node = obja; |
| 3391 | } |
| 3392 | break; |
| 3393 | } |
| 3394 | case JSON_T_ARRAY_END: |
| 3395 | case JSON_T_OBJECT_END: { |
| @@ -4578,10 +4593,39 @@ | |
| 4578 | cson_value * v = NULL; |
| 4579 | cson_object_fetch_sub2( obj, &v, path ); |
| 4580 | return v; |
| 4581 | } |
| 4582 | |
| 4583 | static cson_value * cson_value_clone_array( cson_value const * orig ) |
| 4584 | { |
| 4585 | unsigned int i = 0; |
| 4586 | cson_array const * asrc = cson_value_get_array( orig ); |
| 4587 | unsigned int alen = cson_array_length_get( asrc ); |
| @@ -4600,11 +4644,11 @@ | |
| 4600 | for( ; i < alen; ++i ) |
| 4601 | { |
| 4602 | cson_value * ch = cson_array_get( asrc, i ); |
| 4603 | if( NULL != ch ) |
| 4604 | { |
| 4605 | cson_value * cl = cson_value_clone( ch ); |
| 4606 | if( NULL == cl ) |
| 4607 | { |
| 4608 | cson_value_free( destV ); |
| 4609 | return NULL; |
| 4610 | } |
| @@ -4612,15 +4656,16 @@ | |
| 4612 | { |
| 4613 | cson_value_free( cl ); |
| 4614 | cson_value_free( destV ); |
| 4615 | return NULL; |
| 4616 | } |
| 4617 | } |
| 4618 | } |
| 4619 | return destV; |
| 4620 | } |
| 4621 | |
| 4622 | static cson_value * cson_value_clone_object( cson_value const * orig ) |
| 4623 | { |
| 4624 | cson_object const * src = cson_value_get_object( orig ); |
| 4625 | cson_value * destV = NULL; |
| 4626 | cson_object * dest = NULL; |
| @@ -4639,32 +4684,33 @@ | |
| 4639 | cson_value_free( destV ); |
| 4640 | return NULL; |
| 4641 | } |
| 4642 | while( (kvp = cson_object_iter_next( &iter )) ) |
| 4643 | { |
| 4644 | /* |
| 4645 | FIXME: refcount the keys! We first need a setter which takes |
| 4646 | a cson_string or cson_value key type. |
| 4647 | */ |
| 4648 | cson_value * key = NULL; |
| 4649 | cson_value * val = NULL; |
| 4650 | key = cson_value_clone(kvp->key); |
| 4651 | val = key ? cson_value_clone( kvp->value ) : NULL; |
| 4652 | if( ! key || !val ){ |
| 4653 | cson_value_free(key); |
| 4654 | cson_value_free(val); |
| 4655 | cson_value_free(destV); |
| 4656 | return NULL; |
| 4657 | } |
| 4658 | assert( CSON_STR(key) ); |
| 4659 | if( 0 != cson_object_set_s( dest, CSON_STR(key), val ) ) |
| 4660 | { |
| 4661 | cson_value_free(key); |
| 4662 | cson_value_free(val); |
| 4663 | cson_value_free(destV); |
| 4664 | return NULL; |
| 4665 | } |
| 4666 | } |
| 4667 | return destV; |
| 4668 | } |
| 4669 | |
| 4670 | cson_value * cson_value_clone( cson_value const * orig ) |
| @@ -4733,17 +4779,17 @@ | |
| 4733 | void cson_free_array(cson_array *x) |
| 4734 | { |
| 4735 | if(x) cson_value_free(cson_array_value(x)); |
| 4736 | } |
| 4737 | |
| 4738 | void cson_free_string(cson_string const *x) |
| 4739 | { |
| 4740 | if(x) cson_value_free(cson_string_value(x)); |
| 4741 | } |
| 4742 | void cson_free_value(cson_value *x) |
| 4743 | { |
| 4744 | cson_value_free(x); |
| 4745 | } |
| 4746 | |
| 4747 | |
| 4748 | #if 0 |
| 4749 | /* i'm not happy with this... */ |
| @@ -4907,11 +4953,11 @@ | |
| 4907 | assert( 0 && "Should have been caught by is-builtin check!" ); |
| 4908 | break; |
| 4909 | default: |
| 4910 | assert(0 && "Invalid typeID!"); |
| 4911 | return 0; |
| 4912 | |
| 4913 | } |
| 4914 | return rc; |
| 4915 | } |
| 4916 | } |
| 4917 | |
| 4918 |
| --- src/cson_amalgamation.c | |
| +++ src/cson_amalgamation.c | |
| @@ -1427,11 +1427,11 @@ | |
| 1427 | /** |
| 1428 | Type IDs corresponding to JavaScript/JSON types. |
| 1429 | */ |
| 1430 | enum cson_type_id { |
| 1431 | /** |
| 1432 | The special "undefined" value constant. |
| 1433 | |
| 1434 | Its value must be 0 for internal reasons. |
| 1435 | */ |
| 1436 | CSON_TYPE_UNDEF = 0, |
| 1437 | /** |
| @@ -1597,10 +1597,18 @@ | |
| 1597 | static const cson_value cson_value_double_empty = { &cson_value_api_double, NULL, 0 }; |
| 1598 | static const cson_value cson_value_string_empty = { &cson_value_api_string, NULL, 0 }; |
| 1599 | static const cson_value cson_value_array_empty = { &cson_value_api_array, NULL, 0 }; |
| 1600 | static const cson_value cson_value_object_empty = { &cson_value_api_object, NULL, 0 }; |
| 1601 | |
| 1602 | /** |
| 1603 | Strings are allocated as an instances of this class with N+1 |
| 1604 | trailing bytes, where N is the length of the string being |
| 1605 | allocated. To convert a cson_string to c-string we simply increment |
| 1606 | the cson_string pointer. To do the opposite we use (cstr - |
| 1607 | sizeof(cson_string)). Zero-length strings are a special case |
| 1608 | handled by a couple of the cson_string functions. |
| 1609 | */ |
| 1610 | struct cson_string |
| 1611 | { |
| 1612 | unsigned int length; |
| 1613 | }; |
| 1614 | #define cson_string_empty_m {0/*length*/} |
| @@ -1668,11 +1676,11 @@ | |
| 1676 | { &cson_value_api_bool, &CSON_EMPTY_HOLDER.trueValue, 0 }, /* TRUE */ |
| 1677 | { &cson_value_api_bool, NULL, 0 }, /* FALSE */ |
| 1678 | { &cson_value_api_integer, NULL, 0 }, /* INT_0 */ |
| 1679 | { &cson_value_api_double, NULL, 0 }, /* DBL_0 */ |
| 1680 | { &cson_value_api_string, &CSON_EMPTY_HOLDER.stringValue, 0 }, /* STR_EMPTY */ |
| 1681 | { NULL, NULL, 0 } |
| 1682 | }; |
| 1683 | |
| 1684 | |
| 1685 | /** |
| 1686 | Returns non-0 (true) if m is one of our special |
| @@ -1916,11 +1924,11 @@ | |
| 1924 | |
| 1925 | cson_strings are supposed to be immutable, but this form provides |
| 1926 | access to the immutable bits, which are v->length bytes long. A |
| 1927 | length-0 string is returned as NULL from here, as opposed to |
| 1928 | "". (This is a side-effect of the string allocation mechanism.) |
| 1929 | Returns NULL if !v or if v is the internal empty-string singleton. |
| 1930 | */ |
| 1931 | static char * cson_string_str(cson_string *v) |
| 1932 | { |
| 1933 | /* |
| 1934 | See http://groups.google.com/group/comp.lang.c.moderated/browse_thread/thread/2e0c0df5e8a0cd6a |
| @@ -1948,11 +1956,14 @@ | |
| 1956 | See http://groups.google.com/group/comp.lang.c.moderated/browse_thread/thread/2e0c0df5e8a0cd6a |
| 1957 | */ |
| 1958 | #if 1 |
| 1959 | if( ! v ) return NULL; |
| 1960 | else if( v == &CSON_EMPTY_HOLDER.stringValue ) return ""; |
| 1961 | else { |
| 1962 | assert((0 < v->length) && "How do we have a non-singleton empty string?"); |
| 1963 | return (char *)((unsigned char *)(v+1)); |
| 1964 | } |
| 1965 | #else |
| 1966 | return (NULL == v) |
| 1967 | ? NULL |
| 1968 | : (v->length |
| 1969 | ? (char const *) ((unsigned char const *)(v+1)) |
| @@ -2222,13 +2233,11 @@ | |
| 2233 | @see cson_value_new_integer() |
| 2234 | @see cson_value_new_double() |
| 2235 | @see cson_value_new_bool() |
| 2236 | @see cson_value_free() |
| 2237 | */ |
| 2238 | static cson_value * cson_value_new(cson_type_id t, size_t extra) |
| 2239 | { |
| 2240 | static const size_t vsz = sizeof(cson_value); |
| 2241 | const size_t sz = vsz + extra; |
| 2242 | size_t tx = 0; |
| 2243 | cson_value def = cson_value_undef; |
| @@ -3142,11 +3151,11 @@ | |
| 3151 | kvp->value = v; |
| 3152 | } |
| 3153 | return 0; |
| 3154 | } |
| 3155 | if( !obj->kvp.alloced || (obj->kvp.count == obj->kvp.alloced-1)) |
| 3156 | { /* reserve space */ |
| 3157 | unsigned int const n = obj->kvp.count ? (obj->kvp.count*2) : 6; |
| 3158 | if( n > cson_kvp_list_reserve( &obj->kvp, n ) ) |
| 3159 | { |
| 3160 | return cson_rc.AllocError; |
| 3161 | } |
| @@ -3281,10 +3290,12 @@ | |
| 3290 | { |
| 3291 | cson_value_free(val); |
| 3292 | return cson_rc.AllocError; |
| 3293 | } |
| 3294 | kvp->key = cson_string_value(p->ckey)/*transfer ownership*/; |
| 3295 | assert(0 == kvp->key->refcount); |
| 3296 | cson_refcount_incr(kvp->key); |
| 3297 | p->ckey = NULL; |
| 3298 | kvp->value = val; |
| 3299 | cson_refcount_incr( val ); |
| 3300 | rc = cson_kvp_list_append( &obj->kvp, kvp ); |
| 3301 | if( 0 != rc ) |
| @@ -3361,11 +3372,11 @@ | |
| 3372 | ? cson_value_new_array() |
| 3373 | : cson_value_new_object(); |
| 3374 | if( ! obja ) |
| 3375 | { |
| 3376 | p->errNo = cson_rc.AllocError; |
| 3377 | break; |
| 3378 | } |
| 3379 | if( 0 != rc ) break; |
| 3380 | if( ! p->root ) |
| 3381 | { |
| 3382 | p->root = p->node = obja; |
| @@ -3384,12 +3395,16 @@ | |
| 3395 | } |
| 3396 | } |
| 3397 | else |
| 3398 | { |
| 3399 | rc = cson_array_append( &p->stack, obja ); |
| 3400 | if(rc) cson_value_free( obja ); |
| 3401 | else |
| 3402 | { |
| 3403 | rc = cson_parser_push_value( p, obja ); |
| 3404 | if( 0 == rc ) p->node = obja; |
| 3405 | } |
| 3406 | } |
| 3407 | break; |
| 3408 | } |
| 3409 | case JSON_T_ARRAY_END: |
| 3410 | case JSON_T_OBJECT_END: { |
| @@ -4578,10 +4593,39 @@ | |
| 4593 | cson_value * v = NULL; |
| 4594 | cson_object_fetch_sub2( obj, &v, path ); |
| 4595 | return v; |
| 4596 | } |
| 4597 | |
| 4598 | |
| 4599 | /** |
| 4600 | If v is-a Object or Array then this function returns a deep |
| 4601 | clone, otherwise it returns v. In either case, the refcount |
| 4602 | of the returned value is increased by 1. |
| 4603 | */ |
| 4604 | static cson_value * cson_value_clone_shared( cson_value * v ) |
| 4605 | { |
| 4606 | cson_value * rc = NULL; |
| 4607 | #define TRY_SHARING 1 |
| 4608 | #if TRY_SHARING |
| 4609 | if(!v ) return rc; |
| 4610 | else if( cson_value_is_object(v) |
| 4611 | || cson_value_is_array(v)) |
| 4612 | { |
| 4613 | rc = cson_value_clone( v ); |
| 4614 | } |
| 4615 | else |
| 4616 | { |
| 4617 | rc = v; |
| 4618 | } |
| 4619 | #else |
| 4620 | rc = cson_value_clone(v); |
| 4621 | #endif |
| 4622 | #undef TRY_SHARING |
| 4623 | cson_value_add_reference(rc); |
| 4624 | return rc; |
| 4625 | } |
| 4626 | |
| 4627 | static cson_value * cson_value_clone_array( cson_value const * orig ) |
| 4628 | { |
| 4629 | unsigned int i = 0; |
| 4630 | cson_array const * asrc = cson_value_get_array( orig ); |
| 4631 | unsigned int alen = cson_array_length_get( asrc ); |
| @@ -4600,11 +4644,11 @@ | |
| 4644 | for( ; i < alen; ++i ) |
| 4645 | { |
| 4646 | cson_value * ch = cson_array_get( asrc, i ); |
| 4647 | if( NULL != ch ) |
| 4648 | { |
| 4649 | cson_value * cl = cson_value_clone_shared( ch ); |
| 4650 | if( NULL == cl ) |
| 4651 | { |
| 4652 | cson_value_free( destV ); |
| 4653 | return NULL; |
| 4654 | } |
| @@ -4612,15 +4656,16 @@ | |
| 4656 | { |
| 4657 | cson_value_free( cl ); |
| 4658 | cson_value_free( destV ); |
| 4659 | return NULL; |
| 4660 | } |
| 4661 | cson_value_free(cl)/*remove our artificial reference */; |
| 4662 | } |
| 4663 | } |
| 4664 | return destV; |
| 4665 | } |
| 4666 | |
| 4667 | static cson_value * cson_value_clone_object( cson_value const * orig ) |
| 4668 | { |
| 4669 | cson_object const * src = cson_value_get_object( orig ); |
| 4670 | cson_value * destV = NULL; |
| 4671 | cson_object * dest = NULL; |
| @@ -4639,32 +4684,33 @@ | |
| 4684 | cson_value_free( destV ); |
| 4685 | return NULL; |
| 4686 | } |
| 4687 | while( (kvp = cson_object_iter_next( &iter )) ) |
| 4688 | { |
| 4689 | cson_value * key = NULL; |
| 4690 | cson_value * val = NULL; |
| 4691 | assert( kvp->key && (kvp->key->refcount>0) ); |
| 4692 | key = cson_value_clone_shared(kvp->key); |
| 4693 | val = key ? cson_value_clone_shared(kvp->value) : NULL; |
| 4694 | if( ! key || !val ){ |
| 4695 | goto error; |
| 4696 | } |
| 4697 | assert( CSON_STR(key) ); |
| 4698 | if( 0 != cson_object_set_s( dest, CSON_STR(key), val ) ) |
| 4699 | { |
| 4700 | goto error; |
| 4701 | } |
| 4702 | /* remove our references */ |
| 4703 | cson_value_free(key); |
| 4704 | cson_value_free(val); |
| 4705 | continue; |
| 4706 | error: |
| 4707 | cson_value_free(key); |
| 4708 | cson_value_free(val); |
| 4709 | cson_value_free(destV); |
| 4710 | destV = NULL; |
| 4711 | break; |
| 4712 | } |
| 4713 | return destV; |
| 4714 | } |
| 4715 | |
| 4716 | cson_value * cson_value_clone( cson_value const * orig ) |
| @@ -4733,17 +4779,17 @@ | |
| 4779 | void cson_free_array(cson_array *x) |
| 4780 | { |
| 4781 | if(x) cson_value_free(cson_array_value(x)); |
| 4782 | } |
| 4783 | |
| 4784 | void cson_free_string(cson_string *x) |
| 4785 | { |
| 4786 | if(x) cson_value_free(cson_string_value(x)); |
| 4787 | } |
| 4788 | void cson_free_value(cson_value *x) |
| 4789 | { |
| 4790 | if(x) cson_value_free(x); |
| 4791 | } |
| 4792 | |
| 4793 | |
| 4794 | #if 0 |
| 4795 | /* i'm not happy with this... */ |
| @@ -4907,11 +4953,11 @@ | |
| 4953 | assert( 0 && "Should have been caught by is-builtin check!" ); |
| 4954 | break; |
| 4955 | default: |
| 4956 | assert(0 && "Invalid typeID!"); |
| 4957 | return 0; |
| 4958 | #undef RCCHECK |
| 4959 | } |
| 4960 | return rc; |
| 4961 | } |
| 4962 | } |
| 4963 | |
| 4964 |
+45
-16
| --- src/cson_amalgamation.h | ||
| +++ src/cson_amalgamation.h | ||
| @@ -928,11 +928,12 @@ | ||
| 928 | 928 | /** |
| 929 | 929 | Returns a pointer to the NULL-terminated string bytes of str. |
| 930 | 930 | The bytes are owned by string and will be invalided when it |
| 931 | 931 | is cleaned up. |
| 932 | 932 | |
| 933 | - If str is NULL then NULL is returned. | |
| 933 | + If str is NULL then NULL is returned. If the string has a length | |
| 934 | + of 0 then "" is returned. | |
| 934 | 935 | |
| 935 | 936 | @see cson_string_length_bytes() |
| 936 | 937 | @see cson_value_get_string() |
| 937 | 938 | */ |
| 938 | 939 | char const * cson_string_cstr( cson_string const * str ); |
| @@ -1261,11 +1262,11 @@ | ||
| 1261 | 1262 | void cson_free_array(cson_array *x); |
| 1262 | 1263 | |
| 1263 | 1264 | /** |
| 1264 | 1265 | Equivalent to cson_value_free(cson_string_value(x)). |
| 1265 | 1266 | */ |
| 1266 | -void cson_free_string(cson_string const *x); | |
| 1267 | +void cson_free_string(cson_string *x); | |
| 1267 | 1268 | |
| 1268 | 1269 | |
| 1269 | 1270 | /** |
| 1270 | 1271 | Allocates a new "array" value and transfers ownership of it to the |
| 1271 | 1272 | caller. It must eventually be destroyed, by the caller or its |
| @@ -2037,14 +2038,14 @@ | ||
| 2037 | 2038 | |
| 2038 | 2039 | /** |
| 2039 | 2040 | Deeply copies a JSON value, be it an object/array or a "plain" |
| 2040 | 2041 | value (e.g. number/string/boolean). If cv is not NULL then this |
| 2041 | 2042 | function makes a deep clone of it and returns that clone. Ownership |
| 2042 | - of the clone is transfered to the caller, who must eventually free | |
| 2043 | - the value using cson_value_free() or add it to a container | |
| 2044 | - object/array to transfer ownership to the container. The returned | |
| 2045 | - object will be of the same logical type as orig. | |
| 2043 | + of the clone is identical t transfered to the caller, who must | |
| 2044 | + eventually free the value using cson_value_free() or add it to a | |
| 2045 | + container object/array to transfer ownership to the container. The | |
| 2046 | + returned object will be of the same logical type as orig. | |
| 2046 | 2047 | |
| 2047 | 2048 | ACHTUNG: if orig contains any cyclic references at any depth level |
| 2048 | 2049 | this function will endlessly recurse. (Having _any_ cyclic |
| 2049 | 2050 | references violates this library's requirements.) |
| 2050 | 2051 | |
| @@ -2051,10 +2052,36 @@ | ||
| 2051 | 2052 | Returns NULL if orig is NULL or if cloning fails. Assuming that |
| 2052 | 2053 | orig is in a valid state, the only "likely" error case is that an |
| 2053 | 2054 | allocation fails while constructing the clone. In other words, if |
| 2054 | 2055 | cloning fails due to something other than an allocation error then |
| 2055 | 2056 | either orig is in an invalid state or there is a bug. |
| 2057 | + | |
| 2058 | + When this function clones Objects or Arrays it shares any immutable | |
| 2059 | + values (including object keys) between the parent and the | |
| 2060 | + clone. Mutable values (Objects and Arrays) are copied, however. | |
| 2061 | + For example, if we clone: | |
| 2062 | + | |
| 2063 | + @code | |
| 2064 | + { a: 1, b: 2, c:["hi"] } | |
| 2065 | + @endcode | |
| 2066 | + | |
| 2067 | + The cloned object and the array "c" would be a new Object/Array | |
| 2068 | + instances but the object keys (a,b,b) and the values of (a,b), as | |
| 2069 | + well as the string value within the "c" array, would be shared | |
| 2070 | + between the original and the clone. The "c" array itself would be | |
| 2071 | + deeply cloned, such that future changes to the clone are not | |
| 2072 | + visible to the parent, and vice versa, but immutable values within | |
| 2073 | + the array are shared (in this case the string "hi"). The | |
| 2074 | + justification for this heuristic is that immutable values can never | |
| 2075 | + be changed, so there is no harm in sharing them across | |
| 2076 | + clones. Additionally, such types can never contribute to cycles in | |
| 2077 | + a JSON tree, so they are safe to share this way. Objects and | |
| 2078 | + Arrays, on the other hand, can be modified later and can contribute | |
| 2079 | + to cycles, and thus the clone needs to be an independent instance. | |
| 2080 | + Note, however, that if this function directly passed a | |
| 2081 | + non-Object/Array, that value is deeply cloned. The sharing | |
| 2082 | + behaviour only applies when traversing Objects/Arrays. | |
| 2056 | 2083 | */ |
| 2057 | 2084 | cson_value * cson_value_clone( cson_value const * orig ); |
| 2058 | 2085 | |
| 2059 | 2086 | /** |
| 2060 | 2087 | Returns the value handle associated with s. The handle itself owns |
| @@ -2062,11 +2089,12 @@ | ||
| 2062 | 2089 | function. If the returned handle is part of a container, calling |
| 2063 | 2090 | cson_value_free() on the returned handle invoked undefined |
| 2064 | 2091 | behaviour (quite possibly downstream when the container tries to |
| 2065 | 2092 | use it). |
| 2066 | 2093 | |
| 2067 | - This function only returns NULL if s. is NULL. | |
| 2094 | + This function only returns NULL if s is NULL. The length of the | |
| 2095 | + returned string is cson_string_length_bytes(). | |
| 2068 | 2096 | */ |
| 2069 | 2097 | cson_value * cson_string_value(cson_string const * s); |
| 2070 | 2098 | /** |
| 2071 | 2099 | The Object form of cson_string_value(). See that function |
| 2072 | 2100 | for full details. |
| @@ -2079,19 +2107,20 @@ | ||
| 2079 | 2107 | */ |
| 2080 | 2108 | cson_value * cson_array_value(cson_array const * s); |
| 2081 | 2109 | |
| 2082 | 2110 | |
| 2083 | 2111 | /** |
| 2084 | - Calculates the in-memory-allocated size of v, recursively if it is | |
| 2085 | - a container type, with the following caveats and limitations: | |
| 2086 | - | |
| 2087 | - If a given value is reference counted and multiple times within a | |
| 2088 | - traversed container, each reference is counted at full cost. We | |
| 2089 | - have no what of knowing if a given reference has been visited | |
| 2090 | - already and whether it should or should not be counted, so we | |
| 2091 | - pessimistically count them even though the _might_ not really count | |
| 2092 | - for the given object tree (it depends on where the other open | |
| 2112 | + Calculates the approximate in-memory-allocated size of v, | |
| 2113 | + recursively if it is a container type, with the following caveats | |
| 2114 | + and limitations: | |
| 2115 | + | |
| 2116 | + If a given value is reference counted then it is only and multiple | |
| 2117 | + times within a traversed container, each reference is counted at | |
| 2118 | + full cost. We have no way of knowing if a given reference has been | |
| 2119 | + visited already and whether it should or should not be counted, so | |
| 2120 | + we pessimistically count them even though the _might_ not really | |
| 2121 | + count for the given object tree (it depends on where the other open | |
| 2093 | 2122 | references live). |
| 2094 | 2123 | |
| 2095 | 2124 | This function returns 0 if any of the following are true: |
| 2096 | 2125 | |
| 2097 | 2126 | - v is NULL |
| 2098 | 2127 |
| --- src/cson_amalgamation.h | |
| +++ src/cson_amalgamation.h | |
| @@ -928,11 +928,12 @@ | |
| 928 | /** |
| 929 | Returns a pointer to the NULL-terminated string bytes of str. |
| 930 | The bytes are owned by string and will be invalided when it |
| 931 | is cleaned up. |
| 932 | |
| 933 | If str is NULL then NULL is returned. |
| 934 | |
| 935 | @see cson_string_length_bytes() |
| 936 | @see cson_value_get_string() |
| 937 | */ |
| 938 | char const * cson_string_cstr( cson_string const * str ); |
| @@ -1261,11 +1262,11 @@ | |
| 1261 | void cson_free_array(cson_array *x); |
| 1262 | |
| 1263 | /** |
| 1264 | Equivalent to cson_value_free(cson_string_value(x)). |
| 1265 | */ |
| 1266 | void cson_free_string(cson_string const *x); |
| 1267 | |
| 1268 | |
| 1269 | /** |
| 1270 | Allocates a new "array" value and transfers ownership of it to the |
| 1271 | caller. It must eventually be destroyed, by the caller or its |
| @@ -2037,14 +2038,14 @@ | |
| 2037 | |
| 2038 | /** |
| 2039 | Deeply copies a JSON value, be it an object/array or a "plain" |
| 2040 | value (e.g. number/string/boolean). If cv is not NULL then this |
| 2041 | function makes a deep clone of it and returns that clone. Ownership |
| 2042 | of the clone is transfered to the caller, who must eventually free |
| 2043 | the value using cson_value_free() or add it to a container |
| 2044 | object/array to transfer ownership to the container. The returned |
| 2045 | object will be of the same logical type as orig. |
| 2046 | |
| 2047 | ACHTUNG: if orig contains any cyclic references at any depth level |
| 2048 | this function will endlessly recurse. (Having _any_ cyclic |
| 2049 | references violates this library's requirements.) |
| 2050 | |
| @@ -2051,10 +2052,36 @@ | |
| 2051 | Returns NULL if orig is NULL or if cloning fails. Assuming that |
| 2052 | orig is in a valid state, the only "likely" error case is that an |
| 2053 | allocation fails while constructing the clone. In other words, if |
| 2054 | cloning fails due to something other than an allocation error then |
| 2055 | either orig is in an invalid state or there is a bug. |
| 2056 | */ |
| 2057 | cson_value * cson_value_clone( cson_value const * orig ); |
| 2058 | |
| 2059 | /** |
| 2060 | Returns the value handle associated with s. The handle itself owns |
| @@ -2062,11 +2089,12 @@ | |
| 2062 | function. If the returned handle is part of a container, calling |
| 2063 | cson_value_free() on the returned handle invoked undefined |
| 2064 | behaviour (quite possibly downstream when the container tries to |
| 2065 | use it). |
| 2066 | |
| 2067 | This function only returns NULL if s. is NULL. |
| 2068 | */ |
| 2069 | cson_value * cson_string_value(cson_string const * s); |
| 2070 | /** |
| 2071 | The Object form of cson_string_value(). See that function |
| 2072 | for full details. |
| @@ -2079,19 +2107,20 @@ | |
| 2079 | */ |
| 2080 | cson_value * cson_array_value(cson_array const * s); |
| 2081 | |
| 2082 | |
| 2083 | /** |
| 2084 | Calculates the in-memory-allocated size of v, recursively if it is |
| 2085 | a container type, with the following caveats and limitations: |
| 2086 | |
| 2087 | If a given value is reference counted and multiple times within a |
| 2088 | traversed container, each reference is counted at full cost. We |
| 2089 | have no what of knowing if a given reference has been visited |
| 2090 | already and whether it should or should not be counted, so we |
| 2091 | pessimistically count them even though the _might_ not really count |
| 2092 | for the given object tree (it depends on where the other open |
| 2093 | references live). |
| 2094 | |
| 2095 | This function returns 0 if any of the following are true: |
| 2096 | |
| 2097 | - v is NULL |
| 2098 |
| --- src/cson_amalgamation.h | |
| +++ src/cson_amalgamation.h | |
| @@ -928,11 +928,12 @@ | |
| 928 | /** |
| 929 | Returns a pointer to the NULL-terminated string bytes of str. |
| 930 | The bytes are owned by string and will be invalided when it |
| 931 | is cleaned up. |
| 932 | |
| 933 | If str is NULL then NULL is returned. If the string has a length |
| 934 | of 0 then "" is returned. |
| 935 | |
| 936 | @see cson_string_length_bytes() |
| 937 | @see cson_value_get_string() |
| 938 | */ |
| 939 | char const * cson_string_cstr( cson_string const * str ); |
| @@ -1261,11 +1262,11 @@ | |
| 1262 | void cson_free_array(cson_array *x); |
| 1263 | |
| 1264 | /** |
| 1265 | Equivalent to cson_value_free(cson_string_value(x)). |
| 1266 | */ |
| 1267 | void cson_free_string(cson_string *x); |
| 1268 | |
| 1269 | |
| 1270 | /** |
| 1271 | Allocates a new "array" value and transfers ownership of it to the |
| 1272 | caller. It must eventually be destroyed, by the caller or its |
| @@ -2037,14 +2038,14 @@ | |
| 2038 | |
| 2039 | /** |
| 2040 | Deeply copies a JSON value, be it an object/array or a "plain" |
| 2041 | value (e.g. number/string/boolean). If cv is not NULL then this |
| 2042 | function makes a deep clone of it and returns that clone. Ownership |
| 2043 | of the clone is identical t transfered to the caller, who must |
| 2044 | eventually free the value using cson_value_free() or add it to a |
| 2045 | container object/array to transfer ownership to the container. The |
| 2046 | returned object will be of the same logical type as orig. |
| 2047 | |
| 2048 | ACHTUNG: if orig contains any cyclic references at any depth level |
| 2049 | this function will endlessly recurse. (Having _any_ cyclic |
| 2050 | references violates this library's requirements.) |
| 2051 | |
| @@ -2051,10 +2052,36 @@ | |
| 2052 | Returns NULL if orig is NULL or if cloning fails. Assuming that |
| 2053 | orig is in a valid state, the only "likely" error case is that an |
| 2054 | allocation fails while constructing the clone. In other words, if |
| 2055 | cloning fails due to something other than an allocation error then |
| 2056 | either orig is in an invalid state or there is a bug. |
| 2057 | |
| 2058 | When this function clones Objects or Arrays it shares any immutable |
| 2059 | values (including object keys) between the parent and the |
| 2060 | clone. Mutable values (Objects and Arrays) are copied, however. |
| 2061 | For example, if we clone: |
| 2062 | |
| 2063 | @code |
| 2064 | { a: 1, b: 2, c:["hi"] } |
| 2065 | @endcode |
| 2066 | |
| 2067 | The cloned object and the array "c" would be a new Object/Array |
| 2068 | instances but the object keys (a,b,b) and the values of (a,b), as |
| 2069 | well as the string value within the "c" array, would be shared |
| 2070 | between the original and the clone. The "c" array itself would be |
| 2071 | deeply cloned, such that future changes to the clone are not |
| 2072 | visible to the parent, and vice versa, but immutable values within |
| 2073 | the array are shared (in this case the string "hi"). The |
| 2074 | justification for this heuristic is that immutable values can never |
| 2075 | be changed, so there is no harm in sharing them across |
| 2076 | clones. Additionally, such types can never contribute to cycles in |
| 2077 | a JSON tree, so they are safe to share this way. Objects and |
| 2078 | Arrays, on the other hand, can be modified later and can contribute |
| 2079 | to cycles, and thus the clone needs to be an independent instance. |
| 2080 | Note, however, that if this function directly passed a |
| 2081 | non-Object/Array, that value is deeply cloned. The sharing |
| 2082 | behaviour only applies when traversing Objects/Arrays. |
| 2083 | */ |
| 2084 | cson_value * cson_value_clone( cson_value const * orig ); |
| 2085 | |
| 2086 | /** |
| 2087 | Returns the value handle associated with s. The handle itself owns |
| @@ -2062,11 +2089,12 @@ | |
| 2089 | function. If the returned handle is part of a container, calling |
| 2090 | cson_value_free() on the returned handle invoked undefined |
| 2091 | behaviour (quite possibly downstream when the container tries to |
| 2092 | use it). |
| 2093 | |
| 2094 | This function only returns NULL if s is NULL. The length of the |
| 2095 | returned string is cson_string_length_bytes(). |
| 2096 | */ |
| 2097 | cson_value * cson_string_value(cson_string const * s); |
| 2098 | /** |
| 2099 | The Object form of cson_string_value(). See that function |
| 2100 | for full details. |
| @@ -2079,19 +2107,20 @@ | |
| 2107 | */ |
| 2108 | cson_value * cson_array_value(cson_array const * s); |
| 2109 | |
| 2110 | |
| 2111 | /** |
| 2112 | Calculates the approximate in-memory-allocated size of v, |
| 2113 | recursively if it is a container type, with the following caveats |
| 2114 | and limitations: |
| 2115 | |
| 2116 | If a given value is reference counted then it is only and multiple |
| 2117 | times within a traversed container, each reference is counted at |
| 2118 | full cost. We have no way of knowing if a given reference has been |
| 2119 | visited already and whether it should or should not be counted, so |
| 2120 | we pessimistically count them even though the _might_ not really |
| 2121 | count for the given object tree (it depends on where the other open |
| 2122 | references live). |
| 2123 | |
| 2124 | This function returns 0 if any of the following are true: |
| 2125 | |
| 2126 | - v is NULL |
| 2127 |