add file src/tests/test_sorted_array.c

pull/37/merge
YuQing 2021-09-13 16:54:09 +08:00
parent 2993b34e80
commit 55f1e139a9
7 changed files with 331 additions and 63 deletions

1
.gitignore vendored
View File

@ -58,6 +58,7 @@ src/tests/test_pthread_wait
src/tests/test_mutex_lock_perf src/tests/test_mutex_lock_perf
src/tests/test_queue_perf src/tests/test_queue_perf
src/tests/test_normalize_path src/tests/test_normalize_path
src/tests/test_sorted_array
# other # other
*.swp *.swp

View File

@ -71,6 +71,7 @@ extern "C" {
int array_compare_element_int32(const int32_t *n1, const int32_t *n2); int array_compare_element_int32(const int32_t *n1, const int32_t *n2);
#define i64_array_allocator_init(ctx, min_bits, max_bits) \ #define i64_array_allocator_init(ctx, min_bits, max_bits) \
array_allocator_init(ctx, "i64", sizeof(int64_t), 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) 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 #ifdef __cplusplus
} }
#endif #endif

View File

@ -148,14 +148,13 @@ static int region_init(struct fast_allocator_context *acontext,
int result; int result;
int bytes; int bytes;
int element_size; int element_size;
int allocator_count;
struct fast_allocator_info *allocator; struct fast_allocator_info *allocator;
char *name; char *name;
char name_buff[FAST_MBLOCK_NAME_SIZE]; char name_buff[FAST_MBLOCK_NAME_SIZE];
region->pad_mask = region->step - 1; region->pad_mask = region->step - 1;
allocator_count = (region->end - region->start) / region->step; region->count = (region->end - region->start) / region->step;
bytes = sizeof(struct fast_allocator_info) * allocator_count; bytes = sizeof(struct fast_allocator_info) * region->count;
region->allocators = (struct fast_allocator_info *)fc_malloc(bytes); region->allocators = (struct fast_allocator_info *)fc_malloc(bytes);
if (region->allocators == NULL) if (region->allocators == NULL)
{ {
@ -163,7 +162,7 @@ static int region_init(struct fast_allocator_context *acontext,
} }
memset(region->allocators, 0, bytes); 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; return result;
} }
@ -171,9 +170,10 @@ static int region_init(struct fast_allocator_context *acontext,
name = name_buff; name = name_buff;
result = 0; result = 0;
allocator = region->allocators; allocator = region->allocators;
for (element_size=region->start+region->step; element_size<=region->end; for (element_size = region->start + region->step;
element_size+=region->step,allocator++) element_size <= region->end;
{ element_size += region->step, allocator++)
{
if (mblock_name_prefix != NULL) if (mblock_name_prefix != NULL)
{ {
snprintf(name, FAST_MBLOCK_NAME_SIZE, "%s-%d", snprintf(name, FAST_MBLOCK_NAME_SIZE, "%s-%d",
@ -276,30 +276,42 @@ int fast_allocator_init_ex(struct fast_allocator_context *acontext,
result = EINVAL; result = EINVAL;
break; break;
} }
if (pRegion->step <= 0 || !is_power2(pRegion->step)) if (pRegion->step <= 0)
{ {
logError("file: "__FILE__", line: %d, " logError("file: "__FILE__", line: %d, "
"invalid step: %d", "invalid step: %d <= 0",
__LINE__, pRegion->step); __LINE__, pRegion->step);
result = EINVAL; result = EINVAL;
break; break;
} }
if (pRegion->start % pRegion->step != 0)
{ if ((pRegion->end - pRegion->start) / pRegion->step > 1)
logError("file: "__FILE__", line: %d, " {
"invalid start: %d, must multiple of step: %d", if (!is_power2(pRegion->step))
__LINE__, pRegion->start, pRegion->step); {
result = EINVAL; logError("file: "__FILE__", line: %d, "
break; "invalid step: %d, expect power of 2",
} __LINE__, pRegion->step);
if (pRegion->end % pRegion->step != 0) result = EINVAL;
{ break;
logError("file: "__FILE__", line: %d, " }
"invalid end: %d, must multiple of step: %d", if (pRegion->start % pRegion->step != 0)
__LINE__, pRegion->end, pRegion->step); {
result = EINVAL; logError("file: "__FILE__", line: %d, "
break; "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; previous_end = pRegion->end;
if ((result=region_init(acontext, mblock_name_prefix, pRegion)) != 0) 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)); memset(acontext, 0, sizeof(*acontext));
} }
static struct fast_allocator_info *get_allocator(struct fast_allocator_context *acontext, static struct fast_allocator_info *get_allocator(struct fast_allocator_context
int *alloc_bytes) *acontext, int *alloc_bytes)
{ {
struct fast_region_info *pRegion; struct fast_region_info *pRegion;
struct fast_region_info *region_end; 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; pRegion<region_end; pRegion++) for (pRegion=acontext->regions; pRegion<region_end; pRegion++)
{ {
if (*alloc_bytes <= pRegion->end) if (*alloc_bytes <= pRegion->end)
{ {
*alloc_bytes = BYTES_ALIGN(*alloc_bytes, pRegion->pad_mask); if (pRegion->count == 1) {
return pRegion->allocators + ((*alloc_bytes - *alloc_bytes = pRegion->allocators[0].mblock.info.element_size;
pRegion->start) / pRegion->step) - 1; 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; return &malloc_allocator;
@ -513,7 +530,8 @@ void fast_allocator_free(struct fast_allocator_context *acontext, void *ptr)
return; 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) if (pWrapper->magic_number != allocator_info->magic_number)
{ {
logError("file: "__FILE__", line: %d, " 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); fast_mblock_free_object(&allocator_info->mblock, obj);
} }
else else
{ {
fast_allocator_malloc_trunk_notify_func(-1 * pWrapper->alloc_bytes, acontext); fast_allocator_malloc_trunk_notify_func(-1 *
free(obj); pWrapper->alloc_bytes, acontext);
} free(obj);
}
} }
char *fast_allocator_memdup(struct fast_allocator_context *acontext, char *fast_allocator_memdup(struct fast_allocator_context *acontext,

View File

@ -40,6 +40,7 @@ struct fast_region_info
int step; int step;
int alloc_elements_once; int alloc_elements_once;
int pad_mask; //for internal use int pad_mask; //for internal use
int count;
struct fast_allocator_info *allocators; struct fast_allocator_info *allocators;
}; };

View File

@ -25,25 +25,28 @@ void sorted_array_init(SortedArrayContext *ctx,
ctx->compare_func = compare_func; 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) const int count, const void *element, int *insert_pos)
{ {
int low; int low;
int high; int high;
int mid; int mid;
int compr; int compr;
char *current;
*insert_pos = 0; *insert_pos = 0;
low = 0; low = 0;
high = count - 1; high = count - 1;
while (low <= high) { while (low <= high) {
mid = (low + high) / 2; 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) { if (compr < 0) {
low = mid + 1; low = mid + 1;
*insert_pos = low; *insert_pos = low;
} else if (compr == 0) { } else if (compr == 0) {
return base + mid; *insert_pos = mid;
return current;
} else { } else {
high = mid - 1; high = mid - 1;
*insert_pos = mid; *insert_pos = mid;
@ -58,8 +61,9 @@ int sorted_array_insert(SortedArrayContext *ctx,
{ {
int insert_pos; int insert_pos;
int move_count; int move_count;
void *found; char *current;
void *end; char *found;
char *end;
found = sorted_array_bsearch(ctx, base, *count, element, &insert_pos); found = sorted_array_bsearch(ctx, base, *count, element, &insert_pos);
if (found != NULL) { if (found != NULL) {
@ -67,20 +71,39 @@ int sorted_array_insert(SortedArrayContext *ctx,
return EEXIST; return EEXIST;
} }
found++; found += ctx->element_size;
end = base + *count; end = (char *)base + ctx->element_size * (*count);
while (found < end && ctx->compare_func(found, element) == 0) { 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; move_count = *count - insert_pos;
if (move_count > 0) { if (move_count > 0) {
memmove(base + insert_pos + 1, base + insert_pos, memmove((char *)base + ctx->element_size * (insert_pos + 1),
ctx->element_size * move_count); 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)++; (*count)++;
return 0; return 0;
} }
@ -90,11 +113,11 @@ int sorted_array_delete(SortedArrayContext *ctx,
{ {
int move_count; int move_count;
struct { struct {
void *current; char *current;
void *start; char *start;
void *end; char *end;
} found; } found;
void *array_end; char *array_end;
if ((found.current=bsearch(element, base, *count, ctx->element_size, if ((found.current=bsearch(element, base, *count, ctx->element_size,
ctx->compare_func)) == NULL) ctx->compare_func)) == NULL)
@ -102,29 +125,29 @@ int sorted_array_delete(SortedArrayContext *ctx,
return ENOENT; return ENOENT;
} }
array_end = base + *count; array_end = (char *)base + ctx->element_size * (*count);
if (ctx->allow_duplicate) { if (ctx->allow_duplicate) {
found.start = found.current; found.start = found.current;
while (found.start > base && ctx->compare_func( while (found.start > (char *)base && ctx->compare_func(
found.start - 1, element) == 0) 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( while (found.end < array_end && ctx->compare_func(
found.end, element) == 0) 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 { } else {
found.start = found.current; found.start = found.current;
found.end = found.start + 1; found.end = found.start + ctx->element_size;
(*count)--; (*count)--;
} }
move_count = array_end - found.end; move_count = (array_end - found.end) / ctx->element_size;
if (move_count > 0) { if (move_count > 0) {
memmove(found.start, found.end, ctx-> memmove(found.start, found.end, ctx->
element_size * move_count); element_size * move_count);

View File

@ -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_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_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_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) all: $(ALL_PRGS)
.c: .c:

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <inttypes.h>
#include <sys/time.h>
#include <assert.h>
#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; i<input->count; i++) {
input->elts[i] = i + 1;
}
last_index = ELEMENT_COUNT - 1;
for (i=0; i<input->count; 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; i<input->count; i++) {
sorted_array_insert(&sarray_ctx, output->elts,
&output->count, input->elts + i);
}
assert(output->count == ELEMENT_COUNT);
for (i=0; i<output->count; 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; i<input->count; i++) {
input->elts[i] = i + 1;
}
last_index = ELEMENT_COUNT - 1;
for (i=0; i<input->count; 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; i<input->count; i++) {
sorted_array_insert(&sarray_ctx, output->elts,
&output->count, input->elts + i);
}
assert(output->count == ELEMENT_COUNT);
for (i=0; i<output->count; 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;
}