Initial commit
This commit is contained in:
411
src/algo.c
Executable file
411
src/algo.c
Executable file
@@ -0,0 +1,411 @@
|
||||
#include <bh/algo.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
void bh_swap(void *dest,
|
||||
void *src,
|
||||
size_t size)
|
||||
{
|
||||
int tmp;
|
||||
|
||||
/* Swap bytes in int-sized chunks */
|
||||
while (size >= sizeof(tmp))
|
||||
{
|
||||
memmove(&tmp, dest, sizeof(tmp));
|
||||
memmove(dest, src, sizeof(tmp));
|
||||
memmove(src, &tmp, sizeof(tmp));
|
||||
|
||||
dest = (char *)dest + sizeof(tmp);
|
||||
src = (char *)src + sizeof(tmp);
|
||||
size -= sizeof(tmp);
|
||||
}
|
||||
|
||||
/* Swap the remaining size */
|
||||
if (size)
|
||||
{
|
||||
memmove(&tmp, dest, size);
|
||||
memmove(dest, src, size);
|
||||
memmove(src, &tmp, size);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void *bh_partition(void *pivot,
|
||||
void *array,
|
||||
size_t size,
|
||||
size_t element,
|
||||
bh_equal_cb_t equal)
|
||||
{
|
||||
char *start, *end, *i, *j;
|
||||
|
||||
/* Calculate start, end and item pointers */
|
||||
start = (char *)array;
|
||||
end = start + size * element;
|
||||
i = start;
|
||||
j = start + (size - 1) * element;
|
||||
|
||||
/* Iterate over array */
|
||||
while (1)
|
||||
{
|
||||
/* Find first element from the left that are bigger then pivot */
|
||||
while (i < end && equal(i, pivot) < 0)
|
||||
i += element;
|
||||
|
||||
/* Find first element from the right that are less then pivot*/
|
||||
while (j >= start && equal(j, pivot) >= 0)
|
||||
j -= element;
|
||||
|
||||
/* If item elemetns passed each other - we are done */
|
||||
if (i >= j)
|
||||
break;
|
||||
|
||||
/* Special case when pivot is actually part of the array */
|
||||
if (pivot == i)
|
||||
pivot = j;
|
||||
else if (pivot == j)
|
||||
pivot = i;
|
||||
|
||||
/* Swap elements and continue */
|
||||
bh_swap(i, j, element);
|
||||
i += element;
|
||||
j -= element;
|
||||
}
|
||||
|
||||
/* Return pointer to the middle of the partition */
|
||||
return j + element;
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
static void bh_sort_insert(void *array,
|
||||
size_t size,
|
||||
size_t element,
|
||||
bh_equal_cb_t equal)
|
||||
{
|
||||
size_t i, j;
|
||||
|
||||
/* Standard insert sort */
|
||||
for (i = 1; i < size; i++)
|
||||
{
|
||||
for (j = i; j >= 1; j -= 1)
|
||||
{
|
||||
char *lhs, *rhs;
|
||||
|
||||
lhs = (char *)array + j * element;
|
||||
rhs = (char *)array + (j - 1) * element;
|
||||
|
||||
if (equal(lhs, rhs) < 0)
|
||||
bh_swap(lhs, rhs, element);
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static void bh_sort_shell(void *array,
|
||||
size_t size,
|
||||
size_t element,
|
||||
bh_equal_cb_t equal)
|
||||
{
|
||||
static const size_t gaps[10] = {1750, 701, 301, 132, 57, 23, 10, 4, 1, 0};
|
||||
const size_t *gap;
|
||||
size_t i, j;
|
||||
|
||||
/* Shell sort with A102549 sequence */
|
||||
for (gap = gaps; *gap; ++gap)
|
||||
{
|
||||
for (i = *gap; i < size; i++)
|
||||
{
|
||||
for (j = i; j >= *gap; j -= *gap)
|
||||
{
|
||||
char *lhs, *rhs;
|
||||
|
||||
lhs = (char *)array + j * element;
|
||||
rhs = (char *)array + (j - *gap) * element;
|
||||
|
||||
if (equal(lhs, rhs) < 0)
|
||||
bh_swap(lhs, rhs, element);
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void bh_sort_heap(void *array,
|
||||
size_t size,
|
||||
size_t element,
|
||||
bh_equal_cb_t equal)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
bh_heap_make(array, size, element, equal);
|
||||
for (i = size; i > 0; i--)
|
||||
bh_heap_remove(array, i, element, equal);
|
||||
}
|
||||
|
||||
|
||||
static void bh_sort_intro_r(void *array,
|
||||
size_t size,
|
||||
size_t element,
|
||||
bh_equal_cb_t equal,
|
||||
size_t depth)
|
||||
{
|
||||
/* Introsort (with manual tail call optimization) */
|
||||
while (1)
|
||||
{
|
||||
char *start, *middle, *end, *pivot;
|
||||
|
||||
if (size < 16)
|
||||
{
|
||||
/* There are less then 16 elements left - use Shell/Insert sort */
|
||||
bh_sort_shell(array, size, element, equal);
|
||||
return;
|
||||
}
|
||||
else if (!depth)
|
||||
{
|
||||
/* Max depth reached - use heap sort */
|
||||
bh_sort_heap(array, size, element, equal);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Calculate start, middle and end pointers */
|
||||
start = (char *)array;
|
||||
middle = start + (size / 2) * element;
|
||||
end = start + (size - 1) * element;
|
||||
|
||||
/* Select middle element */
|
||||
if (equal(start, middle) > 0)
|
||||
{
|
||||
if (equal(middle, end) > 0)
|
||||
pivot = middle;
|
||||
else if (equal(start, end) > 0)
|
||||
pivot = end;
|
||||
else
|
||||
pivot = start;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (equal(start, end) > 0)
|
||||
pivot = start;
|
||||
else if (equal(middle, end) > 0)
|
||||
pivot = end;
|
||||
else
|
||||
pivot = middle;
|
||||
}
|
||||
|
||||
/* Partition the array */
|
||||
middle = bh_partition(pivot, array, size, element, equal);
|
||||
|
||||
/* Recursive call into first half */
|
||||
bh_sort_intro_r(array, (middle - start) / element, element, equal, depth - 1);
|
||||
|
||||
/* Setup array and size for the second half */
|
||||
array = middle;
|
||||
size = size - (middle - start) / element;
|
||||
depth--;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void bh_sort(void *array,
|
||||
size_t size,
|
||||
size_t element,
|
||||
bh_equal_cb_t equal)
|
||||
{
|
||||
size_t depth, depth_log;
|
||||
|
||||
/* Calculate max depth */
|
||||
depth = 0;
|
||||
depth_log = 1;
|
||||
while (depth_log < size)
|
||||
{
|
||||
depth++;
|
||||
depth_log <<= 1;
|
||||
}
|
||||
|
||||
/* Call main sorting function */
|
||||
bh_sort_intro_r(array, size, element, equal, depth);
|
||||
}
|
||||
|
||||
|
||||
void bh_heap_make(void *array,
|
||||
size_t size,
|
||||
size_t element,
|
||||
bh_equal_cb_t equal)
|
||||
{
|
||||
char *start, *end;
|
||||
size_t i;
|
||||
|
||||
/* Calculate start and end pointers of the array */
|
||||
start = (char *)array;
|
||||
end = start + size * element;
|
||||
|
||||
/* Bottom up heapify algorithm */
|
||||
for (i = size / 2 + 1; i; --i)
|
||||
{
|
||||
char *current, *left, *right;
|
||||
|
||||
/* Calculate current and children pointers */
|
||||
current = start + (i - 1) * element;
|
||||
left = start + (current - start) * 2 + element;
|
||||
right = left + element;
|
||||
|
||||
while (left < end)
|
||||
{
|
||||
char *biggest;
|
||||
|
||||
/* Determine biggest child */
|
||||
biggest = left;
|
||||
if (right < end && equal(left, right) < 0)
|
||||
biggest = right;
|
||||
|
||||
/* Compare biggest child with current */
|
||||
if (equal(current, biggest) < 0)
|
||||
{
|
||||
/* Swap content and recalculate children pointers */
|
||||
bh_swap(current, biggest, element);
|
||||
current = biggest;
|
||||
left = start + (current - start) * 2 + element;
|
||||
right = left + element;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void bh_heap_remove(void *array,
|
||||
size_t size,
|
||||
size_t element,
|
||||
bh_equal_cb_t equal)
|
||||
{
|
||||
char *start, *end, *current, *left, *right;
|
||||
|
||||
if (size <= 1)
|
||||
return;
|
||||
|
||||
/* Calculate start, end and children pointers */
|
||||
start = (char *)array;
|
||||
end = start + (size - 1) * element;
|
||||
current = start;
|
||||
left = start + (current - start) * 2 + element;
|
||||
right = left + element;
|
||||
|
||||
/* Swap first and last element */
|
||||
bh_swap(current, end, element);
|
||||
|
||||
/* Iterate until we reach the end */
|
||||
while (left < end)
|
||||
{
|
||||
char *biggest;
|
||||
|
||||
/* Determine biggest child */
|
||||
biggest = left;
|
||||
if (right < end && equal(left, right) < 0)
|
||||
biggest = right;
|
||||
|
||||
/* Compare biggest child with current */
|
||||
if (equal(current, biggest) < 0)
|
||||
{
|
||||
/* Swap content and recalculate children pointers */
|
||||
bh_swap(current, biggest, element);
|
||||
current = biggest;
|
||||
left = start + (current - start) * 2 + element;
|
||||
right = left + element;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void bh_heap_insert(void *value,
|
||||
void *array,
|
||||
size_t size,
|
||||
size_t element,
|
||||
bh_equal_cb_t equal)
|
||||
{
|
||||
char *start, *end, *current;
|
||||
|
||||
/* Calculate begin and end pointers */
|
||||
start = (char *)array;
|
||||
end = start + size * element;
|
||||
current = end;
|
||||
|
||||
/* Copy value into array */
|
||||
if (value)
|
||||
memmove(current, value, element);
|
||||
|
||||
while (current > start)
|
||||
{
|
||||
char *parent;
|
||||
|
||||
/* Calculate parent pointer */
|
||||
parent = start + (((current - start) / element - 1) / 2) * element;
|
||||
|
||||
/* Compare current and parent */
|
||||
if (equal(parent, current) < 0)
|
||||
{
|
||||
/* Swap current and parent */
|
||||
bh_swap(parent, current, element);
|
||||
current = parent;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void bh_heap_replace(void *value,
|
||||
void *array,
|
||||
size_t size,
|
||||
size_t element,
|
||||
bh_equal_cb_t equal)
|
||||
{
|
||||
char *start, *end, *current, *left, *right;
|
||||
|
||||
if (size < 1)
|
||||
return;
|
||||
|
||||
/* Calculate start, end and children pointers */
|
||||
start = (char *)array;
|
||||
end = start + size * element;
|
||||
current = start;
|
||||
left = start + (current - start) * 2 + element;
|
||||
right = left + element;
|
||||
|
||||
/* Copy supplied element to the first (top) position */
|
||||
if (value)
|
||||
memmove(current, value, element);
|
||||
else
|
||||
memmove(current, start + size * element, element);
|
||||
|
||||
/* Iterate until we reach the end */
|
||||
while (left < end)
|
||||
{
|
||||
char *biggest;
|
||||
|
||||
/* Determine biggest child */
|
||||
biggest = left;
|
||||
if (right < end && equal(left, right) < 0)
|
||||
biggest = right;
|
||||
|
||||
/* Compare biggest child with current */
|
||||
if (equal(current, biggest) < 0)
|
||||
{
|
||||
/* Swap content and recalculate children pointers */
|
||||
bh_swap(current, biggest, element);
|
||||
current = biggest;
|
||||
left = start + (current - start) * 2 + element;
|
||||
right = left + element;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
170
src/dummy/file.c
Normal file
170
src/dummy/file.c
Normal file
@@ -0,0 +1,170 @@
|
||||
#include <bh/io.h>
|
||||
|
||||
typedef struct bh_file_s
|
||||
{
|
||||
int implement;
|
||||
int me;
|
||||
} bh_file_t;
|
||||
|
||||
static int file_info(bh_file_t *file,
|
||||
size_t *size,
|
||||
const char **name);
|
||||
|
||||
static int file_init(bh_file_t *file,
|
||||
const char *path);
|
||||
|
||||
static int file_destroy(bh_file_t *file);
|
||||
|
||||
static int file_open(bh_file_t *file,
|
||||
int *mode);
|
||||
|
||||
static int file_close(bh_file_t *file);
|
||||
|
||||
static int file_read(bh_file_t *file,
|
||||
char *data,
|
||||
size_t *size);
|
||||
|
||||
static int file_write(bh_file_t *file,
|
||||
const char *data,
|
||||
size_t *size);
|
||||
|
||||
static int file_peek(bh_file_t* file,
|
||||
char *data,
|
||||
size_t *size);
|
||||
|
||||
static int file_flush(bh_file_t *file);
|
||||
|
||||
static int file_seek(bh_file_t *file,
|
||||
int64_t *pos,
|
||||
int *dir);
|
||||
|
||||
static int file_tell(bh_file_t *file,
|
||||
int64_t *pos);
|
||||
|
||||
static int file_size(bh_file_t *file,
|
||||
int64_t *size);
|
||||
|
||||
static int file_flags(bh_file_t *file);
|
||||
|
||||
static int file_clear(bh_file_t *file);
|
||||
|
||||
static int file_info(bh_file_t *file,
|
||||
size_t *size,
|
||||
const char **name)
|
||||
{
|
||||
static const char classname[] = BH_FILE_CLASSNAME;
|
||||
|
||||
if (size)
|
||||
*size = sizeof(*file);
|
||||
if (name)
|
||||
*name = classname;
|
||||
|
||||
return BH_NOIMPL;
|
||||
}
|
||||
|
||||
static int file_init(bh_file_t *file,
|
||||
const char *path)
|
||||
{
|
||||
return BH_NOIMPL;
|
||||
}
|
||||
|
||||
static int file_destroy(bh_file_t *file)
|
||||
{
|
||||
return BH_NOIMPL;
|
||||
}
|
||||
|
||||
static int file_open(bh_file_t *file,
|
||||
int *mode)
|
||||
{
|
||||
return BH_NOIMPL;
|
||||
}
|
||||
|
||||
static int file_close(bh_file_t *file)
|
||||
{
|
||||
return BH_NOIMPL;
|
||||
}
|
||||
|
||||
static int file_read(bh_file_t *file,
|
||||
char *data,
|
||||
size_t *size)
|
||||
{
|
||||
return BH_NOIMPL;
|
||||
}
|
||||
|
||||
static int file_write(bh_file_t *file,
|
||||
const char *data,
|
||||
size_t *size)
|
||||
{
|
||||
return BH_NOIMPL;
|
||||
}
|
||||
|
||||
static int file_peek(bh_file_t *file,
|
||||
char *data,
|
||||
size_t *size)
|
||||
{
|
||||
return BH_NOIMPL;
|
||||
}
|
||||
|
||||
static int file_flush(bh_file_t *file)
|
||||
{
|
||||
return BH_NOIMPL;
|
||||
}
|
||||
|
||||
static int file_seek(bh_file_t *file,
|
||||
int64_t *pos,
|
||||
int *dir)
|
||||
{
|
||||
return BH_NOIMPL;
|
||||
}
|
||||
|
||||
static int file_tell(bh_file_t *file,
|
||||
int64_t *pos)
|
||||
{
|
||||
return BH_NOIMPL;
|
||||
}
|
||||
|
||||
static int file_size(bh_file_t *file,
|
||||
int64_t *size)
|
||||
{
|
||||
return BH_NOIMPL;
|
||||
}
|
||||
|
||||
static int file_flags(bh_file_t *file)
|
||||
{
|
||||
return BH_IO_FLAG_ERROR;
|
||||
}
|
||||
|
||||
static int file_clear(bh_file_t *file)
|
||||
{
|
||||
return BH_NOIMPL;
|
||||
}
|
||||
|
||||
static int file_proc(bh_file_t *file,
|
||||
int type,
|
||||
void *arg1,
|
||||
void *arg2)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case BH_IO_INFO_CB: return file_info(file, (size_t *)arg1, (const char **)arg2);
|
||||
case BH_IO_INIT_CB: return file_init(file, (const char *)arg1);
|
||||
case BH_IO_DESTROY_CB: return file_destroy(file);
|
||||
case BH_IO_OPEN_CB: return file_open(file, (int *)arg1);
|
||||
case BH_IO_CLOSE_CB: return file_close(file);
|
||||
case BH_IO_READ_CB: return file_read(file, (char *)arg1, (size_t *)arg2);
|
||||
case BH_IO_WRITE_CB: return file_write(file, (const char *)arg1, (size_t *)arg2);
|
||||
case BH_IO_PEEK_CB: return file_peek(file, (char *)arg1, (size_t *)arg2);
|
||||
case BH_IO_FLUSH_CB: return file_flush(file);
|
||||
case BH_IO_SEEK_CB: return file_seek(file, (int64_t *)arg1, (int *)arg2);
|
||||
case BH_IO_TELL_CB: return file_tell(file, (int64_t *)arg1);
|
||||
case BH_IO_SIZE_CB: return file_size(file, (int64_t *)arg1);
|
||||
case BH_IO_FLAGS_CB: return file_flags(file);
|
||||
case BH_IO_CLEAR_CB: return file_clear(file);
|
||||
default: return BH_NOIMPL;
|
||||
}
|
||||
}
|
||||
|
||||
bh_io_t *bh_file_new(const char *path)
|
||||
{
|
||||
return bh_io_new((bh_io_func_t)file_proc, (void *)path);
|
||||
}
|
||||
390
src/hashmap.c
Executable file
390
src/hashmap.c
Executable file
@@ -0,0 +1,390 @@
|
||||
#include <bh/hashmap.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
typedef struct bh_hashmap_node_s
|
||||
{
|
||||
void *key;
|
||||
void *value;
|
||||
} bh_hashmap_node_t;
|
||||
|
||||
|
||||
struct bh_hashmap_s
|
||||
{
|
||||
bh_hashmap_node_t *data;
|
||||
size_t *psls;
|
||||
size_t size;
|
||||
size_t capacity;
|
||||
size_t threshold;
|
||||
bh_equal_cb_t equal;
|
||||
bh_hash_cb_t hash;
|
||||
float factor;
|
||||
};
|
||||
|
||||
|
||||
static void bh_hashmap_init(bh_hashmap_t *hashmap,
|
||||
bh_equal_cb_t equal,
|
||||
bh_hash_cb_t hash)
|
||||
{
|
||||
memset(hashmap, 0, sizeof(*hashmap));
|
||||
hashmap->factor = 0.75f;
|
||||
hashmap->equal = equal;
|
||||
hashmap->hash = hash;
|
||||
}
|
||||
|
||||
|
||||
static void bh_hashmap_destroy(bh_hashmap_t *hashmap)
|
||||
{
|
||||
if (hashmap->capacity)
|
||||
{
|
||||
free(hashmap->data);
|
||||
free(hashmap->psls);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int calc_capacity(size_t size,
|
||||
float factor,
|
||||
size_t *capacity,
|
||||
size_t *threshold)
|
||||
{
|
||||
/* Check if we need any capacity at all */
|
||||
if (!size)
|
||||
{
|
||||
*capacity = 0;
|
||||
*threshold = 0;
|
||||
return BH_OK;
|
||||
}
|
||||
|
||||
/* Calculate nearest power of 2 capacity */
|
||||
*capacity = 16;
|
||||
*threshold = *capacity * factor;
|
||||
while (size > *threshold)
|
||||
{
|
||||
*capacity *= 2;
|
||||
*threshold = *capacity * factor;
|
||||
|
||||
/* Catch capacity overflow */
|
||||
if (*capacity < 16)
|
||||
return BH_OOM;
|
||||
}
|
||||
|
||||
/* Catch malloc overflow */
|
||||
if (*capacity >= ((size_t)-1) / sizeof(bh_hashmap_node_t))
|
||||
return BH_OOM;
|
||||
|
||||
return BH_OK;
|
||||
}
|
||||
|
||||
|
||||
static void copy_hashmap(bh_hashmap_t *dest,
|
||||
bh_hashmap_t *src)
|
||||
{
|
||||
void *iter;
|
||||
|
||||
/* Iterate and insert data into hashmap */
|
||||
iter = bh_hashmap_iter_next(src, NULL);
|
||||
while (iter)
|
||||
{
|
||||
void *key, *value;
|
||||
|
||||
key = bh_hashmap_iter_key(iter);
|
||||
value = bh_hashmap_iter_value(iter);
|
||||
bh_hashmap_insert(dest, key, value);
|
||||
|
||||
iter = bh_hashmap_iter_next(src, iter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bh_hashmap_t *bh_hashmap_new(bh_equal_cb_t equal,
|
||||
bh_hash_cb_t hash)
|
||||
{
|
||||
bh_hashmap_t *result;
|
||||
|
||||
result = malloc(sizeof(*result));
|
||||
if (result)
|
||||
bh_hashmap_init(result, equal, hash);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void bh_hashmap_free(bh_hashmap_t *hashmap)
|
||||
{
|
||||
bh_hashmap_destroy(hashmap);
|
||||
free(hashmap);
|
||||
}
|
||||
|
||||
|
||||
void bh_hashmap_clear(bh_hashmap_t *hashmap)
|
||||
{
|
||||
if (hashmap->capacity)
|
||||
memset(hashmap->psls, 0, hashmap->capacity * sizeof(size_t));
|
||||
hashmap->size = 0;
|
||||
}
|
||||
|
||||
|
||||
int bh_hashmap_reserve(bh_hashmap_t *hashmap,
|
||||
size_t size)
|
||||
{
|
||||
bh_hashmap_t other;
|
||||
size_t capacity, threshold;
|
||||
|
||||
/* New capacity can't be smaller then current hashmap size */
|
||||
if (size < hashmap->size)
|
||||
size = hashmap->size;
|
||||
|
||||
/* Calculate new capacity */
|
||||
if (calc_capacity(size, hashmap->factor, &capacity, &threshold))
|
||||
return BH_OOM;
|
||||
|
||||
/* Prevent same size reallocation */
|
||||
if (capacity == hashmap->capacity)
|
||||
return BH_OK;
|
||||
|
||||
/* Initialize new hashmap */
|
||||
bh_hashmap_init(&other, hashmap->equal, hashmap->hash);
|
||||
other.factor = hashmap->factor;
|
||||
|
||||
if (capacity)
|
||||
{
|
||||
/* Allocate new memory for the hashmap */
|
||||
other.data = malloc(sizeof(*other.data) * capacity);
|
||||
other.psls = malloc(sizeof(size_t) * capacity);
|
||||
other.threshold = threshold;
|
||||
other.capacity = capacity;
|
||||
|
||||
/* Check for allocations failure */
|
||||
if (!other.data || !other.psls)
|
||||
{
|
||||
if (other.data)
|
||||
free(other.data);
|
||||
if (other.psls)
|
||||
free(other.psls);
|
||||
return BH_OOM;
|
||||
}
|
||||
|
||||
/* Reset probe sequence lengths */
|
||||
memset(other.psls, 0, sizeof(size_t) * other.capacity);
|
||||
|
||||
/* Copy data from old hashmap to the new hashmap */
|
||||
copy_hashmap(&other, hashmap);
|
||||
}
|
||||
|
||||
/* Swap hashmaps */
|
||||
bh_hashmap_destroy(hashmap);
|
||||
*hashmap = other;
|
||||
return BH_OK;
|
||||
}
|
||||
|
||||
|
||||
int bh_hashmap_insert(bh_hashmap_t *hashmap,
|
||||
void *key,
|
||||
void *value)
|
||||
{
|
||||
size_t bucket, psl, tmp_psl;
|
||||
bh_hashmap_node_t item, tmp;
|
||||
|
||||
/* Try to stay below hashmap threshold */
|
||||
if (hashmap->size + 1 > hashmap->threshold)
|
||||
if (bh_hashmap_reserve(hashmap, hashmap->size + 1))
|
||||
if (hashmap->size >= hashmap->capacity)
|
||||
return BH_OOM;
|
||||
|
||||
/* Prepare inserted data and set PSL to 1 */
|
||||
item.key = key;
|
||||
item.value = value;
|
||||
psl = 1;
|
||||
|
||||
/* Calculate prefered bucket index */
|
||||
bucket = hashmap->hash(key) & (hashmap->capacity - 1);
|
||||
|
||||
/* Find empty bucket */
|
||||
while (hashmap->psls[bucket])
|
||||
{
|
||||
/* Current bucket is richer then us - swap elements */
|
||||
if (psl > hashmap->psls[bucket])
|
||||
{
|
||||
tmp = hashmap->data[bucket];
|
||||
tmp_psl = hashmap->psls[bucket];
|
||||
hashmap->data[bucket] = item;
|
||||
hashmap->psls[bucket] = psl;
|
||||
item = tmp;
|
||||
psl = tmp_psl;
|
||||
}
|
||||
|
||||
bucket = (bucket + 1) & (hashmap->capacity - 1);
|
||||
psl++;
|
||||
}
|
||||
|
||||
/* Found empty bucket - place item here */
|
||||
hashmap->data[bucket] = item;
|
||||
hashmap->psls[bucket] = psl;
|
||||
hashmap->size++;
|
||||
|
||||
return BH_OK;
|
||||
}
|
||||
|
||||
|
||||
void bh_hashmap_remove(bh_hashmap_t *hashmap,
|
||||
void *key)
|
||||
{
|
||||
void *iter;
|
||||
|
||||
iter = bh_hashmap_iter_at(hashmap, key);
|
||||
if (iter)
|
||||
bh_hashmap_iter_remove(hashmap, iter);
|
||||
}
|
||||
|
||||
|
||||
int bh_hashmap_at(bh_hashmap_t *hashmap,
|
||||
void *key,
|
||||
void **value)
|
||||
{
|
||||
void *iter;
|
||||
iter = bh_hashmap_iter_at(hashmap, key);
|
||||
|
||||
if (!iter)
|
||||
return BH_NOTFOUND;
|
||||
|
||||
if (value)
|
||||
*value = bh_hashmap_iter_value(iter);
|
||||
|
||||
return BH_OK;
|
||||
}
|
||||
|
||||
|
||||
int bh_hashmap_empty(bh_hashmap_t *hashmap)
|
||||
{
|
||||
return !hashmap->size;
|
||||
}
|
||||
|
||||
|
||||
size_t bh_hashmap_size(bh_hashmap_t *hashmap)
|
||||
{
|
||||
return hashmap->size;
|
||||
}
|
||||
|
||||
|
||||
size_t bh_hashmap_capacity(bh_hashmap_t *hashmap)
|
||||
{
|
||||
return hashmap->capacity;
|
||||
}
|
||||
|
||||
|
||||
float bh_hashmap_factor(bh_hashmap_t *hashmap)
|
||||
{
|
||||
return hashmap->factor;
|
||||
}
|
||||
|
||||
|
||||
void bh_hashmap_set_factor(bh_hashmap_t *hashmap,
|
||||
float factor)
|
||||
{
|
||||
/* Limit the factor value to [0.15, 1.0] */
|
||||
factor = (factor > 1.0f) ? (1.0f) : (factor);
|
||||
factor = (factor < 0.15f) ? (0.15f) : (factor);
|
||||
|
||||
/* Calculate new threshold value */
|
||||
hashmap->factor = factor;
|
||||
hashmap->threshold = hashmap->capacity * factor;
|
||||
}
|
||||
|
||||
|
||||
void *bh_hashmap_iter_at(bh_hashmap_t *hashmap,
|
||||
void *key)
|
||||
{
|
||||
size_t bucket, psl;
|
||||
|
||||
/* Nothing can be in empty map */
|
||||
if (!hashmap->size)
|
||||
return NULL;
|
||||
|
||||
/* Calculate prefered bucket index and set PSL to 1 */
|
||||
bucket = hashmap->hash(key) & (hashmap->capacity - 1);
|
||||
psl = 1;
|
||||
|
||||
/* Iterate hashmap until we find element or find richer bucket */
|
||||
while (hashmap->psls[bucket] >= psl && hashmap->equal(hashmap->data[bucket].key, key))
|
||||
{
|
||||
bucket = (bucket + 1) & (hashmap->capacity - 1);
|
||||
psl++;
|
||||
}
|
||||
|
||||
/* If bucket is poorer or equal to us - we found our element */
|
||||
if (hashmap->psls[bucket] >= psl)
|
||||
return hashmap->data + bucket;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void *bh_hashmap_iter_next(bh_hashmap_t *hashmap,
|
||||
void *iter)
|
||||
{
|
||||
bh_hashmap_node_t *item;
|
||||
|
||||
item = (bh_hashmap_node_t *)iter;
|
||||
while (1)
|
||||
{
|
||||
/* Advance or set iterator to the first element */
|
||||
if (item)
|
||||
item++;
|
||||
else
|
||||
item = hashmap->data;
|
||||
|
||||
/* Check iterator for validity */
|
||||
if (item >= hashmap->data + hashmap->capacity)
|
||||
return NULL;
|
||||
|
||||
/* Check iterator's item PSL */
|
||||
if (hashmap->psls[item - hashmap->data])
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void bh_hashmap_iter_remove(bh_hashmap_t *hashmap,
|
||||
void *iter)
|
||||
{
|
||||
size_t bucket, next_bucket;
|
||||
|
||||
/* Check if hashmap is empty or we are given NULL iterator */
|
||||
if (!iter || !hashmap->size)
|
||||
return;
|
||||
|
||||
/* Adjust hashmap size, calculate current and next bucket index */
|
||||
hashmap->size--;
|
||||
bucket = (bh_hashmap_node_t *)iter - hashmap->data;
|
||||
next_bucket = (bucket + 1) & (hashmap->capacity - 1);
|
||||
|
||||
/* Shift all elements toward their preffered place */
|
||||
while (hashmap->psls[next_bucket] > 1)
|
||||
{
|
||||
/* Copy item and adjust PSL */
|
||||
hashmap->data[bucket] = hashmap->data[next_bucket];
|
||||
hashmap->psls[bucket] = hashmap->psls[next_bucket] - 1;
|
||||
|
||||
/* Calculate next bucket index */
|
||||
bucket = next_bucket;
|
||||
next_bucket = (bucket + 1) & (hashmap->capacity - 1);
|
||||
}
|
||||
|
||||
/* Reset bucket's PSL (mark empty) */
|
||||
hashmap->psls[bucket] = 0;
|
||||
}
|
||||
|
||||
|
||||
void *bh_hashmap_iter_key(void *iter)
|
||||
{
|
||||
return ((bh_hashmap_node_t *)iter)->key;
|
||||
}
|
||||
|
||||
|
||||
void *bh_hashmap_iter_value(void *iter)
|
||||
{
|
||||
return ((bh_hashmap_node_t *)iter)->value;
|
||||
}
|
||||
|
||||
224
src/io.c
Normal file
224
src/io.c
Normal file
@@ -0,0 +1,224 @@
|
||||
#include <bh/io.h>
|
||||
#include <malloc.h>
|
||||
|
||||
|
||||
#define BUFFER_SIZE (sizeof(char *))
|
||||
|
||||
|
||||
struct bh_io_s
|
||||
{
|
||||
bh_io_func_t func;
|
||||
};
|
||||
|
||||
|
||||
bh_io_t *bh_io_new(bh_io_func_t func,
|
||||
void *data)
|
||||
{
|
||||
size_t requested;
|
||||
bh_io_t *io;
|
||||
|
||||
/* Get information about IO device size */
|
||||
if (func(NULL, BH_IO_INFO_CB, &requested, NULL))
|
||||
return NULL;
|
||||
|
||||
/* Allocate space for the IO device */
|
||||
io = malloc(sizeof(*io) + requested);
|
||||
if (!io)
|
||||
return NULL;
|
||||
|
||||
/* Initialize IO device */
|
||||
io->func = func;
|
||||
if (func(io + 1, BH_IO_INIT_CB, data, NULL))
|
||||
{
|
||||
free(io);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return io;
|
||||
}
|
||||
|
||||
|
||||
void bh_io_free(bh_io_t *io)
|
||||
{
|
||||
/* Prevent working with NULL io */
|
||||
if (!io)
|
||||
return;
|
||||
|
||||
/* Call the IO device destruction handler */
|
||||
io->func(io + 1, BH_IO_DESTROY_CB, NULL, NULL);
|
||||
}
|
||||
|
||||
|
||||
const char *bh_io_classname(bh_io_t *io)
|
||||
{
|
||||
const char *name;
|
||||
|
||||
if (!io)
|
||||
goto error;
|
||||
|
||||
if (io->func(io + 1, BH_IO_INFO_CB, NULL, &name) != BH_OK)
|
||||
goto error;
|
||||
|
||||
return name;
|
||||
|
||||
error:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
int bh_io_open(bh_io_t *io,
|
||||
int mode)
|
||||
{
|
||||
/* Prevent working with NULL io */
|
||||
if (!io)
|
||||
return BH_ERROR;
|
||||
|
||||
/* Call the IO device open handler with specified mode */
|
||||
return io->func(io + 1, BH_IO_OPEN_CB, &mode, NULL);
|
||||
}
|
||||
|
||||
|
||||
int bh_io_close(bh_io_t *io)
|
||||
{
|
||||
/* Prevent working with NULL io */
|
||||
if (!io)
|
||||
return BH_ERROR;
|
||||
|
||||
/* Call the IO device close handler */
|
||||
return io->func(io + 1, BH_IO_CLOSE_CB, NULL, NULL);
|
||||
}
|
||||
|
||||
|
||||
int bh_io_read(bh_io_t *io,
|
||||
char *buffer,
|
||||
size_t size,
|
||||
size_t *actual)
|
||||
{
|
||||
int code;
|
||||
|
||||
/* Prevent working with NULL io */
|
||||
if (!io)
|
||||
return BH_ERROR;
|
||||
|
||||
/* Call the IO device read handler */
|
||||
code = io->func(io + 1, BH_IO_READ_CB, buffer, &size);
|
||||
|
||||
/* If caller wants to know actual read size - report it back */
|
||||
if (actual)
|
||||
*actual = size;
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
|
||||
int bh_io_write(bh_io_t *io,
|
||||
const char *buffer,
|
||||
size_t size,
|
||||
size_t *actual)
|
||||
{
|
||||
int code;
|
||||
|
||||
/* Prevent working with NULL io */
|
||||
if (!io)
|
||||
return BH_ERROR;
|
||||
|
||||
/* Call the IO device write handler */
|
||||
code = io->func(io + 1, BH_IO_WRITE_CB, (void *)buffer, &size);
|
||||
|
||||
/* If caller wants to know actual written size - report it back */
|
||||
if (actual)
|
||||
*actual = size;
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
int bh_io_peek(bh_io_t *io,
|
||||
char *buffer,
|
||||
size_t size,
|
||||
size_t *actual)
|
||||
{
|
||||
int code;
|
||||
|
||||
/* Prevent working with NULL io */
|
||||
if (!io)
|
||||
return BH_ERROR;
|
||||
|
||||
/* Call the IO device peek handler */
|
||||
code = io->func(io + 1, BH_IO_PEEK_CB, (void *)buffer, &size);
|
||||
|
||||
/* If caller wants to know actual written size - report it back */
|
||||
if (actual)
|
||||
*actual = size;
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
|
||||
int bh_io_tell(bh_io_t *io,
|
||||
int64_t *position)
|
||||
{
|
||||
/* Prevent working with NULL io */
|
||||
if (!io)
|
||||
return BH_ERROR;
|
||||
|
||||
/* Call the IO device tell handler */
|
||||
return io->func(io + 1, BH_IO_TELL_CB, position, NULL);
|
||||
}
|
||||
|
||||
|
||||
int bh_io_seek(bh_io_t *io,
|
||||
int64_t position,
|
||||
int direction)
|
||||
{
|
||||
/* Prevent working with NULL io */
|
||||
if (!io)
|
||||
return BH_ERROR;
|
||||
|
||||
/* Call the IO device seek handler */
|
||||
return io->func(io + 1, BH_IO_SEEK_CB, &position, &direction);
|
||||
}
|
||||
|
||||
|
||||
int bh_io_flush(bh_io_t *io)
|
||||
{
|
||||
/* Prevent working with NULL io */
|
||||
if (!io)
|
||||
return BH_ERROR;
|
||||
|
||||
/* Call the IO device flush handler */
|
||||
return io->func(io + 1, BH_IO_FLUSH_CB, NULL, NULL);
|
||||
}
|
||||
|
||||
|
||||
int bh_io_size(bh_io_t *io,
|
||||
int64_t *size)
|
||||
{
|
||||
/* Prevent working with NULL io */
|
||||
if (!io)
|
||||
return BH_ERROR;
|
||||
|
||||
/* Call the IO device size handler */
|
||||
return io->func(io + 1, BH_IO_SIZE_CB, size, NULL);
|
||||
}
|
||||
|
||||
|
||||
int bh_io_flags(bh_io_t *io)
|
||||
{
|
||||
/* Prevent working with NULL io */
|
||||
if (!io)
|
||||
return BH_IO_FLAG_ERROR;
|
||||
|
||||
/* Call the IO device flags handler */
|
||||
return io->func(io + 1, BH_IO_FLAGS_CB, NULL, NULL);
|
||||
}
|
||||
|
||||
|
||||
int bh_io_clear(bh_io_t *io)
|
||||
{
|
||||
/* Prevent working with NULL io */
|
||||
if (!io)
|
||||
return BH_OK;
|
||||
|
||||
/* Call the IO device clear error handler */
|
||||
return io->func(io + 1, BH_IO_CLEAR_CB, NULL, NULL);
|
||||
}
|
||||
1778
src/math.c
Normal file
1778
src/math.c
Normal file
File diff suppressed because it is too large
Load Diff
405
src/posix/file.c
Normal file
405
src/posix/file.c
Normal file
@@ -0,0 +1,405 @@
|
||||
#include <bh/io.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
typedef struct bh_file_s
|
||||
{
|
||||
char *path;
|
||||
int mode;
|
||||
int flags;
|
||||
int handle;
|
||||
} bh_file_t;
|
||||
|
||||
|
||||
static int file_info(bh_file_t *file,
|
||||
size_t *size,
|
||||
const char **ident);
|
||||
|
||||
|
||||
static int file_init(bh_file_t *file,
|
||||
const char *path);
|
||||
|
||||
|
||||
static int file_destroy(bh_file_t *file);
|
||||
|
||||
|
||||
static int file_open(bh_file_t *file,
|
||||
int *mode);
|
||||
|
||||
|
||||
static int file_close(bh_file_t *file);
|
||||
|
||||
|
||||
static int file_read(bh_file_t *file,
|
||||
char *data,
|
||||
size_t *size);
|
||||
|
||||
|
||||
static int file_write(bh_file_t *file,
|
||||
const char *data,
|
||||
size_t *size);
|
||||
|
||||
|
||||
static int file_peek(bh_file_t *file,
|
||||
char *data,
|
||||
size_t *size);
|
||||
|
||||
|
||||
static int file_flush(bh_file_t *file);
|
||||
|
||||
|
||||
static int file_seek(bh_file_t *file,
|
||||
int64_t *pos,
|
||||
int *dir);
|
||||
|
||||
|
||||
static int file_tell(bh_file_t *file,
|
||||
int64_t *pos);
|
||||
|
||||
|
||||
static int file_size(bh_file_t *file,
|
||||
int64_t *size);
|
||||
|
||||
|
||||
static int file_flags(bh_file_t *file);
|
||||
|
||||
|
||||
static int file_clear(bh_file_t *file);
|
||||
|
||||
|
||||
static int file_info(bh_file_t *file,
|
||||
size_t *size,
|
||||
const char **name)
|
||||
{
|
||||
static const char classname[] = BH_FILE_CLASSNAME;
|
||||
|
||||
if (size)
|
||||
*size = sizeof(*file);
|
||||
if (name)
|
||||
*name = classname;
|
||||
return BH_OK;
|
||||
}
|
||||
|
||||
|
||||
static int file_init(bh_file_t *file,
|
||||
const char *path)
|
||||
{
|
||||
/* Check if path is valid */
|
||||
if (!path)
|
||||
return BH_ERROR;
|
||||
|
||||
/* Duplicate path string and initialize the file struct */
|
||||
file->path = strdup(path);
|
||||
file->mode = 0;
|
||||
file->handle = -1;
|
||||
file->flags = 0;
|
||||
|
||||
return BH_OK;
|
||||
}
|
||||
|
||||
|
||||
static int file_destroy(bh_file_t *file)
|
||||
{
|
||||
/* Close the file handle on destruction */
|
||||
if (file->handle != -1)
|
||||
file_close(file);
|
||||
|
||||
/* Free path string */
|
||||
free(file->path);
|
||||
|
||||
return BH_OK;
|
||||
}
|
||||
|
||||
|
||||
static int file_openflags(int mode)
|
||||
{
|
||||
int flags = 0;
|
||||
|
||||
/* Determine read/write flags */
|
||||
if ((mode & BH_IO_READWRITE) == BH_IO_READWRITE)
|
||||
flags |= O_RDWR;
|
||||
else if (mode & BH_IO_WRITE)
|
||||
flags |= O_WRONLY;
|
||||
else if (mode & BH_IO_READ)
|
||||
flags |= O_RDONLY;
|
||||
else
|
||||
return -1;
|
||||
|
||||
/* Check if existing file should be opened */
|
||||
if (!(mode & BH_IO_EXIST))
|
||||
{
|
||||
flags |= O_CREAT;
|
||||
|
||||
/* Check if file should be created */
|
||||
if (mode & BH_IO_CREATE)
|
||||
flags |= O_EXCL;
|
||||
}
|
||||
|
||||
/* Check if file should be opened in append mode */
|
||||
if (mode & BH_IO_APPEND)
|
||||
flags |= O_APPEND;
|
||||
|
||||
/* Check if file should be truncated */
|
||||
if (mode & BH_IO_TRUNCATE)
|
||||
flags |= O_TRUNC;
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
|
||||
static int file_open(bh_file_t *file,
|
||||
int *mode)
|
||||
{
|
||||
static const mode_t open_mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
|
||||
int flags;
|
||||
|
||||
/* If file is already opened - report error */
|
||||
if (file->handle != -1)
|
||||
return BH_ERROR;
|
||||
|
||||
/* Determine file open flags */
|
||||
flags = file_openflags(*mode);
|
||||
if (flags == -1)
|
||||
return BH_ERROR;
|
||||
|
||||
/* Open the file */
|
||||
file->handle = open(file->path, flags, open_mode);
|
||||
if (file->handle == -1)
|
||||
return BH_ERROR;
|
||||
|
||||
return BH_OK;
|
||||
}
|
||||
|
||||
|
||||
static int file_close(bh_file_t *file)
|
||||
{
|
||||
/* If file is closed - report error */
|
||||
if (file->handle == -1)
|
||||
return BH_ERROR;
|
||||
|
||||
/* Close and reset the file handle */
|
||||
close(file->handle);
|
||||
file->handle = -1;
|
||||
|
||||
return BH_OK;
|
||||
}
|
||||
|
||||
|
||||
static int file_read(bh_file_t *file,
|
||||
char *data,
|
||||
size_t *size)
|
||||
{
|
||||
ssize_t readed;
|
||||
|
||||
/* Check if file is open */
|
||||
if (file->handle == -1)
|
||||
goto error;
|
||||
|
||||
/* Read data from the file */
|
||||
readed = read(file->handle, data, *size);
|
||||
if (readed < 0)
|
||||
goto error;
|
||||
|
||||
/* Check for EOF condition */
|
||||
if (readed > 0)
|
||||
file->flags &= ~BH_IO_FLAG_EOF;
|
||||
else
|
||||
file->flags |= BH_IO_FLAG_EOF;
|
||||
|
||||
*size = readed;
|
||||
return BH_OK;
|
||||
|
||||
error:
|
||||
file->flags |= BH_IO_FLAG_ERROR;
|
||||
return BH_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static int file_write(bh_file_t *file,
|
||||
const char *data,
|
||||
size_t *size)
|
||||
{
|
||||
ssize_t written;
|
||||
|
||||
/* Check if file is open */
|
||||
if (file->handle == -1)
|
||||
goto error;
|
||||
|
||||
/* Write data to the file */
|
||||
written = write(file->handle, data, *size);
|
||||
if (written < 0)
|
||||
goto error;
|
||||
|
||||
/* Check for EOF condition */
|
||||
if (!written)
|
||||
file->flags |= BH_IO_FLAG_EOF;
|
||||
else
|
||||
file->flags &= ~BH_IO_FLAG_EOF;
|
||||
|
||||
*size = written;
|
||||
return BH_OK;
|
||||
|
||||
error:
|
||||
file->flags |= BH_IO_FLAG_ERROR;
|
||||
return BH_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static int file_peek(bh_file_t *file,
|
||||
char *data,
|
||||
size_t *size)
|
||||
{
|
||||
int64_t position;
|
||||
int direction;
|
||||
|
||||
/* Check if file is open */
|
||||
if (file->handle == -1)
|
||||
goto error;
|
||||
|
||||
/* Read data from the file */
|
||||
if (file_read(file, data, size))
|
||||
goto error;
|
||||
|
||||
/* Backtrack by the read amount */
|
||||
position = -((int64_t)*size);
|
||||
direction = BH_IO_SEEK_CUR;
|
||||
if (file_seek(file, &position, &direction))
|
||||
goto error;
|
||||
|
||||
return BH_OK;
|
||||
|
||||
error:
|
||||
file->flags |= BH_IO_FLAG_ERROR;
|
||||
return BH_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static int file_flush(bh_file_t *file)
|
||||
{
|
||||
/* Check if file is open */
|
||||
if (file->handle == -1)
|
||||
{
|
||||
file->flags |= BH_IO_FLAG_ERROR;
|
||||
return BH_ERROR;
|
||||
}
|
||||
|
||||
/* Flush the buffers */
|
||||
fsync(file->handle);
|
||||
return BH_OK;
|
||||
}
|
||||
|
||||
|
||||
static int file_seek(bh_file_t *file,
|
||||
int64_t *pos,
|
||||
int *dir)
|
||||
{
|
||||
/* Check if file is open */
|
||||
if (file->handle == -1)
|
||||
goto error;
|
||||
|
||||
/* Seek to the specified position */
|
||||
if (lseek(file->handle, *pos, *dir) == -1)
|
||||
goto error;
|
||||
|
||||
return BH_OK;
|
||||
|
||||
error:
|
||||
file->flags |= BH_IO_FLAG_ERROR;
|
||||
return BH_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static int file_tell(bh_file_t *file,
|
||||
int64_t *pos)
|
||||
{
|
||||
/* Check if file is open */
|
||||
if (file->handle == -1)
|
||||
goto error;
|
||||
|
||||
/* Get current offset in the file */
|
||||
if ((*pos = lseek(file->handle, 0, SEEK_CUR)) == -1)
|
||||
goto error;
|
||||
|
||||
return BH_OK;
|
||||
|
||||
error:
|
||||
file->flags |= BH_IO_FLAG_ERROR;
|
||||
return BH_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static int file_size(bh_file_t *file,
|
||||
int64_t *size)
|
||||
{
|
||||
struct stat sb;
|
||||
|
||||
/* Check if file is open */
|
||||
if (file->handle == -1)
|
||||
goto error;
|
||||
|
||||
/* Get file size from the OS */
|
||||
if (fstat(file->handle, &sb))
|
||||
goto error;
|
||||
|
||||
*size = sb.st_size;
|
||||
return BH_OK;
|
||||
|
||||
error:
|
||||
file->flags |= BH_IO_FLAG_ERROR;
|
||||
return BH_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static int file_flags(bh_file_t *file)
|
||||
{
|
||||
/* If file handle is valid - append IO_OPEN flag */
|
||||
if (file->handle != -1)
|
||||
return file->flags | BH_IO_FLAG_OPEN;
|
||||
|
||||
return file->flags;
|
||||
}
|
||||
|
||||
|
||||
static int file_clear(bh_file_t *file)
|
||||
{
|
||||
/* Clear IO_ERROR flag */
|
||||
file->flags &= ~BH_IO_FLAG_ERROR;
|
||||
return BH_OK;
|
||||
}
|
||||
|
||||
|
||||
static int file_proc(bh_file_t *file,
|
||||
int type,
|
||||
void *arg1,
|
||||
void *arg2)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case BH_IO_INFO_CB: return file_info(file, (size_t *)arg1, (const char **)arg2);
|
||||
case BH_IO_INIT_CB: return file_init(file, (const char *)arg1);
|
||||
case BH_IO_DESTROY_CB: return file_destroy(file);
|
||||
case BH_IO_OPEN_CB: return file_open(file, (int *)arg1);
|
||||
case BH_IO_CLOSE_CB: return file_close(file);
|
||||
case BH_IO_READ_CB: return file_read(file, (char *)arg1, (size_t *)arg2);
|
||||
case BH_IO_WRITE_CB: return file_write(file, (const char *)arg1, (size_t *)arg2);
|
||||
case BH_IO_PEEK_CB: return file_peek(file, (char *)arg1, (size_t *)arg2);
|
||||
case BH_IO_FLUSH_CB: return file_flush(file);
|
||||
case BH_IO_SEEK_CB: return file_seek(file, (int64_t *)arg1, (int *)arg2);
|
||||
case BH_IO_TELL_CB: return file_tell(file, (int64_t *)arg1);
|
||||
case BH_IO_SIZE_CB: return file_size(file, (int64_t *)arg1);
|
||||
case BH_IO_FLAGS_CB: return file_flags(file);
|
||||
case BH_IO_CLEAR_CB: return file_clear(file);
|
||||
default: return BH_NOIMPL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bh_io_t *bh_file_new(const char *path)
|
||||
{
|
||||
return bh_io_new((bh_io_func_t)file_proc, (void *)path);
|
||||
}
|
||||
209
src/queue.c
Executable file
209
src/queue.c
Executable file
@@ -0,0 +1,209 @@
|
||||
#include <bh/queue.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
struct bh_queue_s
|
||||
{
|
||||
void **data;
|
||||
size_t size;
|
||||
size_t head;
|
||||
size_t tail;
|
||||
size_t capacity;
|
||||
};
|
||||
|
||||
|
||||
static void bh_queue_init(bh_queue_t *queue)
|
||||
{
|
||||
memset(queue, 0, sizeof(*queue));
|
||||
}
|
||||
|
||||
|
||||
static void bh_queue_destroy(bh_queue_t *queue)
|
||||
{
|
||||
if (queue->capacity)
|
||||
free(queue->data);
|
||||
}
|
||||
|
||||
|
||||
static void queue_copy(bh_queue_t *dest,
|
||||
bh_queue_t *src)
|
||||
{
|
||||
void *iter;
|
||||
|
||||
/* Iterate over old queue and insert data into new queue */
|
||||
iter = bh_queue_iter_next(src, NULL);
|
||||
while (iter)
|
||||
{
|
||||
bh_queue_insert(dest, bh_queue_iter_value(iter));
|
||||
iter = bh_queue_iter_next(src, iter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bh_queue_t *bh_queue_new(void)
|
||||
{
|
||||
bh_queue_t *result;
|
||||
|
||||
result = malloc(sizeof(*result));
|
||||
if (result)
|
||||
bh_queue_init(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void bh_queue_free(bh_queue_t *queue)
|
||||
{
|
||||
bh_queue_destroy(queue);
|
||||
free(queue);
|
||||
}
|
||||
|
||||
|
||||
void bh_queue_clear(bh_queue_t *queue)
|
||||
{
|
||||
queue->head = 0;
|
||||
queue->tail = 0;
|
||||
queue->size = 0;
|
||||
}
|
||||
|
||||
|
||||
int bh_queue_reserve(bh_queue_t *queue,
|
||||
size_t size)
|
||||
{
|
||||
bh_queue_t other;
|
||||
|
||||
/* New capacity should be great or equal to current size */
|
||||
if (size < queue->size)
|
||||
size = queue->size;
|
||||
|
||||
/* Catch malloc overflow */
|
||||
if (size >= ((size_t)-1) / sizeof(void *))
|
||||
return BH_OOM;
|
||||
|
||||
/* Prevent same size memory reallocation */
|
||||
if (size == queue->capacity)
|
||||
return BH_OK;
|
||||
|
||||
/* Prepare new empty queue */
|
||||
bh_queue_init(&other);
|
||||
if (size)
|
||||
{
|
||||
/* Allocate new capacity for the queue */
|
||||
other.data = malloc(size * sizeof(void *));
|
||||
other.capacity = size;
|
||||
if (!other.data)
|
||||
return BH_OOM;
|
||||
|
||||
/* Iterate over old queue and insert data into new queue */
|
||||
queue_copy(&other, queue);
|
||||
}
|
||||
|
||||
/* If old queue had allocated data - free it */
|
||||
if (queue->capacity)
|
||||
free(queue->data);
|
||||
|
||||
/* Copy queue information */
|
||||
*queue = other;
|
||||
return BH_OK;
|
||||
}
|
||||
|
||||
|
||||
int bh_queue_insert(bh_queue_t *queue,
|
||||
void *value)
|
||||
{
|
||||
/* Check if queue can contain new element */
|
||||
if (queue->size + 1 > queue->capacity)
|
||||
{
|
||||
size_t capacity;
|
||||
|
||||
/* Check for capacity overflow and reserve capacity */
|
||||
capacity = (queue->capacity) ? (queue->capacity * 2) : (16);
|
||||
if (capacity < queue->capacity || bh_queue_reserve(queue, capacity))
|
||||
return BH_OOM;
|
||||
}
|
||||
|
||||
/* Increase queue size and advance tail index */
|
||||
queue->data[queue->tail] = value;
|
||||
queue->size++;
|
||||
if (++queue->tail >= queue->capacity)
|
||||
queue->tail = 0;
|
||||
|
||||
return BH_OK;
|
||||
}
|
||||
|
||||
|
||||
void bh_queue_remove(bh_queue_t *queue)
|
||||
{
|
||||
/* Do nothing if queue is empty */
|
||||
if (!queue->size)
|
||||
return;
|
||||
|
||||
/* Decrease queue size and advance head index */
|
||||
queue->size--;
|
||||
if (++queue->head >= queue->capacity)
|
||||
queue->head = 0;
|
||||
}
|
||||
|
||||
|
||||
int bh_queue_front(bh_queue_t *queue, void **value)
|
||||
{
|
||||
/* Do nothing if queue is empty */
|
||||
if (!queue->size)
|
||||
return BH_ERROR;
|
||||
|
||||
/* Return front element */
|
||||
*value = queue->data[queue->head];
|
||||
return BH_OK;
|
||||
}
|
||||
|
||||
|
||||
int bh_queue_empty(bh_queue_t *queue)
|
||||
{
|
||||
return !queue->size;
|
||||
}
|
||||
|
||||
|
||||
size_t bh_queue_size(bh_queue_t *queue)
|
||||
{
|
||||
return queue->size;
|
||||
}
|
||||
|
||||
|
||||
size_t bh_queue_capacity(bh_queue_t *queue)
|
||||
{
|
||||
return queue->capacity;
|
||||
}
|
||||
|
||||
|
||||
void *bh_queue_iter_next(bh_queue_t *queue,
|
||||
void *iter)
|
||||
{
|
||||
void **element = (void **)iter;
|
||||
|
||||
/* Do nothing if queue is empty */
|
||||
if (!queue->size)
|
||||
return NULL;
|
||||
|
||||
/* Advance or set iterator */
|
||||
if (element)
|
||||
{
|
||||
element++;
|
||||
if (element == queue->data + queue->capacity)
|
||||
element = queue->data;
|
||||
|
||||
/* Check if we reached the end */
|
||||
if (element == queue->data + queue->tail)
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
element = queue->data + queue->head;
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
|
||||
void *bh_queue_iter_value(void *iter)
|
||||
{
|
||||
return *(void **)iter;
|
||||
}
|
||||
405
src/win32/file.c
Normal file
405
src/win32/file.c
Normal file
@@ -0,0 +1,405 @@
|
||||
#include <bh/io.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <windows.h>
|
||||
|
||||
|
||||
typedef struct bh_file_s
|
||||
{
|
||||
char *path;
|
||||
int mode;
|
||||
int flags;
|
||||
HANDLE handle;
|
||||
} bh_file_t;
|
||||
|
||||
|
||||
static int file_info(bh_file_t *file,
|
||||
size_t *size,
|
||||
const char **name);
|
||||
|
||||
|
||||
static int file_init(bh_file_t *file,
|
||||
const char *path);
|
||||
|
||||
|
||||
static int file_destroy(bh_file_t *file);
|
||||
|
||||
|
||||
static int file_open(bh_file_t *file,
|
||||
int *mode);
|
||||
|
||||
|
||||
static int file_close(bh_file_t *file);
|
||||
|
||||
|
||||
static int file_read(bh_file_t *file,
|
||||
char *data,
|
||||
size_t *size);
|
||||
|
||||
|
||||
static int file_write(bh_file_t *file,
|
||||
const char *data,
|
||||
size_t *size);
|
||||
|
||||
|
||||
static int file_peek(bh_file_t *file,
|
||||
char *data,
|
||||
size_t *size);
|
||||
|
||||
|
||||
static int file_flush(bh_file_t *file);
|
||||
|
||||
|
||||
static int file_seek(bh_file_t *file,
|
||||
int64_t *pos,
|
||||
int *dir);
|
||||
|
||||
|
||||
static int file_tell(bh_file_t *file,
|
||||
int64_t *pos);
|
||||
|
||||
|
||||
static int file_size(bh_file_t *file,
|
||||
int64_t *size);
|
||||
|
||||
|
||||
static int file_flags(bh_file_t *file);
|
||||
|
||||
|
||||
static int file_clear(bh_file_t *file);
|
||||
|
||||
|
||||
static int file_info(bh_file_t *file,
|
||||
size_t *size,
|
||||
const char **name)
|
||||
{
|
||||
static const char classname[] = BH_FILE_CLASSNAME;
|
||||
|
||||
if (size)
|
||||
*size = sizeof(*file);
|
||||
if (name)
|
||||
*name = classname;
|
||||
|
||||
return BH_OK;
|
||||
}
|
||||
|
||||
|
||||
static int file_init(bh_file_t *file,
|
||||
const char *path)
|
||||
{
|
||||
/* Check if path is valid */
|
||||
if (!path)
|
||||
return BH_ERROR;
|
||||
|
||||
/* Duplicate path string and initialize the file struct */
|
||||
file->path = strdup(path);
|
||||
file->mode = 0;
|
||||
file->handle = INVALID_HANDLE_VALUE;
|
||||
file->flags = 0;
|
||||
|
||||
return BH_OK;
|
||||
}
|
||||
|
||||
|
||||
static int file_destroy(bh_file_t *file)
|
||||
{
|
||||
/* Close the file handle on destruction */
|
||||
if (file->handle != INVALID_HANDLE_VALUE)
|
||||
file_close(file);
|
||||
|
||||
/* Free path string */
|
||||
free(file->path);
|
||||
|
||||
return BH_OK;
|
||||
}
|
||||
|
||||
|
||||
static int file_open(bh_file_t *file,
|
||||
int *mode)
|
||||
{
|
||||
DWORD access = 0, how = 0;
|
||||
|
||||
/* Check if file is already openned */
|
||||
if (file->handle != INVALID_HANDLE_VALUE)
|
||||
return BH_OK;
|
||||
|
||||
/* Determine read/write access flags */
|
||||
if (*mode & BH_IO_READ)
|
||||
access |= GENERIC_READ;
|
||||
if (*mode & BH_IO_WRITE)
|
||||
access |= GENERIC_WRITE;
|
||||
|
||||
if (!access)
|
||||
return BH_ERROR;
|
||||
|
||||
/* Determine open mode flags */
|
||||
if (*mode & BH_IO_TRUNCATE)
|
||||
{
|
||||
switch (*mode & (BH_IO_CREATE | BH_IO_EXIST))
|
||||
{
|
||||
case 0: how = CREATE_ALWAYS; break;
|
||||
case BH_IO_CREATE: how = CREATE_NEW; break;
|
||||
case BH_IO_EXIST: how = TRUNCATE_EXISTING; break;
|
||||
default: return BH_ERROR;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (*mode & (BH_IO_CREATE | BH_IO_EXIST))
|
||||
{
|
||||
case 0: how = OPEN_ALWAYS; break;
|
||||
case BH_IO_CREATE: how = CREATE_NEW; break;
|
||||
case BH_IO_EXIST: how = OPEN_EXISTING; break;
|
||||
default: return BH_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/* Save mode that we are in and open file */
|
||||
file->mode = *mode;
|
||||
file->handle = CreateFileA(file->path, access, FILE_SHARE_READ, NULL, how, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
|
||||
if (file->handle == INVALID_HANDLE_VALUE)
|
||||
return BH_ERROR;
|
||||
|
||||
/* Truncate file if needed */
|
||||
if (*mode & BH_IO_TRUNCATE)
|
||||
SetEndOfFile(file->handle);
|
||||
|
||||
return BH_OK;
|
||||
}
|
||||
|
||||
|
||||
static int file_close(bh_file_t *file)
|
||||
{
|
||||
/* If file is opened - close it */
|
||||
if (file->handle == INVALID_HANDLE_VALUE)
|
||||
return BH_ERROR;
|
||||
|
||||
/* Reset handle and mode values */
|
||||
CloseHandle(file->handle);
|
||||
file->handle = INVALID_HANDLE_VALUE;
|
||||
file->mode = 0;
|
||||
return BH_OK;
|
||||
}
|
||||
|
||||
|
||||
static int file_read(bh_file_t *file,
|
||||
char *data,
|
||||
size_t *size)
|
||||
{
|
||||
DWORD readed;
|
||||
|
||||
/* Check if file is opened */
|
||||
if (file->handle == INVALID_HANDLE_VALUE)
|
||||
goto error;
|
||||
|
||||
/* Read data from the file */
|
||||
if (!ReadFile(file->handle, data, (DWORD)*size, &readed, NULL))
|
||||
goto error;
|
||||
|
||||
/* Check if we reached end of file */
|
||||
if (!readed)
|
||||
file->flags |= BH_IO_FLAG_EOF;
|
||||
else
|
||||
file->flags &= ~BH_IO_FLAG_EOF;
|
||||
|
||||
*size = readed;
|
||||
return BH_OK;
|
||||
|
||||
error:
|
||||
file->flags |= BH_IO_FLAG_ERROR;
|
||||
return BH_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static int file_write(bh_file_t *file,
|
||||
const char *data,
|
||||
size_t *size)
|
||||
{
|
||||
DWORD written;
|
||||
|
||||
/* Check if file is opened */
|
||||
if (file->handle == INVALID_HANDLE_VALUE)
|
||||
goto error;
|
||||
|
||||
/* Adjust current position in the file to the end */
|
||||
if (file->mode & BH_IO_APPEND)
|
||||
{
|
||||
LARGE_INTEGER position;
|
||||
|
||||
position.QuadPart = 0;
|
||||
if (!SetFilePointerEx(file->handle, position, NULL, FILE_END))
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Write data to the file */
|
||||
if (!WriteFile(file->handle, data, (DWORD)*size, &written, NULL))
|
||||
goto error;
|
||||
|
||||
/* Check for end of file */
|
||||
if (!written)
|
||||
file->flags |= BH_IO_FLAG_EOF;
|
||||
else
|
||||
file->flags &= ~BH_IO_FLAG_EOF;
|
||||
|
||||
*size = written;
|
||||
return BH_OK;
|
||||
|
||||
error:
|
||||
file->flags |= BH_IO_FLAG_ERROR;
|
||||
return BH_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static int file_peek(bh_file_t *file,
|
||||
char *data,
|
||||
size_t *size)
|
||||
{
|
||||
int64_t position;
|
||||
int direction;
|
||||
|
||||
/* Read data from the file */
|
||||
if (file_read(file, data, size))
|
||||
return BH_ERROR;
|
||||
|
||||
/* Backtrack by the read amount */
|
||||
position = -((int64_t)*size);
|
||||
direction = BH_IO_SEEK_CUR;
|
||||
if (file_seek(file, &position, &direction))
|
||||
return BH_ERROR;
|
||||
|
||||
return BH_OK;
|
||||
}
|
||||
|
||||
|
||||
static int file_flush(bh_file_t *file)
|
||||
{
|
||||
/* Check if file is opened */
|
||||
if (file->handle == INVALID_HANDLE_VALUE)
|
||||
goto error;
|
||||
|
||||
/* Flush OS buffers */
|
||||
FlushFileBuffers(file->handle);
|
||||
return BH_OK;
|
||||
|
||||
error:
|
||||
file->flags |= BH_IO_FLAG_ERROR;
|
||||
return BH_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static int file_seek(bh_file_t *file,
|
||||
int64_t *pos,
|
||||
int *dir)
|
||||
{
|
||||
LARGE_INTEGER position;
|
||||
|
||||
/* Check if file is opened */
|
||||
if (file->handle == INVALID_HANDLE_VALUE)
|
||||
goto error;
|
||||
|
||||
/* Set read/write position in the file */
|
||||
position.QuadPart = *pos;
|
||||
if (!SetFilePointerEx(file->handle, position, NULL, *dir))
|
||||
goto error;
|
||||
|
||||
return BH_OK;
|
||||
|
||||
error:
|
||||
file->flags |= BH_IO_FLAG_ERROR;
|
||||
return BH_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static int file_tell(bh_file_t *file,
|
||||
int64_t *pos)
|
||||
{
|
||||
LARGE_INTEGER dummy, position;
|
||||
|
||||
/* Check if file is opened */
|
||||
if (file->handle == INVALID_HANDLE_VALUE)
|
||||
goto error;
|
||||
|
||||
/* Readback current position in the file */
|
||||
dummy.QuadPart = 0;
|
||||
if (!SetFilePointerEx(file->handle, dummy, &position, BH_IO_SEEK_CUR))
|
||||
goto error;
|
||||
|
||||
*pos = position.QuadPart;
|
||||
return BH_OK;
|
||||
|
||||
error:
|
||||
file->flags |= BH_IO_FLAG_ERROR;
|
||||
return BH_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static int file_size(bh_file_t *file,
|
||||
int64_t *size)
|
||||
{
|
||||
LARGE_INTEGER dummy;
|
||||
|
||||
/* Check if file is opened */
|
||||
if (file->handle == INVALID_HANDLE_VALUE)
|
||||
goto error;
|
||||
|
||||
/* Get current file size */
|
||||
if (!GetFileSizeEx(file->handle, &dummy))
|
||||
goto error;
|
||||
|
||||
*size = dummy.QuadPart;
|
||||
return BH_OK;
|
||||
|
||||
error:
|
||||
file->flags |= BH_IO_FLAG_ERROR;
|
||||
return BH_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static int file_flags(bh_file_t *file)
|
||||
{
|
||||
/* If file handle is valid - append IO_OPEN flag */
|
||||
if (file->handle != INVALID_HANDLE_VALUE)
|
||||
return file->flags | BH_IO_FLAG_OPEN;
|
||||
return file->flags;
|
||||
}
|
||||
|
||||
|
||||
static int file_clear(bh_file_t *file)
|
||||
{
|
||||
/* Clear IO_ERROR flag */
|
||||
file->flags &= ~BH_IO_FLAG_ERROR;
|
||||
return BH_OK;
|
||||
}
|
||||
|
||||
|
||||
static int file_proc(bh_file_t *file,
|
||||
int type,
|
||||
void *arg1,
|
||||
void *arg2)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case BH_IO_INFO_CB: return file_info(file, (size_t *)arg1, (const char **)arg2);
|
||||
case BH_IO_INIT_CB: return file_init(file, (const char *)arg1);
|
||||
case BH_IO_DESTROY_CB: return file_destroy(file);
|
||||
case BH_IO_OPEN_CB: return file_open(file, (int *)arg1);
|
||||
case BH_IO_CLOSE_CB: return file_close(file);
|
||||
case BH_IO_READ_CB: return file_read(file, (char *)arg1, (size_t *)arg2);
|
||||
case BH_IO_WRITE_CB: return file_write(file, (const char *)arg1, (size_t *)arg2);
|
||||
case BH_IO_PEEK_CB: return file_peek(file, (char *)arg1, (size_t *)arg2);
|
||||
case BH_IO_FLUSH_CB: return file_flush(file);
|
||||
case BH_IO_SEEK_CB: return file_seek(file, (int64_t *)arg1, (int *)arg2);
|
||||
case BH_IO_TELL_CB: return file_tell(file, (int64_t *)arg1);
|
||||
case BH_IO_SIZE_CB: return file_size(file, (int64_t *)arg1);
|
||||
case BH_IO_FLAGS_CB: return file_flags(file);
|
||||
case BH_IO_CLEAR_CB: return file_clear(file);
|
||||
default: return BH_NOIMPL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bh_io_t *bh_file_new(const char *path)
|
||||
{
|
||||
return bh_io_new((bh_io_func_t)file_proc, (void *)path);
|
||||
}
|
||||
Reference in New Issue
Block a user