diff --git a/lib/generic/array.h b/lib/generic/array.h index 8fa93809fbe51e8f4a08d0972c98ba199b8112bb..96dcf19f0ffd8fa343a0e812e365db58b5fd5892 100644 --- a/lib/generic/array.h +++ b/lib/generic/array.h @@ -21,7 +21,7 @@ * Be aware of that, as direct usage of the macros in the evaluating macros * may lead to different expectations, i.e. * - * MIN(array_push(arr, val)) + * MIN(array_push(arr, val), other) * * May evaluate the code twice, leading to unexpected behaviour. * This is a price to pay for absence of proper generics. @@ -58,9 +58,40 @@ */ #pragma once +#include <stdlib.h> -/** @todo Implement mreserve over custom memory context. */ -#include <libknot/internal/mem.h> +/** Simplified Qt containers growth strategy. */ +static inline size_t array_next_count(size_t want) +{ + if (want < 2048) { + return (want < 20) ? want + 4 : want * 2; + } else { + return want + 2048; + } +} + +/** @internal Incremental memory reservation */ +static inline int array_std_reserve(void *baton, char **mem, size_t elm_size, size_t want, size_t *have) +{ + if (*have >= want) { + return 0; + } + /* Simplified Qt containers growth strategy */ + size_t next_size = array_next_count(want); + void *mem_new = realloc(*mem, next_size * elm_size); + if (mem_new != NULL) { + *mem = mem_new; + *have = next_size; + return 0; + } + return -1; +} + +/** @internal Wrapper for stdlib free. */ +static inline void array_std_free(void *baton, void *p) +{ + free(p); +} /** Declare an array structure. */ #define array_t(type) struct {type * at; size_t len; size_t cap; } @@ -70,14 +101,20 @@ /** Free and zero-initialize the array. */ #define array_clear(array) \ - free((array).at), array_init(array) + array_clear_mm(array, array_std_free, NULL) +/** @internal Clear array with a callback. */ +#define array_clear_mm(array, free, baton) \ + (free)((baton), (array).at), array_init(array) /** * Reserve capacity up to 'n' bytes. * @return >=0 if success */ #define array_reserve(array, n) \ - mreserve((char **) &(array).at, sizeof((array).at[0]), n, 0, &(array).cap) + array_reserve_mm(array, n, array_std_reserve, NULL) +/** @internal Reserve capacity using callback. */ +#define array_reserve_mm(array, n, reserve, baton) \ + (reserve)((baton), (char **) &(array).at, sizeof((array).at[0]), (n), &(array).cap) /** * Push value at the end of the array, resize it if necessary. @@ -86,7 +123,7 @@ */ #define array_push(array, val) \ (array).len < (array).cap ? ((array).at[(array).len] = val, (array).len++) \ - : (array_reserve(array, ((array).cap + 1) * 2) < 0 ? -1 \ + : (array_reserve(array, ((array).cap + 1)) < 0 ? -1 \ : ((array).at[(array).len] = val, (array).len++)) /** diff --git a/lib/generic/map.c b/lib/generic/map.c index 683db284658a8f0e090fe0cccf4546c0c8507132..86c2b19aa4deb26b662c9a7f0a3495309e895a92 100644 --- a/lib/generic/map.c +++ b/lib/generic/map.c @@ -53,12 +53,12 @@ static inline cb_node_t *ref_get_internal(uint8_t *p) } /* Standard memory allocation functions */ -static void *malloc_std(size_t size, void *baton) { +static void *malloc_std(void *baton, size_t size) { (void)baton; /* Prevent compiler warnings */ return malloc(size); } -static void free_std(void *ptr, void *baton) { +static void free_std(void *baton, void *ptr) { (void)baton; /* Prevent compiler warnings */ free(ptr); } @@ -71,9 +71,9 @@ static void cbt_traverse_delete(map_t *map, void *top) cb_node_t *q = ref_get_internal(p); cbt_traverse_delete(map, q->child[0]); cbt_traverse_delete(map, q->child[1]); - map->free(q, map->baton); + map->free(map->baton, q); } else { - map->free(p, map->baton); + map->free(map->baton, p); } } @@ -103,7 +103,7 @@ static int cbt_traverse_prefixed(void *top, static cb_data_t *cbt_make_data(map_t *map, const uint8_t *str, size_t len, void *value) { - cb_data_t *x = map->malloc(sizeof(cb_data_t) + len, map->baton); + cb_data_t *x = map->malloc(map->baton, sizeof(cb_data_t) + len); if (x != NULL) { x->value = value; memcpy(x->key, str, len); @@ -215,14 +215,14 @@ different_byte_found: c = data->key[newbyte]; newdirection = (1 + (newotherbits | c)) >> 8; - newnode = map->malloc(sizeof(cb_node_t), map->baton); + newnode = map->malloc(map->baton, sizeof(cb_node_t)); if (newnode == NULL) { return ENOMEM; } x = (uint8_t *)cbt_make_data(map, ubytes, ulen + 1, value); if (x == NULL) { - map->free(newnode, map->baton); + map->free(map->baton, newnode); return ENOMEM; } @@ -293,7 +293,7 @@ int map_del(map_t *map, const char *str) if (strcmp(str, (const char *)data->key) != 0) { return 1; } - map->free(p, map->baton); + map->free(map->baton, p); if (!whereq) { map->root = NULL; @@ -301,7 +301,7 @@ int map_del(map_t *map, const char *str) } *whereq = q->child[1 - direction]; - map->free(q, map->baton); + map->free(map->baton, q); return 0; } diff --git a/lib/generic/map.h b/lib/generic/map.h index 25258d198f34c4a38b8656ed1828b4cba1a41386..96ce01714709a8b9b783d80f9e6a73d2a944ef39 100644 --- a/lib/generic/map.h +++ b/lib/generic/map.h @@ -58,8 +58,8 @@ extern "C" { /*! Main data structure */ typedef struct { void *root; - void *(*malloc)(size_t size, void *baton); - void (*free)(void *ptr, void *baton); + void *(*malloc)(void *baton, size_t size); + void (*free)(void *baton, void *ptr); void *baton; /*! Passed to malloc() and free() */ } map_t; diff --git a/lib/generic/pack.h b/lib/generic/pack.h new file mode 100644 index 0000000000000000000000000000000000000000..171b7f1d3ca6286a40c2c043f88e0e9173798899 --- /dev/null +++ b/lib/generic/pack.h @@ -0,0 +1,118 @@ +/* Copyright (C) 2015 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** + * Generics - array of lenght-prefixed packed objects + * + * Each object is prefixed by item length, unlike array this structure + * permits variable-length data. It is also equivallent to forward-only list + * backed by an array. + * + * @note Maximum object size is 2^16 bytes, @see pack_objlen_t + * + * Example usage: + * + * pack_t pack; + * pack_init(pack); + * + * // Reserve 2 objects, 6 bytes total + * pack_reserve(pack, 2, 4 + 2); + * + * // Push 2 objects + * pack_obj_push(pack, U8("jedi"), 4) + * pack_obj_push(pack, U8("\xbe\xef"), 2); + * + * // Iterate length-value pairs + * uint8_t *it = pack_head(pack); + * while (it != pack_tail(pack)) { + * uint8_t *val = pack_obj_val(it); + * it = pack_obj_next(it); + * } + * + * pack_clear(pack); + * + * \addtogroup generics + * @{ + */ + +#include <stdint.h> +#include <string.h> +#include "array.h" + +/** Packed object length type. */ +typedef uint16_t pack_objlen_t; + +/** Pack is defined as an array of bytes */ +typedef array_t(uint8_t) pack_t; + +/** Zero-initialize the pack. */ +#define pack_init(pack) \ + array_init(pack) +/** Free and the pack. */ +#define pack_clear(pack) \ + array_clear(pack) +/** @internal Clear pack with a callback. */ +#define pack_clear_mm(pack, free, baton) \ + array_clear_mm(pack, array_std_free, baton) +/** Incrementally reserve objects in the pack. */ +#define pack_reserve(pack, objs_count, objs_len) \ + pack_reserve_mm((pack), (objs_count), (objs_len), array_std_reserve, NULL) +/** @internal Reservation with a callback. */ +#define pack_reserve_mm(pack, objs_count, objs_len, reserve, baton) \ + array_reserve_mm((pack), (pack).len + (sizeof(pack_objlen_t)*(objs_count) + (objs_len)), (reserve), (baton)) +/** Return pointer to first packed object. */ +#define pack_head(pack) \ + &((pack).at[0]) +/** Return pack end pointer. */ +#define pack_tail(pack) \ + &((pack).at[(pack).len]) + +/** Return packed object length. */ +static inline pack_objlen_t pack_obj_len(uint8_t *it) +{ + pack_objlen_t len = 0; + memcpy(&len, it, sizeof(len)); + return len; +} + +/** Return packed object value. */ +static inline uint8_t *pack_obj_val(uint8_t *it) +{ + return it + sizeof(pack_objlen_t); +} + +/** Return pointer to next packed object. */ +static inline uint8_t *pack_obj_next(uint8_t *it) +{ + return pack_obj_val(it) + pack_obj_len(it); +} + +/** Push object to the end of the pack + * @return 0 on success, negative number on failure + */ +static inline int pack_obj_push(pack_t *pack, const uint8_t *obj, pack_objlen_t len) +{ + uint8_t *endp = pack_tail(*pack); + size_t packed_len = len + sizeof(len); + if (pack == NULL || (pack->len + packed_len) > pack->cap) { + return -1; + } + + memcpy(endp, (char *)&len, sizeof(len)); + memcpy(endp + sizeof(len), obj, len); + pack->len += packed_len; + return 0; +} \ No newline at end of file diff --git a/tests/test_pack.c b/tests/test_pack.c new file mode 100644 index 0000000000000000000000000000000000000000..27c09bffdea585b62964162bd58b67e9869fa4b9 --- /dev/null +++ b/tests/test_pack.c @@ -0,0 +1,64 @@ +/* Copyright (C) 2015 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "tests/test.h" +#include "lib/generic/pack.h" + +#define U8(x) (const uint8_t *)(x) +mm_ctx_t global_mm; + +static void test_pack_std(void **state) +{ + int ret = 0; + pack_t pack; + pack_init(pack); + assert_int_equal(pack.len, 0); + + /* Push without reservation. */ + assert_int_not_equal(pack_obj_push(&pack, U8(""), 1), 0); + + /* Reserve capacity and fill. */ + assert_true(pack_reserve(pack, 10, 10 * 2) >= 0); + for (unsigned i = 0; i < 10; ++i) { + ret = pack_obj_push(&pack, U8("de"), 2); + assert_true(ret >= 0); + } + + /* Iterate */ + uint8_t *it = pack_head(pack); + assert_non_null(it); + unsigned count = 0; + while (it != pack_tail(pack)) { + assert_int_equal(pack_obj_len(it), 2); + assert_true(memcmp(pack_obj_val(it), "de", 2) == 0); + it = pack_obj_next(it); + count += 1; + } + + + pack_clear(pack); +} + +int main(void) +{ + test_mm_ctx_init(&global_mm); + + const UnitTest tests[] = { + unit_test(test_pack_std), + }; + + return run_tests(tests); +} diff --git a/tests/unit.mk b/tests/unit.mk index d6e09da09444103998632b8e76d408dc06408b65..d8ac592e79ef0095b506df2c1d4e16c12efdcefe 100644 --- a/tests/unit.mk +++ b/tests/unit.mk @@ -6,6 +6,7 @@ tests_BIN := \ test_set \ test_map \ test_array \ + test_pack \ test_utils \ test_module \ test_rplan \