From af7667d169a4c1fa6c67f8b8e85357cc5f004993 Mon Sep 17 00:00:00 2001 From: Aleksandr Lyapunov <alyapunov@tarantool.org> Date: Wed, 24 Nov 2021 11:41:00 +0300 Subject: [PATCH] salad: introduce group alloc gpr_alloc is a small library that is designed for simplification of allocation of several objects in one memory block. It could be anything, but special attention is given to string objects, that are arrays of chars. Typical usage consist of two phases: gathering total needed size of memory block and creation of objects in given block. NO_DOC=refactoring NO_CHANGELOG=refactoring --- src/lib/salad/grp_alloc.h | 138 +++++++++++++++++++++++++++++++++++++ test/unit/CMakeLists.txt | 3 + test/unit/grp_alloc.c | 95 +++++++++++++++++++++++++ test/unit/grp_alloc.result | 52 ++++++++++++++ 4 files changed, 288 insertions(+) create mode 100644 src/lib/salad/grp_alloc.h create mode 100644 test/unit/grp_alloc.c create mode 100644 test/unit/grp_alloc.result diff --git a/src/lib/salad/grp_alloc.h b/src/lib/salad/grp_alloc.h new file mode 100644 index 0000000000..2084d7bb0f --- /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 1f0eee4f71..81ba9aa503 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 0000000000..eafa6772e8 --- /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 0000000000..98c00f4eb3 --- /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 *** -- GitLab