Initial commit
This commit is contained in:
79
.gitignore
vendored
Normal file
79
.gitignore
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
# 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/
|
||||
x86/
|
||||
x64/
|
||||
[Ww][Ii][Nn]32/
|
||||
[Aa][Rr][Mm]/
|
||||
[Aa][Rr][Mm]64/
|
||||
|
||||
!platform/*
|
||||
|
||||
# Visual Studio Code
|
||||
.vscode/
|
||||
|
||||
# Doxygen
|
||||
[Dd]ocs/[Hh]tml
|
||||
|
||||
# Coverage
|
||||
[Cc]overage
|
||||
45
CMakeLists.txt
Normal file
45
CMakeLists.txt
Normal file
@@ -0,0 +1,45 @@
|
||||
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)
|
||||
|
||||
# Check for IPO/LTO
|
||||
include(CheckIPOSupported)
|
||||
check_ipo_supported(RESULT supported)
|
||||
|
||||
if(supported)
|
||||
message(STATUS "IPO/LTO enabled")
|
||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
|
||||
endif()
|
||||
|
||||
# Enable testing
|
||||
include(CTest)
|
||||
enable_testing()
|
||||
|
||||
# Set library code
|
||||
set(BHLIB_SOURCE
|
||||
src/hashmap.c
|
||||
src/queue.c
|
||||
)
|
||||
|
||||
set(BHLIB_HEADER
|
||||
include/bh/hashmap.h
|
||||
include/bh/queue.h
|
||||
)
|
||||
|
||||
# Library
|
||||
add_library(bhlib STATIC ${BHLIB_SOURCE} ${BHLIB_HEADER})
|
||||
target_include_directories(bhlib PUBLIC include)
|
||||
|
||||
# Runtime definition
|
||||
add_executable(main
|
||||
main.c
|
||||
)
|
||||
|
||||
target_link_libraries(main bhlib)
|
||||
|
||||
# Tests
|
||||
add_subdirectory(unit)
|
||||
add_subdirectory(tests)
|
||||
12
LICENSE
Normal file
12
LICENSE
Normal file
@@ -0,0 +1,12 @@
|
||||
Copyright (c) 2024 by Mikhail Romanko
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for
|
||||
any purpose with or without fee is hereby granted.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE
|
||||
FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
|
||||
DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
|
||||
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||||
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
22
include/bh/bh.h
Normal file
22
include/bh/bh.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifndef BHLIB_H
|
||||
#define BHLIB_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#define BH_INT_TO_PTR(x) \
|
||||
((void *)((long)(x)))
|
||||
|
||||
#define BH_UINT_TO_PTR(x) \
|
||||
((void *)((unsigned long)(x)))
|
||||
|
||||
#define BH_PTR_TO_INT(x) \
|
||||
((long)(x))
|
||||
|
||||
#define BH_PTR_TO_UINT(x) \
|
||||
((unsigned long)(x))
|
||||
|
||||
typedef int (*bh_equal_cb_t)(const void *, const void *);
|
||||
typedef size_t (*bh_hash_cb_t)(const void *);
|
||||
|
||||
#endif /* BHLIB_H */
|
||||
|
||||
237
include/bh/hashmap.h
Normal file
237
include/bh/hashmap.h
Normal file
@@ -0,0 +1,237 @@
|
||||
#ifndef BHLIB_HASHMAP_H
|
||||
#define BHLIB_HASHMAP_H
|
||||
|
||||
#include <bh/bh.h>
|
||||
|
||||
typedef struct bh_hashmap_s bh_hashmap_t;
|
||||
|
||||
/**
|
||||
* @brief Create new hashmap object.
|
||||
*
|
||||
* @param equal Function used for comparing keys
|
||||
* @param hash Function used to calculate hash value of the key
|
||||
*
|
||||
* @return If the function succeeds, the return value is a pointer to the new
|
||||
* hashmap object.
|
||||
* @return If the function fails, the return value is NULL.
|
||||
*
|
||||
* @warning The quality of the supplied hash function will affect performance
|
||||
* of the hashmap.
|
||||
*
|
||||
* @sa bh_hashmap_free, bh_hashmap_reserve, bh_hashmap_insert
|
||||
*/
|
||||
bh_hashmap_t *bh_hashmap_new(bh_equal_cb_t equal,
|
||||
bh_hash_cb_t hash);
|
||||
|
||||
/**
|
||||
* @brief Free hashmap object.
|
||||
*
|
||||
* @param hashmap Valid pointer to the hashmap object.
|
||||
*
|
||||
* @sa bh_hashmap_clear
|
||||
*/
|
||||
void bh_hashmap_free(bh_hashmap_t *hashmap);
|
||||
|
||||
/**
|
||||
* @brief Clear the hashmap.
|
||||
*
|
||||
* @param hashmap Valid pointer to the hashmap object.
|
||||
*
|
||||
* @warning Clearing the hashmap does invalidate iterators.
|
||||
*
|
||||
* @sa bh_hashmap_remove
|
||||
*/
|
||||
void bh_hashmap_clear(bh_hashmap_t *hashmap);
|
||||
|
||||
/**
|
||||
* @brief Reserve space for the specified amount of elements.
|
||||
*
|
||||
* @param hashmap Valid pointer to the hashmap object.
|
||||
* @param size The amount of elements.
|
||||
*
|
||||
* @return If the function succeeds, the return value is zero.
|
||||
* @return If the function fails, the return value is non-zero.
|
||||
*
|
||||
* @warning Reserving hashmap space does invalidate iterators.
|
||||
*
|
||||
* @sa bh_hashmap_capacity, bh_hashmap_insert
|
||||
*/
|
||||
int bh_hashmap_reserve(bh_hashmap_t *hashmap,
|
||||
size_t size);
|
||||
|
||||
/**
|
||||
* @brief Insert key/value into the hashmap.
|
||||
*
|
||||
* @param hashmap Valid pointer to the hashmap object.
|
||||
* @param key Key
|
||||
* @param value Value
|
||||
*
|
||||
* @return If the function succeeds, the return value is zero.
|
||||
* @return If the function fails, the return value is non-zero.
|
||||
*
|
||||
* @warning Inserted element is owned by the caller of the function.
|
||||
* @warning Inserting elements into the hashmap does invalidate iterators.
|
||||
*
|
||||
* @sa bbh_hashmap_remove, bh_hashmap_at
|
||||
*/
|
||||
int bh_hashmap_insert(bh_hashmap_t *hashmap,
|
||||
void *key,
|
||||
void *value);
|
||||
|
||||
/**
|
||||
* @brief Remove element from the hashmap.
|
||||
*
|
||||
* @param hashmap Valid pointer to the hashmap object.
|
||||
* @param key Key.
|
||||
*
|
||||
* @warning Removing elements from the hashmap does invalidate iterators.
|
||||
*
|
||||
* @sa bh_hashmap_insert, bh_hashmap_at
|
||||
*/
|
||||
void bh_hashmap_remove(bh_hashmap_t *hashmap,
|
||||
void *key);
|
||||
|
||||
/**
|
||||
* @brief Return element value by the key from the hashmap.
|
||||
*
|
||||
* @param hashmap Valid pointer to the hashmap.
|
||||
* @param key Key.
|
||||
* @param exists Pointer to the exists flag (optional).
|
||||
*
|
||||
* @return If the function succeeds, the return value is a valid pointer to
|
||||
* the element value.
|
||||
* @return If the function fails, the return value is NULL.
|
||||
*
|
||||
* @note If the hashmap does not contain any element with the key, the
|
||||
* function will fail.
|
||||
*
|
||||
* @sa bh_hashmap_empty, bh_hashmap_insert
|
||||
*/
|
||||
void *bh_hashmap_at(bh_hashmap_t *hashmap,
|
||||
void *key,
|
||||
int *exists);
|
||||
|
||||
/**
|
||||
* @brief Check if the hashmap is empty.
|
||||
*
|
||||
* @param hashmap Valid pointer to the hashmap object.
|
||||
*
|
||||
* @return The return value is non-zero if the hashmap is empty, otherwise
|
||||
* zero.
|
||||
*
|
||||
* @sa bh_hashmap_size
|
||||
*/
|
||||
int bh_hashmap_empty(bh_hashmap_t *hashmap);
|
||||
|
||||
/**
|
||||
* @brief Return hashmap size.
|
||||
*
|
||||
* @param hashmap Valid pointer to the hashmap object.
|
||||
*
|
||||
* @return The return value is current hashmap size.
|
||||
*
|
||||
* @sa bh_hashmap_empty, bh_hashmap_capacity
|
||||
*/
|
||||
size_t bh_hashmap_size(bh_hashmap_t *hashmap);
|
||||
|
||||
/**
|
||||
* @brief Return hashmap capacity.
|
||||
*
|
||||
* @param hashmap Valid pointer to the hashmap object.
|
||||
*
|
||||
* @return The return value is current hashmap capacity.
|
||||
*
|
||||
* @sa bh_hashmap_reserve, bh_hashmap_size
|
||||
*/
|
||||
size_t bh_hashmap_capacity(bh_hashmap_t *hashmap);
|
||||
|
||||
/**
|
||||
* @brief Return hashmap load factor.
|
||||
*
|
||||
* @param hashmap Valid pointer to the hashmap object.
|
||||
*
|
||||
* @return The return value is current hashmap load factor.
|
||||
*
|
||||
* @sa bh_hashmap_set_factor, bh_hashmap_capacity
|
||||
*/
|
||||
float bh_hashmap_factor(bh_hashmap_t *hashmap);
|
||||
|
||||
/**
|
||||
* @brief Set hashmap load factor.
|
||||
*
|
||||
* @param hashmap Valid pointer to the hashmap object.
|
||||
* @param factor Load factor.
|
||||
*
|
||||
* @sa bh_hashmap_factor
|
||||
*/
|
||||
void bh_hashmap_set_factor(bh_hashmap_t *hashmap,
|
||||
float factor);
|
||||
|
||||
/**
|
||||
* @brief Return iterator for the element by the key.
|
||||
*
|
||||
* @param hashmap Valid pointer to the hashmap object.
|
||||
* @param key Key
|
||||
*
|
||||
* @return The return value is the valid iterator for the element in the
|
||||
* hashmap.
|
||||
* @return The return value is the NULL iterator if there is no element with
|
||||
* specified key.
|
||||
*
|
||||
* @sa bh_hashmap_iter_key, bh_hashmap_iter_value
|
||||
*/
|
||||
void *bh_hashmap_iter_at(bh_hashmap_t *hashmap,
|
||||
void *key);
|
||||
|
||||
/**
|
||||
* @brief Return iterator for the next element in the hashmap.
|
||||
*
|
||||
* @param hashmap Valid pointer to the hashmap object.
|
||||
* @param iter Valid or NULL iterator.
|
||||
*
|
||||
* @return The return value is the valid iterator for the next element in the
|
||||
* hashmap.
|
||||
* @return The return value is the NULL iterator if there is no more elements
|
||||
* in the hashmap.
|
||||
*
|
||||
* @sa bh_hashmap_iter_key, bh_hashmap_iter_value
|
||||
*/
|
||||
void *bh_hashmap_iter_next(bh_hashmap_t *hashmap,
|
||||
void *iter);
|
||||
|
||||
/**
|
||||
* @brief Remove element from the hashmap by the iterator.
|
||||
*
|
||||
* @param hashmap Valid pointer to the hashmap object.
|
||||
* @param key Valid iterator.
|
||||
*
|
||||
* @warning Removing elements from the hashmap does invalidate iterators.
|
||||
*
|
||||
* @sa bh_hashmap_insert, bh_hashmap_at
|
||||
*/
|
||||
void bh_hashmap_iter_remove(bh_hashmap_t *hashmap,
|
||||
void *iter);
|
||||
|
||||
/**
|
||||
* @brief Return pointer to the element's key.
|
||||
*
|
||||
* @param iter Valid iterator.
|
||||
*
|
||||
* @return The return value is the stored element key.
|
||||
*
|
||||
* @sa bh_hashmap_iter_value, bh_hashmap_iter_next
|
||||
*/
|
||||
void *bh_hashmap_iter_key(void *iter);
|
||||
|
||||
/**
|
||||
* @brief Return pointer to the element's value.
|
||||
*
|
||||
* @param iter Valid iterator.
|
||||
*
|
||||
* @return The return value is the stored element value.
|
||||
*
|
||||
* @sa bh_hashmap_iter_key, bh_hashmap_iter_next
|
||||
*/
|
||||
void *bh_hashmap_iter_value(void *iter);
|
||||
|
||||
#endif /* BHLIB_HASHMAP_H */
|
||||
30
include/bh/internal/hashmap.h
Normal file
30
include/bh/internal/hashmap.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifndef BHLIB_INTERNAL_HASHMAP_H
|
||||
#define BHLIB_INTERNAL_HASHMAP_H
|
||||
|
||||
#include <bh/hashmap.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;
|
||||
};
|
||||
|
||||
void bh_hashmap_init(bh_hashmap_t *hashmap,
|
||||
bh_equal_cb_t equal,
|
||||
bh_hash_cb_t hash);
|
||||
|
||||
void bh_hashmap_destroy(bh_hashmap_t *hashmap);
|
||||
|
||||
#endif /* BHLIB_INTERNAL_HASHMAP_H */
|
||||
35
include/bh/internal/queue.h
Normal file
35
include/bh/internal/queue.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#ifndef BHLIB_INTERNAL_QUEUE_H
|
||||
#define BHLIB_INTERNAL_QUEUE_H
|
||||
|
||||
#include <bh/queue.h>
|
||||
|
||||
struct bh_queue_s
|
||||
{
|
||||
void **data;
|
||||
size_t size;
|
||||
size_t head;
|
||||
size_t tail;
|
||||
size_t capacity;
|
||||
};
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @brief Initialize embedded queue object.
|
||||
*
|
||||
* @param queue Valid pointer to the queue object.
|
||||
*
|
||||
* @sa bh_queue_destroy
|
||||
*/
|
||||
void bh_queue_init(bh_queue_t *queue);
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @brief Destroy embedded queue object.
|
||||
*
|
||||
* @param queue Valid pointer to the queue object.
|
||||
*
|
||||
* @sa bh_queue_init
|
||||
*/
|
||||
void bh_queue_destroy(bh_queue_t *queue);
|
||||
|
||||
#endif /* BHLIB_INTERNAL_QUEUE_H */
|
||||
159
include/bh/queue.h
Normal file
159
include/bh/queue.h
Normal file
@@ -0,0 +1,159 @@
|
||||
#ifndef BH_QUEUE_H
|
||||
#define BH_QUEUE_H
|
||||
|
||||
#include <bh/bh.h>
|
||||
|
||||
typedef struct bh_queue_s bh_queue_t;
|
||||
|
||||
/**
|
||||
* @brief Create new queue object.
|
||||
*
|
||||
* @return If the function succeeds, the return value is a pointer to the new
|
||||
* queue object.
|
||||
* @return If the function fails, the return value is NULL.
|
||||
*
|
||||
* @sa bh_queue_free, bh_queue_reserve, bh_queue_insert
|
||||
*/
|
||||
bh_queue_t *bh_queue_new(void);
|
||||
|
||||
/**
|
||||
* @brief Free queue object.
|
||||
*
|
||||
* @param queue Valid pointer to the queue object.
|
||||
*
|
||||
* @sa bh_queue_clear
|
||||
*/
|
||||
void bh_queue_free(bh_queue_t *queue);
|
||||
|
||||
/**
|
||||
* @brief Clear the queue.
|
||||
*
|
||||
* @param queue Valid pointer to the queue object.
|
||||
*
|
||||
* @warning Clearing the queue does invalidate iterators.
|
||||
*
|
||||
* @sa bh_queue_remove
|
||||
*/
|
||||
void bh_queue_clear(bh_queue_t *queue);
|
||||
|
||||
/**
|
||||
* @brief Reserve space for the specified amount of elements.
|
||||
*
|
||||
* @param queue Valid pointer to the queue object.
|
||||
* @param size The amount of elements.
|
||||
*
|
||||
* @return If the function succeeds, the return value is zero.
|
||||
* @return If the function fails, the return value is non-zero.
|
||||
*
|
||||
* @warning Reserving queue space does invalidate iterators.
|
||||
*
|
||||
* @sa bh_queue_capacity, bh_queue_insert
|
||||
*/
|
||||
int bh_queue_reserve(bh_queue_t *queue,
|
||||
size_t size);
|
||||
|
||||
/**
|
||||
* @brief Insert element at the end of the queue.
|
||||
*
|
||||
* @param queue Valid pointer to the queue object.
|
||||
* @param value Element.
|
||||
*
|
||||
* @return If the function succeeds, the return value is zero.
|
||||
* @return If the function fails, the return value is non-zero.
|
||||
*
|
||||
* @warning Inserted element is owned by the caller of the function.
|
||||
* @warning Inserting elements into the queue does invalidate iterators.
|
||||
*
|
||||
* @sa bh_queue_remove, bh_queue_front
|
||||
*/
|
||||
int bh_queue_insert(bh_queue_t *queue,
|
||||
void *value);
|
||||
|
||||
/**
|
||||
* @brief Remove element from the front of the queue.
|
||||
*
|
||||
* @param queue Valid pointer to the queue object.
|
||||
*
|
||||
* @warning Removing elements from the queue does invalidate iterators.
|
||||
*
|
||||
* @sa bh_queue_insert, bh_queue_front
|
||||
*/
|
||||
void bh_queue_remove(bh_queue_t *queue);
|
||||
|
||||
/**
|
||||
* @brief Return element from the front of the queue.
|
||||
*
|
||||
* @param queue Valid pointer to the queue object.
|
||||
*
|
||||
* @return If the function succeeds, the return value is a valid pointer to
|
||||
* the element.
|
||||
* @return If the function fails, the return value is NULL.
|
||||
*
|
||||
* @note If the queue is empty, function will fail to return element from
|
||||
* the queue.
|
||||
*
|
||||
* @sa bh_queue_empty, bh_queue_insert
|
||||
*/
|
||||
void *bh_queue_front(bh_queue_t *queue);
|
||||
|
||||
/**
|
||||
* @brief Check if the queue is empty.
|
||||
*
|
||||
* @param queue Valid pointer to the queue object.
|
||||
*
|
||||
* @return The return value is non-zero if the queue is empty, otherwise zero.
|
||||
*
|
||||
* @sa bh_queue_size
|
||||
*/
|
||||
int bh_queue_empty(bh_queue_t *queue);
|
||||
|
||||
/**
|
||||
* @brief Return queue size.
|
||||
*
|
||||
* @param queue Valid pointer to the queue object.
|
||||
*
|
||||
* @return The return value is current queue size.
|
||||
*
|
||||
* @sa bh_queue_empty, bh_queue_capacity
|
||||
*/
|
||||
size_t bh_queue_size(bh_queue_t *queue);
|
||||
|
||||
/**
|
||||
* @brief Return queue capacity.
|
||||
*
|
||||
* @param queue Valid pointer to the queue object.
|
||||
*
|
||||
* @return The return value is current queue capacity.
|
||||
*
|
||||
* @sa bh_queue_reserve, bh_queue_size
|
||||
*/
|
||||
size_t bh_queue_capacity(bh_queue_t *queue);
|
||||
|
||||
/**
|
||||
* @brief Return iterator for the next element in the queue.
|
||||
*
|
||||
* @param queue Valid pointer to the queue object.
|
||||
* @param iter Valid or NULL iterator.
|
||||
*
|
||||
* @return The return value is the valid iterator for the next element in the
|
||||
* queue.
|
||||
* @return The return value is the NULL iterator if there is no more elements
|
||||
* in the queue.
|
||||
*
|
||||
* @sa bh_queue_iter_value
|
||||
*/
|
||||
void *bh_queue_iter_next(bh_queue_t *queue,
|
||||
void *iter);
|
||||
|
||||
/**
|
||||
* @brief Return pointer to the element's value.
|
||||
*
|
||||
* @param iter Valid iterator.
|
||||
*
|
||||
* @return The return value is the stored element.
|
||||
*
|
||||
* @sa bh_queue_iter_next
|
||||
*/
|
||||
void *bh_queue_iter_value(void *iter);
|
||||
|
||||
#endif /* BH_QUEUE_H */
|
||||
83
main.c
Normal file
83
main.c
Normal file
@@ -0,0 +1,83 @@
|
||||
#include <bh/queue.h>
|
||||
#include <bh/hashmap.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define BH_INT_TO_PTR(x) \
|
||||
((void *)((long)(x)))
|
||||
|
||||
#define BH_UINT_TO_PTR(x) \
|
||||
((void *)((unsigned long)(x)))
|
||||
|
||||
#define BH_PTR_TO_INT(x) \
|
||||
((long)(x))
|
||||
|
||||
#define BH_PTR_TO_UINT(x) \
|
||||
((unsigned long)(x))
|
||||
|
||||
size_t ptr_hash(const void *item)
|
||||
{
|
||||
return BH_PTR_TO_INT(item);
|
||||
}
|
||||
|
||||
int ptr_equal(const void *lhs, const void *rhs)
|
||||
{
|
||||
return BH_PTR_TO_INT(lhs) - BH_PTR_TO_INT(rhs);
|
||||
}
|
||||
|
||||
void foo()
|
||||
{
|
||||
bh_hashmap_t *hashmap;
|
||||
size_t i;
|
||||
void *iter;
|
||||
|
||||
hashmap = bh_hashmap_new((bh_equal_cb_t)ptr_equal, (bh_hash_cb_t)ptr_hash);
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
bh_hashmap_insert(hashmap, (void*)i, (void*)(i * 4));
|
||||
|
||||
iter = bh_hashmap_iter_next(hashmap, NULL);
|
||||
while (iter)
|
||||
{
|
||||
printf("%zu: %zu\n", BH_PTR_TO_INT(bh_hashmap_iter_key(iter)), BH_PTR_TO_INT(bh_hashmap_iter_value(iter)));
|
||||
iter = bh_hashmap_iter_next(hashmap, iter);
|
||||
}
|
||||
|
||||
bh_hashmap_free(hashmap);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
bh_queue_t *queue;
|
||||
void *iter;
|
||||
size_t i, j;
|
||||
|
||||
foo();
|
||||
|
||||
queue = bh_queue_new();
|
||||
|
||||
for (j = 0; j < 32; j++)
|
||||
{
|
||||
printf("%zu %zu\n", bh_queue_size(queue), bh_queue_capacity(queue));
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
bh_queue_insert(queue, (void *)(j * 4 + i));
|
||||
|
||||
printf("%zu %zu\n", bh_queue_size(queue), bh_queue_capacity(queue));
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
bh_queue_remove(queue);
|
||||
}
|
||||
|
||||
printf("%zu %zu\n", bh_queue_size(queue), bh_queue_capacity(queue));
|
||||
|
||||
iter = bh_queue_iter_next(queue, NULL);
|
||||
while (iter)
|
||||
{
|
||||
printf("%d\n", (int)bh_queue_iter_value(iter));
|
||||
iter = bh_queue_iter_next(queue, iter);
|
||||
}
|
||||
|
||||
bh_queue_free(queue);
|
||||
return 0;
|
||||
}
|
||||
14
scripts/coverage.sh
Executable file
14
scripts/coverage.sh
Executable file
@@ -0,0 +1,14 @@
|
||||
#!/bin/sh
|
||||
ulimit -Sv 320000
|
||||
rm -r build coverage
|
||||
mkdir build coverage
|
||||
cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_FLAGS_DEBUG="-g -fprofile-arcs -ftest-coverage"
|
||||
cmake --build build
|
||||
cd build
|
||||
#ctest -T Test -T Coverage
|
||||
ctest
|
||||
cd ..
|
||||
#echo Report > coverage.txt
|
||||
#find . -iname "*.gcda" -exec gcov {} \;
|
||||
#find . -iname "*.gcno" -exec echo {} >> coverage.txt \; -exec gcov -t {} >> coverage.txt \;
|
||||
gcovr --html-details coverage/index.html
|
||||
2
scripts/trim-whitespace.sh
Executable file
2
scripts/trim-whitespace.sh
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
find . \( -iname "*.h" -o -iname "*.c" \) -exec sed -i "s/[ \t]*$//" {} \;
|
||||
343
src/hashmap.c
Normal file
343
src/hashmap.c
Normal file
@@ -0,0 +1,343 @@
|
||||
#include <bh/internal/hashmap.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
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_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;
|
||||
}
|
||||
|
||||
void bh_hashmap_destroy(bh_hashmap_t *hashmap)
|
||||
{
|
||||
if (hashmap->capacity)
|
||||
{
|
||||
free(hashmap->data);
|
||||
free(hashmap->psls);
|
||||
}
|
||||
}
|
||||
|
||||
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, max_capacity, threshold;
|
||||
|
||||
/* Calculate hashmap max capacity and capacity threshold */
|
||||
capacity = hashmap->capacity;
|
||||
threshold = hashmap->capacity * hashmap->factor;
|
||||
max_capacity = ((size_t)-1) / sizeof(bh_hashmap_node_t);
|
||||
|
||||
/* New capacity can't be smaller then current hashmap size */
|
||||
if (size < hashmap->size)
|
||||
size = hashmap->size;
|
||||
|
||||
/* Find new hashmap capacity */
|
||||
if (!size)
|
||||
{
|
||||
/* No capacity needed */
|
||||
capacity = 0;
|
||||
threshold = 0;
|
||||
}
|
||||
else if (size > threshold)
|
||||
{
|
||||
/* Bigger capacity needed - grow the hashmap */
|
||||
while (size > threshold)
|
||||
{
|
||||
capacity = (capacity) ? (capacity * 2) : (16);
|
||||
threshold = capacity * hashmap->factor;
|
||||
|
||||
/* Capacity can't be bigger than max capacity and overflow */
|
||||
if (capacity > max_capacity || capacity < 16)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Smaller capacity needed - shrink the hashmap */
|
||||
while (size <= threshold / 2 && capacity > 16)
|
||||
{
|
||||
capacity /= 2;
|
||||
threshold = capacity * hashmap->factor;
|
||||
}
|
||||
}
|
||||
|
||||
/* Prevent same size reallocation */
|
||||
if (capacity == hashmap->capacity)
|
||||
return 0;
|
||||
|
||||
/* Initialize new hashmap */
|
||||
bh_hashmap_init(&other, hashmap->equal, hashmap->hash);
|
||||
other.factor = hashmap->factor;
|
||||
|
||||
if (capacity)
|
||||
{
|
||||
void *iter;
|
||||
|
||||
/* 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 -1;
|
||||
}
|
||||
|
||||
/* Reset probe sequence lengths */
|
||||
memset(other.psls, 0, sizeof(size_t) * other.capacity);
|
||||
|
||||
/* Iterate and insert data into hashmap */
|
||||
iter = bh_hashmap_iter_next(hashmap, NULL);
|
||||
while (iter)
|
||||
{
|
||||
void *key, *value;
|
||||
|
||||
key = bh_hashmap_iter_key(iter);
|
||||
value = bh_hashmap_iter_value(iter);
|
||||
bh_hashmap_insert(&other, key, value);
|
||||
|
||||
iter = bh_hashmap_iter_next(hashmap, iter);
|
||||
}
|
||||
}
|
||||
|
||||
/* Swap hashmaps */
|
||||
bh_hashmap_destroy(hashmap);
|
||||
*hashmap = other;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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 -1;
|
||||
|
||||
/* 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 0;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void *bh_hashmap_at(bh_hashmap_t *hashmap,
|
||||
void *key,
|
||||
int *exists)
|
||||
{
|
||||
void *iter;
|
||||
|
||||
iter = bh_hashmap_iter_at(hashmap, key);
|
||||
if (iter)
|
||||
{
|
||||
/* If exists flag passed - set to 1 */
|
||||
if (exists)
|
||||
*exists = 1;
|
||||
return bh_hashmap_iter_value(iter);
|
||||
}
|
||||
|
||||
/* If exists flag passed - set to 0 */
|
||||
if (exists)
|
||||
*exists = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
factor = (factor > 1.0f) ? (1.0f) : (factor);
|
||||
factor = (factor < 0.15f) ? (0.15f) : (factor);
|
||||
|
||||
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;
|
||||
}
|
||||
176
src/queue.c
Normal file
176
src/queue.c
Normal file
@@ -0,0 +1,176 @@
|
||||
#include <bh/internal/queue.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
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_init(bh_queue_t *queue)
|
||||
{
|
||||
memset(queue, 0, sizeof(*queue));
|
||||
}
|
||||
|
||||
void bh_queue_destroy(bh_queue_t *queue)
|
||||
{
|
||||
if (queue->capacity)
|
||||
free(queue->data);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/* New capacity should not exceed maximum capacity */
|
||||
if (size > ((size_t)-1) / sizeof(void *))
|
||||
return -1;
|
||||
|
||||
/* Prevent same size memory reallocation */
|
||||
if (size == queue->capacity)
|
||||
return 0;
|
||||
|
||||
/* Prepare new empty queue */
|
||||
bh_queue_init(&other);
|
||||
if (size)
|
||||
{
|
||||
void *iter;
|
||||
|
||||
/* Allocate new capacity for the queue */
|
||||
other.data = malloc(size * sizeof(void *));
|
||||
other.capacity = size;
|
||||
if (!other.data)
|
||||
return -1;
|
||||
|
||||
/* Iterate over old queue and insert data into new queue */
|
||||
iter = bh_queue_iter_next(queue, NULL);
|
||||
while (iter)
|
||||
{
|
||||
bh_queue_insert(&other, bh_queue_iter_value(iter));
|
||||
iter = bh_queue_iter_next(queue, iter);
|
||||
}
|
||||
}
|
||||
|
||||
/* If old queue had allocated data - free it */
|
||||
if (queue->capacity)
|
||||
free(queue->data);
|
||||
|
||||
/* Copy queue information */
|
||||
*queue = other;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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 potential size overflow and reserve capacity */
|
||||
capacity = (queue->capacity) ? (queue->capacity * 2) : (16);
|
||||
if (capacity < queue->capacity || bh_queue_reserve(queue, capacity))
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Increase queue size and advance tail index */
|
||||
queue->data[queue->tail] = value;
|
||||
queue->size++;
|
||||
if (++queue->tail >= queue->capacity)
|
||||
queue->tail = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void *bh_queue_front(bh_queue_t *queue)
|
||||
{
|
||||
/* Do nothing if queue is empty */
|
||||
if (!queue->size)
|
||||
return NULL;
|
||||
|
||||
/* Return front element */
|
||||
return queue->data[queue->head];
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
20
tests/CMakeLists.txt
Normal file
20
tests/CMakeLists.txt
Normal file
@@ -0,0 +1,20 @@
|
||||
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}")
|
||||
endforeach()
|
||||
278
tests/src/hashmap.c
Normal file
278
tests/src/hashmap.c
Normal file
@@ -0,0 +1,278 @@
|
||||
#include <bh/internal/hashmap.h>
|
||||
#include <bh/unit.h>
|
||||
|
||||
static size_t direct_hash(const void *ptr)
|
||||
{
|
||||
return BH_PTR_TO_INT(ptr);
|
||||
}
|
||||
|
||||
static int direct_equal(const void *lhs, const void *rhs)
|
||||
{
|
||||
return BH_PTR_TO_INT(lhs) - BH_PTR_TO_INT(rhs);
|
||||
}
|
||||
|
||||
static int init_destroy(void)
|
||||
{
|
||||
bh_hashmap_t hashmap;
|
||||
|
||||
bh_hashmap_init(&hashmap, direct_equal, direct_hash);
|
||||
|
||||
/* Check internal state is valid and empty */
|
||||
bh_unit_assert(hashmap.data == NULL);
|
||||
bh_unit_assert(hashmap.psls == NULL);
|
||||
bh_unit_assert(hashmap.size == 0);
|
||||
bh_unit_assert(hashmap.capacity == 0);
|
||||
bh_unit_assert(hashmap.threshold == 0);
|
||||
bh_unit_assert(hashmap.factor >= 0.15f && hashmap.factor <= 1.0f);
|
||||
bh_unit_assert(hashmap.equal == direct_equal);
|
||||
bh_unit_assert(hashmap.hash == direct_hash);
|
||||
|
||||
bh_hashmap_destroy(&hashmap);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int new_free(void)
|
||||
{
|
||||
bh_hashmap_t *hashmap;
|
||||
|
||||
hashmap = bh_hashmap_new(direct_equal, direct_hash);
|
||||
bh_unit_assert(hashmap != NULL);
|
||||
|
||||
/* Check internal state is valid and empty */
|
||||
bh_unit_assert(hashmap->data == NULL);
|
||||
bh_unit_assert(hashmap->psls == NULL);
|
||||
bh_unit_assert(hashmap->size == 0);
|
||||
bh_unit_assert(hashmap->capacity == 0);
|
||||
bh_unit_assert(hashmap->threshold == 0);
|
||||
bh_unit_assert(hashmap->factor >= 0.15f && hashmap->factor <= 1.0f);
|
||||
bh_unit_assert(hashmap->equal == direct_equal);
|
||||
bh_unit_assert(hashmap->hash == direct_hash);
|
||||
|
||||
bh_hashmap_free(hashmap);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int grow_shrink(void)
|
||||
{
|
||||
bh_hashmap_t *hashmap;
|
||||
bh_hashmap_node_t *old_data;
|
||||
size_t *old_psls;
|
||||
void *iter;
|
||||
|
||||
hashmap = bh_hashmap_new(direct_equal, direct_hash);
|
||||
bh_unit_assert(hashmap != NULL);
|
||||
|
||||
/* Allocate space for 1024 entries and insert 1 element */
|
||||
bh_unit_assert(bh_hashmap_reserve(hashmap, 1024) == 0);
|
||||
bh_unit_assert(bh_hashmap_insert(hashmap, BH_INT_TO_PTR(1337), BH_INT_TO_PTR(80085)) == 0);
|
||||
|
||||
/* Check hashmap contents */
|
||||
iter = bh_hashmap_iter_next(hashmap, NULL);
|
||||
bh_unit_assert(iter != NULL);
|
||||
bh_unit_assert(BH_PTR_TO_INT(bh_hashmap_iter_key(iter)) == 1337);
|
||||
bh_unit_assert(BH_PTR_TO_INT(bh_hashmap_iter_value(iter)) == 80085);
|
||||
bh_unit_assert(bh_hashmap_iter_next(hashmap, iter) == NULL);
|
||||
|
||||
bh_unit_assert(hashmap->data != NULL);
|
||||
bh_unit_assert(hashmap->psls != NULL);
|
||||
bh_unit_assert(hashmap->capacity >= 1024);
|
||||
bh_unit_assert(hashmap->threshold == (size_t)(hashmap->capacity * hashmap->factor));
|
||||
bh_unit_assert(hashmap->size == 1);
|
||||
|
||||
/* Save old pointers */
|
||||
old_data = hashmap->data;
|
||||
old_psls = hashmap->psls;
|
||||
|
||||
/* Change factor and grow */
|
||||
bh_hashmap_set_factor(hashmap, 0.35f);
|
||||
bh_unit_assert(bh_hashmap_reserve(hashmap, 8192) == 0);
|
||||
|
||||
bh_unit_assert(hashmap->data != NULL);
|
||||
bh_unit_assert(hashmap->psls != NULL);
|
||||
bh_unit_assert(hashmap->capacity >= 8192);
|
||||
bh_unit_assert(hashmap->data != old_data);
|
||||
bh_unit_assert(hashmap->psls != old_psls);
|
||||
bh_unit_assert(hashmap->factor == 0.35f);
|
||||
bh_unit_assert(hashmap->threshold == (size_t)(hashmap->capacity * hashmap->factor));
|
||||
bh_unit_assert(hashmap->size == 1);
|
||||
|
||||
old_data = hashmap->data;
|
||||
old_psls = hashmap->psls;
|
||||
|
||||
/* Check hashmap contents */
|
||||
iter = bh_hashmap_iter_next(hashmap, NULL);
|
||||
bh_unit_assert(iter != NULL);
|
||||
bh_unit_assert(BH_PTR_TO_INT(bh_hashmap_iter_key(iter)) == 1337);
|
||||
bh_unit_assert(BH_PTR_TO_INT(bh_hashmap_iter_value(iter)) == 80085);
|
||||
bh_unit_assert(bh_hashmap_iter_next(hashmap, iter) == NULL);
|
||||
|
||||
/* Shrink */
|
||||
bh_unit_assert(bh_hashmap_reserve(hashmap, 0) == 0);
|
||||
|
||||
bh_unit_assert(hashmap->data != NULL);
|
||||
bh_unit_assert(hashmap->psls != NULL);
|
||||
bh_unit_assert(hashmap->capacity < 8192);
|
||||
bh_unit_assert(hashmap->data != old_data);
|
||||
bh_unit_assert(hashmap->psls != old_psls);
|
||||
bh_unit_assert(hashmap->factor == 0.35f);
|
||||
bh_unit_assert(hashmap->threshold == (size_t)(hashmap->capacity * hashmap->factor));
|
||||
bh_unit_assert(hashmap->size == 1);
|
||||
|
||||
/* Check hashmap contents */
|
||||
iter = bh_hashmap_iter_next(hashmap, NULL);
|
||||
bh_unit_assert(iter != NULL);
|
||||
bh_unit_assert(BH_PTR_TO_INT(bh_hashmap_iter_key(iter)) == 1337);
|
||||
bh_unit_assert(BH_PTR_TO_INT(bh_hashmap_iter_value(iter)) == 80085);
|
||||
bh_unit_assert(bh_hashmap_iter_next(hashmap, iter) == NULL);
|
||||
|
||||
/* Shrink to 0 (deallocate) */
|
||||
bh_hashmap_clear(hashmap);
|
||||
bh_unit_assert(hashmap->size == 0);
|
||||
|
||||
bh_unit_assert(bh_hashmap_reserve(hashmap, 0) == 0);
|
||||
bh_unit_assert(hashmap->capacity == 0);
|
||||
bh_unit_assert(hashmap->size == 0);
|
||||
|
||||
/* Check hashmap contents */
|
||||
iter = bh_hashmap_iter_next(hashmap, NULL);
|
||||
bh_unit_assert(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_unit_assert(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_unit_assert(bh_hashmap_insert(hashmap, BH_INT_TO_PTR((i - 1) / 4), BH_INT_TO_PTR(i)) == 0);
|
||||
}
|
||||
|
||||
/* Remove elements */
|
||||
iter = bh_hashmap_iter_next(hashmap, NULL);
|
||||
removed = 0;
|
||||
while (iter)
|
||||
{
|
||||
removed += BH_PTR_TO_INT(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_unit_assert(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_unit_assert(hashmap != NULL);
|
||||
|
||||
/* Insert elements into hashmap */
|
||||
for (i = 0; i < 256; i++)
|
||||
bh_unit_assert(bh_hashmap_insert(hashmap, BH_INT_TO_PTR(i * 4), BH_INT_TO_PTR(i)) == 0);
|
||||
|
||||
/* Lookup inserted elements */
|
||||
for (i = 0; i < 256; i++)
|
||||
{
|
||||
int exists;
|
||||
|
||||
bh_unit_assert(BH_PTR_TO_INT(bh_hashmap_at(hashmap, BH_INT_TO_PTR(i * 4), NULL)) == i);
|
||||
bh_hashmap_at(hashmap, BH_INT_TO_PTR(i * 4), &exists);
|
||||
bh_unit_assert(exists == 1);
|
||||
}
|
||||
|
||||
/* Lookup non-existing elements */
|
||||
for (i = 256; i < 512; i++)
|
||||
{
|
||||
int exists;
|
||||
|
||||
bh_unit_assert(bh_hashmap_at(hashmap, BH_INT_TO_PTR(i * 4), NULL) == NULL);
|
||||
bh_hashmap_at(hashmap, BH_INT_TO_PTR(i * 4), &exists);
|
||||
bh_unit_assert(exists == 0);
|
||||
}
|
||||
|
||||
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_unit_assert(hashmap != NULL);
|
||||
|
||||
/* Insert elements into hashmap */
|
||||
for (i = 0; i < 128; i++)
|
||||
bh_unit_assert(bh_hashmap_insert(hashmap, BH_INT_TO_PTR(i), 0) == 0);
|
||||
|
||||
bh_hashmap_clear(hashmap);
|
||||
|
||||
/* Remove non-existing elements */
|
||||
for (i = 0; i < 128; i++)
|
||||
bh_hashmap_remove(hashmap, BH_INT_TO_PTR(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_unit_assert(hashmap != NULL);
|
||||
bh_unit_assert(bh_hashmap_empty(hashmap) == 1);
|
||||
|
||||
/* Insert elements into hashmap */
|
||||
for (i = 0; i < 14; i++)
|
||||
bh_unit_assert(bh_hashmap_insert(hashmap, BH_INT_TO_PTR(i), NULL) == 0);
|
||||
|
||||
/* Check hashmap fields correspond to getter functions */
|
||||
bh_unit_assert(hashmap->size == 14);
|
||||
bh_unit_assert(hashmap->capacity >= 14);
|
||||
bh_unit_assert(bh_hashmap_size(hashmap) == hashmap->size);
|
||||
bh_unit_assert(bh_hashmap_capacity(hashmap) == hashmap->capacity);
|
||||
bh_unit_assert(bh_hashmap_factor(hashmap) == hashmap->factor);
|
||||
bh_unit_assert(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("init_destroy", init_destroy);
|
||||
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();
|
||||
}
|
||||
183
tests/src/queue.c
Normal file
183
tests/src/queue.c
Normal file
@@ -0,0 +1,183 @@
|
||||
#include <bh/internal/queue.h>
|
||||
#include <bh/unit.h>
|
||||
|
||||
static int init_destroy(void)
|
||||
{
|
||||
bh_queue_t queue;
|
||||
|
||||
bh_queue_init(&queue);
|
||||
|
||||
bh_unit_assert(queue.size == 0);
|
||||
bh_unit_assert(queue.capacity == 0);
|
||||
bh_unit_assert(queue.data == NULL);
|
||||
bh_unit_assert(queue.head == queue.tail);
|
||||
|
||||
bh_queue_destroy(&queue);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int new_free(void)
|
||||
{
|
||||
bh_queue_t *queue;
|
||||
|
||||
queue = bh_queue_new();
|
||||
bh_unit_assert(queue != NULL);
|
||||
bh_unit_assert(queue->size == 0);
|
||||
bh_unit_assert(queue->capacity == 0);
|
||||
bh_unit_assert(queue->data == NULL);
|
||||
bh_unit_assert(queue->head == queue->tail);
|
||||
|
||||
bh_queue_free(queue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int grow_shrink(void)
|
||||
{
|
||||
bh_queue_t *queue;
|
||||
void *old_data;
|
||||
|
||||
queue = bh_queue_new();
|
||||
bh_unit_assert(queue != NULL);
|
||||
|
||||
/* Reserve 1024 elements and insert item into queue */
|
||||
bh_unit_assert(bh_queue_reserve(queue, 1024) == 0);
|
||||
bh_unit_assert(bh_queue_insert(queue, BH_INT_TO_PTR(1337)) == 0);
|
||||
bh_unit_assert(queue->capacity >= 1024);
|
||||
bh_unit_assert(queue->size == 1);
|
||||
bh_unit_assert(bh_queue_empty(queue) == 0);
|
||||
bh_unit_assert(queue->data != NULL);
|
||||
|
||||
/* Check queue content */
|
||||
bh_unit_assert(BH_PTR_TO_INT(bh_queue_front(queue)) == 1337);
|
||||
|
||||
old_data = queue->data;
|
||||
|
||||
/* Grow queue */
|
||||
bh_unit_assert(bh_queue_reserve(queue, 8192) == 0);
|
||||
bh_unit_assert(queue->capacity >= 8192);
|
||||
bh_unit_assert(queue->size == 1);
|
||||
bh_unit_assert(bh_queue_empty(queue) == 0);
|
||||
bh_unit_assert(queue->data != NULL);
|
||||
bh_unit_assert(queue->data != old_data);
|
||||
|
||||
/* Check queue content */
|
||||
bh_unit_assert(BH_PTR_TO_INT(bh_queue_front(queue)) == 1337);
|
||||
|
||||
old_data = queue->data;
|
||||
|
||||
/* Shrink the queue */
|
||||
bh_unit_assert(bh_queue_reserve(queue, 0) == 0);
|
||||
bh_unit_assert(queue->capacity >= 1 && queue->capacity <= 8192);
|
||||
bh_unit_assert(queue->size == 1);
|
||||
bh_unit_assert(bh_queue_empty(queue) == 0);
|
||||
bh_unit_assert(queue->data != NULL);
|
||||
bh_unit_assert(queue->data != old_data);
|
||||
|
||||
/* Check queue content */
|
||||
bh_unit_assert(BH_PTR_TO_INT(bh_queue_front(queue)) == 1337);
|
||||
|
||||
/* Shrink to 0 (deallocate) */
|
||||
bh_queue_clear(queue);
|
||||
bh_unit_assert(queue->size == 0);
|
||||
|
||||
bh_unit_assert(bh_queue_reserve(queue, 0) == 0);
|
||||
bh_unit_assert(queue->capacity == 0);
|
||||
bh_unit_assert(queue->size == 0);
|
||||
bh_unit_assert(bh_queue_empty(queue) == 1);
|
||||
bh_unit_assert(queue->data == NULL);
|
||||
|
||||
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_unit_assert(queue != NULL);
|
||||
|
||||
added = 0;
|
||||
for (i = 0; i < 256; i++)
|
||||
{
|
||||
added += i * 2;
|
||||
bh_unit_assert(bh_queue_insert(queue, BH_INT_TO_PTR(i * 2)) == 0);
|
||||
}
|
||||
|
||||
removed = 0;
|
||||
iter = bh_queue_iter_next(queue, NULL);
|
||||
while (iter)
|
||||
{
|
||||
removed += BH_PTR_TO_INT(bh_queue_front(queue));
|
||||
bh_queue_remove(queue);
|
||||
iter = bh_queue_iter_next(queue, NULL);
|
||||
}
|
||||
|
||||
bh_unit_assert(added == removed);
|
||||
bh_unit_assert(queue->size == 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_unit_assert(queue != NULL);
|
||||
|
||||
bh_unit_assert(bh_queue_reserve(queue, 128) == 0);
|
||||
capacity = queue->capacity;
|
||||
|
||||
for (i = 0; i < 128; i++)
|
||||
{
|
||||
for (j = 0; j < 3; j++)
|
||||
bh_queue_remove(queue);
|
||||
|
||||
for (j = 0; j < 4 && queue->size < 128; j++)
|
||||
bh_unit_assert(bh_queue_insert(queue, BH_INT_TO_PTR(i * 4 + j)) == 0);
|
||||
}
|
||||
|
||||
bh_unit_assert(queue->size == 128);
|
||||
bh_unit_assert(queue->capacity == capacity);
|
||||
|
||||
bh_queue_free(queue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fields(void)
|
||||
{
|
||||
bh_queue_t *queue;
|
||||
|
||||
queue = bh_queue_new();
|
||||
bh_unit_assert(queue != NULL);
|
||||
|
||||
bh_unit_assert(bh_queue_insert(queue, BH_INT_TO_PTR(1337)) == 0);
|
||||
bh_unit_assert(queue->size == 1);
|
||||
bh_unit_assert(queue->capacity >= 1);
|
||||
bh_unit_assert(bh_queue_size(queue) == queue->size);
|
||||
bh_unit_assert(bh_queue_capacity(queue) == queue->capacity);
|
||||
|
||||
bh_queue_free(queue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
bh_unit_add("init_destroy", init_destroy);
|
||||
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();
|
||||
}
|
||||
22
unit/CMakeLists.txt
Normal file
22
unit/CMakeLists.txt
Normal file
@@ -0,0 +1,22 @@
|
||||
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)
|
||||
24
unit/include/bh/unit.h
Normal file
24
unit/include/bh/unit.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef BHLIB_UNIT_H
|
||||
#define BHLIB_UNIT_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
typedef int (*bh_unit_cb_t)(void);
|
||||
|
||||
#define bh_unit_assert(e) \
|
||||
if (!(e)) { \
|
||||
printf("%s:%d\t%s", __FILE__, __LINE__, #e); \
|
||||
return -1; \
|
||||
}
|
||||
|
||||
|
||||
#define bh_unit_assert_delta(x, y, e) \
|
||||
if ((((x)>(y))?((x)-(y)):((y)-(x)))>(e)) { \
|
||||
printf("%s:%d\t%s", __FILE__, __LINE__, #x " == " #y); \
|
||||
return -1; \
|
||||
}
|
||||
|
||||
void bh_unit_add(const char *name, bh_unit_cb_t func);
|
||||
int bh_unit_run(void);
|
||||
|
||||
#endif /* BHLIB_UNIT_H */
|
||||
55
unit/src/unit.c
Normal file
55
unit/src/unit.c
Normal file
@@ -0,0 +1,55 @@
|
||||
#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");
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user