diff --git a/.gitignore b/.gitignore index e50a5cc..c32eebb 100644 --- a/.gitignore +++ b/.gitignore @@ -58,6 +58,7 @@ src/tests/test_pthread_wait src/tests/test_mutex_lock_perf src/tests/test_queue_perf src/tests/test_normalize_path +src/tests/test_sorted_array # other *.swp diff --git a/src/array_allocator.h b/src/array_allocator.h index de6ff3a..cc5a17d 100644 --- a/src/array_allocator.h +++ b/src/array_allocator.h @@ -71,6 +71,7 @@ extern "C" { int array_compare_element_int32(const int32_t *n1, const int32_t *n2); + #define i64_array_allocator_init(ctx, min_bits, max_bits) \ array_allocator_init(ctx, "i64", sizeof(int64_t), min_bits, max_bits) @@ -84,6 +85,19 @@ extern "C" { array_allocator_free(ctx, (VoidArray *)array) +#define i32_array_allocator_init(ctx, min_bits, max_bits) \ + array_allocator_init(ctx, "i32", sizeof(int32_t), min_bits, max_bits) + +#define i32_array_allocator_alloc(ctx, target_count) \ + (I32Array *)array_allocator_alloc(ctx, target_count) + +#define i32_array_allocator_realloc(ctx, old_array, target_count) \ + (I32Array *)array_allocator_realloc(ctx, (VoidArray *)old_array, target_count) + +#define i32_array_allocator_free(ctx, array) \ + array_allocator_free(ctx, (VoidArray *)array) + + #ifdef __cplusplus } #endif diff --git a/src/fast_allocator.c b/src/fast_allocator.c index da6b6a0..23941eb 100644 --- a/src/fast_allocator.c +++ b/src/fast_allocator.c @@ -148,14 +148,13 @@ static int region_init(struct fast_allocator_context *acontext, int result; int bytes; int element_size; - int allocator_count; struct fast_allocator_info *allocator; char *name; char name_buff[FAST_MBLOCK_NAME_SIZE]; region->pad_mask = region->step - 1; - allocator_count = (region->end - region->start) / region->step; - bytes = sizeof(struct fast_allocator_info) * allocator_count; + region->count = (region->end - region->start) / region->step; + bytes = sizeof(struct fast_allocator_info) * region->count; region->allocators = (struct fast_allocator_info *)fc_malloc(bytes); if (region->allocators == NULL) { @@ -163,7 +162,7 @@ static int region_init(struct fast_allocator_context *acontext, } memset(region->allocators, 0, bytes); - if ((result=allocator_array_check_capacity(acontext, allocator_count)) != 0) + if ((result=allocator_array_check_capacity(acontext, region->count)) != 0) { return result; } @@ -171,9 +170,10 @@ static int region_init(struct fast_allocator_context *acontext, name = name_buff; result = 0; allocator = region->allocators; - for (element_size=region->start+region->step; element_size<=region->end; - element_size+=region->step,allocator++) - { + for (element_size = region->start + region->step; + element_size <= region->end; + element_size += region->step, allocator++) + { if (mblock_name_prefix != NULL) { snprintf(name, FAST_MBLOCK_NAME_SIZE, "%s-%d", @@ -276,30 +276,42 @@ int fast_allocator_init_ex(struct fast_allocator_context *acontext, result = EINVAL; break; } - if (pRegion->step <= 0 || !is_power2(pRegion->step)) + if (pRegion->step <= 0) { logError("file: "__FILE__", line: %d, " - "invalid step: %d", + "invalid step: %d <= 0", __LINE__, pRegion->step); result = EINVAL; break; } - if (pRegion->start % pRegion->step != 0) - { - logError("file: "__FILE__", line: %d, " - "invalid start: %d, must multiple of step: %d", - __LINE__, pRegion->start, pRegion->step); - result = EINVAL; - break; - } - if (pRegion->end % pRegion->step != 0) - { - logError("file: "__FILE__", line: %d, " - "invalid end: %d, must multiple of step: %d", - __LINE__, pRegion->end, pRegion->step); - result = EINVAL; - break; - } + + if ((pRegion->end - pRegion->start) / pRegion->step > 1) + { + if (!is_power2(pRegion->step)) + { + logError("file: "__FILE__", line: %d, " + "invalid step: %d, expect power of 2", + __LINE__, pRegion->step); + result = EINVAL; + break; + } + if (pRegion->start % pRegion->step != 0) + { + logError("file: "__FILE__", line: %d, " + "invalid start: %d, must multiple of step: %d", + __LINE__, pRegion->start, pRegion->step); + result = EINVAL; + break; + } + if (pRegion->end % pRegion->step != 0) + { + logError("file: "__FILE__", line: %d, " + "invalid end: %d, must multiple of step: %d", + __LINE__, pRegion->end, pRegion->step); + result = EINVAL; + break; + } + } previous_end = pRegion->end; if ((result=region_init(acontext, mblock_name_prefix, pRegion)) != 0) @@ -368,8 +380,8 @@ void fast_allocator_destroy(struct fast_allocator_context *acontext) memset(acontext, 0, sizeof(*acontext)); } -static struct fast_allocator_info *get_allocator(struct fast_allocator_context *acontext, - int *alloc_bytes) +static struct fast_allocator_info *get_allocator(struct fast_allocator_context + *acontext, int *alloc_bytes) { struct fast_region_info *pRegion; struct fast_region_info *region_end; @@ -378,11 +390,16 @@ static struct fast_allocator_info *get_allocator(struct fast_allocator_context * for (pRegion=acontext->regions; pRegionend) - { - *alloc_bytes = BYTES_ALIGN(*alloc_bytes, pRegion->pad_mask); - return pRegion->allocators + ((*alloc_bytes - - pRegion->start) / pRegion->step) - 1; - } + { + if (pRegion->count == 1) { + *alloc_bytes = pRegion->allocators[0].mblock.info.element_size; + return pRegion->allocators + 0; + } else { + *alloc_bytes = BYTES_ALIGN(*alloc_bytes, pRegion->pad_mask); + return pRegion->allocators + ((*alloc_bytes - + pRegion->start) / pRegion->step) - 1; + } + } } return &malloc_allocator; @@ -513,7 +530,8 @@ void fast_allocator_free(struct fast_allocator_context *acontext, void *ptr) return; } - allocator_info = acontext->allocator_array.allocators[pWrapper->allocator_index]; + allocator_info = acontext->allocator_array. + allocators[pWrapper->allocator_index]; if (pWrapper->magic_number != allocator_info->magic_number) { logError("file: "__FILE__", line: %d, " @@ -531,10 +549,11 @@ void fast_allocator_free(struct fast_allocator_context *acontext, void *ptr) fast_mblock_free_object(&allocator_info->mblock, obj); } else - { - fast_allocator_malloc_trunk_notify_func(-1 * pWrapper->alloc_bytes, acontext); - free(obj); - } + { + fast_allocator_malloc_trunk_notify_func(-1 * + pWrapper->alloc_bytes, acontext); + free(obj); + } } char *fast_allocator_memdup(struct fast_allocator_context *acontext, diff --git a/src/fast_allocator.h b/src/fast_allocator.h index 57fa987..eaaa3b3 100644 --- a/src/fast_allocator.h +++ b/src/fast_allocator.h @@ -40,6 +40,7 @@ struct fast_region_info int step; int alloc_elements_once; int pad_mask; //for internal use + int count; struct fast_allocator_info *allocators; }; diff --git a/src/sorted_array.c b/src/sorted_array.c index bde4822..b291738 100644 --- a/src/sorted_array.c +++ b/src/sorted_array.c @@ -25,25 +25,28 @@ void sorted_array_init(SortedArrayContext *ctx, ctx->compare_func = compare_func; } -static void *sorted_array_bsearch(SortedArrayContext *ctx, void *base, +static char *sorted_array_bsearch(SortedArrayContext *ctx, char *base, const int count, const void *element, int *insert_pos) { int low; int high; int mid; int compr; + char *current; *insert_pos = 0; low = 0; high = count - 1; while (low <= high) { mid = (low + high) / 2; - compr = ctx->compare_func(base + mid, element); + current = base + ctx->element_size * mid; + compr = ctx->compare_func(current, element); if (compr < 0) { low = mid + 1; *insert_pos = low; } else if (compr == 0) { - return base + mid; + *insert_pos = mid; + return current; } else { high = mid - 1; *insert_pos = mid; @@ -58,8 +61,9 @@ int sorted_array_insert(SortedArrayContext *ctx, { int insert_pos; int move_count; - void *found; - void *end; + char *current; + char *found; + char *end; found = sorted_array_bsearch(ctx, base, *count, element, &insert_pos); if (found != NULL) { @@ -67,20 +71,39 @@ int sorted_array_insert(SortedArrayContext *ctx, return EEXIST; } - found++; - end = base + *count; + found += ctx->element_size; + end = (char *)base + ctx->element_size * (*count); while (found < end && ctx->compare_func(found, element) == 0) { - found++; + insert_pos++; + found += ctx->element_size; } - insert_pos = found - base; } + current = (char *)base + ctx->element_size * insert_pos; move_count = *count - insert_pos; if (move_count > 0) { - memmove(base + insert_pos + 1, base + insert_pos, - ctx->element_size * move_count); + memmove((char *)base + ctx->element_size * (insert_pos + 1), + current, ctx->element_size * move_count); } - memcpy(base + insert_pos, element, ctx->element_size); + + switch (ctx->element_size) { + case 1: + *current = *((char *)element); + break; + case 2: + *((short *)current) = *((short *)element); + break; + case 4: + *((int32_t *)current) = *((int32_t *)element); + break; + case 8: + *((int64_t *)current) = *((int64_t *)element); + break; + default: + memcpy(current, element, ctx->element_size); + break; + } + (*count)++; return 0; } @@ -90,11 +113,11 @@ int sorted_array_delete(SortedArrayContext *ctx, { int move_count; struct { - void *current; - void *start; - void *end; + char *current; + char *start; + char *end; } found; - void *array_end; + char *array_end; if ((found.current=bsearch(element, base, *count, ctx->element_size, ctx->compare_func)) == NULL) @@ -102,29 +125,29 @@ int sorted_array_delete(SortedArrayContext *ctx, return ENOENT; } - array_end = base + *count; + array_end = (char *)base + ctx->element_size * (*count); if (ctx->allow_duplicate) { found.start = found.current; - while (found.start > base && ctx->compare_func( - found.start - 1, element) == 0) + while (found.start > (char *)base && ctx->compare_func( + found.start - ctx->element_size, element) == 0) { - found.start--; + found.start -= ctx->element_size; } - found.end = ++found.current; + found.end = found.current + ctx->element_size; while (found.end < array_end && ctx->compare_func( found.end, element) == 0) { - found.end++; + found.end += ctx->element_size; } - *count -= found.end - found.start; + *count -= (found.end - found.start) / ctx->element_size; } else { found.start = found.current; - found.end = found.start + 1; + found.end = found.start + ctx->element_size; (*count)--; } - move_count = array_end - found.end; + move_count = (array_end - found.end) / ctx->element_size; if (move_count > 0) { memmove(found.start, found.end, ctx-> element_size * move_count); diff --git a/src/tests/Makefile b/src/tests/Makefile index 1a73a05..25370c4 100644 --- a/src/tests/Makefile +++ b/src/tests/Makefile @@ -10,7 +10,7 @@ ALL_PRGS = test_allocator test_skiplist test_multi_skiplist test_mblock test_blo test_json_parser test_pthread_lock test_uniq_skiplist test_split_string \ test_server_id_func test_pipe test_atomic test_file_write_hole test_file_lock \ test_pthread_wait test_thread_pool test_data_visible test_mutex_lock_perf \ - test_queue_perf test_normalize_path + test_queue_perf test_normalize_path test_sorted_array all: $(ALL_PRGS) .c: diff --git a/src/tests/test_sorted_array.c b/src/tests/test_sorted_array.c new file mode 100644 index 0000000..918f993 --- /dev/null +++ b/src/tests/test_sorted_array.c @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2020 YuQing <384681@qq.com> + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the Lesser GNU General Public License, version 3 + * or later ("LGPL"), as published by the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the Lesser GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "fastcommon/logger.h" +#include "fastcommon/shared_func.h" +#include "fastcommon/array_allocator.h" +#include "fastcommon/sorted_array.h" + +#define ELEMENT_COUNT 5 * 1000 + +static bool silence; + +static int test_i64() +{ + const int min_bits = 2; + const int max_bits = 16; + const bool allow_duplicate = false; + int result; + int i; + int index; + int last_index; + int64_t tmp; + int64_t start_time; + ArrayAllocatorContext allocator_ctx; + SortedArrayContext sarray_ctx; + I64Array *input; + I64Array *output; + + start_time = get_current_time_us(); + + sorted_i64_array_init(&sarray_ctx, allow_duplicate); + if ((result=i64_array_allocator_init(&allocator_ctx, + min_bits, max_bits)) != 0) + { + return result; + } + + if ((input=i64_array_allocator_alloc(&allocator_ctx, + ELEMENT_COUNT)) == NULL) + { + return ENOMEM; + } + if ((output=i64_array_allocator_alloc(&allocator_ctx, + ELEMENT_COUNT)) == NULL) + { + return ENOMEM; + } + + input->count = ELEMENT_COUNT; + for (i=0; icount; i++) { + input->elts[i] = i + 1; + } + + last_index = ELEMENT_COUNT - 1; + for (i=0; icount; i++) { + index = (int64_t)rand() * last_index / (int64_t)RAND_MAX; + tmp = input->elts[index]; + input->elts[index] = input->elts[last_index - index]; + input->elts[last_index - index] = tmp; + } + + for (i=0; icount; i++) { + sorted_array_insert(&sarray_ctx, output->elts, + &output->count, input->elts + i); + } + + assert(output->count == ELEMENT_COUNT); + for (i=0; icount; i++) { + assert(output->elts[i] == i + 1); + } + + for (i=last_index; i>=0; i--) { + sorted_array_delete(&sarray_ctx, output->elts, + &output->count, input->elts + i); + } + assert(output->count == 0); + + i64_array_allocator_free(&allocator_ctx, input); + i64_array_allocator_free(&allocator_ctx, output); + + if (!silence) { + printf("test i64 time used: %"PRId64" us\n", + get_current_time_us() - start_time); + } + return 0; +} + +static int test_i32() +{ + const int min_bits = 2; + const int max_bits = 16; + const bool allow_duplicate = false; + int result; + int i; + int index; + int last_index; + int32_t tmp; + int64_t start_time; + ArrayAllocatorContext allocator_ctx; + SortedArrayContext sarray_ctx; + I32Array *input; + I32Array *output; + + start_time = get_current_time_us(); + + sorted_i32_array_init(&sarray_ctx, allow_duplicate); + if ((result=i32_array_allocator_init(&allocator_ctx, + min_bits, max_bits)) != 0) + { + return result; + } + + if ((input=i32_array_allocator_alloc(&allocator_ctx, + ELEMENT_COUNT)) == NULL) + { + return ENOMEM; + } + if ((output=i32_array_allocator_alloc(&allocator_ctx, + ELEMENT_COUNT)) == NULL) + { + return ENOMEM; + } + + input->count = ELEMENT_COUNT; + for (i=0; icount; i++) { + input->elts[i] = i + 1; + } + + last_index = ELEMENT_COUNT - 1; + for (i=0; icount; i++) { + index = (int64_t)rand() * last_index / (int64_t)RAND_MAX; + tmp = input->elts[index]; + input->elts[index] = input->elts[last_index - index]; + input->elts[last_index - index] = tmp; + } + + for (i=0; icount; i++) { + sorted_array_insert(&sarray_ctx, output->elts, + &output->count, input->elts + i); + } + + assert(output->count == ELEMENT_COUNT); + for (i=0; icount; i++) { + assert(output->elts[i] == i + 1); + } + + for (i=last_index; i>=0; i--) { + sorted_array_delete(&sarray_ctx, output->elts, + &output->count, input->elts + i); + } + assert(output->count == 0); + + i32_array_allocator_free(&allocator_ctx, input); + i32_array_allocator_free(&allocator_ctx, output); + + if (!silence) { + printf("test i32 time used: %"PRId64" us\n", + get_current_time_us() - start_time); + } + return 0; +} + +int main(int argc, char *argv[]) +{ + int result; + int ch; + + srand(time(NULL)); + log_init(); + + while ((ch=getopt(argc, argv, "s")) != -1) { + switch (ch) { + case 's': + silence = true; + break; + default: + break; + } + } + + if ((result=test_i64()) != 0) { + return result; + } + + if ((result=test_i32()) != 0) { + return result; + } + + return 0; +}