pebble/src/fw/util/dict.c
2025-01-27 11:38:16 -08:00

440 lines
16 KiB
C

/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "dict.h"
#include <string.h>
#include "system/passert.h"
#include "kernel/pbl_malloc.h"
#include "util/list.h"
#include "util/net.h"
static DictionaryResult dict_init(DictionaryIterator *iter, const uint8_t * const buffer, const uint16_t length) {
if (iter == NULL ||
buffer == NULL) {
return DICT_INVALID_ARGS;
}
if (length < sizeof(Dictionary)) {
return DICT_NOT_ENOUGH_STORAGE;
}
iter->dictionary = (Dictionary *) buffer;
iter->cursor = iter->dictionary->head;
iter->end = buffer + length;
return DICT_OK;
}
uint32_t dict_size(DictionaryIterator* iter) {
return (uint8_t*)iter->end - (uint8_t*)iter->dictionary;
}
DictionaryResult dict_write_begin(DictionaryIterator *iter, uint8_t * const buffer, const uint16_t length) {
const DictionaryResult result = dict_init(iter, buffer, length);
if (result == DICT_OK) {
iter->dictionary->count = 0;
}
return result;
}
static Tuple * cursor_after_tuple_with_data_length(const DictionaryIterator *iter, const uint16_t length) {
return (Tuple *) (((uint8_t *)iter->cursor) + sizeof(Tuple) + length);
}
static DictionaryResult dict_write_data_internal(DictionaryIterator *iter, const uint32_t key, const uint8_t * const data, const uint16_t data_length, const TupleType type) {
if (iter == NULL ||
iter->dictionary == NULL ||
iter->cursor == NULL) {
return DICT_INVALID_ARGS;
}
if (iter->cursor == iter->dictionary->head) {
// Reset implicitly if the cursor is at the head, so writing again after
// calling dict_write_end() won't screw up the count and will just work:
iter->dictionary->count = 0;
}
Tuple * const next_cursor = cursor_after_tuple_with_data_length(iter, data_length);
if (iter->end < (void *)next_cursor) {
return DICT_NOT_ENOUGH_STORAGE;
}
iter->cursor->key = key;
iter->cursor->length = data_length;
iter->cursor->type = type;
if (data_length > 0) {
if (data == NULL) {
return DICT_INVALID_ARGS;
}
memcpy(iter->cursor->value->data, data, data_length);
}
iter->cursor = next_cursor;
++iter->dictionary->count;
return DICT_OK;
}
DictionaryResult dict_write_data(DictionaryIterator *iter, const uint32_t key, const uint8_t * const data, const uint16_t length) {
return dict_write_data_internal(iter, key, data, length, TUPLE_BYTE_ARRAY);
}
DictionaryResult dict_write_cstring(DictionaryIterator *iter, const uint32_t key, const char * const cstring) {
return dict_write_data_internal(iter, key, (const uint8_t * const) cstring, cstring ? strlen(cstring) + 1 : 0, TUPLE_CSTRING);
}
DictionaryResult dict_write_uint8(DictionaryIterator *iter, const uint32_t key, const uint8_t value) {
return dict_write_data_internal(iter, key, (const uint8_t * const) &value, sizeof(value), TUPLE_UINT);
}
DictionaryResult dict_write_uint16(DictionaryIterator *iter, const uint32_t key, const uint16_t value) {
return dict_write_data_internal(iter, key, (const uint8_t * const) &value, sizeof(value), TUPLE_UINT);
}
DictionaryResult dict_write_uint32(DictionaryIterator *iter, const uint32_t key, const uint32_t value) {
return dict_write_data_internal(iter, key, (const uint8_t * const) &value, sizeof(value), TUPLE_UINT);
}
DictionaryResult dict_write_int8(DictionaryIterator *iter, const uint32_t key, const int8_t value) {
return dict_write_data_internal(iter, key, (const uint8_t * const) &value, sizeof(value), TUPLE_INT);
}
DictionaryResult dict_write_int16(DictionaryIterator *iter, const uint32_t key, const int16_t value) {
return dict_write_data_internal(iter, key, (const uint8_t * const) &value, sizeof(value), TUPLE_INT);
}
DictionaryResult dict_write_int32(DictionaryIterator *iter, const uint32_t key, const int32_t value) {
return dict_write_data_internal(iter, key, (const uint8_t * const) &value, sizeof(value), TUPLE_INT);
}
DictionaryResult dict_write_int(DictionaryIterator *iter, const uint32_t key, const void *integer, const uint8_t width_bytes, const bool is_signed) {
return dict_write_data_internal(iter, key, integer, width_bytes, is_signed ? TUPLE_INT : TUPLE_UINT);
}
uint32_t dict_write_end(DictionaryIterator *iter) {
if (iter == NULL ||
iter->dictionary == NULL ||
iter->cursor == NULL) {
return 0;
}
iter->end = iter->cursor;
return dict_size(iter);
}
//! Returns the cursor, or NULL if the tuple at cursor extends beyond the bounds of the backing storage
static Tuple * get_safe_cursor(DictionaryIterator *iter) {
// If iter->cursor is already at the end, return now so we don't try and read past the end of
// the malloc'ed block (when fetching iter->cursor->length) and possibly cause a memory read
// exception.
if ((void*)iter->cursor >= iter->end) {
return NULL;
}
Tuple * const next_cursor = cursor_after_tuple_with_data_length(iter, iter->cursor->length);
if ((void *)next_cursor > iter->end) {
return NULL;
}
return iter->cursor;
}
Tuple * dict_read_begin_from_buffer(DictionaryIterator *iter, const uint8_t * const buffer, const uint16_t length) {
const DictionaryResult result = dict_init(iter, buffer, length);
if (result != DICT_OK) {
return NULL;
}
return get_safe_cursor(iter);
}
Tuple * dict_read_next(DictionaryIterator *iter) {
if (iter == NULL ||
iter->dictionary == NULL ||
iter->cursor == NULL) {
return NULL;
}
iter->cursor = cursor_after_tuple_with_data_length(iter, iter->cursor->length);
return get_safe_cursor(iter);
}
Tuple * dict_read_first(DictionaryIterator *iter) {
if (iter == NULL ||
iter->dictionary == NULL ||
iter->cursor == NULL) {
return NULL;
}
iter->cursor = iter->dictionary->head;
return get_safe_cursor(iter);
}
uint32_t dict_calc_buffer_size(const uint8_t count, ...) {
uint32_t total_size = sizeof(Dictionary);
if (count == 0) {
return total_size;
}
va_list vl;
va_start(vl, count);
for (unsigned int i = 0; i < count; ++i) {
total_size += va_arg(vl, unsigned int) + sizeof(Tuple);
}
va_end(vl);
return total_size;
}
uint32_t dict_calc_buffer_size_from_tuplets(const Tuplet * const tuplets, const uint8_t tuplets_count) {
uint32_t total_size = sizeof(Dictionary);
if (tuplets_count == 0) {
return total_size;
}
for (unsigned int i = 0; i < tuplets_count; ++i) {
const Tuplet * const tuplet = &tuplets[i];
switch (tuplet->type) {
case TUPLE_BYTE_ARRAY:
total_size += tuplet->bytes.length;
break;
case TUPLE_CSTRING:
total_size += tuplet->cstring.length;
break;
case TUPLE_INT:
case TUPLE_UINT:
total_size += tuplet->integer.width;
break;
}
total_size += sizeof(Tuple);
}
return total_size;
}
// Legacy version to prevent previous app breakage, __deprecated preserves order
uint32_t dict_calc_buffer_size_from_tuplets__deprecated(const uint8_t tuplets_count, const Tuplet * const tuplets) {
return dict_calc_buffer_size_from_tuplets(tuplets, tuplets_count);
}
static DictionaryResult dict_write_tuple(DictionaryIterator* iter, Tuple* tuple) {
return dict_write_data_internal(iter, tuple->key, (uint8_t*)tuple->value,
tuple->length, tuple->type);
}
DictionaryResult dict_write_tuplet(DictionaryIterator *iter, const Tuplet * const tuplet) {
if (iter == NULL ||
iter->dictionary == NULL ||
iter->cursor == NULL) {
return DICT_INVALID_ARGS;
}
switch (tuplet->type) {
case TUPLE_BYTE_ARRAY:
return dict_write_data_internal(iter, tuplet->key, tuplet->bytes.data, tuplet->bytes.length, tuplet->type);
case TUPLE_CSTRING:
return dict_write_data_internal(iter, tuplet->key, (uint8_t *)tuplet->cstring.data, tuplet->cstring.length, tuplet->type);
case TUPLE_UINT:
case TUPLE_INT:
return dict_write_data_internal(iter, tuplet->key, (uint8_t *)&tuplet->integer.storage, tuplet->integer.width, tuplet->type);
}
return 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) {
if (size_in_out == NULL ||
buffer == NULL ||
tuplets == NULL) {
return DICT_INVALID_ARGS;
}
DictionaryResult result;
if ((result = dict_write_begin(iter, buffer, *size_in_out)) != DICT_OK) {
return result;
}
for (unsigned int i = 0; i < tuplets_count; ++i) {
if ((result = dict_write_tuplet(iter, &tuplets[i])) != DICT_OK) {
return result;
}
}
*size_in_out = dict_write_end(iter);
return DICT_OK;
}
// Legacy version to prevent previous app breakage, __deprecated preserves order
DictionaryResult dict_serialize_tuplets_to_buffer_with_iter__deprecated(const uint8_t tuplets_count,
const Tuplet * const tuplets, DictionaryIterator *iter, uint8_t *buffer, uint32_t *size_in_out) {
return dict_serialize_tuplets_to_buffer_with_iter(iter,
tuplets, tuplets_count, buffer, size_in_out);
}
DictionaryResult dict_serialize_tuplets_to_buffer(const Tuplet * const tuplets, const uint8_t tuplets_count, uint8_t *buffer, uint32_t *size_in_out) {
DictionaryIterator iter;
return dict_serialize_tuplets_to_buffer_with_iter(&iter, tuplets, tuplets_count, buffer, size_in_out);
}
// Legacy version to prevent previous app breakage, __deprecated preserves order
DictionaryResult dict_serialize_tuplets_to_buffer__deprecated(
const uint8_t tuplets_count, const Tuplet * const tuplets,
uint8_t *buffer, uint32_t *size_in_out) {
DictionaryIterator iter;
return dict_serialize_tuplets_to_buffer_with_iter(&iter,
tuplets, tuplets_count, buffer, size_in_out);
}
DictionaryResult dict_serialize_tuplets(DictionarySerializeCallback callback, void *context, const Tuplet * const tuplets, const uint8_t tuplets_count) {
if (tuplets_count == 0) {
const Dictionary dict = { .count = 0 };
callback((const uint8_t *)&dict, sizeof(Dictionary), context);
return DICT_OK;
}
uint32_t size = dict_calc_buffer_size_from_tuplets(tuplets, tuplets_count);
uint8_t buffer[size];
DictionaryResult result = dict_serialize_tuplets_to_buffer(tuplets, tuplets_count, buffer, &size);
if (result != DICT_OK) {
return result;
}
callback(buffer, size, context);
return DICT_OK;
}
// Legacy version to prevent previous app breakage, __deprecated preserves order
DictionaryResult dict_serialize_tuplets__deprecated(DictionarySerializeCallback callback, void *context, const uint8_t tuplets_count, const Tuplet * const tuplets) {
return dict_serialize_tuplets(callback, context, tuplets, tuplets_count);
}
static const uint8_t NULL_TUPLE_BUFFER[sizeof(Tuple) + sizeof(uint32_t)] = { 0 };
const Tuple * const NULL_TUPLE = (const Tuple * const) NULL_TUPLE_BUFFER;
static uint8_t* dict_copy(DictionaryIterator* iter) {
size_t size = dict_size(iter);
uint8_t* buf = task_malloc(size);
if (buf == NULL) return NULL;
memcpy(buf, iter->dictionary, size);
return buf;
}
// Merge orig_iter and new_iter into dest_iter. Keys which exist in both
// orig_iter and new_iter will get the value they have in new_iter.
static DictionaryResult dict_merge_to(DictionaryIterator* dest_iter,
DictionaryIterator* orig_iter,
DictionaryIterator* new_iter,
const bool update_existing_keys_only,
const DictionaryKeyUpdatedCallback update_key_callback,
void* context) {
DictionaryResult result = DICT_OK;
// First, write the updated keys.
for (Tuple* new = dict_read_first(new_iter); new; new = dict_read_next(new_iter)) {
uint32_t key = new->key;
const Tuple* orig = dict_find(orig_iter, key);
if (orig == NULL && update_existing_keys_only) {
continue;
}
if (orig == NULL) {
orig = NULL_TUPLE;
}
Tuple* dest = dest_iter->cursor;
result = dict_write_tuple(dest_iter, new);
if (result != DICT_OK) return result;
update_key_callback(key, dest, orig, context);
}
// Then, write any old keys which were not updated this round.
// We still call update_key_callback here, even though the values
// themselves have not changed, because we have shuffled them
// around in memory, so their old buffers are no longer valid.
for (Tuple* orig = dict_read_first(orig_iter); orig; orig = dict_read_next(orig_iter)) {
uint32_t key = orig->key;
Tuple* new = dict_find(new_iter, key);
if (new != NULL) {
// We already wrote this key, above.
continue;
}
Tuple* dest = dest_iter->cursor;
result = dict_write_tuple(dest_iter, orig);
if (result != DICT_OK) return result;
update_key_callback(key, dest, orig, context);
}
return DICT_OK;
}
// Calculate the amount of space needed for a dest_iter which can fit the result
// of merging orig_iter and new_iter. This logic should always mirror the logic
// in dict_merge_to, except it should simply count the size, rather than
// actually merging the results.
static size_t dict_merge_to_size(DictionaryIterator* orig_iter,
DictionaryIterator* new_iter,
const bool update_existing_keys_only) {
size_t total_size_required = sizeof(Dictionary);
// First, calculate the size of the new/updated keys.
for (Tuple* new = dict_read_first(new_iter); new; new = dict_read_next(new_iter)) {
if (dict_find(orig_iter, new->key) == NULL && update_existing_keys_only) continue;
total_size_required += sizeof(*new) + new->length;
}
// Then, add in the size of the keys which have not changed.
for (Tuple* orig = dict_read_first(orig_iter); orig; orig = dict_read_next(orig_iter)) {
if (dict_find(new_iter, orig->key) != NULL) continue;
total_size_required += sizeof(*orig) + orig->length;
}
return total_size_required;
}
DictionaryResult dict_merge(DictionaryIterator* dest_iter,
uint32_t* dest_buf_length_in_out,
DictionaryIterator* new_iter,
const bool update_existing_keys_only,
const DictionaryKeyUpdatedCallback update_key_callback,
void* context) {
if (dest_iter == NULL || new_iter == NULL || dest_buf_length_in_out == NULL) {
return DICT_INVALID_ARGS;
}
size_t required_size = dict_merge_to_size(dest_iter, new_iter, update_existing_keys_only);
if (*dest_buf_length_in_out < required_size) {
return DICT_NOT_ENOUGH_STORAGE;
}
uint8_t* orig_buffer = dict_copy(dest_iter);
if (orig_buffer == NULL) return DICT_MALLOC_FAILED;
DictionaryIterator orig_iter;
DictionaryResult result = dict_init(&orig_iter, orig_buffer, dict_size(dest_iter));
if (result != DICT_OK) goto cleanup;
result = dict_write_begin(dest_iter,
(uint8_t*)dest_iter->dictionary,
(uint16_t)*dest_buf_length_in_out);
if (result != DICT_OK) goto cleanup;
result = dict_merge_to(dest_iter, &orig_iter, new_iter,
update_existing_keys_only,
update_key_callback, context);
if (result != DICT_OK) goto cleanup;
*dest_buf_length_in_out = dict_write_end(dest_iter);
cleanup:
task_free(orig_buffer);
return result;
}
Tuple *dict_find(const DictionaryIterator *iter, const uint32_t key) {
DictionaryIterator iter_copy = *iter;
Tuple *tuple = dict_read_first(&iter_copy);
while (tuple) {
if (tuple->key == key) {
return tuple;
}
tuple = dict_read_next(&iter_copy);
}
return NULL;
}