From 453843f51a13f12ceb28553d05147dd03ff2c1b6 Mon Sep 17 00:00:00 2001 From: Mikhail Romanko Date: Sat, 18 Jan 2025 17:24:36 +0300 Subject: Initial commit --- .gitignore | 72 ++ CMakeLists.txt | 100 ++ cmake/toolchain/i686-w64-mingw32.cmake | 18 + cmake/toolchain/x86_64-w64-mingw32.cmake | 18 + include/bh/algo.h | 126 +++ include/bh/common.h | 24 + include/bh/hashmap.h | 223 ++++ include/bh/io.h | 251 +++++ include/bh/math.h | 715 ++++++++++++ include/bh/queue.h | 149 +++ main.c | 10 + src/algo.c | 411 +++++++ src/dummy/file.c | 170 +++ src/hashmap.c | 390 +++++++ src/io.c | 224 ++++ src/math.c | 1778 ++++++++++++++++++++++++++++++ src/posix/file.c | 405 +++++++ src/queue.c | 209 ++++ src/win32/file.c | 405 +++++++ test/CMakeLists.txt | 24 + test/src/testalgo.c | 483 ++++++++ test/src/testfile.c | 630 +++++++++++ test/src/testhashmap.c | 234 ++++ test/src/testqueue.c | 171 +++ unit/CMakeLists.txt | 29 + unit/include/bh/unit.h | 29 + unit/src/unit.c | 56 + util/trim-whitespace.sh | 2 + 28 files changed, 7356 insertions(+) create mode 100755 .gitignore create mode 100644 CMakeLists.txt create mode 100644 cmake/toolchain/i686-w64-mingw32.cmake create mode 100644 cmake/toolchain/x86_64-w64-mingw32.cmake create mode 100755 include/bh/algo.h create mode 100644 include/bh/common.h create mode 100755 include/bh/hashmap.h create mode 100644 include/bh/io.h create mode 100644 include/bh/math.h create mode 100755 include/bh/queue.h create mode 100644 main.c create mode 100755 src/algo.c create mode 100644 src/dummy/file.c create mode 100755 src/hashmap.c create mode 100644 src/io.c create mode 100644 src/math.c create mode 100644 src/posix/file.c create mode 100755 src/queue.c create mode 100644 src/win32/file.c create mode 100755 test/CMakeLists.txt create mode 100644 test/src/testalgo.c create mode 100644 test/src/testfile.c create mode 100644 test/src/testhashmap.c create mode 100644 test/src/testqueue.c create mode 100755 unit/CMakeLists.txt create mode 100755 unit/include/bh/unit.h create mode 100755 unit/src/unit.c create mode 100755 util/trim-whitespace.sh diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..f1c4374 --- /dev/null +++ b/.gitignore @@ -0,0 +1,72 @@ +# CMake +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps + +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Build directories +[Bb]uild/ +[Dd]ebug/ +[Rr]elease/ +[Bb]in/ +[Oo]bj/ + +# Visual Studio Code +.vscode/ + +# Doxygen +[Dd]ocs/[Hh]tml + +# Coverage +[Cc]overage diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..761929d --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,100 @@ +cmake_minimum_required(VERSION 3.10) + +# Project and C standard configuration +project(bhlib LANGUAGES C) +set(CMAKE_C_STANDARD 90) +set(CMAKE_C_STANDARD_REQUIRED ON) + +# Project includes +include(CheckIPOSupported) +include(CheckIncludeFile) +include(CheckSymbolExists) + +# Check for IPO/LTO +check_ipo_supported(RESULT supported) + +# Unit testing and coverage configuration +set(TESTING ON CACHE BOOL "Enable unit-testing") +set(COVERAGE OFF CACHE BOOL "Enable coverage") + +if(supported) + message(STATUS "IPO/LTO enabled") + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) +endif() + +if(TESTING) + # Enable testing + include(CTest) + enable_testing() +endif(TESTING) + +# Set library code +set(BH_SOURCE + src/algo.c + src/hashmap.c + src/io.c + src/math.c + src/queue.c +) + +set(BH_HEADER + include/bh/common.h + include/bh/algo.h + include/bh/hashmap.h + include/bh/io.h + include/bh/math.h + include/bh/queue.h +) + +set(BH_INCLUDE_DIRS + include + ${PROJECT_BINARY_DIR}/include +) + +# Determine platform +if(WIN32) + message(STATUS "Platform - Win32") + + # Add platform dependent files + list(APPEND BH_SOURCE + src/win32/file.c + ) +elseif(UNIX) + message(STATUS "Platform: Unix (Linux/BSD/MacOS X)") + + # Add platform dependent files + list(APPEND BH_SOURCE + src/posix/file.c + ) +else() + message(STATUS "Platform: Unknown") + + # Add platform dependent files + list(APPEND BH_SOURCE + src/dummy/file.c + ) +endif() + +# Library +add_library(bhlib STATIC ${BH_SOURCE} ${BH_HEADER}) +target_include_directories(bhlib PUBLIC ${BH_INCLUDE_DIRS}) +target_link_libraries(bhlib PUBLIC m) + +# Enable all compiler warnings +if(MSVC) + target_compile_options(bhlib PRIVATE /W4 /WX) +else() + target_compile_options(bhlib PRIVATE -Wall -Wextra -Wpedantic -Werror) +endif() + +# Coverage +if(COVERAGE) + target_compile_options(bhlib PRIVATE -coverage) + target_link_options(bhlib PRIVATE -coverage) +endif() + +# Tests +if(TESTING) + add_subdirectory(unit) + add_subdirectory(test) +endif() diff --git a/cmake/toolchain/i686-w64-mingw32.cmake b/cmake/toolchain/i686-w64-mingw32.cmake new file mode 100644 index 0000000..4c98f9f --- /dev/null +++ b/cmake/toolchain/i686-w64-mingw32.cmake @@ -0,0 +1,18 @@ +# the name of the target operating system +SET(CMAKE_SYSTEM_NAME Windows) +SET(CMAKE_SYSTEM_PROCESSOR i686) + +# which compilers to use for C and C++ and ASM-ATT +SET(CMAKE_C_COMPILER /usr/bin/i686-w64-mingw32-gcc) +SET(CMAKE_CXX_COMPILER /usr/bin/i686-w64-mingw32-g++) +SET(CMAKE_ASM-ATT_COMPILER /usr/bin/i686-w64-mingw32-as) + +# here is the target environment located +SET(CMAKE_FIND_ROOT_PATH /usr/i686-w64-mingw32) + +# adjust the default behaviour of the FIND_XXX() commands: +# search headers and libraries in the target environment, search +# programs in the host environment +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/cmake/toolchain/x86_64-w64-mingw32.cmake b/cmake/toolchain/x86_64-w64-mingw32.cmake new file mode 100644 index 0000000..c486f6a --- /dev/null +++ b/cmake/toolchain/x86_64-w64-mingw32.cmake @@ -0,0 +1,18 @@ +# the name of the target operating system +SET(CMAKE_SYSTEM_NAME Windows) +SET(CMAKE_SYSTEM_PROCESSOR x86_64) + +# which compilers to use for C and C++ and ASM-ATT +SET(CMAKE_C_COMPILER /usr/bin/x86_64-w64-mingw32-gcc) +SET(CMAKE_CXX_COMPILER /usr/bin/x86_64-w64-mingw32-g++) +SET(CMAKE_ASM-ATT_COMPILER /usr/bin/x86_64-w64-mingw32-as) + +# here is the target environment located +SET(CMAKE_FIND_ROOT_PATH /usr/x86_64-w64-mingw32) + +# adjust the default behaviour of the FIND_XXX() commands: +# search headers and libraries in the target environment, search +# programs in the host environment +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/include/bh/algo.h b/include/bh/algo.h new file mode 100755 index 0000000..f549f1a --- /dev/null +++ b/include/bh/algo.h @@ -0,0 +1,126 @@ +#ifndef BH_ALGO_H +#define BH_ALGO_H + + +#include "common.h" + + +/** + * Exchanges the values of two elements. + * + * \param dest Pointer to the element + * \param src Pointer to the element + * \param size Element size in bytes + */ +void bh_swap(void *dest, + void *src, + size_t size); + + +/** + * Partitions the array relative to the specified pivot element. + * + * The pivot element can be part of the partitioned array. + * + * \param pivot Pointer to the pivot element + * \param array Pointer to the array + * \param size Array size + * \param element Element size in bytes + * \param equal Comparision function + * + * \return Pointer to the first element of the second partition. + */ +void *bh_partition(void *pivot, + void *array, + size_t size, + size_t element, + bh_equal_cb_t equal); + + +/** + * Sorts the array. + * + * \param array Pointer to the array + * \param size Array size + * \param element Element size in bytes + * \param equal Comparision function + */ +void bh_sort(void *array, + size_t size, + size_t element, + bh_equal_cb_t equal); + + +/** + * Makes heap in an array from the array. + * + * \param array Pointer to the array + * \param size Array size + * \param element Element size in bytes + * \param equal Comparision function + */ +void bh_heap_make(void *array, + size_t size, + size_t element, + bh_equal_cb_t equal); + + +/** + * Removes top/first element from a heap in an array. + * + * \param array Pointer to the array + * \param size Array size + * \param element Element size in bytes + * \param equal Comparasion function + */ +void bh_heap_remove(void *array, + size_t size, + size_t element, + bh_equal_cb_t equal); + + +/** + * Inserts new element into heap in an array. + * + * If the pointer to value is NULL, this function assumes that the new value + * is at the end of the array. + * + * \param value Pointer to the value + * \param array Pointer to the array + * \param size Array size + * \param element Element size in bytes + * \param equal Comparasion function + */ +void bh_heap_insert(void *value, + void *array, + size_t size, + size_t element, + bh_equal_cb_t equal); + + +/** + * Removes top/first element and inserts new element into heap in an array. + * + * If the pointer to value is NULL, this function assumes that the new value + * is at the end of the array. + * + * This function is roughly equivalent to the following code: + * \code + * bh_heap_remove(array, size, element, equal); + * bh_heap_insert(value, array, size - 1, element, equal); + * \endcode + * + * \param value Pointer to the value + * \param array Pointer to the array + * \param size Array size + * \param element Element size in bytes + * \param equal Comparasion function + */ +void bh_heap_replace(void *value, + void *array, + size_t size, + size_t element, + bh_equal_cb_t equal); + + +#endif /* BH_ALGO_H */ diff --git a/include/bh/common.h b/include/bh/common.h new file mode 100644 index 0000000..6f4ac88 --- /dev/null +++ b/include/bh/common.h @@ -0,0 +1,24 @@ +#ifndef BH_COMMON_H +#define BH_COMMON_H + +#include +#include + +#define BH_OK 0x0000 +#define BH_ERROR 0x0001 +#define BH_NOIMPL 0x0002 +#define BH_OOM 0x0003 +#define BH_INVALID 0x0004 +#define BH_FOUND 0x0005 +#define BH_NOTFOUND 0x0006 +#define BH_TIMEOUT 0x0007 + +#define BH_UNUSED(x) (void)(x) +#define BH_PTR2INT(x) ((intptr_t)(x)) +#define BH_INT2PTR(x) ((void*)(x)) + +typedef int (*bh_equal_cb_t)(const void *, const void *); +typedef size_t (*bh_hash_cb_t)(const void *); + +#endif /* BH_COMMON_H */ + diff --git a/include/bh/hashmap.h b/include/bh/hashmap.h new file mode 100755 index 0000000..da89609 --- /dev/null +++ b/include/bh/hashmap.h @@ -0,0 +1,223 @@ +#ifndef BH_HASHMAP_H +#define BH_HASHMAP_H + + +#include "common.h" + + +typedef struct bh_hashmap_s bh_hashmap_t; + + +/** + * Creates the new hashmap handle. + * + * \param equal Comparision function + * \param hash Key hash function + * + * \return On success, returns hashmap handle. + * \return On failure, returns a null pointer. + */ +bh_hashmap_t *bh_hashmap_new(bh_equal_cb_t equal, + bh_hash_cb_t hash); + + +/** + * Destroys the hashmap. + * + * \param hashmap Hashmap handle + */ +void bh_hashmap_free(bh_hashmap_t *hashmap); + + +/** + * Clears the hashmap. + * + * \param hashmap Hashmap handle + */ +void bh_hashmap_clear(bh_hashmap_t *hashmap); + + +/** + * Reserves space in the hashmap. + * + * This function can both expand and shrink the available space in hashmap. + * This function takes into account current hashmap load factor. + * + * \param hashmap Hahsmap handle + * \param size Capacity + * + * \note Calling this function will invalidate iterators. + * \note Actual hashmap capacity can be bigger then requested. + * + * \return On success, returns zero value. + * \return On failure, returns error code. + */ +int bh_hashmap_reserve(bh_hashmap_t *hashmap, + size_t size); + + +/** + * Inserts the pair of key-value into the hashmap. + * + * \param hashmap Hashmap handle + * \param key Key + * \param value Value + * + * \note This function allows duplicates to be inserted. + * \note Calling this function will invalidate iterators. + * + * \return On success, returns zero value. + * \return On failure, returns error code. + */ +int bh_hashmap_insert(bh_hashmap_t *hashmap, + void *key, + void *value); + + +/** + * Removes value from the hashmap. + * + * \param hashmap Hashmap handle + * \param key Key + * + * \note Calling this function will invalidate iterators. + * \note If hashmap contains several elements with the same key, this function + * will remove only one key-value pair. + */ +void bh_hashmap_remove(bh_hashmap_t *hashmap, + void *key); + + +/** + * Returns value from the hashmap by key. + * + * \param hashmap Hashmap handle + * \param key Key + * \param value Value (optional) + * + * \return On success, returns zero value. + * \return On failure, returns error code. + */ +int bh_hashmap_at(bh_hashmap_t *hashmap, + void *key, + void **value); + + +/** + * Checks if the hashmap is empty. + * + * \param hashmap Hashmap handle + * + * \return If hashmap is empty, returns non-zero value + * \return If hashmap is not empty, returns zero value + */ +int bh_hashmap_empty(bh_hashmap_t *hashmap); + + +/** + * Returns the size of the hashmap. + * + * \param hashmap Hashmap handle + * + * \return Returns the size of the hashmap. + */ +size_t bh_hashmap_size(bh_hashmap_t *hashmap); + + +/** + * Returns the capacity of the hashmap. + * + * \param hashmap Hashmap handle + * + * \return Returns the capacity of the hashmap. + */ +size_t bh_hashmap_capacity(bh_hashmap_t *hashmap); + + +/** + * Returns the load factor of the hashmap. + * + * \param hashmap Hashmap handle + * + * \return Returns the load factor of the hashmap. + */ +float bh_hashmap_factor(bh_hashmap_t *hashmap); + + +/** + * Sets the load factor of the hashmap. + * + * \param hashmap Hashmap handle + * \param factor Load factor + * + * \note New load factor will be applied on the next reserve/insert operation. + */ +void bh_hashmap_set_factor(bh_hashmap_t *hashmap, + float factor); + + +/** + * Returns the iterator to the element in the hashmap with specified key. + * + * \param hashmap Hashmap handle + * \param iter Iterator + * + * \return On success, returns iterator value. + * \return On failure, returns NULL pointer. + * + * \note If hashmap contains several elements with the same key, this function + * will return iterator to one of them + */ +void *bh_hashmap_iter_at(bh_hashmap_t *hashmap, + void *key); + + +/** + * Returns the iterator to the next element in the hashmap. + * + * If the iterator is NULL pointer, this function will return iterator to the + * first element of the hashmap. + * + * \param hashmap Hashmap handle + * \param iter Iterator + * + * \return On success, returns new iterator value for the next element. + * \return On failure, returns NULL pointer. + */ +void *bh_hashmap_iter_next(bh_hashmap_t *hashmap, + void *iter); + + +/** + * Removes value from the hashmap pointed by by iterator. + * + * \param hashmap Hashmap handle + * \param iter Iterator + * + * \note Calling this function will invalidate iterators. + */ +void bh_hashmap_iter_remove(bh_hashmap_t *hashmap, + void *iter); + + +/** + * Returns key, pointed by the iterator. + * + * \param iter Iterator + * + * \return Returns key. + */ +void *bh_hashmap_iter_key(void *iter); + + +/** + * Returns value, pointed by the iterator. + * + * \param iter Iterator + * + * \return Returns value. + */ +void *bh_hashmap_iter_value(void *iter); + + +#endif /* BH_HASHMAP_H */ diff --git a/include/bh/io.h b/include/bh/io.h new file mode 100644 index 0000000..1d964bf --- /dev/null +++ b/include/bh/io.h @@ -0,0 +1,251 @@ +#ifndef BH_IO_H +#define BH_IO_H + + +#include "common.h" + + +#define BH_IO_INFO_CB 0x0000 +#define BH_IO_INIT_CB 0x0001 +#define BH_IO_DESTROY_CB 0x0002 +#define BH_IO_OPEN_CB 0x0003 +#define BH_IO_CLOSE_CB 0x0004 +#define BH_IO_READ_CB 0x0005 +#define BH_IO_WRITE_CB 0x0006 +#define BH_IO_PEEK_CB 0x0007 +#define BH_IO_TELL_CB 0x0008 +#define BH_IO_SEEK_CB 0x0009 +#define BH_IO_FLUSH_CB 0x000A +#define BH_IO_SIZE_CB 0x000B +#define BH_IO_FLAGS_CB 0x000C +#define BH_IO_CLEAR_CB 0x000D + + +#define BH_IO_READ 0x0001 +#define BH_IO_WRITE 0x0002 +#define BH_IO_READWRITE 0x0003 +#define BH_IO_APPEND 0x0010 +#define BH_IO_TRUNCATE 0x0020 +#define BH_IO_CREATE 0x0040 +#define BH_IO_EXIST 0x0080 + + +#define BH_IO_SEEK_SET 0x0000 +#define BH_IO_SEEK_CUR 0x0001 +#define BH_IO_SEEK_END 0x0002 + + +#define BH_IO_FLAG_OK 0x0000 +#define BH_IO_FLAG_ERROR 0x0001 +#define BH_IO_FLAG_EOF 0x0002 +#define BH_IO_FLAG_OPEN 0x0004 + +#define BH_FILE_CLASSNAME "bh_file" + +typedef struct bh_io_s bh_io_t; +typedef int (*bh_io_func_t)(void *, int, void *, void *); + + +/** + * Creates the IO handle that represents file. + * + * \param path Path to the file + * + * \return On success, returns IO handle. + * \return On failure, returns NULL pointer. + */ +bh_io_t *bh_file_new(const char *path); + + +/** + * Creates the IO handle that represents buffered IO. + * + * \param io IO handle + * + * \return On success, returns IO handle. + * \return On failure, returns NULL pointer. + */ +bh_io_t *bh_buffer_new(bh_io_t *io); + + +/** + * Creates the IO handle with specified handler and context. + * + * \param func IO actions handler + * \param data Initialization data + * + * \return On success, returns IO handle. + * \return On failure, returns NULL pointer. + */ +bh_io_t *bh_io_new(bh_io_func_t func, + void *data); + + +/** + * Destroys the IO. + * + * \param io IO handle + */ +void bh_io_free(bh_io_t *io); + + +/** + * Returns the IO instance classname. + * + * \param io IO handle + * + * \return On success, returns pointer to constant string. + * \return On failure, returns NULL pointer + */ +const char *bh_io_classname(bh_io_t *io); + + +/** + * Opens the IO in specified mode of operation. + * + * \param io IO handle + * \param mode Mode of operation + * + * \return On success, returns zero value. + * \return On failure, returns error code. + */ +int bh_io_open(bh_io_t *io, + int mode); + + +/** + * Closes the IO. + * + * \param io IO handle + * + * \return On success, returns zero value. + * \return On failure, returns error code. + */ +int bh_io_close(bh_io_t *io); + + +/** + * Reads up to specified amount of bytes from the IO into memory buffer. + * + * \param io IO handle + * \param buffer Pointer to the buffer + * \param size Bytes to read + * \param actual Bytes read + * + * \return On success, returns zero value. + * \return On failure, returns error code. + */ +int bh_io_read(bh_io_t *io, + char *buffer, + size_t size, + size_t *actual); + + +/** + * Writes up to specified amount of bytes into the IO from the memory buffer. + * + * \param io IO handle + * \param buffer Pointer to the buffer + * \param size Bytes to write + * \param actual Bytes written + * + * \return On success, returns zero value. + * \return On failure, returns error code. + */ +int bh_io_write(bh_io_t *io, + const char *buffer, + size_t size, + size_t *actual); + + +/** + * Peeks up to specified amount of bytes from the IO handle. + * + * \param io IO handle + * \param buffer Pointer to the buffer + * \param size Bytes to peek + * \param actial Bytes peeked + * + * \return On success, returns zero value. + * \return On failure, returns error code. + */ +int bh_io_peek(bh_io_t *io, + char *buffer, + size_t size, + size_t *actual); + + +/** + * Tells current offset in the IO. + * + * \param io IO handle + * \param position Offset + * + * \return On success, returns zero value. + * \return On failure, returns error code. + */ +int bh_io_tell(bh_io_t *io, + int64_t *position); + + +/** + * Seeks to specified offset in the IO. + * + * \param io IO handle + * \param position Offset + * \param direction Direction + * + * \return On success, returns zero value. + * \return On failure, returns error code. + */ +int bh_io_seek(bh_io_t *io, + int64_t position, + int direction); + + +/** + * Flushes the internal buffers of the IO. + * + * \param io IO handle + * + * \return On success, returns zero value. + * \return On failure, returns error code. + */ +int bh_io_flush(bh_io_t *io); + + +/** + * Returns the size of the IO (either total or available size). + * + * \param io IO handle + * \param size Available/total size + * + * \return On success, returns zero value. + * \return On failure, returns error code. + */ +int bh_io_size(bh_io_t *io, + int64_t *size); + + +/** + * Returns flags of the IO. + * + * \param io IO handle + * + * \return Flags of the IO + */ +int bh_io_flags(bh_io_t *io); + + +/** + * Clears errors of the IO. + * + * \param io IO handle + * + * \return On success, returns zero value. + * \return On failure, returns error code. + */ +int bh_io_clear(bh_io_t *io); + + +#endif /* BH_IO_H */ diff --git a/include/bh/math.h b/include/bh/math.h new file mode 100644 index 0000000..bdf1efa --- /dev/null +++ b/include/bh/math.h @@ -0,0 +1,715 @@ +#ifndef BH_MATH_H +#define BH_MATH_H + + +#include "common.h" + + +typedef struct bh_point2f_s +{ + float x; + float y; +} bh_point2f_t; + + +typedef struct bh_point3f_s +{ + float x; + float y; + float z; +} bh_point3f_t; + + +typedef struct bh_point4f_s +{ + float x; + float y; + float z; + float w; +} bh_point4f_t; + + +typedef struct bh_point2i_s +{ + int x; + int y; +} bh_point2i_t; + + +typedef struct bh_point3i_s +{ + int x; + int y; + int z; +} bh_point3i_t; + + +typedef struct bh_point4i_s +{ + int x; + int y; + int z; + int w; +} bh_point4i_t; + + +typedef struct bh_matrix3f_s +{ + bh_point3f_t x; + bh_point3f_t y; + bh_point3f_t z; +} bh_matrix3f_t; + + +typedef struct bh_matrix4f_s +{ + bh_point4f_t x; + bh_point4f_t y; + bh_point4f_t z; + bh_point4f_t w; +} bh_matrix4f_t; + + +typedef struct bh_line2f_s +{ + bh_point2f_t normal; + float distance; +} bh_line2f_t; + + +typedef struct bh_plane3f_s +{ + bh_point3f_t normal; + float distance; +} bh_plane3f_t; + + +typedef struct bh_ray2f_s +{ + bh_point2f_t origin; + bh_point2f_t normal; +} bh_ray2f_t; + + +typedef struct bh_ray3f_s +{ + bh_point3f_t origin; + bh_point3f_t normal; +} bh_ray3f_t; + + +typedef struct bh_aabb2f_s +{ + bh_point2f_t origin; + bh_point2f_t size; +} bh_aabb2f_t; + + +typedef struct bh_aabb3f_s +{ + bh_point3f_t origin; + bh_point3f_t size; +} bh_aabb3f_t; + + +typedef struct bh_aabb2i_s +{ + bh_point2i_t origin; + bh_point2i_t size; +} bh_aabb2i_t; + + +typedef struct bh_aabb3i_s +{ + bh_point3i_t origin; + bh_point3i_t size; +} bh_aabb3i_t; + + +typedef bh_point4f_t bh_quat_t; + + +bh_point4f_t *bh_point4f_add(const bh_point4f_t *a, + const bh_point4f_t *b, + bh_point4f_t *result); + + +bh_point4f_t *bh_point4f_sub(const bh_point4f_t *a, + const bh_point4f_t *b, + bh_point4f_t *result); + + +bh_point4f_t *bh_point4f_mul(const bh_point4f_t *a, + const bh_point4f_t *b, + bh_point4f_t *result); + + +bh_point4f_t *bh_point4f_scale(const bh_point4f_t *a, + float b, + bh_point4f_t *result); + + +bh_point4f_t *bh_point4f_madd(const bh_point4f_t *a, + const bh_point4f_t *b, + const bh_point4f_t *c, + bh_point4f_t *result); + + +bh_point4f_t *bh_point4f_negate(const bh_point4f_t *in, + bh_point4f_t *result); + + +float bh_point4f_dot(const bh_point4f_t *a, + const bh_point4f_t *b); + + +float bh_point4f_dot3(const bh_point4f_t *a, + const bh_point4f_t *b); + + +bh_point4f_t *bh_point4f_cross(const bh_point4f_t *a, + const bh_point4f_t *b, + bh_point4f_t *result); + + +float bh_point4f_length(const bh_point4f_t *in); + + +bh_point4f_t *bh_point4f_normal(const bh_point4f_t *in, + bh_point4f_t *result); + + +bh_point4f_t *bh_point4f_min(const bh_point4f_t *a, + const bh_point4f_t *b, + bh_point4f_t *result); + + +bh_point4f_t *bh_point4f_max(const bh_point4f_t *a, + const bh_point4f_t *b, + bh_point4f_t *result); + + +bh_point4f_t *bh_point4f_lerp(const bh_point4f_t *a, + const bh_point4f_t *b, + float t, + bh_point4f_t *result); + + +bh_point4f_t *bh_point4f_slerp(const bh_point4f_t *a, + const bh_point4f_t *b, + float t, + bh_point4f_t *result); + + +bh_point3f_t *bh_point3f_add(const bh_point3f_t *a, + const bh_point3f_t *b, + bh_point3f_t *result); + + +bh_point3f_t *bh_point3f_sub(const bh_point3f_t *a, + const bh_point3f_t *b, + bh_point3f_t *result); + + +bh_point3f_t *bh_point3f_mul(const bh_point3f_t *a, + const bh_point3f_t *b, + bh_point3f_t *result); + + +bh_point3f_t *bh_point3f_scale(const bh_point3f_t *a, + float b, + bh_point3f_t *result); + + +bh_point3f_t *bh_point3f_madd(const bh_point3f_t *a, + const bh_point3f_t *b, + const bh_point3f_t *c, + bh_point3f_t *result); + + +bh_point3f_t *bh_point3f_negate(const bh_point3f_t *in, + bh_point3f_t *result); + + +float bh_point3f_dot(const bh_point3f_t *a, + const bh_point3f_t *b); + + +bh_point3f_t *bh_point3f_cross(const bh_point3f_t *a, + const bh_point3f_t *b, + bh_point3f_t *result); + + +float bh_point3f_length(const bh_point3f_t *in); + + +bh_point3f_t *bh_point3f_normal(const bh_point3f_t *in, + bh_point3f_t *result); + + +bh_point3f_t *bh_point3f_min(const bh_point3f_t *a, + const bh_point3f_t *b, + bh_point3f_t *result); + + +bh_point3f_t *bh_point3f_max(const bh_point3f_t *a, + const bh_point3f_t *b, + bh_point3f_t *result); + + +bh_point3f_t *bh_point3f_lerp(const bh_point3f_t *a, + const bh_point3f_t *b, + float t, + bh_point3f_t *result); + + +bh_point3f_t *bh_point3f_slerp(const bh_point3f_t *a, + const bh_point3f_t *b, + float t, + bh_point3f_t *result); + + +bh_point2f_t *bh_point2f_add(const bh_point2f_t *a, + const bh_point2f_t *b, + bh_point2f_t *result); + + +bh_point2f_t *bh_point2f_sub(const bh_point2f_t *a, + const bh_point2f_t *b, + bh_point2f_t *result); + + +bh_point2f_t *bh_point2f_mul(const bh_point2f_t *a, + const bh_point2f_t *b, + bh_point2f_t *result); + + +bh_point2f_t *bh_point2f_scale(const bh_point2f_t *a, + float b, + bh_point2f_t *result); + + +bh_point2f_t *bh_point2f_madd(const bh_point2f_t *a, + const bh_point2f_t *b, + const bh_point2f_t *c, + bh_point2f_t *result); + + +bh_point2f_t *bh_point2f_negate(const bh_point2f_t *in, + bh_point2f_t *result); + + +float bh_point2f_dot(const bh_point2f_t *a, + const bh_point2f_t *b); + + +float bh_point2f_cross(const bh_point2f_t *a, + const bh_point2f_t *b); + + +float bh_point2f_length(const bh_point2f_t *in); + + +bh_point2f_t *bh_point2f_normal(const bh_point2f_t *in, + bh_point2f_t *result); + + +bh_point2f_t *bh_point2f_min(const bh_point2f_t *a, + const bh_point2f_t *b, + bh_point2f_t *result); + + +bh_point2f_t *bh_point2f_max(const bh_point2f_t *a, + const bh_point2f_t *b, + bh_point2f_t *result); + + +bh_point2f_t *bh_point2f_lerp(const bh_point2f_t *a, + const bh_point2f_t *b, + float t, + bh_point2f_t *result); + + +bh_point2f_t *bh_point2f_slerp(const bh_point2f_t *a, + const bh_point2f_t *b, + float t, + bh_point2f_t *result); + + +bh_point4i_t *bh_point4i_add(const bh_point4i_t *a, + const bh_point4i_t *b, + bh_point4i_t *result); + + +bh_point4i_t *bh_point4i_sub(const bh_point4i_t *a, + const bh_point4i_t *b, + bh_point4i_t *result); + + +bh_point4i_t *bh_point4i_mul(const bh_point4i_t *a, + const bh_point4i_t *b, + bh_point4i_t *result); + + +bh_point4i_t *bh_point4i_scale(const bh_point4i_t *a, + int b, + bh_point4i_t *result); + + +bh_point4i_t *bh_point4i_madd(const bh_point4i_t *a, + const bh_point4i_t *b, + const bh_point4i_t *c, + bh_point4i_t *result); + + +bh_point4i_t *bh_point4i_negate(const bh_point4i_t *in, + bh_point4i_t *result); + + +bh_point4i_t *bh_point4i_min(const bh_point4i_t *a, + const bh_point4i_t *b, + bh_point4i_t *result); + + +bh_point4i_t *bh_point4i_max(const bh_point4i_t *a, + const bh_point4i_t *b, + bh_point4i_t *result); + + +bh_point4i_t *bh_point4i_lerp(const bh_point4i_t *a, + const bh_point4i_t *b, + float t, + bh_point4i_t *result); + + +bh_point3i_t *bh_point3i_add(const bh_point3i_t *a, + const bh_point3i_t *b, + bh_point3i_t *result); + + +bh_point3i_t *bh_point3i_sub(const bh_point3i_t *a, + const bh_point3i_t *b, + bh_point3i_t *result); + + +bh_point3i_t *bh_point3i_mul(const bh_point3i_t *a, + const bh_point3i_t *b, + bh_point3i_t *result); + + +bh_point3i_t *bh_point3i_scale(const bh_point3i_t *a, + int b, + bh_point3i_t *result); + + +bh_point3i_t *bh_point3i_madd(const bh_point3i_t *a, + const bh_point3i_t *b, + const bh_point3i_t *c, + bh_point3i_t *result); + + +bh_point3i_t *bh_point3i_negate(const bh_point3i_t *in, + bh_point3i_t *result); + + +bh_point3i_t *bh_point3i_min(const bh_point3i_t *a, + const bh_point3i_t *b, + bh_point3i_t *result); + + +bh_point3i_t *bh_point3i_max(const bh_point3i_t *a, + const bh_point3i_t *b, + bh_point3i_t *result); + + +bh_point3i_t *bh_point3i_lerp(const bh_point3i_t *a, + const bh_point3i_t *b, + float t, + bh_point3i_t *result); + + +bh_point2i_t *bh_point2i_add(const bh_point2i_t *a, + const bh_point2i_t *b, + bh_point2i_t *result); + + +bh_point2i_t *bh_point2i_sub(const bh_point2i_t *a, + const bh_point2i_t *b, + bh_point2i_t *result); + + +bh_point2i_t *bh_point2i_mul(const bh_point2i_t *a, + const bh_point2i_t *b, + bh_point2i_t *result); + + +bh_point2i_t *bh_point2i_scale(const bh_point2i_t *a, + int b, + bh_point2i_t *result); + + +bh_point2i_t *bh_point2i_madd(const bh_point2i_t *a, + const bh_point2i_t *b, + const bh_point2i_t *c, + bh_point2i_t *result); + + +bh_point2i_t *bh_point2i_negate(const bh_point2i_t *in, + bh_point2i_t *result); + + +bh_point2i_t *bh_point2i_min(const bh_point2i_t *a, + const bh_point2i_t *b, + bh_point2i_t *result); + + +bh_point2i_t *bh_point2i_max(const bh_point2i_t *a, + const bh_point2i_t *b, + bh_point2i_t *result); + + +bh_point2i_t *bh_point2i_lerp(const bh_point2i_t *a, + const bh_point2i_t *b, + float t, + bh_point2i_t *result); + + +#define bh_quat_add bh_point4f_add + + +#define bh_quat_sub bh_point4f_sub + + +#define bh_quat_scale bh_point4f_scale + + +#define bh_quat_negate bh_point4f_negate + + +#define bh_quat_length bh_point4f_length + + +#define bh_quat_normal bh_point4f_normal + + +#define bh_quat_dot bh_point4f_dot + + +#define bh_quat_lerp bh_point4f_lerp + + +#define bh_quat_slerp bh_point4f_slerp + + +bh_quat_t *bh_quat_identity(bh_quat_t *result); + + +bh_quat_t *bh_quat_conjugate(const bh_quat_t *in, + bh_quat_t *result); + + +bh_quat_t *bh_quat_inverse(const bh_quat_t *in, + bh_quat_t *result); + + +bh_quat_t *bh_quat_mul(const bh_quat_t *a, + const bh_quat_t *b, + bh_quat_t *result); + + +bh_quat_t *bh_quat_set_euler(float roll, + float pitch, + float yaw, + bh_quat_t *result); + + +bh_quat_t *bh_quat_set_rotation(const bh_point3f_t *axis, + float angle, + bh_quat_t *result); + + +void bh_quat_euler(const bh_quat_t *in, + float *roll, + float *pitch, + float *yaw); + + +void bh_quat_rotation(const bh_quat_t *in, + bh_point3f_t *axis, + float *angle); + + +bh_matrix4f_t *bh_quat_matrix(const bh_quat_t *in, + bh_matrix4f_t *result); + + +bh_matrix4f_t *bh_matrix4f_identity(bh_matrix4f_t *result); + + +bh_matrix4f_t *bh_matrix4f_add(const bh_matrix4f_t *a, + const bh_matrix4f_t *b, + bh_matrix4f_t *result); + + +bh_matrix4f_t *bh_matrix4f_sub(const bh_matrix4f_t *a, + const bh_matrix4f_t *b, + bh_matrix4f_t *result); + + +bh_matrix4f_t *bh_matrix4f_mul(const bh_matrix4f_t *a, + const bh_matrix4f_t *b, + bh_matrix4f_t *result); + + +bh_matrix4f_t *bh_matrix4f_scale(const bh_matrix4f_t *a, + float b, + bh_matrix4f_t *result); + + +bh_matrix4f_t *bh_matrix4f_transpose(const bh_matrix4f_t *in, + bh_matrix4f_t *result); + + +float bh_matrix4f_trace(const bh_matrix4f_t *in); + + +float bh_matrix4f_determinant(const bh_matrix4f_t *in); + + +bh_matrix4f_t *bh_matrix4f_inverse(const bh_matrix4f_t *in, + bh_matrix4f_t *result); + + +bh_matrix4f_t *bh_matrix4f_scaling(float x, + float y, + float z, + bh_matrix4f_t *result); + + +bh_matrix4f_t *bh_matrix4f_translation(float x, + float y, + float z, + bh_matrix4f_t *result); + + +bh_matrix4f_t *bh_matrix4f_rotation_x(float angle, + bh_matrix4f_t *result); + + +bh_matrix4f_t *bh_matrix4f_rotation_y(float angle, + bh_matrix4f_t *result); + + +bh_matrix4f_t *bh_matrix4f_rotation_z(float angle, + bh_matrix4f_t *result); + + +bh_matrix4f_t *bh_matrix4f_rotation(const bh_point3f_t *axis, + float angle, + bh_matrix4f_t *result); + + +bh_matrix4f_t *bh_matrix4f_rotation_euler(float roll, + float pitch, + float yaw, + bh_matrix4f_t *result); + + +bh_matrix4f_t *bh_matrix4f_rotation_quat(bh_quat_t *rotation, + bh_matrix4f_t *result); + + +bh_matrix4f_t *bh_matrix4f_ortho(float x_min, + float x_max, + float y_min, + float y_max, + float z_min, + float z_max, + bh_matrix4f_t *result); + + +bh_matrix4f_t *bh_matrix4f_perspective(float fov, + float aspect, + float z_min, + float z_max, + bh_matrix4f_t *result); + + +bh_matrix4f_t *bh_matrix4f_lookat(const bh_point3f_t *camera, + const bh_point3f_t *at, + const bh_point3f_t *up, + bh_matrix4f_t *result); + + +bh_point3f_t *bh_matrix4f_transform_point3f(const bh_matrix4f_t *a, + const bh_point3f_t *b, + bh_point3f_t *result); + + +bh_point4f_t *bh_matrix4f_transform_point4f(const bh_matrix4f_t *a, + const bh_point4f_t *b, + bh_point4f_t *result); + + +bh_matrix3f_t *bh_matrix3f_identity(bh_matrix3f_t *result); + + +bh_matrix3f_t *bh_matrix3f_add(const bh_matrix3f_t *a, + const bh_matrix3f_t *b, + bh_matrix3f_t *result); + + +bh_matrix3f_t *bh_matrix3f_sub(const bh_matrix3f_t *a, + const bh_matrix3f_t *b, + bh_matrix3f_t *result); + + +bh_matrix3f_t *bh_matrix3f_mul(const bh_matrix3f_t *a, + const bh_matrix3f_t *b, + bh_matrix3f_t *result); + + +bh_matrix3f_t *bh_matrix3f_scale(const bh_matrix3f_t *a, + float b, + bh_matrix3f_t *result); + + +bh_matrix3f_t *bh_matrix3f_transpose(const bh_matrix3f_t *in, + bh_matrix3f_t *result); + + +float bh_matrix3f_trace(const bh_matrix3f_t *in); + + +float bh_matrix3f_determinant(const bh_matrix3f_t *in); + + +bh_matrix3f_t *bh_matrix3f_inverse(const bh_matrix3f_t *in, + bh_matrix3f_t *result); + + +bh_matrix3f_t *bh_matrix3f_scaling(float x, + float y, + bh_matrix3f_t *result); + + +bh_matrix3f_t *bh_matrix3f_translation(float x, + float y, + bh_matrix3f_t *result); + + +bh_matrix3f_t *bh_matrix3f_rotation(float angle, + bh_matrix3f_t *result); + + +bh_point2f_t *bh_matrix3f_transform_point2f(const bh_matrix3f_t *a, + const bh_point2f_t *b, + bh_point2f_t *result); + + +bh_point3f_t *bh_matrix3f_transform_point3f(const bh_matrix3f_t *a, + const bh_point3f_t *b, + bh_point3f_t *result); + + +#endif /* BH_MATH_H */ diff --git a/include/bh/queue.h b/include/bh/queue.h new file mode 100755 index 0000000..7905c77 --- /dev/null +++ b/include/bh/queue.h @@ -0,0 +1,149 @@ +#ifndef BH_QUEUE_H +#define BH_QUEUE_H + + +#include "common.h" + + +typedef struct bh_queue_s bh_queue_t; + + +/** + * Creates the new queue object. + * + * \return On success, returns the pointer to the new queue object. + * \return On failure, returns a null pointer. + */ +bh_queue_t *bh_queue_new(void); + + +/** + * Frees the \a queue object. + * + * \param queue Pointer to the queue object to be freed + */ +void bh_queue_free(bh_queue_t *queue); + + +/** + * Clears the \a queue object. + * + * \param queue Pointer to the queue object to be cleared + */ +void bh_queue_clear(bh_queue_t *queue); + + +/** + * Reserves the space for \a size elements in the \a queue. + * + * This function can both expand and shrink the available space in \a queue. + * + * \param queue Pointer to the queue object to be resized in terms of capacity + * \param size New capacity of the queue + * + * \note Calling this function will invalidate iterators. + * \note Actual hashmap capacity can be bigger then requested. + * + * \return On success, returns zero value. + * \return On failure, returns error code. + */ +int bh_queue_reserve(bh_queue_t *queue, + size_t size); + + +/** + * Inserts the \a value into the \a queue. + * + * \param queue Pointer to the queue object + * \param value Value to be inserted + * + * \note Calling this function will invalidate iterators. + * + * \return On success, returns zero value. + * \return On failure, returns error code. + */ +int bh_queue_insert(bh_queue_t *queue, + void *value); + + +/** + * Removes front value from the \a queue. + * + * \param queue Pointer to the queue object + * + * \note Calling this function will invalidate iterators. + */ +void bh_queue_remove(bh_queue_t *queue); + + +/** + * Returns front value from the \a queue. + * + * \param queue Pointer to the queue object + * + * \return On success, returns front value from the queue. + * \return On failure, returns null pointer. + */ +int bh_queue_front(bh_queue_t *queue, + void **value); + + +/** + * Checks if the \a queue is empty. + * + * \param queue Pointer to the queue object + * + * \return If queue is empty, returns non-zero value + * \return If queue is not empty, returns zero value + */ +int bh_queue_empty(bh_queue_t *queue); + + +/** + * Returns the size of the \a queue. + * + * \param queue Pointer to the queue object + * + * \return Returns the size of the queue. + */ +size_t bh_queue_size(bh_queue_t *queue); + + +/** + * Returns the capacity of the \a queue. + * + * \param queue Pointer to the queue object + * + * \return Returns the capacity of the queue. + */ +size_t bh_queue_capacity(bh_queue_t *queue); + + +/** + * Returns the iterator to the next element in the \a queue. + * + * \param queue Pointer to the queue object + * \param iter Opaque iterator value + * + * \return If the \a iter doesn't point to the last element of the queue, + * returns next iterator value. + * \return If the \a iter point to the last element of the queue, returns + * null pointer. + * \return If the \a iter is the null pointer, returns iterator to the + * first element of the queue. + */ +void *bh_queue_iter_next(bh_queue_t *queue, + void *iter); + + +/** + * Returns the value, pointed by the queue iterator \a iter. + * + * \param iter Opaque iterator value + * + * \return Returns value, pointed by iterator. + */ +void *bh_queue_iter_value(void *iter); + + +#endif /* BH_QUEUE_H */ diff --git a/main.c b/main.c new file mode 100644 index 0000000..1f85fab --- /dev/null +++ b/main.c @@ -0,0 +1,10 @@ +#include + +int main() +{ + bh_io_t *io = bh_file_new("hello.txt"); + bh_io_open(io, BH_IO_WRITE); + bh_io_write(io, "Hello, world!", 13, NULL); + bh_io_free(io); + return 0; +} \ No newline at end of file diff --git a/src/algo.c b/src/algo.c new file mode 100755 index 0000000..591a1d5 --- /dev/null +++ b/src/algo.c @@ -0,0 +1,411 @@ +#include +#include + + +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; + } +} diff --git a/src/dummy/file.c b/src/dummy/file.c new file mode 100644 index 0000000..7b35553 --- /dev/null +++ b/src/dummy/file.c @@ -0,0 +1,170 @@ +#include + +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); +} diff --git a/src/hashmap.c b/src/hashmap.c new file mode 100755 index 0000000..7bb3199 --- /dev/null +++ b/src/hashmap.c @@ -0,0 +1,390 @@ +#include +#include +#include + + +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; +} + diff --git a/src/io.c b/src/io.c new file mode 100644 index 0000000..b005307 --- /dev/null +++ b/src/io.c @@ -0,0 +1,224 @@ +#include +#include + + +#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); +} diff --git a/src/math.c b/src/math.c new file mode 100644 index 0000000..18ec135 --- /dev/null +++ b/src/math.c @@ -0,0 +1,1778 @@ +#include +#include + + +bh_point4f_t *bh_point4f_add(const bh_point4f_t *a, + const bh_point4f_t *b, + bh_point4f_t *result) +{ + result->x = a->x + b->x; + result->y = a->y + b->y; + result->z = a->z + b->z; + result->w = a->w + b->w; + + return result; +} + + +bh_point4f_t *bh_point4f_sub(const bh_point4f_t *a, + const bh_point4f_t *b, + bh_point4f_t *result) +{ + result->x = a->x - b->x; + result->y = a->y - b->y; + result->z = a->z - b->z; + result->w = a->w - b->w; + + return result; +} + + +bh_point4f_t *bh_point4f_mul(const bh_point4f_t *a, + const bh_point4f_t *b, + bh_point4f_t *result) +{ + result->x = a->x * b->x; + result->y = a->y * b->y; + result->z = a->z * b->z; + result->w = a->w * b->w; + + return result; +} + + +bh_point4f_t *bh_point4f_scale(const bh_point4f_t *a, + float b, + bh_point4f_t *result) +{ + result->x = a->x * b; + result->y = a->y * b; + result->z = a->z * b; + result->w = a->w * b; + + return result; +} + + +bh_point4f_t *bh_point4f_madd(const bh_point4f_t *a, + const bh_point4f_t *b, + const bh_point4f_t *c, + bh_point4f_t *result) +{ + result->x = a->x * b->x + c->x; + result->y = a->y * b->y + c->y; + result->z = a->z * b->z + c->z; + result->w = a->w * b->w + c->w; + + return result; +} + + +bh_point4f_t *bh_point4f_negate(const bh_point4f_t *in, + bh_point4f_t *result) +{ + result->x = -in->x; + result->y = -in->y; + result->z = -in->z; + result->w = -in->w; + + return result; +} + + +float bh_point4f_dot(const bh_point4f_t *a, + const bh_point4f_t *b) +{ + return a->x * b->x + a->y * b->y + a->z * b->y + a->w * b->w; +} + + +float bh_point4f_dot3(const bh_point4f_t *a, + const bh_point4f_t *b) +{ + return a->x * b->x + a->y * b->y + a->z * b->y; +} + + +bh_point4f_t *bh_point4f_cross(const bh_point4f_t *a, + const bh_point4f_t *b, + bh_point4f_t *result) +{ + bh_point4f_t tmp; + + tmp.x = a->y * b->z - a->z * b->y; + tmp.y = a->z * b->x - a->x * b->z; + tmp.z = a->x * b->y - a->y * b->x; + tmp.w = 0.0f; + + *result = tmp; + return result; +} + + +float bh_point4f_length(const bh_point4f_t *in) +{ + return sqrtf(bh_point4f_dot(in, in)); +} + + +bh_point4f_t *bh_point4f_normal(const bh_point4f_t *in, + bh_point4f_t *result) +{ + float length; + + length = 1.0f / bh_point4f_length(in); + return bh_point4f_scale(in, length, result); +} + + +bh_point4f_t *bh_point4f_min(const bh_point4f_t *a, + const bh_point4f_t *b, + bh_point4f_t *result) +{ + if (a->x < b->x) result->x = a->x; else result->x = b->x; + if (a->y < b->y) result->y = a->y; else result->y = b->y; + if (a->z < b->z) result->z = a->z; else result->z = b->z; + if (a->w < b->w) result->w = a->w; else result->w = b->w; + + return result; +} + + +bh_point4f_t *bh_point4f_max(const bh_point4f_t *a, + const bh_point4f_t *b, + bh_point4f_t *result) +{ + if (a->x > b->x) result->x = a->x; else result->x = b->x; + if (a->y > b->y) result->y = a->y; else result->y = b->y; + if (a->z > b->z) result->z = a->z; else result->z = b->z; + if (a->w > b->w) result->w = a->w; else result->w = b->w; + + return result; +} + + +bh_point4f_t *bh_point4f_lerp(const bh_point4f_t *a, + const bh_point4f_t *b, + float t, + bh_point4f_t *result) +{ + bh_point4f_t tmp; + bh_point4f_scale(bh_point4f_sub(b, a, &tmp), t, &tmp); + return bh_point4f_add(a, &tmp, result); +} + + +bh_point4f_t *bh_point4f_slerp(const bh_point4f_t *a, + const bh_point4f_t *b, + float t, + bh_point4f_t *result) +{ + float angle, denom; + bh_point4f_t from, to; + + angle = acosf(bh_point4f_dot(a, b)); + + /* Special case - reducing to linear interpolation */ + if (angle == 0.0f) + return bh_point4f_lerp(a, b, t, result); + + denom = 1.0f / sinf(angle); + bh_point4f_scale(a, sinf((1 - t) * angle) * denom, &from); + bh_point4f_scale(b, sinf(t * angle) * denom, &to); + return bh_point4f_add(&from, &to, result); +} + + +bh_point3f_t *bh_point3f_add(const bh_point3f_t *a, + const bh_point3f_t *b, + bh_point3f_t *result) +{ + result->x = a->x + b->x; + result->y = a->y + b->y; + result->z = a->z + b->z; + + return result; +} + + +bh_point3f_t *bh_point3f_sub(const bh_point3f_t *a, + const bh_point3f_t *b, + bh_point3f_t *result) +{ + result->x = a->x - b->x; + result->y = a->y - b->y; + result->z = a->z - b->z; + + return result; +} + + +bh_point3f_t *bh_point3f_mul(const bh_point3f_t *a, + const bh_point3f_t *b, + bh_point3f_t *result) +{ + result->x = a->x * b->x; + result->y = a->y * b->y; + result->z = a->z * b->z; + + return result; +} + + +bh_point3f_t *bh_point3f_scale(const bh_point3f_t *a, + float b, + bh_point3f_t *result) +{ + result->x = a->x * b; + result->y = a->y * b; + result->z = a->z * b; + + return result; +} + + +bh_point3f_t *bh_point3f_madd(const bh_point3f_t *a, + const bh_point3f_t *b, + const bh_point3f_t *c, + bh_point3f_t *result) +{ + result->x = a->x * b->x + c->x; + result->y = a->y * b->y + c->y; + result->z = a->z * b->z + c->z; + + return result; +} + + +bh_point3f_t *bh_point3f_negate(const bh_point3f_t *in, + bh_point3f_t *result) +{ + result->x = -in->x; + result->y = -in->y; + result->z = -in->z; + + return result; +} + + +float bh_point3f_dot(const bh_point3f_t *a, + const bh_point3f_t *b) +{ + return a->x * b->x + a->y * b->y + a->z * b->y; +} + + +bh_point3f_t *bh_point3f_cross(const bh_point3f_t *a, + const bh_point3f_t *b, + bh_point3f_t *result) +{ + bh_point3f_t tmp; + + tmp.x = a->y * b->z - a->z * b->y; + tmp.y = a->z * b->x - a->x * b->z; + tmp.z = a->x * b->y - a->y * b->x; + + *result = tmp; + return result; +} + + +float bh_point3f_length(const bh_point3f_t *in) +{ + return sqrtf(bh_point3f_dot(in, in)); +} + + +bh_point3f_t *bh_point3f_normal(const bh_point3f_t *in, + bh_point3f_t *result) +{ + float length; + + length = 1.0f / bh_point3f_length(in); + return bh_point3f_scale(in, length, result); +} + + +bh_point3f_t *bh_point3f_min(const bh_point3f_t *a, + const bh_point3f_t *b, + bh_point3f_t *result) +{ + if (a->x < b->x) result->x = a->x; else result->x = b->x; + if (a->y < b->y) result->y = a->y; else result->y = b->y; + if (a->z < b->z) result->z = a->z; else result->z = b->z; + + return result; +} + + +bh_point3f_t *bh_point3f_max(const bh_point3f_t *a, + const bh_point3f_t *b, + bh_point3f_t *result) +{ + if (a->x > b->x) result->x = a->x; else result->x = b->x; + if (a->y > b->y) result->y = a->y; else result->y = b->y; + if (a->z > b->z) result->z = a->z; else result->z = b->z; + + return result; +} + + +bh_point3f_t *bh_point3f_lerp(const bh_point3f_t *a, + const bh_point3f_t *b, + float t, + bh_point3f_t *result) +{ + bh_point3f_t tmp; + bh_point3f_scale(bh_point3f_sub(b, a, &tmp), t, &tmp); + return bh_point3f_add(a, &tmp, result); +} + + +bh_point3f_t *bh_point3f_slerp(const bh_point3f_t *a, + const bh_point3f_t *b, + float t, + bh_point3f_t *result) +{ + float angle, denom; + bh_point3f_t from, to; + + angle = acosf(bh_point3f_dot(a, b)); + + /* Special case - reducing to linear interpolation */ + if (angle == 0.0f) + return bh_point3f_lerp(a, b, t, result); + + denom = 1.0f / sinf(angle); + bh_point3f_scale(a, sinf((1 - t) * angle) * denom, &from); + bh_point3f_scale(b, sinf(t * angle) * denom, &to); + return bh_point3f_add(&from, &to, result); +} + + +bh_point2f_t *bh_point2f_add(const bh_point2f_t *a, + const bh_point2f_t *b, + bh_point2f_t *result) +{ + result->x = a->x + b->x; + result->y = a->y + b->y; + + return result; +} + + +bh_point2f_t *bh_point2f_sub(const bh_point2f_t *a, + const bh_point2f_t *b, + bh_point2f_t *result) +{ + result->x = a->x - b->x; + result->y = a->y - b->y; + + return result; +} + + +bh_point2f_t *bh_point2f_mul(const bh_point2f_t *a, + const bh_point2f_t *b, + bh_point2f_t *result) +{ + result->x = a->x * b->x; + result->y = a->y * b->y; + + return result; +} + + +bh_point2f_t *bh_point2f_scale(const bh_point2f_t *a, + float b, + bh_point2f_t *result) +{ + result->x = a->x * b; + result->y = a->y * b; + + return result; +} + + +bh_point2f_t *bh_point2f_madd(const bh_point2f_t *a, + const bh_point2f_t *b, + const bh_point2f_t *c, + bh_point2f_t *result) +{ + result->x = a->x * b->x + c->x; + result->y = a->y * b->y + c->y; + + return result; +} + + +bh_point2f_t *bh_point2f_negate(const bh_point2f_t *in, + bh_point2f_t *result) +{ + result->x = -in->x; + result->y = -in->y; + + return result; +} + + +float bh_point2f_dot(const bh_point2f_t *a, + const bh_point2f_t *b) +{ + return a->x * b->x + a->y * b->y; +} + + +float bh_point2f_cross(const bh_point2f_t *a, + const bh_point2f_t *b) +{ + return a->x * b->y - a->y * b->x; +} + + +float bh_point2f_length(const bh_point2f_t *in) +{ + return sqrtf(bh_point2f_dot(in, in)); +} + + +bh_point2f_t *bh_point2f_normal(const bh_point2f_t *in, + bh_point2f_t *result) +{ + float length; + + length = 1.0f / bh_point2f_length(in); + return bh_point2f_scale(in, length, result); +} + + +bh_point2f_t *bh_point2f_min(const bh_point2f_t *a, + const bh_point2f_t *b, + bh_point2f_t *result) +{ + if (a->x < b->x) result->x = a->x; else result->x = b->x; + if (a->y < b->y) result->y = a->y; else result->y = b->y; + + return result; +} + + +bh_point2f_t *bh_point2f_max(const bh_point2f_t *a, + const bh_point2f_t *b, + bh_point2f_t *result) +{ + if (a->x > b->x) result->x = a->x; else result->x = b->x; + if (a->y > b->y) result->y = a->y; else result->y = b->y; + + return result; +} + + +bh_point2f_t *bh_point2f_lerp(const bh_point2f_t *a, + const bh_point2f_t *b, + float t, + bh_point2f_t *result) +{ + bh_point2f_t tmp; + bh_point2f_scale(bh_point2f_sub(b, a, &tmp), t, &tmp); + return bh_point2f_add(a, &tmp, result); +} + + +bh_point2f_t *bh_point2f_slerp(const bh_point2f_t *a, + const bh_point2f_t *b, + float t, + bh_point2f_t *result) +{ + float angle, denom; + bh_point2f_t from, to; + + angle = acosf(bh_point2f_dot(a, b)); + + /* Special case - reducing to linear interpolation */ + if (angle == 0.0f) + return bh_point2f_lerp(a, b, t, result); + + denom = 1.0f / sinf(angle); + bh_point2f_scale(a, sinf((1 - t) * angle) * denom, &from); + bh_point2f_scale(b, sinf(t * angle) * denom, &to); + return bh_point2f_add(&from, &to, result); +} + + +bh_point4i_t *bh_point4i_add(const bh_point4i_t *a, + const bh_point4i_t *b, + bh_point4i_t *result) +{ + result->x = a->x + b->x; + result->y = a->y + b->y; + result->z = a->z + b->z; + result->w = a->w + b->w; + + return result; +} + + +bh_point4i_t *bh_point4i_sub(const bh_point4i_t *a, + const bh_point4i_t *b, + bh_point4i_t *result) +{ + result->x = a->x - b->x; + result->y = a->y - b->y; + result->z = a->z - b->z; + result->w = a->w - b->w; + + return result; +} + + +bh_point4i_t *bh_point4i_mul(const bh_point4i_t *a, + const bh_point4i_t *b, + bh_point4i_t *result) +{ + result->x = a->x * b->x; + result->y = a->y * b->y; + result->z = a->z * b->z; + result->w = a->w * b->w; + + return result; +} + + +bh_point4i_t *bh_point4i_scale(const bh_point4i_t *a, + int b, + bh_point4i_t *result) +{ + result->x = a->x * b; + result->y = a->y * b; + result->z = a->z * b; + result->w = a->w * b; + + return result; +} + + +bh_point4i_t *bh_point4i_madd(const bh_point4i_t *a, + const bh_point4i_t *b, + const bh_point4i_t *c, + bh_point4i_t *result) +{ + result->x = a->x * b->x + c->x; + result->y = a->y * b->y + c->y; + result->z = a->z * b->z + c->z; + result->w = a->w * b->w + c->w; + + return result; +} + + +bh_point4i_t *bh_point4i_negate(const bh_point4i_t *in, + bh_point4i_t *result) +{ + result->x = -in->x; + result->y = -in->y; + result->z = -in->z; + result->w = -in->w; + + return result; +} + + +bh_point4i_t *bh_point4i_min(const bh_point4i_t *a, + const bh_point4i_t *b, + bh_point4i_t *result) +{ + if (a->x < b->x) result->x = a->x; else result->x = b->x; + if (a->y < b->y) result->y = a->y; else result->y = b->y; + if (a->z < b->z) result->z = a->z; else result->z = b->z; + if (a->w < b->w) result->w = a->w; else result->w = b->w; + + return result; +} + + +bh_point4i_t *bh_point4i_max(const bh_point4i_t *a, + const bh_point4i_t *b, + bh_point4i_t *result) +{ + if (a->x > b->x) result->x = a->x; else result->x = b->x; + if (a->y > b->y) result->y = a->y; else result->y = b->y; + if (a->z > b->z) result->z = a->z; else result->z = b->z; + if (a->w > b->w) result->w = a->w; else result->w = b->w; + + return result; +} + + +bh_point4i_t *bh_point4i_lerp(const bh_point4i_t *a, + const bh_point4i_t *b, + float t, + bh_point4i_t *result) +{ + bh_point4i_t tmp; + + tmp.x = (b->x - a->x) * t; + tmp.y = (b->y - a->y) * t; + tmp.z = (b->z - a->z) * t; + tmp.w = (b->w - a->w) * t; + + return bh_point4i_add(a, &tmp, result); +} + + +bh_point3i_t *bh_point3i_add(const bh_point3i_t *a, + const bh_point3i_t *b, + bh_point3i_t *result) +{ + result->x = a->x + b->x; + result->y = a->y + b->y; + result->z = a->z + b->z; + + return result; +} + + +bh_point3i_t *bh_point3i_sub(const bh_point3i_t *a, + const bh_point3i_t *b, + bh_point3i_t *result) +{ + result->x = a->x - b->x; + result->y = a->y - b->y; + result->z = a->z - b->z; + + return result; +} + + +bh_point3i_t *bh_point3i_mul(const bh_point3i_t *a, + const bh_point3i_t *b, + bh_point3i_t *result) +{ + result->x = a->x * b->x; + result->y = a->y * b->y; + result->z = a->z * b->z; + + return result; +} + + +bh_point3i_t *bh_point3i_scale(const bh_point3i_t *a, + int b, + bh_point3i_t *result) +{ + result->x = a->x * b; + result->y = a->y * b; + result->z = a->z * b; + + return result; +} + + +bh_point3i_t *bh_point3i_madd(const bh_point3i_t *a, + const bh_point3i_t *b, + const bh_point3i_t *c, + bh_point3i_t *result) +{ + result->x = a->x * b->x + c->x; + result->y = a->y * b->y + c->y; + result->z = a->z * b->z + c->z; + + return result; +} + + +bh_point3i_t *bh_point3i_negate(const bh_point3i_t *in, + bh_point3i_t *result) +{ + result->x = -in->x; + result->y = -in->y; + result->z = -in->z; + + return result; +} + + +bh_point3i_t *bh_point3i_min(const bh_point3i_t *a, + const bh_point3i_t *b, + bh_point3i_t *result) +{ + if (a->x < b->x) result->x = a->x; else result->x = b->x; + if (a->y < b->y) result->y = a->y; else result->y = b->y; + if (a->z < b->z) result->z = a->z; else result->z = b->z; + + return result; +} + + +bh_point3i_t *bh_point3i_max(const bh_point3i_t *a, + const bh_point3i_t *b, + bh_point3i_t *result) +{ + if (a->x > b->x) result->x = a->x; else result->x = b->x; + if (a->y > b->y) result->y = a->y; else result->y = b->y; + if (a->z > b->z) result->z = a->z; else result->z = b->z; + + return result; +} + + +bh_point3i_t *bh_point3i_lerp(const bh_point3i_t *a, + const bh_point3i_t *b, + float t, + bh_point3i_t *result) +{ + bh_point3i_t tmp; + + tmp.x = (b->x - a->x) * t; + tmp.y = (b->y - a->y) * t; + tmp.z = (b->z - a->z) * t; + + return bh_point3i_add(a, &tmp, result); +} + + +bh_point2i_t *bh_point2i_add(const bh_point2i_t *a, + const bh_point2i_t *b, + bh_point2i_t *result) +{ + result->x = a->x + b->x; + result->y = a->y + b->y; + + return result; +} + + +bh_point2i_t *bh_point2i_sub(const bh_point2i_t *a, + const bh_point2i_t *b, + bh_point2i_t *result) +{ + result->x = a->x - b->x; + result->y = a->y - b->y; + + return result; +} + + +bh_point2i_t *bh_point2i_mul(const bh_point2i_t *a, + const bh_point2i_t *b, + bh_point2i_t *result) +{ + result->x = a->x * b->x; + result->y = a->y * b->y; + + return result; +} + + +bh_point2i_t *bh_point2i_scale(const bh_point2i_t *a, + int b, + bh_point2i_t *result) +{ + result->x = a->x * b; + result->y = a->y * b; + + return result; +} + + +bh_point2i_t *bh_point2i_madd(const bh_point2i_t *a, + const bh_point2i_t *b, + const bh_point2i_t *c, + bh_point2i_t *result) +{ + result->x = a->x * b->x + c->x; + result->y = a->y * b->y + c->y; + + return result; +} + + +bh_point2i_t *bh_point2i_negate(const bh_point2i_t *in, + bh_point2i_t *result) +{ + result->x = -in->x; + result->y = -in->y; + + return result; +} + + +bh_point2i_t *bh_point2i_min(const bh_point2i_t *a, + const bh_point2i_t *b, + bh_point2i_t *result) +{ + if (a->x < b->x) result->x = a->x; else result->x = b->x; + if (a->y < b->y) result->y = a->y; else result->y = b->y; + + return result; +} + + +bh_point2i_t *bh_point2i_max(const bh_point2i_t *a, + const bh_point2i_t *b, + bh_point2i_t *result) +{ + if (a->x > b->x) result->x = a->x; else result->x = b->x; + if (a->y > b->y) result->y = a->y; else result->y = b->y; + + return result; +} + + +bh_point2i_t *bh_point2i_lerp(const bh_point2i_t *a, + const bh_point2i_t *b, + float t, + bh_point2i_t *result) +{ + bh_point2i_t tmp; + + tmp.x = (b->x - a->x) * t; + tmp.y = (b->y - a->y) * t; + + return bh_point2i_add(a, &tmp, result); +} + + +bh_quat_t *bh_quat_identity(bh_quat_t *result) +{ + static const bh_quat_t ident = {0.0f, 0.0f, 0.0f, 1.0f}; + + *result = ident; + return result; +} + + +bh_quat_t *bh_quat_conjugate(const bh_quat_t *in, + bh_quat_t *result) +{ + result->x = -in->x; + result->y = -in->y; + result->z = -in->z; + result->w = in->w; + + return result; +} + + +bh_quat_t *bh_quat_inverse(const bh_quat_t *in, + bh_quat_t *result) +{ + float length; + + length = bh_quat_dot(in, in); + bh_quat_conjugate(in, result); + return bh_quat_scale(result, 1.0f / length, result); +} + + +bh_quat_t *bh_quat_mul(const bh_quat_t *a, + const bh_quat_t *b, + bh_quat_t *result) +{ + bh_quat_t tmp1, tmp2, tmp3; + float w; + + w = a->w * b->w - bh_point4f_dot3(a, b); + + bh_point4f_scale(a, b->w, &tmp1); + bh_point4f_scale(b, a->w, &tmp2); + bh_point4f_cross(a, b, &tmp3); + bh_point4f_add(&tmp1, &tmp2, result); + bh_point4f_add(&tmp3, result, result); + result->w = w; + + return result; +} + + +bh_quat_t *bh_quat_set_euler(float roll, + float pitch, + float yaw, + bh_quat_t *result) +{ + float cr, cp, cy, sr, sp, sy; + + cr = cosf(roll / 2.0f); + cp = cosf(pitch / 2.0f); + cy = cosf(yaw / 2.0f); + sr = sinf(roll / 2.0f); + sp = sinf(pitch / 2.0f); + sy = sinf(yaw / 2.0f); + + result->x = sr * cp * cy - cr * sp * sy; + result->y = cr * sp * cy + sr * cp * sy; + result->z = cr * cp * sy - sr * sp * cy; + result->w = cr * cp * cy + sr * sp * sy; + + return result; +} + + +bh_quat_t *bh_quat_set_rotation(const bh_point3f_t *axis, + float angle, + bh_quat_t *result) +{ + float c, s; + + c = cosf(angle / 2.0f); + s = sinf(angle / 2.0f); + + result->x = axis->x * s; + result->y = axis->y * s; + result->z = axis->z * s; + result->w = c; + + return result; +} + + +void bh_quat_euler(const bh_quat_t *in, + float *roll, + float *pitch, + float *yaw) +{ + float ww, xw, yw, zw, xx, xy, xz, yy, yz, zz, angle; + + xx = in->x * in->x; + xy = in->x * in->y; + xz = in->x * in->z; + xw = in->x * in->w; + yy = in->y * in->y; + yz = in->y * in->z; + yw = in->y * in->w; + zz = in->z * in->z; + zw = in->z * in->w; + ww = in->w * in->w; + + angle = 2.0f * (yw - xz); + if (angle > 1.0f) + angle = 1.0f; + if (angle < -1.0f) + angle = -1.0f; + + *pitch = asinf(angle); + + if (*pitch == (M_PI / 2.0f)) + { + *roll = 0.0f; + *yaw = -2.0f * atan2f(in->x, in->w); + } + else if (*pitch == (M_PI / -2.0f)) + { + *roll = 0.0f; + *yaw = 2.0f * atan2f(in->x, in->w); + } + else + { + *roll = atan2f(2.0f * (xw + yz), ww - xx - yy + zz); + *yaw = atan2f(2.0f * (zw + xy), ww + xx - yy - zz); + } +} + + +void bh_quat_rotation(const bh_quat_t *in, + bh_point3f_t *axis, + float *angle) +{ + float tmp; + + *angle = 2.0f * acosf(in->w); + tmp = sqrtf(1.0f - in->w * in->w); + + if (*angle == 0.0f) + { + axis->x = 1.0f; + axis->y = 0.0f; + axis->z = 0.0f; + } + else + { + axis->x = in->x / tmp; + axis->y = in->y / tmp; + axis->z = in->z / tmp; + } +} + + +bh_matrix4f_t *bh_quat_matrix(const bh_quat_t *in, + bh_matrix4f_t *result) +{ + float xx, xy, xz, xw, yy, yz, yw, zz, zw; + + xx = in->x * in->x; + xy = in->x * in->y; + xz = in->x * in->z; + xw = in->x * in->w; + yy = in->y * in->y; + yz = in->y * in->z; + yw = in->y * in->w; + zz = in->z * in->z; + zw = in->z * in->w; + + bh_matrix4f_identity(result); + result->x.x = 1.0f - 2.0f * (yy + zz); + result->x.y = 2.0f * (xy + zw); + result->x.z = 2.0f * (xz - yw); + result->y.x = 2.0f * (xy - zw); + result->y.y = 1.0f - 2.0f * (xx + zz); + result->y.z = 2.0f * (yz + xw); + result->z.x = 2.0f * (xz + yw); + result->z.y = 2.0f * (yz - xw); + result->z.z = 1.0f - 2.0f * (xx + yy); + + return result; +} + + +bh_matrix4f_t *bh_matrix4f_identity(bh_matrix4f_t *result) +{ + static const bh_matrix4f_t ident = { + {1.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 1.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 1.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 1.0f} + }; + + *result = ident; + return result; +} + + +bh_matrix4f_t *bh_matrix4f_add(const bh_matrix4f_t *a, + const bh_matrix4f_t *b, + bh_matrix4f_t *result) +{ + bh_point4f_add(&a->x, &b->x, &result->x); + bh_point4f_add(&a->y, &b->y, &result->y); + bh_point4f_add(&a->z, &b->z, &result->z); + bh_point4f_add(&a->w, &b->w, &result->w); + + return result; +} + + +bh_matrix4f_t *bh_matrix4f_sub(const bh_matrix4f_t *a, + const bh_matrix4f_t *b, + bh_matrix4f_t *result) +{ + bh_point4f_sub(&a->x, &b->x, &result->x); + bh_point4f_sub(&a->y, &b->y, &result->y); + bh_point4f_sub(&a->z, &b->z, &result->z); + bh_point4f_sub(&a->w, &b->w, &result->w); + + return result; +} + + +bh_matrix4f_t *bh_matrix4f_mul(const bh_matrix4f_t *a, + const bh_matrix4f_t *b, + bh_matrix4f_t *result) +{ + bh_matrix4f_t tmp; + bh_point4f_t row; + + row.x = row.y = row.z = row.w = b->x.x; bh_point4f_mul(&a->x, &row, &tmp.x); + row.x = row.y = row.z = row.w = b->x.y; bh_point4f_madd(&a->y, &row, &tmp.x, &tmp.x); + row.x = row.y = row.z = row.w = b->x.z; bh_point4f_madd(&a->z, &row, &tmp.x, &tmp.x); + row.x = row.y = row.z = row.w = b->x.w; bh_point4f_madd(&a->w, &row, &tmp.x, &tmp.x); + + row.x = row.y = row.z = row.w = b->y.x; bh_point4f_mul(&a->x, &row, &tmp.y); + row.x = row.y = row.z = row.w = b->y.y; bh_point4f_madd(&a->y, &row, &tmp.y, &tmp.y); + row.x = row.y = row.z = row.w = b->y.z; bh_point4f_madd(&a->z, &row, &tmp.y, &tmp.y); + row.x = row.y = row.z = row.w = b->y.w; bh_point4f_madd(&a->w, &row, &tmp.y, &tmp.y); + + row.x = row.y = row.z = row.w = b->z.x; bh_point4f_mul(&a->x, &row, &tmp.z); + row.x = row.y = row.z = row.w = b->z.y; bh_point4f_madd(&a->y, &row, &tmp.z, &tmp.z); + row.x = row.y = row.z = row.w = b->z.z; bh_point4f_madd(&a->z, &row, &tmp.z, &tmp.z); + row.x = row.y = row.z = row.w = b->z.w; bh_point4f_madd(&a->w, &row, &tmp.z, &tmp.z); + + row.x = row.y = row.z = row.w = b->w.x; bh_point4f_mul(&a->x, &row, &tmp.w); + row.x = row.y = row.z = row.w = b->w.y; bh_point4f_madd(&a->y, &row, &tmp.w, &tmp.w); + row.x = row.y = row.z = row.w = b->w.z; bh_point4f_madd(&a->z, &row, &tmp.w, &tmp.w); + row.x = row.y = row.z = row.w = b->w.w; bh_point4f_madd(&a->w, &row, &tmp.w, &tmp.w); + + *result = tmp; + return result; +} + + +bh_matrix4f_t *bh_matrix4f_scale(const bh_matrix4f_t *a, + float b, + bh_matrix4f_t *result) +{ + bh_point4f_scale(&a->x, b, &result->x); + bh_point4f_scale(&a->y, b, &result->y); + bh_point4f_scale(&a->z, b, &result->z); + bh_point4f_scale(&a->w, b, &result->w); + + return result; +} + + +bh_matrix4f_t *bh_matrix4f_transpose(const bh_matrix4f_t *in, + bh_matrix4f_t *result) +{ + bh_matrix4f_t tmp; + + tmp.x.x = in->x.x; + tmp.x.y = in->y.x; + tmp.x.z = in->z.x; + tmp.x.w = in->w.x; + + tmp.y.x = in->x.y; + tmp.y.y = in->y.y; + tmp.y.z = in->z.y; + tmp.y.w = in->w.y; + + tmp.z.x = in->x.z; + tmp.z.y = in->y.z; + tmp.z.z = in->z.z; + tmp.z.w = in->w.z; + + tmp.w.x = in->x.w; + tmp.w.y = in->y.w; + tmp.w.z = in->z.w; + tmp.w.w = in->w.w; + + *result = tmp; + return result; +} + + +float bh_matrix4f_trace(const bh_matrix4f_t *in) +{ + return in->x.x + in->y.y + in->z.z + in->w.w; +} + + +float bh_matrix4f_determinant(const bh_matrix4f_t *in) +{ + float a, b, c, d, e, f, result; + + a = in->x.z * in->y.w - in->x.w * in->y.z; + b = in->x.z * in->z.w - in->x.w * in->z.z; + c = in->x.z * in->w.w - in->x.w * in->w.z; + d = in->y.z * in->z.w - in->y.w * in->z.z; + e = in->y.z * in->w.w - in->y.w * in->w.z; + f = in->z.z * in->w.w - in->z.w * in->w.z; + + result = 0.0f; + result += in->x.x * (in->y.y * f - in->z.y * e + in->w.y * d); + result -= in->y.x * (in->x.y * f - in->z.y * c + in->w.y * b); + result += in->z.x * (in->x.y * e - in->y.y * c + in->w.y * a); + result -= in->w.x * (in->x.y * d - in->y.y * b + in->z.y * a); + + return result; +} + + +bh_matrix4f_t *bh_matrix4f_inverse(const bh_matrix4f_t *in, + bh_matrix4f_t *result) +{ + float a, b, c, d, e, f, det; + bh_matrix4f_t tmp; + + a = in->x.z * in->y.w - in->x.w * in->y.z; + b = in->x.z * in->z.w - in->x.w * in->z.z; + c = in->x.z * in->w.w - in->x.w * in->w.z; + d = in->y.z * in->z.w - in->y.w * in->z.z; + e = in->y.z * in->w.w - in->y.w * in->w.z; + f = in->z.z * in->w.w - in->z.w * in->w.z; + + tmp.x.x = (in->y.y * f - in->z.y * e + in->w.y * d); + tmp.x.y = -(in->x.y * f - in->z.y * c + in->w.y * b); + tmp.x.z = (in->x.y * e - in->y.y * c + in->w.y * a); + tmp.x.w = -(in->x.y * d - in->y.y * b + in->z.y * a); + + det = 0.0f; + det += in->x.x * tmp.x.x; + det += in->y.x * tmp.x.y; + det += in->z.x * tmp.x.z; + det += in->w.x * tmp.x.w; + + if (det == 0.0f) + return bh_matrix4f_identity(result); + + tmp.y.x = -(in->y.x * f - in->z.x * e + in->w.x * d); + tmp.y.y = (in->x.x * f - in->z.x * c + in->w.x * b); + tmp.y.z = -(in->x.x * e - in->y.x * c + in->w.x * a); + tmp.y.w = (in->x.x * d - in->y.x * b + in->z.x * a); + + a = in->x.y * in->y.w - in->x.w * in->y.y; + b = in->x.y * in->z.w - in->x.w * in->z.y; + c = in->x.y * in->w.w - in->x.w * in->w.y; + d = in->y.y * in->z.w - in->y.w * in->z.y; + e = in->y.y * in->w.w - in->y.w * in->w.y; + f = in->z.y * in->w.w - in->z.w * in->w.y; + + tmp.z.x = (in->y.x * f - in->z.x * e + in->w.x * d); + tmp.z.y = -(in->x.x * f - in->z.x * c + in->w.x * b); + tmp.z.z = (in->x.x * e - in->y.x * c + in->w.x * a); + tmp.z.w = -(in->x.x * d - in->y.x * b + in->z.x * a); + + a = in->x.y * in->y.z - in->x.z * in->y.y; + b = in->x.y * in->z.z - in->x.z * in->z.y; + c = in->x.y * in->w.z - in->x.z * in->w.y; + d = in->y.y * in->z.z - in->y.z * in->z.y; + e = in->y.y * in->w.z - in->y.z * in->w.y; + f = in->z.y * in->w.z - in->z.z * in->w.y; + + tmp.w.x = -(in->y.x * f - in->z.x * e + in->w.x * d); + tmp.w.y = (in->x.x * f - in->z.x * c + in->w.x * b); + tmp.w.z = -(in->x.x * e - in->y.x * c + in->w.x * a); + tmp.w.w = (in->x.x * d - in->y.x * b + in->z.x * a); + + return bh_matrix4f_scale(&tmp, 1.0f / det, result); +} + + +bh_matrix4f_t *bh_matrix4f_scaling(float x, + float y, + float z, + bh_matrix4f_t *result) +{ + bh_matrix4f_identity(result); + result->x.x = x; + result->y.y = y; + result->z.z = z; + + return result; +} + + +bh_matrix4f_t *bh_matrix4f_translation(float x, + float y, + float z, + bh_matrix4f_t *result) +{ + bh_matrix4f_identity(result); + result->w.x = x; + result->w.y = y; + result->w.z = z; + + return result; +} + + +bh_matrix4f_t *bh_matrix4f_rotation_x(float angle, + bh_matrix4f_t *result) +{ + float c, s; + + c = cosf(angle); + s = sinf(angle); + + bh_matrix4f_identity(result); + result->y.y = c; + result->z.z = c; + result->z.y = -s; + result->y.z = s; + + return result; +} + + +bh_matrix4f_t *bh_matrix4f_rotation_y(float angle, + bh_matrix4f_t *result) +{ + float c, s; + + c = cosf(angle); + s = sinf(angle); + + bh_matrix4f_identity(result); + result->x.x = c; + result->z.z = c; + result->z.x = s; + result->x.z = -s; + + return result; +} + + +bh_matrix4f_t *bh_matrix4f_rotation_z(float angle, + bh_matrix4f_t *result) +{ + float c, s; + + c = cosf(angle); + s = sinf(angle); + + bh_matrix4f_identity(result); + result->x.x = c; + result->y.y = c; + result->y.x = -s; + result->x.y = s; + + return result; +} + + +bh_matrix4f_t *bh_matrix4f_rotation(const bh_point3f_t *axis, + float angle, + bh_matrix4f_t *result) +{ + float x, y, z, length; + float c, s, moc, xx, xy, xz, yy, yz, zz; + + length = bh_point3f_length(axis); + + if (length == 0.0f) + return bh_matrix4f_identity(result); + + x = axis->x / length; + y = axis->y / length; + z = axis->z / length; + + /* Handle simple axis aligned rotations */ + if (x == 0.0f) + { + if (y == 0.0f) + { + if (z != 0.0f) + { + if (z < 0.0f) + return bh_matrix4f_rotation_z(-angle, result); + + return bh_matrix4f_rotation_z(angle, result); + } + } + else if (z == 0.0f) + { + if (y < 0.0f) + return bh_matrix4f_rotation_y(-angle, result); + + return bh_matrix4f_rotation_y(angle, result); + } + } + else if (y == 0.0f && z == 0.0f) + { + if (x < 0.0f) + return bh_matrix4f_rotation_x(-angle, result); + + return bh_matrix4f_rotation_x(angle, result); + } + + /* Rotate around arbitrary axis */ + bh_matrix4f_identity(result); + + c = cosf(angle); + s = sinf(angle); + moc = 1.0f - c; + + xx = x * x; + xy = x * y; + xz = x * z; + yy = y * y; + yz = y * z; + zz = z * z; + + result->x.x = c + xx * moc; + result->y.x = xy * moc - z * s; + result->z.x = xz * moc + y * s; + + result->x.y = xy * moc + z * s; + result->y.y = c + yy * moc; + result->z.y = yz * moc - x * s; + + result->x.z = xz * moc - y * s; + result->y.z = yz * moc + x * s; + result->z.z = c + zz * moc; + + return result; +} + + +bh_matrix4f_t *bh_matrix4f_rotation_euler(float roll, + float pitch, + float yaw, + bh_matrix4f_t *result) +{ + float rs, rc, ys, yc, ps, pc; + + rs = sinf(roll); + rc = cosf(roll); + ps = sinf(pitch); + pc = cosf(pitch); + ys = sinf(yaw); + yc = cosf(yaw); + + bh_matrix4f_identity(result); + result->x.x = pc * yc; + result->x.y = pc * ys; + result->x.z = -ps; + result->y.x = ps * rs * yc - rc * ys; + result->y.y = ps * rs * ys + rc * yc; + result->y.z = pc * rs; + result->z.x = rs * ys + ps * rc * yc; + result->z.y = ps * rc * ys - rs * yc; + result->z.z = pc * rc; + + return result; +} + + +bh_matrix4f_t *bh_matrix4f_rotation_quat(bh_quat_t *rotation, + bh_matrix4f_t *result) +{ + return bh_quat_matrix(rotation, result); +} + + +bh_matrix4f_t *bh_matrix4f_ortho(float x_min, + float x_max, + float y_min, + float y_max, + float z_min, + float z_max, + bh_matrix4f_t *result) +{ + float dx, dy, dz; + + dx = x_max - x_min; + dy = y_max - y_min; + dz = z_max - z_min; + + bh_matrix4f_identity(result); + + result->x.x = 2.0f / dx; + result->y.y = 2.0f / dy; + result->z.z = -2.0f / dz; + result->w.x = -(x_max + x_min) / dx; + result->w.y = -(y_max + y_min) / dy; + result->w.z = -(z_max + z_min) / dz; + + return result; +} + + +bh_matrix4f_t *bh_matrix4f_perspective(float fov, + float aspect, + float z_min, + float z_max, + bh_matrix4f_t *result) +{ + float t, dz; + + dz = z_max - z_min; + t = tanf(fov / 2.0f); + + bh_matrix4f_identity(result); + + result->x.x = 1.0f / (aspect * t); + result->y.y = 1.0f / t; + result->z.z = -(z_max + z_min) / dz; + result->z.w = -1.0f; + result->w.z = -(2.0f * z_max * z_min) / dz; + result->w.w = 0.0f; + + return result; +} + + +bh_matrix4f_t *bh_matrix4f_lookat(const bh_point3f_t *camera, + const bh_point3f_t *at, + const bh_point3f_t *up, + bh_matrix4f_t *result) +{ + bh_point3f_t cdir, cright, cup; + + bh_point3f_sub(camera, at, &cdir); bh_point3f_normal(&cdir, &cdir); + bh_point3f_cross(up, &cdir, &cright); bh_point3f_normal(&cright, &cright); + bh_point3f_cross(&cdir, &cright, &cup); + + result->x.x = cright.x; + result->x.y = cup.x; + result->x.z = cdir.x; + result->x.w = 0.0f; + + result->y.x = cright.y; + result->y.y = cup.y; + result->y.z = cdir.y; + result->y.w = 0.0f; + + result->z.x = cright.z; + result->z.y = cup.z; + result->z.z = cdir.z; + result->z.w = 0.0f; + + result->w.x = -bh_point3f_dot(&cright, camera); + result->w.y = -bh_point3f_dot(&cup, camera); + result->w.z = -bh_point3f_dot(&cdir, camera); + result->w.w = 1.0f; + + return result; +} + + +bh_point3f_t *bh_matrix4f_transform_point3f(const bh_matrix4f_t *a, + const bh_point3f_t *b, + bh_point3f_t *result) +{ + bh_point4f_t tmp, row; + + row.x = row.y = row.z = row.w = b->x; bh_point4f_mul(&a->x, &row, &tmp); + row.x = row.y = row.z = row.w = b->y; bh_point4f_madd(&a->y, &row, &tmp, &tmp); + row.x = row.y = row.z = row.w = b->z; bh_point4f_madd(&a->z, &row, &tmp, &tmp); + row.x = row.y = row.z = row.w = 1.0f; bh_point4f_madd(&a->w, &row, &tmp, &tmp); + + result->x = tmp.x; + result->y = tmp.y; + result->z = tmp.z; + return result; +} + + +bh_point4f_t *bh_matrix4f_transform_point4f(const bh_matrix4f_t *a, + const bh_point4f_t *b, + bh_point4f_t *result) +{ + bh_point4f_t tmp, row; + + row.x = row.y = row.z = row.w = b->x; bh_point4f_mul(&a->x, &row, &tmp); + row.x = row.y = row.z = row.w = b->y; bh_point4f_madd(&a->y, &row, &tmp, &tmp); + row.x = row.y = row.z = row.w = b->z; bh_point4f_madd(&a->z, &row, &tmp, &tmp); + row.x = row.y = row.z = row.w = b->w; bh_point4f_madd(&a->w, &row, &tmp, &tmp); + + *result = tmp; + return result; +} + + +bh_matrix3f_t *bh_matrix3f_identity(bh_matrix3f_t *result) +{ + static const bh_matrix3f_t ident = { + {1.0f, 0.0f, 0.0f}, + {0.0f, 1.0f, 0.0f}, + {0.0f, 0.0f, 1.0f} + }; + + *result = ident; + return result; +} + + +bh_matrix3f_t *bh_matrix3f_add(const bh_matrix3f_t *a, + const bh_matrix3f_t *b, + bh_matrix3f_t *result) +{ + bh_point3f_add(&a->x, &b->x, &result->x); + bh_point3f_add(&a->y, &b->y, &result->y); + bh_point3f_add(&a->z, &b->z, &result->z); + + return result; +} + + +bh_matrix3f_t *bh_matrix3f_sub(const bh_matrix3f_t *a, + const bh_matrix3f_t *b, + bh_matrix3f_t *result) +{ + bh_point3f_sub(&a->x, &b->x, &result->x); + bh_point3f_sub(&a->y, &b->y, &result->y); + bh_point3f_sub(&a->z, &b->z, &result->z); + + return result; +} + + +bh_matrix3f_t *bh_matrix3f_mul(const bh_matrix3f_t *a, + const bh_matrix3f_t *b, + bh_matrix3f_t *result) +{ + bh_matrix3f_t tmp; + bh_point3f_t row; + + row.x = row.y = row.z = b->x.x; bh_point3f_mul(&a->x, &row, &tmp.x); + row.x = row.y = row.z = b->x.y; bh_point3f_madd(&a->y, &row, &tmp.x, &tmp.x); + row.x = row.y = row.z = b->x.z; bh_point3f_madd(&a->z, &row, &tmp.x, &tmp.x); + + row.x = row.y = row.z = b->y.x; bh_point3f_mul(&a->x, &row, &tmp.y); + row.x = row.y = row.z = b->y.y; bh_point3f_madd(&a->y, &row, &tmp.y, &tmp.y); + row.x = row.y = row.z = b->y.z; bh_point3f_madd(&a->z, &row, &tmp.y, &tmp.y); + + row.x = row.y = row.z = b->z.x; bh_point3f_mul(&a->x, &row, &tmp.z); + row.x = row.y = row.z = b->z.y; bh_point3f_madd(&a->y, &row, &tmp.z, &tmp.z); + row.x = row.y = row.z = b->z.z; bh_point3f_madd(&a->z, &row, &tmp.z, &tmp.z); + + *result = tmp; + return result; +} + + +bh_matrix3f_t *bh_matrix3f_scale(const bh_matrix3f_t *a, + float b, + bh_matrix3f_t *result) +{ + bh_point3f_scale(&a->x, b, &result->x); + bh_point3f_scale(&a->y, b, &result->y); + bh_point3f_scale(&a->z, b, &result->z); + + return result; +} + + +bh_matrix3f_t *bh_matrix3f_transpose(const bh_matrix3f_t *in, + bh_matrix3f_t *result) +{ + bh_matrix3f_t tmp; + + tmp.x.x = in->x.x; + tmp.x.y = in->y.x; + tmp.x.z = in->z.x; + + tmp.y.x = in->x.y; + tmp.y.y = in->y.y; + tmp.y.z = in->z.y; + + tmp.z.x = in->x.z; + tmp.z.y = in->y.z; + tmp.z.z = in->z.z; + + *result = tmp; + return result; +} + + +float bh_matrix3f_trace(const bh_matrix3f_t *in) +{ + return in->x.x + in->y.y + in->z.z; +} + + +float bh_matrix3f_determinant(const bh_matrix3f_t *in) +{ + float a, b, c, result; + + a = in->y.y * in->z.z - in->z.y * in->y.z; + b = in->x.y * in->z.z - in->z.y * in->x.z; + c = in->x.y * in->y.z - in->y.y * in->x.z; + + result = 0.0f; + result += in->x.x * a; + result -= in->y.x * b; + result += in->z.x * c; + + return result; +} + + +bh_matrix3f_t *bh_matrix3f_inverse(const bh_matrix3f_t *in, + bh_matrix3f_t *result) +{ + float a, b, c, det; + bh_matrix3f_t tmp; + + a = in->y.y * in->z.z - in->z.y * in->y.z; + b = in->x.y * in->z.z - in->z.y * in->x.z; + c = in->x.y * in->y.z - in->y.y * in->x.z; + + tmp.x.x = a; + tmp.x.y = -b; + tmp.x.z = c; + + det = 0.0f; + det += in->x.x * tmp.x.x; + det += in->y.x * tmp.x.y; + det += in->z.x * tmp.x.z; + + if (det == 0.0f) + return bh_matrix3f_identity(result); + + a = in->y.x * in->z.z - in->z.x * in->y.z; + b = in->x.x * in->z.z - in->z.x * in->x.z; + c = in->x.x * in->y.z - in->y.x * in->x.z; + + tmp.y.x = -a; + tmp.y.y = b; + tmp.y.z = -c; + + a = in->y.x * in->z.y - in->z.x * in->y.y; + b = in->x.x * in->z.y - in->z.x * in->x.y; + c = in->x.x * in->y.y - in->y.x * in->x.y; + + tmp.z.x = a; + tmp.z.y = -b; + tmp.z.z = c; + + return bh_matrix3f_scale(&tmp, 1.0f / det, result); +} + + +bh_matrix3f_t *bh_matrix3f_scaling(float x, + float y, + bh_matrix3f_t *result) +{ + bh_matrix3f_identity(result); + + result->x.x = x; + result->y.y = y; + + return result; +} + + +bh_matrix3f_t *bh_matrix3f_translation(float x, + float y, + bh_matrix3f_t *result) +{ + bh_matrix3f_identity(result); + + result->z.x = x; + result->z.y = y; + + return result; +} + + +bh_matrix3f_t *bh_matrix3f_rotation(float angle, + bh_matrix3f_t *result) +{ + float c, s; + + c = cosf(angle); + s = sinf(angle); + + bh_matrix3f_identity(result); + result->x.x = c; + result->y.y = c; + result->y.x = -s; + result->x.y = s; + + return result; +} + + +bh_point2f_t *bh_matrix3f_transform_point2f(const bh_matrix3f_t *a, + const bh_point2f_t *b, + bh_point2f_t *result) +{ + bh_point3f_t tmp, row; + + row.x = row.y = row.z = b->x; bh_point3f_mul(&a->x, &row, &tmp); + row.x = row.y = row.z = b->y; bh_point3f_madd(&a->y, &row, &tmp, &tmp); + row.x = row.y = row.z = 1.0f; bh_point3f_madd(&a->z, &row, &tmp, &tmp); + + result->x = tmp.x; + result->y = tmp.y; + + return result; +} + + +bh_point3f_t *bh_matrix3f_transform_point3f(const bh_matrix3f_t *a, + const bh_point3f_t *b, + bh_point3f_t *result) +{ + bh_point3f_t tmp, row; + + row.x = row.y = row.z = b->x; bh_point3f_mul(&a->x, &row, &tmp); + row.x = row.y = row.z = b->y; bh_point3f_madd(&a->y, &row, &tmp, &tmp); + row.x = row.y = row.z = b->z; bh_point3f_madd(&a->z, &row, &tmp, &tmp); + + result->x = tmp.x; + result->y = tmp.y; + result->z = tmp.z; + + return result; +} diff --git a/src/posix/file.c b/src/posix/file.c new file mode 100644 index 0000000..314235c --- /dev/null +++ b/src/posix/file.c @@ -0,0 +1,405 @@ +#include +#include +#include +#include +#include +#include +#include + + +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); +} diff --git a/src/queue.c b/src/queue.c new file mode 100755 index 0000000..3ce8a57 --- /dev/null +++ b/src/queue.c @@ -0,0 +1,209 @@ +#include +#include +#include + + +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; +} diff --git a/src/win32/file.c b/src/win32/file.c new file mode 100644 index 0000000..8effc0d --- /dev/null +++ b/src/win32/file.c @@ -0,0 +1,405 @@ +#include +#include +#include +#include + + +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); +} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100755 index 0000000..a95e1bc --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,24 @@ +set(CMAKE_C_STANDARD 90) +set(CMAKE_C_STANDARD_REQUIRED ON) +set(CMAKE_C_EXTENSIONS OFF) + +# Enable warnings and pedantics +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -pedantic") + +# Enable testing +include(CTest) +enable_testing() + +# Search files +file(GLOB TEST_FILES "src/*.c") + +foreach(TEST_FILENAME ${TEST_FILES}) + get_filename_component(TEST_NAME ${TEST_FILENAME} NAME_WE) + add_executable("${TEST_NAME}" ${TEST_FILENAME}) + target_link_libraries("${TEST_NAME}" bhlib bhunit) + add_test(NAME "${TEST_NAME}" COMMAND "${TEST_NAME}") + if(COVERAGE) + target_compile_options("${TEST_NAME}" PRIVATE -coverage) + target_link_options("${TEST_NAME}" PRIVATE -coverage) + endif() +endforeach() diff --git a/test/src/testalgo.c b/test/src/testalgo.c new file mode 100644 index 0000000..be762f4 --- /dev/null +++ b/test/src/testalgo.c @@ -0,0 +1,483 @@ +#include +#include +#include +#include + + +static int int_equal(const void *lhs, + const void *rhs) +{ + return *(const int*)lhs - *(const int*)rhs; +} + + +static int verify_partition(size_t index, + int pivot, + int *array, + size_t size) +{ + size_t i; + + for (i = 0; i < index; i++) + if (array[i] >= pivot) + return -1; + + for (i = index; i < size; i++) + if (array[i] < pivot) + return -1; + + return 0; +} + + +static int verify_heap(int *array, + size_t size) +{ + size_t i, left, right; + + for (i = 0; i < size; i++) + { + left = i * 2 + 1; + right = i * 2 + 2; + + if (left < size && array[i] < array[left]) + return -1; + if (right < size && array[i] < array[right]) + return -1; + } + + return 0; +} + + +static int reference_rand(void) +{ + static uint32_t next = 2025; + next = next * 1103515245 + 12345; + return (next / 65536) % 32768; +} + + +static void reference_sort(int *array, size_t size) +{ + size_t i, j; + int tmp; + + /* Reference sort is... buble sort! */ + for (i = 0; i < size; i++) + { + for (j = 1; j < size; j++) + { + if (array[j - 1] > array[j]) + { + tmp = array[j - 1]; + array[j - 1] = array[j]; + array[j] = tmp; + } + } + } +} + + +static void fill_arrays(int *array, + int *reference, + size_t size, + int mask) +{ + size_t i; + int negate; + + /* Check if mask needs to be negated */ + negate = mask < 0; + if (negate) + mask = -mask; + + /* Fill the array */ + for (i = 0; i < size; ++i) + { + array[i] = reference_rand(); + + if (mask > 1) + array[i] = array[i] % mask; + + if (negate) + array[i] = -array[i]; + + if (reference) + reference[i] = array[i]; + } +} + + +static int check_sort(void) +{ + size_t sizes[] = {1, 2, 3, 5, 11, 23, 41, 79, 163, 317, 0}, *size; + int reference[317], data[317]; + + /* Test empty array and one element arrays */ + data[0] = 1; data[1] = 2; data[2] = 3; + bh_sort(data, 0, sizeof(int), int_equal); + BH_VERIFY(data[0] == 1); + BH_VERIFY(data[1] == 2); + BH_VERIFY(data[2] == 3); + + bh_sort(data, 1, sizeof(int), int_equal); + BH_VERIFY(data[0] == 1); + BH_VERIFY(data[1] == 2); + BH_VERIFY(data[2] == 3); + + /* Test array against different sizes */ + for (size = sizes; *size; ++size) + { + fill_arrays(data, reference, *size, 0); + reference_sort(reference, *size); + bh_sort(data, *size, sizeof(int), int_equal); + BH_VERIFY(memcmp(reference, data, *size * sizeof(int)) == 0); + } + + /* Test against negative values */ + for (size = sizes; *size; ++size) + { + fill_arrays(data, reference, *size, -1); + reference_sort(reference, *size); + bh_sort(data, *size, sizeof(int), int_equal); + BH_VERIFY(memcmp(reference, data, *size * sizeof(int)) == 0); + } + + /* Test against duplicates */ + for (size = sizes; *size; ++size) + { + fill_arrays(data, reference, *size, 4); + reference_sort(reference, *size); + bh_sort(data, *size, sizeof(int), int_equal); + BH_VERIFY(memcmp(reference, data, *size * sizeof(int)) == 0); + } + + return 0; +} + + +static int check_partition(void) +{ + size_t sizes[] = {1, 2, 3, 5, 11, 23, 41, 79, 163, 317, 0}, *size; + int reference[317], data[317], value, *pivot; + + /* Test empty array and one element array */ + data[0] = 1; data[1] = 2; data[2] = 3; + value = 0; + pivot = bh_partition(&value, data, 0, sizeof(int), int_equal); + BH_VERIFY(pivot != NULL); + BH_VERIFY(data[0] == 1); + BH_VERIFY(data[1] == 2); + BH_VERIFY(data[2] == 3); + + pivot = bh_partition(&value, data, 1, sizeof(int), int_equal); + BH_VERIFY(pivot == data); + BH_VERIFY(data[0] == 1); + BH_VERIFY(data[1] == 2); + BH_VERIFY(data[2] == 3); + + /* Test array against different sizes */ + for (size = sizes; *size; ++size) + { + fill_arrays(data, reference, *size, 0); + value = 16384; + pivot = bh_partition(&value, data, *size, sizeof(int), int_equal); + BH_VERIFY(verify_partition(pivot - data, value, data, *size) == 0); + + reference_sort(data, *size); + reference_sort(reference, *size); + BH_VERIFY(memcmp(data, reference, *size * sizeof(int)) == 0); + } + + /* Test against negative values */ + for (size = sizes; *size; ++size) + { + fill_arrays(data, reference, *size, -1); + value = -16384; + pivot = bh_partition(&value, data, *size, sizeof(int), int_equal); + BH_VERIFY(verify_partition(pivot - data, value, data, *size) == 0); + + reference_sort(data, *size); + reference_sort(reference, *size); + BH_VERIFY(memcmp(data, reference, *size * sizeof(int)) == 0); + } + + /* Test against duplicates */ + for (size = sizes; *size; ++size) + { + fill_arrays(data, reference, *size, 4); + value = 2; + pivot = bh_partition(&value, data, *size, sizeof(int), int_equal); + BH_VERIFY(verify_partition(pivot - data, value, data, *size) == 0); + + reference_sort(data, *size); + reference_sort(reference, *size); + BH_VERIFY(memcmp(data, reference, *size * sizeof(int)) == 0); + } + + /* Test array against small pivots */ + for (size = sizes; *size; ++size) + { + fill_arrays(data, reference, *size, 0); + value = -100; + pivot = bh_partition(&value, data, *size, sizeof(int), int_equal); + BH_VERIFY(verify_partition(pivot - data, value, data, *size) == 0); + + reference_sort(data, *size); + reference_sort(reference, *size); + BH_VERIFY(memcmp(data, reference, *size * sizeof(int)) == 0); + } + + /* Test array against large pivots */ + for (size = sizes; *size; ++size) + { + fill_arrays(data, reference, *size, 0); + value = 65536; + pivot = bh_partition(&value, data, *size, sizeof(int), int_equal); + BH_VERIFY(verify_partition(pivot - data, value, data, *size) == 0); + + reference_sort(data, *size); + reference_sort(reference, *size); + BH_VERIFY(memcmp(data, reference, *size * sizeof(int)) == 0); + } + + /* Test array against different sizes (pivot inside the array) */ + for (size = sizes; *size; ++size) + { + fill_arrays(data, reference, *size, 0); + value = data[0]; + pivot = bh_partition(data, data, *size, sizeof(int), int_equal); + BH_VERIFY(verify_partition(pivot - data, value, data, *size) == 0); + + reference_sort(data, *size); + reference_sort(reference, *size); + BH_VERIFY(memcmp(data, reference, *size * sizeof(int)) == 0); + } + + /* Test against negative values (pivot inside the array) */ + for (size = sizes; *size; ++size) + { + fill_arrays(data, reference, *size, -1); + value = data[0]; + pivot = bh_partition(data, data, *size, sizeof(int), int_equal); + BH_VERIFY(verify_partition(pivot - data, value, data, *size) == 0); + + reference_sort(data, *size); + reference_sort(reference, *size); + BH_VERIFY(memcmp(data, reference, *size * sizeof(int)) == 0); + } + + /* Test against duplicates (pivot inside the array) */ + for (size = sizes; *size; ++size) + { + fill_arrays(data, reference, *size, 4); + value = data[0]; + pivot = bh_partition(data, data, *size, sizeof(int), int_equal); + BH_VERIFY(verify_partition(pivot - data, value, data, *size) == 0); + + reference_sort(data, *size); + reference_sort(reference, *size); + BH_VERIFY(memcmp(data, reference, *size * sizeof(int)) == 0); + } + + /* Same data test */ + for (size = sizes; *size; ++size) + { + memset(data, 0, sizeof(int) * *size); + memset(reference, 0, sizeof(int) * *size); + value = data[*size - 1]; + pivot = bh_partition(data + *size - 1, data, *size, sizeof(int), int_equal); + BH_VERIFY(verify_partition(pivot - data, value, data, *size) == 0); + + reference_sort(data, *size); + reference_sort(reference, *size); + BH_VERIFY(memcmp(data, reference, *size * sizeof(int)) == 0); + } + + return 0; +} + + +static int check_heap_make(void) +{ + size_t sizes[] = {1, 2, 3, 5, 11, 23, 41, 79, 163, 317, 0}, *size; + int data[317]; + + /* Test empty array */ + data[0] = 1; data[1] = 2; data[2] = 3; + bh_heap_make(data, 0, sizeof(int), int_equal); + BH_VERIFY(data[0] == 1); + BH_VERIFY(data[1] == 2); + BH_VERIFY(data[2] == 3); + + /* Test array against different sizes */ + for (size = sizes; *size; ++size) + { + fill_arrays(data, NULL, *size, 0); + bh_heap_make(data, *size, sizeof(int), int_equal); + BH_VERIFY(verify_heap(data, *size) == 0); + } + + /* Test array against negative values */ + for (size = sizes; *size; ++size) + { + fill_arrays(data, NULL, *size, -1); + bh_heap_make(data, *size, sizeof(int), int_equal); + BH_VERIFY(verify_heap(data, *size) == 0); + } + + /* Test against duplicates */ + for (size = sizes; *size; ++size) + { + fill_arrays(data, NULL, *size, 4); + bh_heap_make(data, *size, sizeof(int), int_equal); + BH_VERIFY(verify_heap(data, *size) == 0); + } + + return 0; +} + + +static int check_heap_insert(void) +{ + size_t sizes[] = {1, 2, 3, 5, 11, 23, 41, 79, 163, 317, 0}, *size; + int data[317], reference[317]; + + /* Test array against different sizes */ + for (size = sizes; *size; ++size) + { + size_t i; + fill_arrays(data, reference, *size, 0); + for (i = 0; i < *size; ++i) + { + int value; + + value = data[i]; + bh_heap_insert(&value, data, i, sizeof(int), int_equal); + BH_VERIFY(verify_heap(data, i + 1) == 0); + } + + reference_sort(data, *size); + reference_sort(reference, *size); + BH_VERIFY(memcmp(data, reference, *size) == 0); + } + + /* Test array against different sizes (inplace)*/ + for (size = sizes; *size; ++size) + { + size_t i; + fill_arrays(data, reference, *size, 0); + for (i = 0; i < *size; ++i) + { + bh_heap_insert(NULL, data, i, sizeof(int), int_equal); + BH_VERIFY(verify_heap(data, i + 1) == 0); + } + + reference_sort(data, *size); + reference_sort(reference, *size); + BH_VERIFY(memcmp(data, reference, *size) == 0); + } + + return 0; +} + + +static int check_heap_remove(void) +{ + size_t sizes[] = {1, 2, 3, 5, 11, 23, 41, 79, 163, 317, 0}, *size; + int data[317], reference[317]; + + /* Test array against different sizes */ + for (size = sizes; *size; ++size) + { + size_t i; + fill_arrays(data, reference, *size, 0); + bh_heap_make(data, *size, sizeof(int), int_equal); + + for (i = *size; i > 0; i--) + { + BH_VERIFY(verify_heap(data, i) == 0); + bh_heap_remove(data, i, sizeof(int), int_equal); + } + + reference_sort(data, *size); + reference_sort(reference, *size); + BH_VERIFY(memcmp(data, reference, *size) == 0); + } + + return 0; +} + + +static int check_heap_replace(void) +{ + int data[11] = {1, 2, 3, 5, 11, 23, 41, 79, 163, 317, 0}; + int reference[11] = {1, 2, 3, 5, 11, 23, 41, 79, 163, 317, 0}; + int value; + + /* Prepare test arrays */ + bh_heap_make(data, 10, sizeof(int), int_equal); + bh_heap_make(reference, 10, sizeof(int), int_equal); + BH_VERIFY(verify_heap(data, 10) == 0); + BH_VERIFY(verify_heap(reference, 10) == 0); + + /* Verfify heap replace */ + value = 20; + bh_heap_replace(&value, data, 10, sizeof(int), int_equal); + bh_heap_remove(reference, 10, sizeof(int), int_equal); + bh_heap_insert(&value, reference, 9, sizeof(int), int_equal); + + BH_VERIFY(verify_heap(data, 10) == 0); + BH_VERIFY(verify_heap(reference, 10) == 0); + bh_sort(data, 10, sizeof(int), int_equal); + bh_sort(reference, 10, sizeof(int), int_equal); + BH_VERIFY(memcmp(data, reference, 10 * sizeof(int)) == 0); + + /* Verify heap replace on single element array */ + value = 400; + bh_heap_replace(&value, data, 1, sizeof(int), int_equal); + bh_heap_remove(reference, 1, sizeof(int), int_equal); + bh_heap_insert(&value, reference, 0, sizeof(int), int_equal); + + BH_VERIFY(verify_heap(data, 1) == 0); + BH_VERIFY(verify_heap(reference, 1) == 0); + BH_VERIFY(memcmp(data, reference, 1 * sizeof(int)) == 0); + + /* Prepare test arrays */ + bh_sort(data, 10, sizeof(int), int_equal); + bh_sort(reference, 10, sizeof(int), int_equal); + bh_heap_make(data, 10, sizeof(int), int_equal); + bh_heap_make(reference, 10, sizeof(int), int_equal); + BH_VERIFY(verify_heap(data, 10) == 0); + BH_VERIFY(verify_heap(reference, 10) == 0); + + data[10] = 1000; + value = 1000; + bh_heap_replace(NULL, data, 10, sizeof(int), int_equal); + bh_heap_remove(reference, 10, sizeof(int), int_equal); + bh_heap_insert(&value, reference, 9, sizeof(int), int_equal); + BH_VERIFY(verify_heap(data, 10) == 0); + BH_VERIFY(verify_heap(reference, 10) == 0); + bh_sort(data, 10, sizeof(int), int_equal); + bh_sort(reference, 10, sizeof(int), int_equal); + BH_VERIFY(memcmp(data, reference, 10 * sizeof(int)) == 0); + + return 0; +} + + +int main(int argc, char **argv) +{ + BH_UNUSED(argc); + BH_UNUSED(argv); + + bh_unit_add("sort", check_sort); + bh_unit_add("partition", check_partition); + bh_unit_add("heap_make", check_heap_make); + bh_unit_add("heap_insert", check_heap_insert); + bh_unit_add("heap_remove", check_heap_remove); + bh_unit_add("heap_replace", check_heap_replace); + + return bh_unit_run(); +} \ No newline at end of file diff --git a/test/src/testfile.c b/test/src/testfile.c new file mode 100644 index 0000000..0f006a4 --- /dev/null +++ b/test/src/testfile.c @@ -0,0 +1,630 @@ +#include +#include +#include + + +#define FILENAME1 "bhfile1.dat" +#define FILENAME2 "bhfile2.dat" +#define FILENAME3 "bhfile3.dat" +#define FILENAME4 "bhfile4.dat" + + +/** + * Cleanup any files, that could be left from previous test runs. + */ +static void cleanup(void) +{ + remove(FILENAME1); + remove(FILENAME2); + remove(FILENAME3); + remove(FILENAME4); +} + + +/** + * Check for invalid arguments. + */ +static int check_null(void) +{ + bh_io_t *io; + + /* Check against NULL pointers */ + BH_VERIFY(bh_file_new(NULL) == NULL); + BH_VERIFY(bh_io_classname(NULL) == NULL); + BH_VERIFY(bh_io_open(NULL, 0) != BH_OK); + BH_VERIFY(bh_io_close(NULL) != BH_OK); + BH_VERIFY(bh_io_read(NULL, NULL, 0, NULL) != BH_OK); + BH_VERIFY(bh_io_write(NULL, NULL, 0, NULL) != BH_OK); + BH_VERIFY(bh_io_peek(NULL, NULL, 0, NULL) != BH_OK); + BH_VERIFY(bh_io_tell(NULL, NULL) != BH_OK); + BH_VERIFY(bh_io_seek(NULL, 0, 0) != BH_OK); + BH_VERIFY(bh_io_flush(NULL) != BH_OK); + BH_VERIFY(bh_io_size(NULL, NULL) != BH_OK); + BH_VERIFY(bh_io_flags(NULL) == BH_IO_FLAG_ERROR); + BH_VERIFY(bh_io_clear(NULL) == BH_OK); + bh_io_free(NULL); + + /* Check against NULL pointers and valid IO object */ + BH_VERIFY((io = bh_file_new(FILENAME1)) != NULL); + BH_VERIFY(strcmp(bh_io_classname(io), BH_FILE_CLASSNAME) == 0); + BH_VERIFY(bh_io_open(io, 0) != BH_OK); + BH_VERIFY(bh_io_close(io) != BH_OK); + BH_VERIFY(bh_io_read(io, NULL, 0, NULL) != BH_OK); + BH_VERIFY(bh_io_write(io, NULL, 0, NULL) != BH_OK); + BH_VERIFY(bh_io_peek(io, NULL, 0, NULL) != BH_OK); + BH_VERIFY(bh_io_tell(io, NULL) != BH_OK); + BH_VERIFY(bh_io_seek(io, 0, 0) != BH_OK); + BH_VERIFY(bh_io_flush(io) != BH_OK); + BH_VERIFY(bh_io_size(io, NULL) != BH_OK); + BH_VERIFY(bh_io_flags(io) == BH_IO_FLAG_ERROR); + BH_VERIFY(bh_io_clear(io) == BH_OK); + bh_io_free(io); + + return 0; +} + + +/** + * Check for normal mode. + */ +static int check_normal(void) +{ + int64_t position; + char buffer[128]; + size_t actual; + bh_io_t *io; + + /* Check operations for write only access */ + BH_VERIFY((io = bh_file_new(FILENAME1)) != NULL); + BH_VERIFY(strcmp(bh_io_classname(io), BH_FILE_CLASSNAME) == 0); + BH_VERIFY(bh_io_open(io, BH_IO_WRITE) == BH_OK); + BH_VERIFY(bh_io_flags(io) & BH_IO_FLAG_OPEN); + + BH_VERIFY(bh_io_open(io, BH_IO_READ) != BH_OK); + + BH_VERIFY(bh_io_write(io, "1234567890", 10, &actual) == BH_OK); + BH_VERIFY(actual == 10); + + BH_VERIFY(bh_io_write(io, "1234567890", 10, &actual) == BH_OK); + BH_VERIFY(actual == 10); + + BH_VERIFY(bh_io_seek(io, 10, BH_IO_SEEK_SET) == BH_OK); + BH_VERIFY(bh_io_write(io, "abcde", 5, &actual) == BH_OK); + BH_VERIFY(actual == 5); + + BH_VERIFY(bh_io_seek(io, 0, BH_IO_SEEK_SET) == BH_OK); + BH_VERIFY(bh_io_read(io, buffer, 1, &actual) != BH_OK); + BH_VERIFY(bh_io_seek(io, 0, BH_IO_SEEK_END) == BH_OK); + BH_VERIFY(bh_io_tell(io, &position) == BH_OK); + BH_VERIFY(position == 20); + bh_io_close(io); + + /* Check operations for read only access */ + BH_VERIFY(bh_io_open(io, BH_IO_READ) == BH_OK); + BH_VERIFY(bh_io_flags(io) & BH_IO_FLAG_OPEN); + BH_VERIFY(strcmp(bh_io_classname(io), BH_FILE_CLASSNAME) == 0); + BH_VERIFY(bh_io_read(io, buffer, 10, &actual) == BH_OK); + BH_VERIFY(actual == 10); + BH_VERIFY(memcmp(buffer, "1234567890", 10) == 0); + + BH_VERIFY(bh_io_read(io, buffer, 5, &actual) == BH_OK); + BH_VERIFY(actual == 5); + BH_VERIFY(memcmp(buffer, "abcde", 5) == 0); + + BH_VERIFY(bh_io_seek(io, 0, BH_IO_SEEK_SET) == BH_OK); + BH_VERIFY(bh_io_read(io, buffer, sizeof(buffer), &actual) == BH_OK); + BH_VERIFY(actual == 20); + BH_VERIFY(memcmp(buffer, "1234567890abcde67890", 20) == 0); + + BH_VERIFY(bh_io_write(io, "1234567890", 10, &actual) != BH_OK); + BH_VERIFY(bh_io_seek(io, -5, BH_IO_SEEK_CUR) == BH_OK); + BH_VERIFY(bh_io_read(io, buffer, 5, &actual) == BH_OK); + BH_VERIFY(actual == 5); + BH_VERIFY(memcmp(buffer, "67890", 5) == 0); + bh_io_close(io); + + /* Check operations for read and write access */ + BH_VERIFY((io = bh_file_new(FILENAME1)) != NULL); + BH_VERIFY(strcmp(bh_io_classname(io), BH_FILE_CLASSNAME) == 0); + BH_VERIFY(bh_io_open(io, BH_IO_WRITE | BH_IO_READ) == BH_OK); + BH_VERIFY(bh_io_flags(io) & BH_IO_FLAG_OPEN); + + BH_VERIFY(bh_io_write(io, "abcde", 5, &actual) == BH_OK); + BH_VERIFY(actual == 5); + + BH_VERIFY(bh_io_write(io, "1234567890", 10, &actual) == BH_OK); + BH_VERIFY(actual == 10); + + BH_VERIFY(bh_io_write(io, "1234567890abcde67890", 20, &actual) == BH_OK); + BH_VERIFY(actual == 20); + + BH_VERIFY(bh_io_seek(io, 0, BH_IO_SEEK_SET) == BH_OK); + BH_VERIFY(bh_io_read(io, buffer, 5, &actual) == BH_OK); + BH_VERIFY(actual == 5); + BH_VERIFY(memcmp(buffer, "abcde", 5) == 0); + + BH_VERIFY(bh_io_read(io, buffer, 10, &actual) == BH_OK); + BH_VERIFY(actual == 10); + BH_VERIFY(memcmp(buffer, "1234567890", 10) == 0); + + BH_VERIFY(bh_io_seek(io, 0, BH_IO_SEEK_SET) == BH_OK); + BH_VERIFY(bh_io_read(io, buffer, 35, &actual) == BH_OK); + BH_VERIFY(actual == 35); + BH_VERIFY(memcmp(buffer, "abcde12345678901234567890abcde67890", 35) == 0); + bh_io_close(io); + + bh_io_free(io); + return 0; +} + + +/** + * Check for truncate mode. + */ +static int check_truncate(void) +{ + int64_t position; + char buffer[128]; + size_t actual; + bh_io_t *io; + + /* Check operations for write only access */ + BH_VERIFY((io = bh_file_new(FILENAME1)) != NULL); + BH_VERIFY(strcmp(bh_io_classname(io), BH_FILE_CLASSNAME) == 0); + BH_VERIFY(bh_io_open(io, BH_IO_WRITE | BH_IO_TRUNCATE) == BH_OK); + BH_VERIFY(bh_io_flags(io) & BH_IO_FLAG_OPEN); + + BH_VERIFY(bh_io_write(io, "1234567890", 10, &actual) == BH_OK); + BH_VERIFY(actual == 10); + + BH_VERIFY(bh_io_write(io, "1234567890", 10, &actual) == BH_OK); + BH_VERIFY(actual == 10); + + BH_VERIFY(bh_io_seek(io, 10, BH_IO_SEEK_SET) == BH_OK); + BH_VERIFY(bh_io_write(io, "abcde", 5, &actual) == BH_OK); + BH_VERIFY(actual == 5); + + BH_VERIFY(bh_io_seek(io, 0, BH_IO_SEEK_SET) == BH_OK); + BH_VERIFY(bh_io_read(io, buffer, 1, &actual) != BH_OK); + BH_VERIFY(bh_io_seek(io, 0, BH_IO_SEEK_END) == BH_OK); + BH_VERIFY(bh_io_tell(io, &position) == BH_OK); + BH_VERIFY(position == 20); + bh_io_close(io); + + /* Check operations for read only access without truncate */ + BH_VERIFY(bh_io_open(io, BH_IO_READ) == BH_OK); + BH_VERIFY(bh_io_flags(io) & BH_IO_FLAG_OPEN); + BH_VERIFY(bh_io_read(io, buffer, 10, &actual) == BH_OK); + BH_VERIFY(actual == 10); + BH_VERIFY(memcmp(buffer, "1234567890", 10) == 0); + + BH_VERIFY(bh_io_read(io, buffer, 5, &actual) == BH_OK); + BH_VERIFY(actual == 5); + BH_VERIFY(memcmp(buffer, "abcde", 5) == 0); + + BH_VERIFY(bh_io_seek(io, 0, BH_IO_SEEK_SET) == BH_OK); + BH_VERIFY(bh_io_read(io, buffer, sizeof(buffer), &actual) == BH_OK); + BH_VERIFY(actual == 20); + BH_VERIFY(memcmp(buffer, "1234567890abcde67890", 20) == 0); + + BH_VERIFY(bh_io_seek(io, -5, BH_IO_SEEK_CUR) == BH_OK); + BH_VERIFY(bh_io_read(io, buffer, 5, &actual) == BH_OK); + BH_VERIFY(actual == 5); + BH_VERIFY(memcmp(buffer, "67890", 5) == 0); + bh_io_close(io); + + /* Check operations for read only access */ + BH_VERIFY(bh_io_open(io, BH_IO_READ | BH_IO_TRUNCATE) == BH_OK); + BH_VERIFY(bh_io_flags(io) & BH_IO_FLAG_OPEN); + BH_VERIFY(bh_io_read(io, buffer, 10, &actual) == BH_OK); + BH_VERIFY(actual == 0); + + BH_VERIFY(bh_io_read(io, buffer, 5, &actual) == BH_OK); + BH_VERIFY(actual == 0); + + BH_VERIFY(bh_io_write(io, "1234567890", 10, &actual) != BH_OK); + BH_VERIFY(bh_io_seek(io, 0, BH_IO_SEEK_SET) == BH_OK); + BH_VERIFY(bh_io_read(io, buffer, sizeof(buffer), &actual) == BH_OK); + BH_VERIFY(actual == 0); + bh_io_close(io); + + /* Check operations for read and write access */ + BH_VERIFY((io = bh_file_new(FILENAME1)) != NULL); + BH_VERIFY(strcmp(bh_io_classname(io), BH_FILE_CLASSNAME) == 0); + BH_VERIFY(bh_io_open(io, BH_IO_WRITE | BH_IO_READ | BH_IO_TRUNCATE) == BH_OK); + BH_VERIFY(bh_io_flags(io) & BH_IO_FLAG_OPEN); + + BH_VERIFY(bh_io_write(io, "abcde", 5, &actual) == BH_OK); + BH_VERIFY(actual == 5); + + BH_VERIFY(bh_io_write(io, "1234567890", 10, &actual) == BH_OK); + BH_VERIFY(actual == 10); + + BH_VERIFY(bh_io_write(io, "1234567890abcde67890", 20, &actual) == BH_OK); + BH_VERIFY(actual == 20); + + BH_VERIFY(bh_io_seek(io, 0, BH_IO_SEEK_SET) == BH_OK); + BH_VERIFY(bh_io_read(io, buffer, 5, &actual) == BH_OK); + BH_VERIFY(actual == 5); + BH_VERIFY(memcmp(buffer, "abcde", 5) == 0); + + BH_VERIFY(bh_io_read(io, buffer, 10, &actual) == BH_OK); + BH_VERIFY(actual == 10); + BH_VERIFY(memcmp(buffer, "1234567890", 10) == 0); + + BH_VERIFY(bh_io_seek(io, 0, BH_IO_SEEK_SET) == BH_OK); + BH_VERIFY(bh_io_read(io, buffer, 35, &actual) == BH_OK); + BH_VERIFY(actual == 35); + BH_VERIFY(memcmp(buffer, "abcde12345678901234567890abcde67890", 35) == 0); + bh_io_close(io); + + BH_VERIFY(bh_io_open(io, BH_IO_WRITE | BH_IO_TRUNCATE) == BH_OK); + BH_VERIFY(bh_io_flags(io) & BH_IO_FLAG_OPEN); + bh_io_close(io); + + bh_io_free(io); + return 0; +} + + +/** + * Check for exist mode. + */ +static int check_exist(void) +{ + int64_t position; + char buffer[128]; + size_t actual; + bh_io_t *io; + + /* Check operations for write only access */ + BH_VERIFY((io = bh_file_new(FILENAME1)) != NULL); + BH_VERIFY(strcmp(bh_io_classname(io), BH_FILE_CLASSNAME) == 0); + BH_VERIFY(bh_io_open(io, BH_IO_WRITE | BH_IO_EXIST) == BH_OK); + BH_VERIFY(bh_io_flags(io) & BH_IO_FLAG_OPEN); + + BH_VERIFY(bh_io_write(io, "1234567890", 10, &actual) == BH_OK); + BH_VERIFY(actual == 10); + + BH_VERIFY(bh_io_write(io, "1234567890", 10, &actual) == BH_OK); + BH_VERIFY(actual == 10); + + BH_VERIFY(bh_io_seek(io, 10, BH_IO_SEEK_SET) == BH_OK); + BH_VERIFY(bh_io_write(io, "abcde", 5, &actual) == BH_OK); + BH_VERIFY(actual == 5); + + BH_VERIFY(bh_io_seek(io, 0, BH_IO_SEEK_SET) == BH_OK); + BH_VERIFY(bh_io_read(io, buffer, 1, &actual) != BH_OK); + BH_VERIFY(bh_io_seek(io, 0, BH_IO_SEEK_END) == BH_OK); + BH_VERIFY(bh_io_tell(io, &position) == BH_OK); + BH_VERIFY(position == 20); + bh_io_close(io); + + /* Check operations for read only access */ + BH_VERIFY(bh_io_open(io, BH_IO_READ | BH_IO_EXIST) == BH_OK); + BH_VERIFY(bh_io_flags(io) & BH_IO_FLAG_OPEN); + BH_VERIFY(bh_io_read(io, buffer, 10, &actual) == BH_OK); + BH_VERIFY(actual == 10); + BH_VERIFY(memcmp(buffer, "1234567890", 10) == 0); + + BH_VERIFY(bh_io_read(io, buffer, 5, &actual) == BH_OK); + BH_VERIFY(actual == 5); + BH_VERIFY(memcmp(buffer, "abcde", 5) == 0); + + BH_VERIFY(bh_io_seek(io, 0, BH_IO_SEEK_SET) == BH_OK); + BH_VERIFY(bh_io_read(io, buffer, sizeof(buffer), &actual) == BH_OK); + BH_VERIFY(actual == 20); + BH_VERIFY(memcmp(buffer, "1234567890abcde67890", 20) == 0); + + BH_VERIFY(bh_io_write(io, "1234567890", 10, &actual) != BH_OK); + BH_VERIFY(bh_io_seek(io, -5, BH_IO_SEEK_CUR) == BH_OK); + BH_VERIFY(bh_io_read(io, buffer, 5, &actual) == BH_OK); + BH_VERIFY(actual == 5); + BH_VERIFY(memcmp(buffer, "67890", 5) == 0); + bh_io_close(io); + + /* Check operations for read and write access */ + BH_VERIFY((io = bh_file_new(FILENAME1)) != NULL); + BH_VERIFY(strcmp(bh_io_classname(io), BH_FILE_CLASSNAME) == 0); + BH_VERIFY(bh_io_open(io, BH_IO_WRITE | BH_IO_READ | BH_IO_EXIST) == BH_OK); + BH_VERIFY(bh_io_flags(io) & BH_IO_FLAG_OPEN); + + BH_VERIFY(bh_io_write(io, "abcde", 5, &actual) == BH_OK); + BH_VERIFY(actual == 5); + + BH_VERIFY(bh_io_write(io, "1234567890", 10, &actual) == BH_OK); + BH_VERIFY(actual == 10); + + BH_VERIFY(bh_io_write(io, "1234567890abcde67890", 20, &actual) == BH_OK); + BH_VERIFY(actual == 20); + + BH_VERIFY(bh_io_seek(io, 0, BH_IO_SEEK_SET) == BH_OK); + BH_VERIFY(bh_io_read(io, buffer, 5, &actual) == BH_OK); + BH_VERIFY(actual == 5); + BH_VERIFY(memcmp(buffer, "abcde", 5) == 0); + + BH_VERIFY(bh_io_read(io, buffer, 10, &actual) == BH_OK); + BH_VERIFY(actual == 10); + BH_VERIFY(memcmp(buffer, "1234567890", 10) == 0); + + BH_VERIFY(bh_io_seek(io, 0, BH_IO_SEEK_SET) == BH_OK); + BH_VERIFY(bh_io_read(io, buffer, 35, &actual) == BH_OK); + BH_VERIFY(actual == 35); + BH_VERIFY(memcmp(buffer, "abcde12345678901234567890abcde67890", 35) == 0); + bh_io_close(io); + bh_io_free(io); + + /* Check against non existing files */ + BH_VERIFY((io = bh_file_new(FILENAME2)) != NULL); + BH_VERIFY(strcmp(bh_io_classname(io), BH_FILE_CLASSNAME) == 0); + BH_VERIFY(bh_io_open(io, BH_IO_WRITE | BH_IO_EXIST) != BH_OK); + BH_VERIFY((bh_io_flags(io) & BH_IO_FLAG_OPEN) == 0); + BH_VERIFY(bh_io_open(io, BH_IO_READ | BH_IO_EXIST) != BH_OK); + BH_VERIFY((bh_io_flags(io) & BH_IO_FLAG_OPEN) == 0); + BH_VERIFY(bh_io_open(io, BH_IO_WRITE | BH_IO_READ | BH_IO_EXIST) != BH_OK); + BH_VERIFY((bh_io_flags(io) & BH_IO_FLAG_OPEN) == 0); + bh_io_free(io); + return 0; +} + + +/** + * Check in append mode. + */ +static int check_append(void) +{ + int64_t position; + char buffer[128]; + size_t actual; + bh_io_t *io; + + /* Explicitly call cleanup */ + cleanup(); + + /* Check operations for write only access */ + BH_VERIFY((io = bh_file_new(FILENAME1)) != NULL); + BH_VERIFY(strcmp(bh_io_classname(io), BH_FILE_CLASSNAME) == 0); + BH_VERIFY(bh_io_open(io, BH_IO_WRITE | BH_IO_APPEND) == BH_OK); + BH_VERIFY(bh_io_flags(io) & BH_IO_FLAG_OPEN); + + BH_VERIFY(bh_io_write(io, "1234567890", 10, &actual) == BH_OK); + BH_VERIFY(actual == 10); + + BH_VERIFY(bh_io_write(io, "1234567890", 10, &actual) == BH_OK); + BH_VERIFY(actual == 10); + + BH_VERIFY(bh_io_seek(io, 10, BH_IO_SEEK_SET) == BH_OK); + BH_VERIFY(bh_io_write(io, "abcde", 5, &actual) == BH_OK); + BH_VERIFY(actual == 5); + + BH_VERIFY(bh_io_seek(io, 0, BH_IO_SEEK_SET) == BH_OK); + BH_VERIFY(bh_io_read(io, buffer, 1, &actual) != BH_OK); + BH_VERIFY(bh_io_seek(io, 0, BH_IO_SEEK_END) == BH_OK); + BH_VERIFY(bh_io_tell(io, &position) == BH_OK); + BH_VERIFY(position == 25); + bh_io_close(io); + + /* Check operations for read only access */ + BH_VERIFY(bh_io_open(io, BH_IO_READ | BH_IO_APPEND) == BH_OK); + BH_VERIFY(bh_io_flags(io) & BH_IO_FLAG_OPEN); + BH_VERIFY(strcmp(bh_io_classname(io), BH_FILE_CLASSNAME) == 0); + BH_VERIFY(bh_io_read(io, buffer, 10, &actual) == BH_OK); + BH_VERIFY(actual == 10); + BH_VERIFY(memcmp(buffer, "1234567890", 10) == 0); + + BH_VERIFY(bh_io_read(io, buffer, 10, &actual) == BH_OK); + BH_VERIFY(actual == 10); + BH_VERIFY(memcmp(buffer, "1234567890", 10) == 0); + + BH_VERIFY(bh_io_read(io, buffer, 5, &actual) == BH_OK); + BH_VERIFY(actual == 5); + BH_VERIFY(memcmp(buffer, "abcde", 5) == 0); + + BH_VERIFY(bh_io_write(io, "1234567890", 10, &actual) != BH_OK); + BH_VERIFY(bh_io_seek(io, -5, BH_IO_SEEK_CUR) == BH_OK); + BH_VERIFY(bh_io_read(io, buffer, 5, &actual) == BH_OK); + BH_VERIFY(actual == 5); + BH_VERIFY(memcmp(buffer, "abcde", 5) == 0); + bh_io_close(io); + + /* Check operations for read and write access */ + BH_VERIFY(bh_io_open(io, BH_IO_WRITE | BH_IO_READ | BH_IO_APPEND) == BH_OK); + BH_VERIFY(bh_io_flags(io) & BH_IO_FLAG_OPEN); + + BH_VERIFY(bh_io_write(io, "abcde", 5, &actual) == BH_OK); + BH_VERIFY(actual == 5); + + BH_VERIFY(bh_io_write(io, "1234567890", 10, &actual) == BH_OK); + BH_VERIFY(actual == 10); + + BH_VERIFY(bh_io_seek(io, 0, BH_IO_SEEK_SET) == BH_OK); + BH_VERIFY(bh_io_read(io, buffer, 40, &actual) == BH_OK); + BH_VERIFY(actual == 40); + BH_VERIFY(memcmp(buffer, "12345678901234567890abcdeabcde1234567890", 40) == 0); + bh_io_close(io); + + BH_VERIFY(bh_io_open(io, BH_IO_WRITE | BH_IO_TRUNCATE) == BH_OK); + BH_VERIFY(bh_io_flags(io) & BH_IO_FLAG_OPEN); + bh_io_close(io); + + bh_io_free(io); + return 0; +} + + +/** + * Check for create mode. + */ +static int check_create(void) +{ + bh_io_t *io; + + /* Check for already existing file */ + BH_VERIFY((io = bh_file_new(FILENAME1)) != NULL); + BH_VERIFY(strcmp(bh_io_classname(io), BH_FILE_CLASSNAME) == 0); + BH_VERIFY(bh_io_open(io, BH_IO_WRITE | BH_IO_CREATE) != BH_OK); + BH_VERIFY((bh_io_flags(io) & BH_IO_FLAG_OPEN) == 0); + bh_io_free(io); + + /* Check for new file with write access */ + BH_VERIFY((io = bh_file_new(FILENAME2)) != NULL); + BH_VERIFY(strcmp(bh_io_classname(io), BH_FILE_CLASSNAME) == 0); + BH_VERIFY(bh_io_open(io, BH_IO_WRITE | BH_IO_CREATE) == BH_OK); + BH_VERIFY(bh_io_flags(io) & BH_IO_FLAG_OPEN); + bh_io_free(io); + + /* Check for new file with read access */ + BH_VERIFY((io = bh_file_new(FILENAME3)) != NULL); + BH_VERIFY(strcmp(bh_io_classname(io), BH_FILE_CLASSNAME) == 0); + BH_VERIFY(bh_io_open(io, BH_IO_READ | BH_IO_CREATE) == BH_OK); + BH_VERIFY(bh_io_flags(io) & BH_IO_FLAG_OPEN); + bh_io_free(io); + + /* Check for new file with read/write access */ + BH_VERIFY((io = bh_file_new(FILENAME4)) != NULL); + BH_VERIFY(strcmp(bh_io_classname(io), BH_FILE_CLASSNAME) == 0); + BH_VERIFY(bh_io_open(io, BH_IO_WRITE | BH_IO_READ | BH_IO_CREATE) == BH_OK); + BH_VERIFY(bh_io_flags(io) & BH_IO_FLAG_OPEN); + bh_io_free(io); + + return 0; +} + + +/** + * Check for EOF flags. + */ +static int check_eof(void) +{ + char buffer[128]; + size_t actual; + bh_io_t *io; + + BH_VERIFY((io = bh_file_new(FILENAME1)) != NULL); + BH_VERIFY(strcmp(bh_io_classname(io), BH_FILE_CLASSNAME) == 0); + BH_VERIFY(bh_io_open(io, BH_IO_READ | BH_IO_TRUNCATE) == BH_OK); + BH_VERIFY(bh_io_flags(io) & BH_IO_FLAG_OPEN); + + BH_VERIFY(bh_io_read(io, buffer, 128, &actual) == BH_OK); + BH_VERIFY(actual == 0); + BH_VERIFY(bh_io_flags(io) & BH_IO_FLAG_EOF); + + bh_io_close(io); + bh_io_free(io); + + return 0; +} + + +/** + * Check for error flags. + */ +static int check_error(void) +{ + size_t actual; + bh_io_t *io; + + BH_VERIFY((io = bh_file_new(FILENAME1)) != NULL); + BH_VERIFY(strcmp(bh_io_classname(io), BH_FILE_CLASSNAME) == 0); + BH_VERIFY(bh_io_open(io, BH_IO_READ) == BH_OK); + BH_VERIFY(bh_io_flags(io) & BH_IO_FLAG_OPEN); + + BH_VERIFY(bh_io_write(io, "12345", 5, &actual) != BH_OK); + BH_VERIFY(bh_io_flags(io) & BH_IO_FLAG_ERROR); + BH_VERIFY(bh_io_clear(io) == BH_OK); + BH_VERIFY((bh_io_flags(io) & BH_IO_FLAG_ERROR) == 0); + + bh_io_close(io); + bh_io_free(io); + + return 0; +} + + +/** + * Check peek operation. + */ +static int check_peek(void) +{ + int64_t previous, current; + char buffer[128]; + size_t actual; + bh_io_t *io; + + BH_VERIFY((io = bh_file_new(FILENAME1)) != NULL); + BH_VERIFY(strcmp(bh_io_classname(io), BH_FILE_CLASSNAME) == 0); + BH_VERIFY(bh_io_open(io, BH_IO_WRITE | BH_IO_READ | BH_IO_TRUNCATE) == BH_OK); + BH_VERIFY(bh_io_flags(io) & BH_IO_FLAG_OPEN); + + BH_VERIFY(bh_io_write(io, "1234567890", 10, &actual) == BH_OK); + BH_VERIFY(actual == 10); + + BH_VERIFY(bh_io_write(io, "1234567890", 10, &actual) == BH_OK); + BH_VERIFY(actual == 10); + + BH_VERIFY(bh_io_seek(io, 0, BH_IO_SEEK_SET) == BH_OK); + BH_VERIFY(bh_io_tell(io, &previous) == BH_OK); + BH_VERIFY(bh_io_peek(io, buffer, 128, &actual) == BH_OK); + BH_VERIFY(bh_io_tell(io, ¤t) == BH_OK); + BH_VERIFY(actual == 20); + BH_VERIFY(memcmp(buffer, "12345678901234567890", 20) == 0); + BH_VERIFY(previous == current); + + BH_VERIFY(bh_io_seek(io, 0, BH_IO_SEEK_SET) == BH_OK); + BH_VERIFY(bh_io_tell(io, &previous) == BH_OK); + BH_VERIFY(bh_io_peek(io, buffer, 128, &actual) == BH_OK); + BH_VERIFY(bh_io_tell(io, ¤t) == BH_OK); + BH_VERIFY(actual == 20); + BH_VERIFY(memcmp(buffer, "12345678901234567890", 20) == 0); + BH_VERIFY(previous == current); + + bh_io_close(io); + bh_io_free(io); + + return 0; +} + + +/** + * Check file size operation. + */ +static int check_size(void) +{ + bh_io_t *io; + int64_t size; + + BH_VERIFY((io = bh_file_new(FILENAME1)) != NULL); + BH_VERIFY(strcmp(bh_io_classname(io), BH_FILE_CLASSNAME) == 0); + BH_VERIFY(bh_io_open(io, BH_IO_READ) == BH_OK); + + BH_VERIFY(bh_io_size(io, &size) == BH_OK); + BH_VERIFY(size == 20); + + bh_io_close(io); + bh_io_free(io); + return 0; +} + + +int main(int argc, + char **argv) +{ + BH_UNUSED(argc); + BH_UNUSED(argv); + + /* Cleanup */ + cleanup(); + + bh_unit_add("null", check_null); + bh_unit_add("normal", check_normal); + bh_unit_add("truncate", check_truncate); + bh_unit_add("exist", check_exist); + bh_unit_add("append", check_append); + bh_unit_add("create", check_create); + bh_unit_add("eof", check_eof); + bh_unit_add("error", check_error); + bh_unit_add("peek", check_peek); + bh_unit_add("size", check_size); + + return bh_unit_run(); +} \ No newline at end of file diff --git a/test/src/testhashmap.c b/test/src/testhashmap.c new file mode 100644 index 0000000..9728977 --- /dev/null +++ b/test/src/testhashmap.c @@ -0,0 +1,234 @@ +#include +#include + + +static size_t direct_hash(const void *ptr) +{ + return BH_PTR2INT(ptr); +} + + +static int direct_equal(const void *lhs, const void *rhs) +{ + return BH_PTR2INT(lhs) - BH_PTR2INT(rhs); +} + + +static int new_free(void) +{ + bh_hashmap_t *hashmap; + + hashmap = bh_hashmap_new(direct_equal, direct_hash); + BH_VERIFY(hashmap != NULL); + + BH_VERIFY(bh_hashmap_empty(hashmap) != 0); + BH_VERIFY(bh_hashmap_size(hashmap) == 0); + BH_VERIFY(bh_hashmap_capacity(hashmap) == 0); + BH_VERIFY(bh_hashmap_factor(hashmap) >= 0.15f); + BH_VERIFY(bh_hashmap_factor(hashmap) <= 1.0f); + + bh_hashmap_free(hashmap); + return 0; +} + +static int grow_shrink(void) +{ + bh_hashmap_t *hashmap; + void *iter; + + hashmap = bh_hashmap_new(direct_equal, direct_hash); + BH_VERIFY(hashmap != NULL); + + /* Allocate space for 1024 entries and insert 1 element */ + BH_VERIFY(bh_hashmap_reserve(hashmap, 1024) == 0); + BH_VERIFY(bh_hashmap_insert(hashmap, BH_INT2PTR(1337), BH_INT2PTR(80085)) == 0); + + /* Check hashmap contents */ + iter = bh_hashmap_iter_next(hashmap, NULL); + BH_VERIFY(iter != NULL); + BH_VERIFY(BH_PTR2INT(bh_hashmap_iter_key(iter)) == 1337); + BH_VERIFY(BH_PTR2INT(bh_hashmap_iter_value(iter)) == 80085); + BH_VERIFY(bh_hashmap_iter_next(hashmap, iter) == NULL); + BH_VERIFY(bh_hashmap_empty(hashmap) == 0); + BH_VERIFY(bh_hashmap_size(hashmap) == 1); + BH_VERIFY(bh_hashmap_capacity(hashmap) >= 1024); + + /* Change factor and grow */ + bh_hashmap_set_factor(hashmap, 0.35f); + + /* Check hashmap contents */ + iter = bh_hashmap_iter_next(hashmap, NULL); + BH_VERIFY(iter != NULL); + BH_VERIFY(BH_PTR2INT(bh_hashmap_iter_key(iter)) == 1337); + BH_VERIFY(BH_PTR2INT(bh_hashmap_iter_value(iter)) == 80085); + BH_VERIFY(bh_hashmap_iter_next(hashmap, iter) == NULL); + BH_VERIFY(bh_hashmap_reserve(hashmap, 8192) == 0); + BH_VERIFY(bh_hashmap_empty(hashmap) == 0); + BH_VERIFY(bh_hashmap_size(hashmap) == 1); + BH_VERIFY(bh_hashmap_capacity(hashmap) >= 8192); + BH_VERIFY(bh_hashmap_factor(hashmap) == 0.35f); + + /* Shrink */ + BH_VERIFY(bh_hashmap_reserve(hashmap, 0) == 0); + + /* Check hashmap contents */ + iter = bh_hashmap_iter_next(hashmap, NULL); + BH_VERIFY(iter != NULL); + BH_VERIFY(BH_PTR2INT(bh_hashmap_iter_key(iter)) == 1337); + BH_VERIFY(BH_PTR2INT(bh_hashmap_iter_value(iter)) == 80085); + BH_VERIFY(bh_hashmap_iter_next(hashmap, iter) == NULL); + BH_VERIFY(bh_hashmap_empty(hashmap) == 0); + BH_VERIFY(bh_hashmap_size(hashmap) == 1); + BH_VERIFY(bh_hashmap_capacity(hashmap) >= 1); + BH_VERIFY(bh_hashmap_capacity(hashmap) < 8192); + BH_VERIFY(bh_hashmap_factor(hashmap) == 0.35f); + + /* Shrink to 0 (deallocate) */ + bh_hashmap_clear(hashmap); + BH_VERIFY(bh_hashmap_empty(hashmap) != 0); + BH_VERIFY(bh_hashmap_size(hashmap) == 0); + BH_VERIFY(bh_hashmap_capacity(hashmap) > 0); + + BH_VERIFY(bh_hashmap_reserve(hashmap, 0) == 0); + BH_VERIFY(bh_hashmap_empty(hashmap) != 0); + BH_VERIFY(bh_hashmap_size(hashmap) == 0); + BH_VERIFY(bh_hashmap_capacity(hashmap) == 0); + + /* Check hashmap contents */ + iter = bh_hashmap_iter_next(hashmap, NULL); + BH_VERIFY(iter == NULL); + + bh_hashmap_free(hashmap); + return 0; +} + +static int insert_remove(void) +{ + bh_hashmap_t *hashmap; + size_t i, added, removed; + void *iter; + + hashmap = bh_hashmap_new(direct_equal, direct_hash); + BH_VERIFY(hashmap != NULL); + bh_hashmap_set_factor(hashmap, 1.0f); + + /* Insert elements into hashmap */ + added = 0; + for (i = 1024; i > 0; i--) + { + added += (i - 1) / 4; + BH_VERIFY(bh_hashmap_insert(hashmap, BH_INT2PTR((i - 1) / 4), BH_INT2PTR(i)) == 0); + } + + /* Remove elements */ + iter = bh_hashmap_iter_next(hashmap, NULL); + removed = 0; + while (iter) + { + removed += BH_PTR2INT(bh_hashmap_iter_key(iter)); + bh_hashmap_iter_remove(hashmap, iter); + + iter = bh_hashmap_iter_next(hashmap, NULL); + } + + /* Check inserted elements are equal to removed */ + BH_VERIFY(added == removed); + + bh_hashmap_free(hashmap); + return 0; +} + +static int lookup(void) +{ + bh_hashmap_t *hashmap; + size_t i; + + hashmap = bh_hashmap_new(direct_equal, direct_hash); + BH_VERIFY(hashmap != NULL); + + /* Insert elements into hashmap */ + for (i = 0; i < 256; i++) + BH_VERIFY(bh_hashmap_insert(hashmap, BH_INT2PTR(i * 4), BH_INT2PTR(i)) == 0); + + /* Lookup inserted elements */ + for (i = 0; i < 256; i++) + { + void *value; + + BH_VERIFY(bh_hashmap_at(hashmap, BH_INT2PTR(i * 4), NULL) == BH_OK); + BH_VERIFY(bh_hashmap_at(hashmap, BH_INT2PTR(i * 4), &value) == BH_OK); + BH_VERIFY(BH_PTR2INT(value) == i); + } + + /* Lookup non-existing elements */ + for (i = 256; i < 512; i++) + { + void *value; + + BH_VERIFY(bh_hashmap_at(hashmap, BH_INT2PTR(i * 4), NULL) != BH_OK); + BH_VERIFY(bh_hashmap_at(hashmap, BH_INT2PTR(i * 4), &value) != BH_OK); + } + + bh_hashmap_free(hashmap); + return 0; +} + +static int clear(void) +{ + bh_hashmap_t *hashmap; + size_t i; + + hashmap = bh_hashmap_new(direct_equal, direct_hash); + BH_VERIFY(hashmap != NULL); + + /* Insert elements into hashmap */ + for (i = 0; i < 128; i++) + BH_VERIFY(bh_hashmap_insert(hashmap, BH_INT2PTR(i), 0) == 0); + + bh_hashmap_clear(hashmap); + + /* Remove non-existing elements */ + for (i = 0; i < 128; i++) + bh_hashmap_remove(hashmap, BH_INT2PTR(i)); + + bh_hashmap_free(hashmap); + return 0; +} + +static int fields(void) +{ + bh_hashmap_t *hashmap; + size_t i; + + hashmap = bh_hashmap_new(direct_equal, direct_hash); + BH_VERIFY(hashmap != NULL); + BH_VERIFY(bh_hashmap_empty(hashmap) == 1); + + /* Insert elements into hashmap */ + for (i = 0; i < 14; i++) + BH_VERIFY(bh_hashmap_insert(hashmap, BH_INT2PTR(i), NULL) == 0); + + /* Check hashmap fields correspond to getter functions */ + BH_VERIFY(bh_hashmap_size(hashmap) == 14); + BH_VERIFY(bh_hashmap_capacity(hashmap) >= 14); + BH_VERIFY(bh_hashmap_empty(hashmap) == 0); + + bh_hashmap_free(hashmap); + return 0; +} + +int main(int argc, char **argv) +{ + (void)argc; + (void)argv; + + /* Add unit tests */ + bh_unit_add("new_free", new_free); + bh_unit_add("grow_shrink", grow_shrink); + bh_unit_add("insert_remove", insert_remove); + bh_unit_add("lookup", lookup); + bh_unit_add("clear", clear); + bh_unit_add("fields", fields); + + return bh_unit_run(); +} diff --git a/test/src/testqueue.c b/test/src/testqueue.c new file mode 100644 index 0000000..930774a --- /dev/null +++ b/test/src/testqueue.c @@ -0,0 +1,171 @@ +#include +#include + + +static int new_free(void) +{ + bh_queue_t *queue; + + queue = bh_queue_new(); + BH_VERIFY(queue != NULL); + BH_VERIFY(bh_queue_size(queue) == 0); + BH_VERIFY(bh_queue_capacity(queue) == 0); + BH_VERIFY(bh_queue_empty(queue) != 0); + + bh_queue_free(queue); + return 0; +} + + +static int grow_shrink(void) +{ + bh_queue_t *queue; + void *value; + + queue = bh_queue_new(); + BH_VERIFY(queue != NULL); + + /* Reserve 1024 elements and insert item into queue */ + BH_VERIFY(bh_queue_reserve(queue, 1024) == BH_OK); + BH_VERIFY(bh_queue_insert(queue, BH_INT2PTR(1337)) == BH_OK); + BH_VERIFY(bh_queue_capacity(queue) >= 1024); + BH_VERIFY(bh_queue_size(queue) == 1); + BH_VERIFY(bh_queue_empty(queue) == 0); + + /* Check queue content */ + BH_VERIFY(bh_queue_front(queue, &value) == BH_OK); + BH_VERIFY(BH_PTR2INT(value) == 1337); + + /* Grow queue */ + BH_VERIFY(bh_queue_reserve(queue, 8192) == BH_OK); + BH_VERIFY(bh_queue_capacity(queue) >= 8192); + BH_VERIFY(bh_queue_size(queue) == 1); + BH_VERIFY(bh_queue_empty(queue) == 0); + + /* Check queue content */ + BH_VERIFY(bh_queue_front(queue, &value) == BH_OK); + BH_VERIFY(BH_PTR2INT(value) == 1337); + + /* Shrink the queue */ + BH_VERIFY(bh_queue_reserve(queue, 0) == BH_OK); + BH_VERIFY(bh_queue_capacity(queue) >= 1); + BH_VERIFY(bh_queue_capacity(queue) < 8192); + BH_VERIFY(bh_queue_size(queue) == 1); + BH_VERIFY(bh_queue_empty(queue) == 0); + + /* Check queue content */ + BH_VERIFY(bh_queue_front(queue, &value) == BH_OK); + BH_VERIFY(BH_PTR2INT(value) == 1337); + + /* Shrink to 0 (deallocate) */ + bh_queue_clear(queue); + BH_VERIFY(bh_queue_size(queue) == 0); + BH_VERIFY(bh_queue_empty(queue) != 0); + BH_VERIFY(bh_queue_capacity(queue) >= 1); + + BH_VERIFY(bh_queue_reserve(queue, 0) == BH_OK); + BH_VERIFY(bh_queue_size(queue) == 0); + BH_VERIFY(bh_queue_empty(queue) != 0); + BH_VERIFY(bh_queue_capacity(queue) == 0); + + bh_queue_free(queue); + return 0; +} + + +static int insert_remove(void) +{ + bh_queue_t *queue; + size_t i, added, removed; + void *iter; + + queue = bh_queue_new(); + BH_VERIFY(queue != NULL); + + added = 0; + for (i = 0; i < 256; i++) + { + added += i * 2; + BH_VERIFY(bh_queue_insert(queue, BH_INT2PTR(i * 2)) == BH_OK); + } + + removed = 0; + iter = bh_queue_iter_next(queue, NULL); + while (iter) + { + void *value; + + BH_VERIFY(bh_queue_front(queue, &value) == BH_OK); + removed += BH_PTR2INT(value); + + bh_queue_remove(queue); + iter = bh_queue_iter_next(queue, NULL); + } + + BH_VERIFY(added == removed); + BH_VERIFY(bh_queue_empty(queue) != 0); + BH_VERIFY(bh_queue_size(queue) == 0); + + bh_queue_free(queue); + return 0; +} + + +static int rollover(void) +{ + bh_queue_t *queue; + size_t i, j, capacity; + + queue = bh_queue_new(); + BH_VERIFY(queue != NULL); + + BH_VERIFY(bh_queue_reserve(queue, 128) == 0); + capacity = bh_queue_capacity(queue); + + for (i = 0; i < 128; i++) + { + for (j = 0; j < 3; j++) + bh_queue_remove(queue); + + for (j = 0; j < 4 && bh_queue_size(queue) < 128; j++) + BH_VERIFY(bh_queue_insert(queue, BH_INT2PTR(i * 4 + j)) == 0); + } + + BH_VERIFY(bh_queue_size(queue) == 128); + BH_VERIFY(bh_queue_capacity(queue) == capacity); + + bh_queue_free(queue); + return 0; +} + + +static int fields(void) +{ + bh_queue_t *queue; + + queue = bh_queue_new(); + BH_VERIFY(queue != NULL); + + BH_VERIFY(bh_queue_insert(queue, BH_INT2PTR(1337)) == 0); + BH_VERIFY(bh_queue_size(queue) == 1); + BH_VERIFY(bh_queue_empty(queue) == 0); + BH_VERIFY(bh_queue_capacity(queue) >= 1); + + bh_queue_free(queue); + return 0; +} + + +int main(int argc, char **argv) +{ + (void)argc; + (void)argv; + + bh_unit_add("new_free", new_free); + bh_unit_add("grow_shrink", grow_shrink); + bh_unit_add("insert_remove", insert_remove); + bh_unit_add("rollover", rollover); + bh_unit_add("fields", fields); + + return bh_unit_run(); +} diff --git a/unit/CMakeLists.txt b/unit/CMakeLists.txt new file mode 100755 index 0000000..66ae482 --- /dev/null +++ b/unit/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.10) + +# Project and C standard configuration +project(bhunit LANGUAGES C) +set(CMAKE_C_STANDARD 90) +set(CMAKE_C_STANDARD_REQUIRED ON) + +# Disable extensions +set(CMAKE_C_EXTENSIONS OFF) + +# Library code +set(BHUNIT_SOURCE + src/unit.c +) + +set(BHUNIT_HEADER + include/bh/unit.h +) + +# Library +add_library(bhunit STATIC ${BHUNIT_SOURCE} ${BHUNIT_HEADER}) +target_include_directories(bhunit PUBLIC include) + +# Enable all compiler warnings +if(MSVC) + target_compile_options(bhunit PRIVATE /W4 /WX) +else() + target_compile_options(bhunit PRIVATE -Wall -Wextra -Wpedantic -Werror) +endif() \ No newline at end of file diff --git a/unit/include/bh/unit.h b/unit/include/bh/unit.h new file mode 100755 index 0000000..85785fd --- /dev/null +++ b/unit/include/bh/unit.h @@ -0,0 +1,29 @@ +#ifndef BH_UNIT_H +#define BH_UNIT_H + +#include + +typedef int (*bh_unit_cb_t)(void); + +#define BH_VERIFY(e) \ + do { if (!(e)) { \ + printf("%s:%d\t%s\n", __FILE__, __LINE__, #e); \ + return -1; \ + } } while(0) + + +#define BH_FAIL(msg) \ + do { printf("%s:%d\t%s\n", __FILE__, __LINE__, msg); \ + return -1; } while(0) + + +#define BH_VERIFY_DELTA(x, y, e) \ + do { if ((((x)>(y))?((x)-(y)):((y)-(x)))>(e)) { \ + printf("%s:%d\t%s\n", __FILE__, __LINE__, #x " == " #y); \ + return -1; \ + } while (0) + +void bh_unit_add(const char *name, bh_unit_cb_t func); +int bh_unit_run(void); + +#endif /* BH_UNIT_H */ diff --git a/unit/src/unit.c b/unit/src/unit.c new file mode 100755 index 0000000..a5f143c --- /dev/null +++ b/unit/src/unit.c @@ -0,0 +1,56 @@ +#include +#include + +typedef struct bh_unit_s +{ + struct bh_unit_s *next; + const char *name; + bh_unit_cb_t func; +} bh_unit_t; + +static bh_unit_t *root = NULL; + +void bh_unit_add(const char *name, bh_unit_cb_t func) +{ + bh_unit_t *unit, *current; + + unit = malloc(sizeof(*unit)); + if (!unit) + return; + + unit->name = name; + unit->func = func; + unit->next = NULL; + + current = root; + while (current && current->next) + current = current->next; + + if (current) + current->next = unit; + else + root = unit; +} + +int bh_unit_run(void) +{ + bh_unit_t *current; + printf("Running tests...\n"); + + current = root; + while (current) + { + printf("%s\n", current->name); + if (current->func()) + { + printf("\tFAIL\n"); + return -1; + } + printf("\tPASS\n"); + fflush(stdout); + current = current->next; + } + + return 0; +} + diff --git a/util/trim-whitespace.sh b/util/trim-whitespace.sh new file mode 100755 index 0000000..34a9c58 --- /dev/null +++ b/util/trim-whitespace.sh @@ -0,0 +1,2 @@ +#!/bin/sh +find . \( -iname "*.h" -o -iname "*.c" \) -exec sed -i "s/[ \t]*$//" {} \; \ No newline at end of file -- cgit v1.2.3