* 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.
*/
#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; }
/** 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.
*/
#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++))
/**
--- /dev/null
+/* 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
--- /dev/null
+/* 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);
+}
test_set \
test_map \
test_array \
+ test_pack \
test_utils \
test_module \
test_rplan \