diff --git a/src/lib/salad/grp_alloc.h b/src/lib/salad/grp_alloc.h new file mode 100644 index 0000000000000000000000000000000000000000..2084d7bb0fd968b1aed3fd167853105f8dd2f3c5 --- /dev/null +++ b/src/lib/salad/grp_alloc.h @@ -0,0 +1,138 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright 2010-2021, Tarantool AUTHORS, please see AUTHORS file. + */ +#pragma once + +#include <stddef.h> +#include <string.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Group alloc is a data structure that is designed for simplification of + * allocation of several objects in one memory block. It could be anything, + * but special attention is given to null-terminating strings. Each string is + * allocated with a personal null termination symbol, in the end memory block + * while all other objects will be placed in the beginning of the block. This + * guarantee allows to play with objects' alignment. + * Typical usage consist of two phases: gathering total needed size of memory + * block and creation of objects in given block. + * + * Example of usage: + * struct object { + * int *array; + * const char *name; + * }; + * + * struct object * + * object_new(int *array, size_t array_size, const char *name, size_t name_len) + * { + * // Gather required memory size. + * struct grp_alloc all = grp_alloc_initializer(); + * size_t array_data_size = array_size * sizeof(*array); + * grp_alloc_reserve_data(&all, array_data_size); + * grp_alloc_reserve_str(&all, name_len); + * struct test *res = malloc(sizeof(struct test) + grp_alloc_size(&all)); + * // Use memory block of required size. + * grp_alloc_use(&all, res + 1); + * res->array = grp_alloc_create_data(&all, array_data_size); + * memcpy(res->array, array, array_data_size); + * res->name = grp_alloc_create_str(&all, name, name_len); + * assert(grp_alloc_size(&all) == 0); + * return res; + * } + * + */ +struct grp_alloc { + /** + * Points to the beginning of remaining memory block. Can be NULL in + * the first phase (gathering required memory). + */ + char *data; + /** + * End of required remaining memory block. + */ + char *data_end; +}; + +/** + * Default grp_alloc initializer. Just assign it to new grp_alloc and it's + * ready for the first phase. + */ +static inline struct grp_alloc +grp_alloc_initializer(void) +{ + struct grp_alloc res = {NULL, NULL}; + return res; +} + +/** + * Phase 1: account am arbitrary data of @a size that is needed to be allocated. + */ +static inline void +grp_alloc_reserve_data(struct grp_alloc *bank, size_t size) +{ + bank->data_end += size; +} + +/** + * Phase 1: account a string of @a size that is needed to be allocated, + * including char for null-termination. + */ +static inline void +grp_alloc_reserve_str(struct grp_alloc *bank, size_t size) +{ + bank->data_end += size + 1; +} + +/** + * Phase 1 end: get total memory size required for all data. + */ +static inline size_t +grp_alloc_size(struct grp_alloc *bank) +{ + return bank->data_end - bank->data; +} + +/** + * Phase 2 begin: provide a block of memory of required size. + */ +static inline void +grp_alloc_use(struct grp_alloc *bank, void *data) +{ + bank->data_end = (char *)data + (bank->data_end - bank->data); + bank->data = (char *)data; +} + +/** + * Phase 2: allocate an arbitrary data block with given @a size. + */ +static inline void * +grp_alloc_create_data(struct grp_alloc *bank, size_t size) +{ + char *res = bank->data; + bank->data += size; + return res; +} + +/** + * Phase 2: allocate and fill a string with given data @a src of given size + * @a src_size, including null-termination. Return new string. + */ +static inline char * +grp_alloc_create_str(struct grp_alloc *bank, const char *src, size_t src_size) +{ + bank->data_end--; + *bank->data_end = 0; + bank->data_end -= src_size; + memcpy(bank->data_end, src, src_size); + return bank->data_end; +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 1f0eee4f718579937520becf92f76b4070e608c5..81ba9aa503961364aa76a2f9d7d831f38c2e5134 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -295,3 +295,6 @@ target_link_libraries(serializer.test unit box ${LUAJIT_LIBRARIES}) add_executable(watcher.test watcher.c) target_link_libraries(watcher.test unit box) + +add_executable(grp_alloc.test grp_alloc.c box_test_utils.c) +target_link_libraries(grp_alloc.test unit) diff --git a/test/unit/grp_alloc.c b/test/unit/grp_alloc.c new file mode 100644 index 0000000000000000000000000000000000000000..eafa6772e876c31e554fdb7763b0732e827d13f3 --- /dev/null +++ b/test/unit/grp_alloc.c @@ -0,0 +1,95 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright 2010-2021, Tarantool AUTHORS, please see AUTHORS file. + */ + +#include "salad/grp_alloc.h" +#include "trivia/util.h" + +#include "unit.h" + +struct test { + int *array; + size_t array_size; + const char *name; + const char *description; +}; + +struct test * +test_new(int *array, size_t array_size, + const char *name, size_t name_len, + const char *description, size_t description_len) +{ + struct grp_alloc bank = grp_alloc_initializer(); + size_t array_data_size = array_size * sizeof(*array); + grp_alloc_reserve_data(&bank, array_data_size); + grp_alloc_reserve_str(&bank, name_len); + grp_alloc_reserve_str(&bank, description_len); + size_t total_size = sizeof(struct test) + grp_alloc_size(&bank); + struct test *res = xmalloc(total_size); + grp_alloc_use(&bank, res + 1); + res->array = grp_alloc_create_data(&bank, array_data_size); + memcpy(res->array, array, array_data_size); + res->name = grp_alloc_create_str(&bank, name, name_len); + res->description = grp_alloc_create_str(&bank, description, + description_len); + return res; +} + +static void +check_test_new(int *array, size_t array_size, + const char *name, size_t name_len, + const char *description, size_t description_len) +{ + header(); + plan(11); + + struct test *t = test_new(array, array_size, name, name_len, + description, description_len); + size_t array_data_size = array_size * sizeof(*array); + size_t total = array_data_size + sizeof(*t) + + name_len + 1 + description_len + 1; + char *b = (char *)t; + char *e = b + total; + + ok((char *)t->array > b, "location"); + ok((char *)t->array < e, "location"); + ok((char *)t->name > b, "location"); + ok((char *)t->name < e, "location"); + ok((char *)t->description > b, "location"); + ok((char *)t->description < e, "location"); + is(memcmp(t->array, array, array_data_size), 0, "data"); + is(memcmp(t->name, name, name_len), 0, "data"); + is(t->name[name_len], 0, "null-termination symbol"); + is(memcmp(t->description, description, description_len), 0, "data"); + is(t->description[description_len], 0, "null-termination symbol"); + + check_plan(); + footer(); +} + +static void +test_simple(void) +{ + header(); + plan(3); + + int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + check_test_new(arr, 3, "test", 4, "abc", 3); + check_test_new(arr, 10, "alligator", 9, "x", 1); + check_test_new(arr, 1, "qwerty", 6, "as", 2); + + check_plan(); + footer(); +} + +int +main(void) +{ + header(); + plan(1); + test_simple(); + footer(); + return check_plan(); +} diff --git a/test/unit/grp_alloc.result b/test/unit/grp_alloc.result new file mode 100644 index 0000000000000000000000000000000000000000..98c00f4eb353f0e0085146ed67970e3f00525574 --- /dev/null +++ b/test/unit/grp_alloc.result @@ -0,0 +1,52 @@ + *** main *** +1..1 + *** test_simple *** + 1..3 + *** check_test_new *** + 1..11 + ok 1 - location + ok 2 - location + ok 3 - location + ok 4 - location + ok 5 - location + ok 6 - location + ok 7 - data + ok 8 - data + ok 9 - null-termination symbol + ok 10 - data + ok 11 - null-termination symbol + ok 1 - subtests + *** check_test_new: done *** + *** check_test_new *** + 1..11 + ok 1 - location + ok 2 - location + ok 3 - location + ok 4 - location + ok 5 - location + ok 6 - location + ok 7 - data + ok 8 - data + ok 9 - null-termination symbol + ok 10 - data + ok 11 - null-termination symbol + ok 2 - subtests + *** check_test_new: done *** + *** check_test_new *** + 1..11 + ok 1 - location + ok 2 - location + ok 3 - location + ok 4 - location + ok 5 - location + ok 6 - location + ok 7 - data + ok 8 - data + ok 9 - null-termination symbol + ok 10 - data + ok 11 - null-termination symbol + ok 3 - subtests + *** check_test_new: done *** +ok 1 - subtests + *** test_simple: done *** + *** main: done ***