Compound strings
Overview
Compound strings are an extension to ordinary TE dynamic strings that can hold one-dimensional arrays of string and key-value pairs. More…
// typedefs typedef enum te_compound_kind te_compound_kind; typedef enum te_compound_mod_op te_compound_mod_op; typedef te_errno te_compound_iter_fn( char *key, size_t idx, char *value, bool has_more, void *user ); // enums enum te_compound_kind; enum te_compound_mod_op; // global functions te_compound_kind te_compound_classify(const te_string* comp); bool te_compound_validate(const te_string* comp); bool te_compound_validate_str(const char* comp); bool te_compound_extract(te_string* dst, const te_string* comp, const char* key, size_t idx); size_t te_compound_count(const te_string* comp, const char* key); void te_compound_set_va(te_string* comp, const char* key, te_compound_mod_op mod_op, const char* val_fmt, va_list args); void te_compound_set(te_string* comp, const char* key, te_compound_mod_op mod_op, const char* val_fmt, ...); static void static void te_compound_append_fast(te_string* comp, const char* key, const char* value); void te_compound_merge(te_string* dst, const te_string* src, te_compound_mod_op mod_op); te_errno te_compound_iterate(const te_string* src, te_compound_iter_fn* callback, void* user); te_errno te_compound_iterate_str(const char* src, te_compound_iter_fn* callback, void* user); void te_vec2compound(te_string* dst, const te_vec* vec); void te_kvpair2compound(te_string* dst, const te_kvpair_h* kv); void te_compound2vec(te_vec* dst, const te_string* compound); void te_compound2kvpair(te_kvpair_h* dst, const te_string* compound); void te_json_add_compound(te_json_ctx_t* ctx, const te_string* compound); void te_kvpair_set_compound_va(te_kvpair_h* dst, const char* outer_key, const char* inner_key, te_compound_mod_op mod_op, const char* val_fmt, va_list args); void te_kvpair_set_compound(te_kvpair_h* dst, const char* outer_key, const char* inner_key, te_compound_mod_op mod_op, const char* val_fmt, ...); void void te_compound_build_name(te_string* dst, const char* stem, const char* key, size_t idx); te_errno te_compound_dereference(const te_string* compound, const char* stem, const char* key, te_compound_iter_fn* callback, void* user); te_errno te_compound_dereference_str(const char* compound, const char* stem, const char* key, te_compound_iter_fn* callback, void* user);
Detailed Documentation
Compound strings are an extension to ordinary TE dynamic strings that can hold one-dimensional arrays of string and key-value pairs.
They employ special characters, normally not found in regular strings to separate items from each other and keys from values. So they cannot be used to store arbitrary binary data, but instead they are fully compatible with regular TE strings and C strings (i.e. no embedded zeroes).
The main usecase for compound strings is to extend existing APIs that were designed to handle simple strings only so that they could pass around structured values without losing backward compatibility.
Key-value pairs are stored sorted in compound strings, so that two equivalent key-value sets would compare equal. This makes compound strings slightly more efficient on read that plain key-value pairs API.
Typedefs
typedef enum te_compound_kind te_compound_kind
The kind of a compound string.
This is mostly important for JSON serialization.
typedef enum te_compound_mod_op te_compound_mod_op
The mode of operation for duplicate keys.
typedef te_errno te_compound_iter_fn( char *key, size_t idx, char *value, bool has_more, void *user )
Function type of callbacks for te_compound_iterate().
The callback may freely modify the contents of key
and value
, but it must never try to free it or to store pointers to them across the calls, i.e. they should be treated as local arrays.
Parameters:
key |
Current key (may be |
idx |
Index of the current value with a given key. |
value |
Current value (never |
has_more |
|
user |
Callback user data. |
TE_EOK |
te_compound_iterate() will stop immediately and return success to the caller. |
Returns:
Status code.
Global Functions
te_compound_kind te_compound_classify(const te_string* comp)
Determine the kind of a compound string.
Parameters:
comp |
Compound string. |
Returns:
The kind of the string.
bool te_compound_validate(const te_string* comp)
Validate the compound string.
Errors will be logged by this function.
Parameters:
comp |
Compound string. |
Returns:
true
if the compound string is well-formed.
bool te_compound_validate_str(const char* comp)
Like te_compound_validate(), but accepts a plain C string as an input.
The reason for this function existence is to avoid creating an intermediate te_string if a compound string was obtained from some external source (e.g. read from a file).
Parameters:
comp |
Compound string representation. |
Returns:
true
if comp
represents a valid compound string.
bool te_compound_extract(te_string* dst, const te_string* comp, const char* key, size_t idx)
Extract a value associated with a given key in the compound.
If key
is NULL
, an idx'th
unnamed item is extracted.
A compound that has no structure is treated as single-item array.
If there is no value associated with a given key or if there is less values than idx
, nothing is extracted and the function just returns false
.
Parameters:
dst |
String which is appended the extracted value. |
comp |
Compound string. |
key |
Key of a value (may be |
idx |
Index of a value among equal keys. |
Returns:
true
if a value has been extracted.
size_t te_compound_count(const te_string* comp, const char* key)
Count the number of values associated with a given key or the number of unnamed items if key
is NULL
.
Parameters:
comp |
Compound string. |
key |
Key (may be |
Returns:
The number of values associated with key
.
void te_compound_set_va(te_string* comp, const char* key, te_compound_mod_op mod_op, const char* val_fmt, va_list args)
Set or unset a value inside a compound associated with a given key.
The behaviour of this function when there is already a value associated with the given key depends on mod_op:
TE_COMPOUND_MOD_OP_APPEND : a new value is appended after all existing values;
TE_COMPOUND_MOD_OP_PREPEND : a new value is prepended before all existing values;
TE_COMPOUND_MOD_OP_REPLACE : a new value is replacing all existing values.
If there is no existing value for a given key, all modification modes are identical.
If val_fmt
is NULL
, all existing values are deleted irrespective of the value of mod_op
.
When key
is NULL
, the function operates on unnamed items. Please note that, unlike te_vec, it is not possible to replace a single unnamed item at a given index: if mod_op
is TE_COMPOUND_MOD_OP_REPLACE, all existing unnamed items will be replaced with the new one.
If a value is added to a plain string, the latter is converted to a single-item array.
Parameters:
comp |
Compound string. |
key |
Key (may be |
mod_op |
Modification mode. |
val_fmt |
Format string (may be |
args |
Variadic list to format. |
void te_compound_set(te_string* comp, const char* key, te_compound_mod_op mod_op, const char* val_fmt, ...)
Like te_compound_set_va(), but accepts variable number of arguments.
Parameters:
comp |
Compound string. |
key |
Key (may be |
mod_op |
Modification mode. |
val_fmt |
Format string (may be |
… |
Arguments to format. |
static void static void te_compound_append_fast(te_string* comp, const char* key, const char* value)
Append a new value to the compound without any checks.
Careless use of this function may result in an ill-formed compound string, so it should be used in special cases.
One use case for the function is to implement a filter for another compound, when it is known for sure that items would come in the right order.
Parameters:
comp |
Compound string. |
key |
Key to append (may be |
value |
Value to add (should not be |
void te_compound_merge(te_string* dst, const te_string* src, te_compound_mod_op mod_op)
Merge a compound string into another one.
The behaviour for keys from src
that are already in dst
depends on the value of mod_op:
TE_COMPOUND_MOD_OP_APPEND : all values from
src
for a given key would come after all values originally fromdst
for the same key;TE_COMPOUND_MOD_OP_PREPEND : all values from
src
for a given key would come before all values originally fromdst
for the same key;TE_COMPOUND_MOD_OP_REPLACE : values from
src
would replace values fromdst
for a given key.
The function behaves almost exactly as if src
were iterated and te_compound_set() were called for every item, however, it is much more efficient.
Parameters:
dst |
Destination compound. |
src |
Source compound to merge from. |
mod_op |
Modification mode. |
te_errno te_compound_iterate(const te_string* src, te_compound_iter_fn* callback, void* user)
Iterate over all items in the compound.
All unnamed items will be processed before any named one. Named items will be processed in the ascending order of their keys.
If a compound is empty, the callback will not be called. If a compound is a non-empty plain string, the callback will be called exactly once, as if it were a single-item array.
If a callback returns a non-zero status code, the processing stops and the status code is returned (TE_EOK being replaced with zero).
Parameters:
src |
Compound string. |
callback |
Callback. |
user |
Callback data. |
TE_ENODATA |
The compound is empty and the callback was not called. |
Returns:
Status code.
te_errno te_compound_iterate_str(const char* src, te_compound_iter_fn* callback, void* user)
Like te_compound_iterate(), but accepts a plain C string as an input.
The reason for this function existence is to avoid creating an intermediate te_string if a compound string was obtained from some external source (e.g. read from a file).
te_compound_validate_str() must return true
for src
.
Parameters:
src |
Compound string representation. |
callback |
Callback. |
user |
Callback data. |
TE_ENODATA |
The compound is empty and the callback was not called. |
Returns:
Status code.
void te_vec2compound(te_string* dst, const te_vec* vec)
Append a vector of strings to the compound.
All existing values in src
are preserved.
Parameters:
dst |
Destination compound. |
vec |
Vector of string pointers. |
void te_kvpair2compound(te_string* dst, const te_kvpair_h* kv)
Append key-value pairs to the compound.
All existing values in src
are preserved.
Parameters:
dst |
Destination compound. |
kv |
Key-value pairs. |
void te_compound2vec(te_vec* dst, const te_string* compound)
Append all values from the compound to a vector.
Values of named items will also be appended after unnamed items, but their keys will be ignored.
The values will be copied, so they need to be eventually freed by a vector user.
Parameters:
dst |
Destination vector of string pointers. |
compound |
Source compound. |
void te_compound2kvpair(te_kvpair_h* dst, const te_string* compound)
Append all values from the compound to a key-value list.
Keys for unnamed items will be generated from their indices.
Bug Due to limitations of #te_kvpair_h API, if there are several values associated with a given key, they will appear in dst
in reverse order, so te_compound2kvpair() and te_kvpair2compound() are not exact inverse of each other.
Parameters:
dst |
Destination key-value list. |
compound |
Source compound. |
void te_json_add_compound(te_json_ctx_t* ctx, const te_string* compound)
Serialize a compound as a JSON entity.
The result of the function depends on the result of te_compound_classify() :
TE_COMPOUND_NULL :
null
will be produced;TE_COMPOUND_PLAIN : a JSON string will be produced;
TE_COMPOUND_ARRAY : a JSON array will be produced;
TE_COMPOUND_OBJECT : a JSON object will be produced.
If compound
contains both named and unnamed items, it will be serialized as a JSON object, but unnamed items will have keys generated from their indices.
If compound
contains multiple values associated with some keys, all values for a given key but the first will have an index appended to a key, so the generated JSON would never contain invalid duplicate keys.
Parameters:
ctx |
JSON context. |
compound |
Source compound. |
void te_kvpair_set_compound_va(te_kvpair_h* dst, const char* outer_key, const char* inner_key, te_compound_mod_op mod_op, const char* val_fmt, va_list args)
Updates a compound value associated with a key in key-value pair list.
The behaviour of this function is essentially the same as te_compound_set_va(), but if there was no value associated with outer_key
and inner_key
is NULL
, the added value will be a plain string, not a one-level array compound.
If the compound value is empty after an update, the binding will be removed altogether.
If there are multiple bindings for outer_key
, the function always operates on the most recent one.
Bug This function is not very efficient due to the way key-value pairs are implemented.
Parameters:
dst |
Target key-value list. |
outer_key |
The key for |
inner_key |
The key for the compound (may be |
mod_op |
Modification mode. |
val_fmt |
Format string (may be |
args |
Variadic list to format. |
void te_kvpair_set_compound(te_kvpair_h* dst, const char* outer_key, const char* inner_key, te_compound_mod_op mod_op, const char* val_fmt, ...)
Like te_kvpair_set_compound_va(), but accepts variable number of arguments.
Parameters:
dst |
Target key-value list. |
outer_key |
The key for |
inner_key |
The key for the compound (may be |
mod_op |
Modification mode. |
val_fmt |
Format string (may be |
… |
Arguments to format. |
void void te_compound_build_name(te_string* dst, const char* stem, const char* key, size_t idx)
Construct a name that may reliably identify a compound item within some named value.
For example, if a parameter named param
holds a compound with two values for a field field
, the second value may be addresed as param_field1
.
Parameters:
dst |
TE string to append the name. |
stem |
Name stem (i.e. the name of a parameter holding a compound value, may not be |
key |
Key to address (may be |
idx |
Index of a value in a multi-valued binding. |
te_errno te_compound_dereference(const te_string* compound, const char* stem, const char* key, te_compound_iter_fn* callback, void* user)
Apply a function to a member of compound referenced by key
.
The key
should have been eventually constructed with te_compound_build_name() with stem
as an argument, otherwise TE_ENODATA will be returned.
The callback
has the same signature as for te_compound_iterate(), but at most one field is ever processed.
Parameters:
compound |
Compound string. |
stem |
Stem of |
key |
Name referencing a field in |
callback |
Callback. |
user |
User data to callback. |
TE_ENODATA |
It is returned if |
Returns:
Status code (usuallaly the one returned by callback
).
te_errno te_compound_dereference_str(const char* compound, const char* stem, const char* key, te_compound_iter_fn* callback, void* user)
Like te_compound_dereference(), but accepts a plain C string as an input.
Parameters:
compound |
Compound string. |
stem |
Stem of |
key |
Name referencing a field in |
callback |
Callback. |
user |
User data to callback. |
Returns:
Status code (see te_compound_dereference() for details).