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