add file src/tests/test_sorted_array.c
parent
2993b34e80
commit
55f1e139a9
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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; pRegion<region_end; pRegion++)
|
||||
{
|
||||
if (*alloc_bytes <= pRegion->end)
|
||||
{
|
||||
*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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
Loading…
Reference in New Issue