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 NULL).

idx

Index of the current value with a given key.

value

Current value (never NULL).

has_more

true if there are more items to process (not necessary with the same key),

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 NULL)

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 NULL).

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:

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 NULL).

mod_op

Modification mode.

val_fmt

Format string (may be NULL).

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 NULL).

mod_op

Modification mode.

val_fmt

Format string (may be NULL).

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 NULL).

value

Value to add (should not be NULL).

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:

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() :

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 dst (may not be NULL).

inner_key

The key for the compound (may be NULL).

mod_op

Modification mode.

val_fmt

Format string (may be NULL).

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 dst (may not be NULL).

inner_key

The key for the compound (may be NULL).

mod_op

Modification mode.

val_fmt

Format string (may be NULL).

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 NULL)

key

Key to address (may be NULL).

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.

key

Name referencing a field in compound.

callback

Callback.

user

User data to callback.

TE_ENODATA

It is returned if callback has not been called: if key does not start with stem, if there is no field with a given name or if there are not enough associated values for a given inded.

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.

key

Name referencing a field in compound.

callback

Callback.

user

User data to callback.

Returns:

Status code (see te_compound_dereference() for details).