mirror of
https://github.com/google/pebble.git
synced 2025-05-31 15:33:11 +00:00
440 lines
16 KiB
C
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;
|
|
}
|