All Data Structures Functions Variables Typedefs Enumerations Enumerator Groups Pages
Dictionary

Detailed Description

Data serialization utilities.

On several occassions, pieces of information that might be residing in different parts of Pebble's memory (RAM), need to be gathered into one compact, continuous block of data. One of these occassions is when the pieces of information get sent over the network / Bluetooth connection. This process of gathering the information and assembling the continous block of data is called serialization. The Dictionary, Tuple and Tuplet data structures and accompanying functions help do this in a lightweight way.

Note
There are no transformations done on the actual data. These utilities merely help assembling the data into one continous buffer according to a certain format.

An example subsystem that uses Dictionary and friends is AppMessage, for sending information between watch apps and phone apps.

Write example:

Writing two key/value pairs:

// Byte array + key:
static const uint32_t SOME_DATA_KEY = 0xb00bf00b;
static const uint8_t data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
// CString + key:
static const uint32_t SOME_STRING_KEY = 0xabbababe;
static const char *string = "Hello World";
// Calculate the buffer size that is needed for the final Dictionary:
const uint8_t key_count = 2;
const uint32_t size = dict_calc_buffer_size(key_count, sizeof(data),
strlen(string) + 1);
// Stack-allocated buffer in which to create the Dictionary:
uint8_t buffer[size];
// Iterator variable, keeps the state of the creation serialization process:
// Begin:
dict_write_begin(&iter, buffer, sizeof(buffer));
// Write the Data:
dict_write_data(&iter, SOME_DATA_KEY, data, sizeof(data));
// Write the CString:
dict_write_cstring(&iter, SOME_STRING_KEY, string);
// End:
const uint32_t final_size = dict_write_end(&iter);
// buffer now contains the serialized information

Read example:

Read example: iterating over the key/value pairs in the dictionary that was created in the previous example.

Tuple *tuple = dict_read_begin_from_buffer(&iter, buffer, final_size);
while (tuple) {
switch (tuple->key) {
case SOME_DATA_KEY:
foo(tuple->value->data, tuple->length);
break;
case SOME_STRING_KEY:
bar(tuple->value->cstring);
break;
}
tuple = dict_read_next(&iter);
}

Tuple vs. Tuplet

Note the difference between the Tuple and Tuplet data structures. Tuple is the header for a serialized key/value pair. Tuplet is a helper data structure that references the value you want to serialize. This data structure exists to make the creation of a Dictionary nicer to write. As mnemonic to remember the difference: TupleT(emplate), the Tuplet being a template to create a Dictionary with Tuple structures.

Example:

Tuplet pairs[] = {
TupletInteger(WEATHER_ICON_KEY, (uint8_t) 1),
TupletCString(WEATHER_TEMPERATURE_KEY, "1234 Fahrenheit"),
};
uint8_t buffer[256];
uint32_t size = sizeof(buffer);
dict_serialize_tuplets_to_buffer(pairs, ARRAY_LENGTH(pairs), buffer, &size);
// buffer now contains the serialized information

Function Documentation

uint32_t dict_calc_buffer_size ( const uint8_t  tuple_count,
  ... 
)

Calculates the number of bytes that a dictionary will occupy, given one or more value lengths that need to be stored in the dictionary.

Note
The formula to calculate the size of a Dictionary in bytes is:
1 + (n * 7) + D1 + ... + Dn
Where n is the number of Tuples in the Dictionary and Dx are the sizes of the values in the Tuples. The size of the Dictionary header is 1 byte. The size of the header for each Tuple is 7 bytes.
Parameters
tuple_countThe total number of key/value pairs in the dictionary.
...The sizes of each of the values that need to be stored in the dictionary.
Returns
The total number of bytes of storage needed.
uint32_t dict_calc_buffer_size_from_tuplets ( const Tuplet *const  tuplets,
const uint8_t  tuplets_count 
)

Calculates the number of bytes that a dictionary will occupy, given one or more Tuplets that need to be stored in the dictionary.

Note
See dict_calc_buffer_size() for the formula for the calculation.
Parameters
tupletsAn array of Tuplets that need to be stored in the dictionary.
tuplets_countThe total number of Tuplets that follow.
Returns
The total number of bytes of storage needed.
See Also
Tuplet
Tuple* dict_find ( const DictionaryIterator iter,
const uint32_t  key 
)

Tries to find a Tuple with specified key in a dictionary.

Parameters
iterIterator to the dictionary to search in.
keyThe key for which to find a Tuple
Returns
Pointer to a found Tuple, or NULL if there was no Tuple with the specified key.
DictionaryResult dict_merge ( DictionaryIterator dest,
uint32_t dest_max_size_in_out,
DictionaryIterator source,
const bool  update_existing_keys_only,
const DictionaryKeyUpdatedCallback  key_callback,
void *  context 
)

Merges entries from another "source" dictionary into a "destination" dictionary. All Tuples from the source are written into the destination dictionary, while updating the exsting Tuples with matching keys.

Parameters
destThe destination dictionary to update
[in,out]dest_max_size_in_outIn: the maximum size of buffer backing dest. Out: the final size of the updated dictionary.
sourceThe source dictionary of which its Tuples will be used to update dest.
update_existing_keys_onlySpecify True if only the existing keys in dest should be updated.
key_callbackThe callback that will be called for each Tuple in the merged destination dictionary.
contextPointer to app specific data that will get passed in when update_key_callback is called.
Returns
DICT_OK, DICT_INVALID_ARGS, DICT_NOT_ENOUGH_STORAGE
Tuple* dict_read_begin_from_buffer ( DictionaryIterator iter,
const uint8_t *const  buffer,
const uint16_t  size 
)

Initializes the dictionary iterator with a given buffer and size, in preparation of reading key/value tuples.

Parameters
iterThe dictionary iterator
bufferThe storage of the dictionary
sizeThe storage size of the dictionary
Returns
The first tuple in the dictionary, or NULL in case the dictionary was empty or if there was a parsing error.
Tuple* dict_read_first ( DictionaryIterator iter)

Resets the iterator back to the same state as a call to dict_read_begin_from_buffer() would do.

Parameters
iterThe dictionary iterator
Returns
The first tuple in the dictionary, or NULL in case the dictionary was empty or if there was a parsing error.
Tuple* dict_read_next ( DictionaryIterator iter)

Progresses the iterator to the next key/value pair.

Parameters
iterThe dictionary iterator
Returns
The next tuple in the dictionary, or NULL in case the end has been reached or if there was a parsing error.
DictionaryResult dict_serialize_tuplets ( DictionarySerializeCallback  callback,
void *  context,
const Tuplet *const  tuplets,
const uint8_t  tuplets_count 
)

Utility function that takes a list of Tuplets from which a dictionary will be serialized, ready to transmit or store.

Note
The callback will be called before the function returns, so the data that that context points to, can be stack allocated.
Parameters
callbackThe callback that will be called with the serialized data of the generated dictionary.
contextPointer to any application specific data that gets passed into the callback.
tupletsAn array of Tuplets that need to be serialized into the dictionary.
tuplets_countThe number of tuplets that follow.
Returns
DICT_OK, DICT_NOT_ENOUGH_STORAGE or DICT_INVALID_ARGS
DictionaryResult dict_serialize_tuplets_to_buffer ( const Tuplet *const  tuplets,
const uint8_t  tuplets_count,
uint8_t *  buffer,
uint32_t size_in_out 
)

Utility function that takes an array of Tuplets and serializes them into a dictionary with a given buffer and size.

Parameters
tupletsThe array of tuplets
tuplets_countThe number of tuplets in the array
bufferThe buffer in which to write the serialized dictionary
[in]size_in_outThe available buffer size in bytes
[out]size_in_outThe number of bytes written
Returns
DICT_OK, DICT_NOT_ENOUGH_STORAGE or DICT_INVALID_ARGS
DictionaryResult dict_serialize_tuplets_to_buffer_with_iter ( DictionaryIterator iter,
const Tuplet *const  tuplets,
const uint8_t  tuplets_count,
uint8_t *  buffer,
uint32_t size_in_out 
)

Serializes an array of Tuplets into a dictionary with a given buffer and size.

Parameters
iterThe dictionary iterator
tupletsThe array of tuplets
tuplets_countThe number of tuplets in the array
bufferThe buffer in which to write the serialized dictionary
[in]size_in_outThe available buffer size in bytes
[out]size_in_outThe number of bytes written
Returns
DICT_OK, DICT_NOT_ENOUGH_STORAGE or DICT_INVALID_ARGS
DictionaryResult dict_write_begin ( DictionaryIterator iter,
uint8_t *const  buffer,
const uint16_t  size 
)

Initializes the dictionary iterator with a given buffer and size, resets and empties it, in preparation of writing key/value tuples.

Parameters
iterThe dictionary iterator
bufferThe storage of the dictionary
sizeThe storage size of the dictionary
Returns
DICT_OK, DICT_NOT_ENOUGH_STORAGE or DICT_INVALID_ARGS
See Also
dict_calc_buffer_size
dict_write_end
DictionaryResult dict_write_cstring ( DictionaryIterator iter,
const uint32_t  key,
const char *const  cstring 
)

Adds a key with a C string value pair to the dictionary.

Parameters
iterThe dictionary iterator
keyThe key
cstringPointer to the zero-terminated C string
Returns
DICT_OK, DICT_NOT_ENOUGH_STORAGE or DICT_INVALID_ARGS
Note
The string will be copied into the backing storage of the dictionary.
There is no checking for duplicate keys.
DictionaryResult dict_write_data ( DictionaryIterator iter,
const uint32_t  key,
const uint8_t *const  data,
const uint16_t  size 
)

Adds a key with a byte array value pair to the dictionary.

Parameters
iterThe dictionary iterator
keyThe key
dataPointer to the byte array
sizeLength of the byte array
Returns
DICT_OK, DICT_NOT_ENOUGH_STORAGE or DICT_INVALID_ARGS
Note
The data will be copied into the backing storage of the dictionary.
There is no checking for duplicate keys.
uint32_t dict_write_end ( DictionaryIterator iter)

End a series of writing operations to a dictionary. This must be called before reading back from the dictionary.

Parameters
iterThe dictionary iterator
Returns
The size in bytes of the finalized dictionary, or 0 if the parameters were invalid.
DictionaryResult dict_write_int ( DictionaryIterator iter,
const uint32_t  key,
const void *  integer,
const uint8_t  width_bytes,
const bool  is_signed 
)

Adds a key with an integer value pair to the dictionary.

Parameters
iterThe dictionary iterator
keyThe key
integerPointer to the integer value
width_bytesThe width of the integer value
is_signedWhether the integer's type is signed or not
Returns
DICT_OK, DICT_NOT_ENOUGH_STORAGE or DICT_INVALID_ARGS
Note
There is no checking for duplicate keys.
DictionaryResult dict_write_tuplet ( DictionaryIterator iter,
const Tuplet *const  tuplet 
)

Serializes a Tuplet and writes the resulting Tuple into a dictionary.

Parameters
iterThe dictionary iterator
tupletThe Tuplet describing the key/value pair to write
Returns
DICT_OK, DICT_NOT_ENOUGH_STORAGE or DICT_INVALID_ARGS
DictionaryResult dict_write_uint8 ( DictionaryIterator iter,
const uint32_t  key,
const uint8_t  value 
)

Adds a key with an unsigned, 8-bit integer value pair to the dictionary.

Parameters
iterThe dictionary iterator
keyThe key
valueThe unsigned, 8-bit integer value
Returns
DICT_OK, DICT_NOT_ENOUGH_STORAGE or DICT_INVALID_ARGS
Note
There is no checking for duplicate keys.
There are counterpart functions for different signedness and widths, dict_write_uint16(), dict_write_uint32(), dict_write_int8(), dict_write_int16() and dict_write_int32(). The documentation is not repeated for brevity's sake.

Data Structure Documentation

struct DictionaryIterator

An iterator can be used to iterate over the key/value tuples in an existing dictionary, using dict_read_begin_from_buffer(), dict_read_first() and dict_read_next(). An iterator can also be used to append key/value tuples to a dictionary, for example using dict_write_data() or dict_write_cstring().

Data Fields
Tuple * cursor
Dictionary * dictionary The dictionary being iterated.
const void * end
                 Points to the first memory address after the last byte of the dictionary

Points to the next Tuple in the dictionary. Given the end of the Dictionary has not yet been reached: when writing, the next key/value pair will be written at the cursor. When reading, the next call to dict_read_next() will return the cursor.

struct Tuple

Data structure for one serialized key/value tuple.

Note
The structure is variable length! The length depends on the value data that the tuple contains.
Data Fields
uint32_t key The key.
uint16_t length The length of .value in bytes.
TupleType type:8 The type of data that the .value fields contains.
union Tuple value[] The value itself.

The different union fields are provided for convenience, avoiding the need for manual casts.

Note
The array length is of incomplete length on purpose, to facilitate variable length data and because a data length of zero is valid.
Important: The integers are little endian!
union Tuple.value

The value itself.

The different union fields are provided for convenience, avoiding the need for manual casts.

Note
The array length is of incomplete length on purpose, to facilitate variable length data and because a data length of zero is valid.
Important: The integers are little endian!
Data Fields
char cstring[0] The C-string value. Valid when .type is TUPLE_CSTRING.
uint8_t data[0] The byte array value. Valid when .type is TUPLE_BYTE_ARRAY.
int16_t int16 The 16-bit signed integer value. Valid when .type is TUPLE_INT and .length is 2 byte.
int32_t int32 The 32-bit signed integer value. Valid when .type is TUPLE_INT and .length is 4 byte.
int8_t int8 The 8-bit signed integer value. Valid when .type is TUPLE_INT and .length is 1 byte.
uint16_t uint16 The 16-bit unsigned integer value. Valid when .type is TUPLE_UINT and .length is 2 byte.
uint32_t uint32 The 32-bit unsigned integer value. Valid when .type is TUPLE_UINT and .length is 4 byte.
uint8_t uint8 The 8-bit unsigned integer value. Valid when .type is TUPLE_UINT and .length is 1 byte.
struct Tuplet

Non-serialized, template data structure for a key/value pair. For strings and byte arrays, it only has a pointer to the actual data. For integers, it provides storage for integers up to 32-bits wide. The Tuplet data structure is useful when creating dictionaries from values that are already stored in arbitrary buffers. See also Tuple, with is the header of a serialized key/value pair.

Data Fields
union Tuplet __unnamed__ Anonymous union containing the reference to the Tuplet's value, being either a byte array, c-string or integer. See documentation of .bytes, .cstring and .integer fields.

See documentation of .bytes, .cstring and .integer fields.

uint32_t key The key.
TupleType type The type of the Tuplet. This determines which of the struct fields in the anonymomous union are valid.
union Tuplet.__unnamed__

Anonymous union containing the reference to the Tuplet's value, being either a byte array, c-string or integer. See documentation of .bytes, .cstring and .integer fields.

Data Fields
__unnamed__ bytes Valid when .type. is TUPLE_BYTE_ARRAY.
__unnamed__ cstring Valid when .type. is TUPLE_CSTRING.
__unnamed__ integer Valid when .type. is TUPLE_INT or TUPLE_UINT.
struct Tuplet.__unnamed__.bytes

Valid when .type. is TUPLE_BYTE_ARRAY.

Data Fields
const uint8_t * data Pointer to the data.
const uint16_t length Length of the data.
struct Tuplet.__unnamed__.cstring

Valid when .type. is TUPLE_CSTRING.

Data Fields
const char * data Pointer to the c-string data.
const uint16_t length Length of the c-string, including terminating zero.
struct Tuplet.__unnamed__.integer

Valid when .type. is TUPLE_INT or TUPLE_UINT.

Data Fields
uint32_t storage Actual storage of the integer. The signedness can be derived from the .type value.
const uint16_t width Width of the integer.

Typedef Documentation

typedef void(* DictionaryKeyUpdatedCallback)(const uint32_t key, const Tuple *new_tuple, const Tuple *old_tuple, void *context)

Type of the callback used in dict_merge()

Parameters
keyThe key that is being updated.
new_tupleThe new tuple. The tuple points to the actual, updated destination dictionary or NULL_TUPLE in case there was an error (e.g. backing buffer was too small). Therefore the Tuple can be used after the callback returns, until the destination dictionary storage is free'd (by the application itself).
old_tupleThe values that will be replaced with new_tuple. The key, value and type will be equal to the previous tuple in the old destination dictionary, however the `old_tuple points to a stack-allocated copy of the old data.
contextPointer to application specific data The storage backing old_tuple can only be used during the callback and will no longer be valid after the callback returns.
See Also
dict_merge
typedef void(* DictionarySerializeCallback)(const uint8_t *const data, const uint16_t size, void *context)

Callback for dict_serialize_tuplets() utility.

Parameters
dataThe data of the serialized dictionary
sizeThe size of data
contextThe context pointer as passed in to dict_serialize_tuplets()
See Also
dict_serialize_tuplets

Enumeration Type Documentation

Return values for dictionary write/conversion functions.

Enumerator
DICT_OK 

The operation returned successfully.

DICT_NOT_ENOUGH_STORAGE 

There was not enough backing storage to complete the operation.

DICT_INVALID_ARGS 

One or more arguments were invalid or uninitialized.

DICT_INTERNAL_INCONSISTENCY 

The lengths and/or count of the dictionary its tuples are inconsistent.

enum TupleType

Values representing the type of data that the value field of a Tuple contains.

Enumerator
TUPLE_BYTE_ARRAY 

The value is an array of bytes.

TUPLE_CSTRING 

The value is a zero-terminated, UTF-8 C-string.

TUPLE_UINT 

The value is an unsigned integer. The tuple's .length field is used to determine the size of the integer (1, 2, or 4 bytes).

TUPLE_INT 

The value is a signed integer. The tuple's .length field is used to determine the size of the integer (1, 2, or 4 bytes).