Skip to content
Snippets Groups Projects
Commit 2a3d8e6f authored by Marek Vavruša's avatar Marek Vavruša
Browse files

Merge branch 'generic_pack'

parents cb6f08ba 8dfb40c0
No related branches found
No related tags found
No related merge requests found
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
* Be aware of that, as direct usage of the macros in the evaluating macros * Be aware of that, as direct usage of the macros in the evaluating macros
* may lead to different expectations, i.e. * 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. * May evaluate the code twice, leading to unexpected behaviour.
* This is a price to pay for absence of proper generics. * This is a price to pay for absence of proper generics.
...@@ -58,9 +58,40 @@ ...@@ -58,9 +58,40 @@
*/ */
#pragma once #pragma once
#include <stdlib.h>
/** @todo Implement mreserve over custom memory context. */ /** Simplified Qt containers growth strategy. */
#include <libknot/internal/mem.h> 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. */ /** Declare an array structure. */
#define array_t(type) struct {type * at; size_t len; size_t cap; } #define array_t(type) struct {type * at; size_t len; size_t cap; }
...@@ -70,14 +101,20 @@ ...@@ -70,14 +101,20 @@
/** Free and zero-initialize the array. */ /** Free and zero-initialize the array. */
#define array_clear(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. * Reserve capacity up to 'n' bytes.
* @return >=0 if success * @return >=0 if success
*/ */
#define array_reserve(array, n) \ #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. * Push value at the end of the array, resize it if necessary.
...@@ -86,7 +123,7 @@ ...@@ -86,7 +123,7 @@
*/ */
#define array_push(array, val) \ #define array_push(array, val) \
(array).len < (array).cap ? ((array).at[(array).len] = val, (array).len++) \ (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++)) : ((array).at[(array).len] = val, (array).len++))
/** /**
......
...@@ -53,12 +53,12 @@ static inline cb_node_t *ref_get_internal(uint8_t *p) ...@@ -53,12 +53,12 @@ static inline cb_node_t *ref_get_internal(uint8_t *p)
} }
/* Standard memory allocation functions */ /* 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 */ (void)baton; /* Prevent compiler warnings */
return malloc(size); return malloc(size);
} }
static void free_std(void *ptr, void *baton) { static void free_std(void *baton, void *ptr) {
(void)baton; /* Prevent compiler warnings */ (void)baton; /* Prevent compiler warnings */
free(ptr); free(ptr);
} }
...@@ -71,9 +71,9 @@ static void cbt_traverse_delete(map_t *map, void *top) ...@@ -71,9 +71,9 @@ static void cbt_traverse_delete(map_t *map, void *top)
cb_node_t *q = ref_get_internal(p); cb_node_t *q = ref_get_internal(p);
cbt_traverse_delete(map, q->child[0]); cbt_traverse_delete(map, q->child[0]);
cbt_traverse_delete(map, q->child[1]); cbt_traverse_delete(map, q->child[1]);
map->free(q, map->baton); map->free(map->baton, q);
} else { } else {
map->free(p, map->baton); map->free(map->baton, p);
} }
} }
...@@ -103,7 +103,7 @@ static int cbt_traverse_prefixed(void *top, ...@@ -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) 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) { if (x != NULL) {
x->value = value; x->value = value;
memcpy(x->key, str, len); memcpy(x->key, str, len);
...@@ -215,14 +215,14 @@ int map_set(map_t *map, const char *str, void *value) ...@@ -215,14 +215,14 @@ int map_set(map_t *map, const char *str, void *value)
c = data->key[newbyte]; c = data->key[newbyte];
newdirection = (1 + (newotherbits | c)) >> 8; 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) { if (newnode == NULL) {
return ENOMEM; return ENOMEM;
} }
x = (uint8_t *)cbt_make_data(map, ubytes, ulen + 1, value); x = (uint8_t *)cbt_make_data(map, ubytes, ulen + 1, value);
if (x == NULL) { if (x == NULL) {
map->free(newnode, map->baton); map->free(map->baton, newnode);
return ENOMEM; return ENOMEM;
} }
...@@ -293,7 +293,7 @@ int map_del(map_t *map, const char *str) ...@@ -293,7 +293,7 @@ int map_del(map_t *map, const char *str)
if (strcmp(str, (const char *)data->key) != 0) { if (strcmp(str, (const char *)data->key) != 0) {
return 1; return 1;
} }
map->free(p, map->baton); map->free(map->baton, p);
if (!whereq) { if (!whereq) {
map->root = NULL; map->root = NULL;
...@@ -301,7 +301,7 @@ int map_del(map_t *map, const char *str) ...@@ -301,7 +301,7 @@ int map_del(map_t *map, const char *str)
} }
*whereq = q->child[1 - direction]; *whereq = q->child[1 - direction];
map->free(q, map->baton); map->free(map->baton, q);
return 0; return 0;
} }
......
...@@ -58,8 +58,8 @@ extern "C" { ...@@ -58,8 +58,8 @@ extern "C" {
/*! Main data structure */ /*! Main data structure */
typedef struct { typedef struct {
void *root; void *root;
void *(*malloc)(size_t size, void *baton); void *(*malloc)(void *baton, size_t size);
void (*free)(void *ptr, void *baton); void (*free)(void *baton, void *ptr);
void *baton; /*! Passed to malloc() and free() */ void *baton; /*! Passed to malloc() and free() */
} map_t; } map_t;
......
/* 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
/* 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);
}
...@@ -6,6 +6,7 @@ tests_BIN := \ ...@@ -6,6 +6,7 @@ tests_BIN := \
test_set \ test_set \
test_map \ test_map \
test_array \ test_array \
test_pack \
test_utils \ test_utils \
test_module \ test_module \
test_rplan \ test_rplan \
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment