Maximum container nesting accepted by sig_value_dup, the JSON
serializer and the JSON parser. Builders are not capped (a sig_ht
cannot carry a depth), and sig_value_dtor recurses without a limit.
Shallow copy: for string/bytes kinds, src and dest end up pointing at
the SAME underlying buffer (its refcount is bumped). Containers
(map/list) are shared by refcount too; a shared container is frozen
(immutable) while shared -- mutators return SIG_ERR_STATE -- so
copying a container freezes the original until the other handles are
released or the value is sig_value_dup'd. Both sig_values
must be independently dtor'd. The payload is immutable and its
refcount is atomic, so a value may safely be sig_value_copy'd across
threads and each thread's copy used/dtor'd concurrently.
dest is overwritten unconditionally. If dest already holds an owned
string/bytes value, that value's buffer is leaked -- destroy dest
first if it may already own something.
Deep copy: for string/bytes kinds, dest gets its own independently
allocated buffer with no state shared with src. Contrast with
sig_value_copy(), which shares the buffer via refcount. For map/list
it is a recursive deep copy producing a unique, mutable tree, and
returns SIG_ERR_INVALID_ARG if nesting exceeds SIG_VALUE_MAX_DEPTH.
dest is overwritten unconditionally; the same leak caveat as
sig_value_copy() applies if dest already owns a value.
Heap-allocates a sig_value and copies *v into it via sig_value_copy
(shares the underlying string/bytes buffer via refcount -- boxing
only relocates the small by-value wrapper onto the heap, it does
not deep-duplicate the payload). Boxing a container shares it
(refcount) and so freezes it, like sig_value_copy. The returned pointer is suitable
for storing in a void*-payload container (sig_ht/sig_ll) alongside
sig_value_dtor_v as the destructor.
sig_value_dtor_v
void sig_value_dtor_v(void * v)
Parameter
Type
v
void *
Destructor shaped exactly like sig_ht's/sig_ll's sig_dtor_fn
(void (*)(void*)) -- pass this directly as a container's destructor
with no function-pointer cast. Destroys the boxed value via
sig_value_dtor() and frees the box itself. Only call on a pointer
previously returned by sig_value_box().
out is a borrowed pointer into v's own buffer: valid only for v's
lifetime, not owned by the caller, and must not be freed or used
after v is passed to sig_value_dtor().
out is a borrowed pointer into v's own buffer: valid only for v's
lifetime, not owned by the caller, and must not be freed or used
after v is passed to sig_value_dtor().
---- Containers ---------------------------------------------------
A map or list value owns one reference to a sig_ht / sig_ll whose
elements are boxed sig_values (dtor = sig_value_dtor_v). Sharing
follows the other refcounted kinds: sig_value_copy shares the
container, sig_value_dup deep-copies the whole tree into a new,
uniquely owned (mutable) one.
Mutation (put/remove/append) is allowed only while the container is
uniquely owned (its refcount is 1). Once a container has been
shared with sig_value_copy it is frozen: mutators return
SIG_ERR_STATE until the other handles are released with
sig_value_dtor -- or you sig_value_dup it to get a mutable copy.
Release your handle (sig_value_dtor) as soon as you have handed a
value off, so containers do not stay frozen longer than necessary;
sig_value_is_shared() is there for assertions and debugging.
Like sig_ht/sig_ll themselves, a container must be mutated by one
thread at a time; no locking is added.
put/append MOVE *child into the container: on success *child is
reset to int 0 (so a later sig_value_dtor(child) is harmless); on any
failure *child is untouched and the caller still owns it. put on an
existing key replaces (and destroys) the old value. Errors: NULL
argument, or child is the container itself -> SIG_ERR_INVALID_ARG;
wrong kind -> SIG_ERR_TYPE_MISMATCH; container shared ->
SIG_ERR_STATE; allocation failure -> SIG_ERR_ALLOC.
put/append MOVE *child into the container: on success *child is
reset to int 0 (so a later sig_value_dtor(child) is harmless); on any
failure *child is untouched and the caller still owns it. put on an
existing key replaces (and destroys) the old value. Errors: NULL
argument, or child is the container itself -> SIG_ERR_INVALID_ARG;
wrong kind -> SIG_ERR_TYPE_MISMATCH; container shared ->
SIG_ERR_STATE; allocation failure -> SIG_ERR_ALLOC.
out is a borrowed pointer valid until the container is mutated or
destroyed. SIG_ERR_NOT_FOUND if key is absent / index >= count.
list_get is O(n). Work on shared containers too.
Children reachable through a shared parent are protected only by
const: callers must not cast away const to mutate them. Handles must
come from sig_value_copy (a bitwise copy of a container sig_value is
a misuse).
out is a borrowed pointer valid until the container is mutated or
destroyed. SIG_ERR_NOT_FOUND if key is absent / index >= count.
list_get is O(n). Work on shared containers too.
Children reachable through a shared parent are protected only by
const: callers must not cast away const to mutate them. Handles must
come from sig_value_copy (a bitwise copy of a container sig_value is
a misuse).
sig_value_count
size_t sig_value_count(const sig_value * v)
Parameter
Type
v
const sig_value *
Element count of a map or list; 0 for NULL and for other kinds.
Map walk order is hash order (unspecified); list walk is in append
order. A non-SIG_OK return from fn stops the walk and is returned.
`value` is borrowed for the duration of the callback.
Map walk order is hash order (unspecified); list walk is in append
order. A non-SIG_OK return from fn stops the walk and is returned.
`value` is borrowed for the duration of the callback.
JSON Merge Patch (RFC 7386) over values, without null-deletion (a
sig_value has no null). *out is a new, uniquely owned value; target
and patch are not modified. A non-map patch yields a deep copy of
the patch; a map patch yields a copy of target (an empty map if
target is not a map) with every patch key merged in -- a map patch
value merges recursively into an existing map value, anything else
replaces. SIG_ERR_INVALID_ARG for NULL arguments, SIG_ERR_ALLOC on
allocation failure (*out untouched).
sig_value_is_shared
bool sig_value_is_shared(const sig_value * v)
Parameter
Type
v
const sig_value *
True if v's refcounted payload (string, bytes, map or list) has more
than one reference. False for NULL and for inline kinds.