Add primitives, hash map and dynamic array
This commit is contained in:
commit
2f19f82116
10 changed files with 384 additions and 0 deletions
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
build/
|
||||||
|
.DS_Store
|
||||||
|
.cache/
|
||||||
|
compile_commands.json
|
||||||
31
CMakeLists.txt
Normal file
31
CMakeLists.txt
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
cmake_minimum_required(VERSION 3.21)
|
||||||
|
|
||||||
|
project(htd C)
|
||||||
|
set(CMAKE_C_STANDARD 23)
|
||||||
|
set(CMAKE_C_STANDARD_REQUIRED YES)
|
||||||
|
set(CMAKE_C_EXTENSIONS NO)
|
||||||
|
|
||||||
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
|
file(GLOB HTD_SOURCES
|
||||||
|
src/data_structure/*.c
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(htd STATIC ${HTD_SOURCES})
|
||||||
|
|
||||||
|
target_include_directories(htd PUBLIC
|
||||||
|
${PROJECT_SOURCE_DIR}/include
|
||||||
|
)
|
||||||
|
target_compile_options(htd PRIVATE -Wall -Wextra -Werror)
|
||||||
|
|
||||||
|
set(CMAKE_CTEST_VERBOSE TRUE)
|
||||||
|
enable_testing()
|
||||||
|
|
||||||
|
file(GLOB TEST_SOURCES tests/test_*.c)
|
||||||
|
|
||||||
|
foreach(test_src ${TEST_SOURCES})
|
||||||
|
get_filename_component(test_name ${test_src} NAME_WE)
|
||||||
|
add_executable(${test_name} ${test_src})
|
||||||
|
target_link_libraries(${test_name} PRIVATE htd)
|
||||||
|
add_test(NAME ${test_name} COMMAND ${test_name})
|
||||||
|
endforeach()
|
||||||
52
README.md
Normal file
52
README.md
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
# Personal library for C
|
||||||
|
|
||||||
|
## Features
|
||||||
|
### Primitives
|
||||||
|
Use shorter more concise names for common C types.
|
||||||
|
- `i8`: 8-bit signed integer
|
||||||
|
- `u8`: 8-bit unsigned integer
|
||||||
|
- `i16`: 16-bit signed integer
|
||||||
|
- `u16`: 16-bit unsigned integer
|
||||||
|
- `i32`: 32-bit signed integer
|
||||||
|
- `u32`: 32-bit unsigned integer
|
||||||
|
- `i64`: 64-bit signed integer
|
||||||
|
- `u64`: 64-bit unsigned integer
|
||||||
|
- `f32`: 32-bit floating point
|
||||||
|
- `f64`: 64-bit floating point
|
||||||
|
- `usize`: Unsigned integer of the same size as a pointer
|
||||||
|
- `isize`: Signed integer of the same size as a pointer
|
||||||
|
|
||||||
|
### Data structures
|
||||||
|
- `Dynamic array`: A dynamic array that can grow and shrink in size.
|
||||||
|
- `Hash map`: A hash map that uses linear probing for collision resolution and Murmur3 for hashing.
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd build
|
||||||
|
ctest
|
||||||
|
```
|
||||||
|
|
||||||
|
## Installing
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd build
|
||||||
|
make install
|
||||||
|
```
|
||||||
|
|
||||||
|
## Uninstalling
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd build
|
||||||
|
make uninstall
|
||||||
|
```
|
||||||
|
|
||||||
22
include/htd/data_structure/dynamic_array.h
Normal file
22
include/htd/data_structure/dynamic_array.h
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
#ifndef DYNAMIC_ARRAY_H
|
||||||
|
#define DYNAMIC_ARRAY_H
|
||||||
|
|
||||||
|
#include <htd/primitives/primitives.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
void *data;
|
||||||
|
|
||||||
|
usize len;
|
||||||
|
usize data_size;
|
||||||
|
usize capacity;
|
||||||
|
} DynamicArray;
|
||||||
|
|
||||||
|
void dynarr_init(DynamicArray* arr, usize size);
|
||||||
|
|
||||||
|
void* dynarr_at(DynamicArray* arr, usize idx);
|
||||||
|
|
||||||
|
void dynarr_push(DynamicArray* arr, void* data);
|
||||||
|
|
||||||
|
void dynarr_free(DynamicArray* arr);
|
||||||
|
|
||||||
|
#endif // DYNAMIC_ARRAY_H
|
||||||
28
include/htd/data_structure/hash_map.h
Normal file
28
include/htd/data_structure/hash_map.h
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
#ifndef HTD_HASH_MAP_H
|
||||||
|
#define HTD_HASH_MAP_H
|
||||||
|
|
||||||
|
#include <htd/primitives/primitives.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
void* key;
|
||||||
|
void* val;
|
||||||
|
} HashMapEntry;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
HashMapEntry* table;
|
||||||
|
|
||||||
|
usize len;
|
||||||
|
usize capacity;
|
||||||
|
usize key_size;
|
||||||
|
usize val_size;
|
||||||
|
} HashMap;
|
||||||
|
|
||||||
|
void hmap_init(HashMap* hmap, usize key_size, usize val_size);
|
||||||
|
|
||||||
|
void hmap_put(HashMap* hmap, const void* key, const void* val);
|
||||||
|
|
||||||
|
void* hmap_get(HashMap* hmap, const void* key);
|
||||||
|
|
||||||
|
void hmap_free(HashMap* hmap);
|
||||||
|
|
||||||
|
#endif // HTD_HASH_MAP_H
|
||||||
24
include/htd/primitives/primitives.h
Normal file
24
include/htd/primitives/primitives.h
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
#ifndef PRIMITIVES_H
|
||||||
|
#define PRIMITIVES_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef int8_t i8;
|
||||||
|
typedef int16_t i16;
|
||||||
|
typedef int32_t i32;
|
||||||
|
typedef int64_t i64;
|
||||||
|
|
||||||
|
typedef uint8_t u8;
|
||||||
|
typedef uint16_t u16;
|
||||||
|
typedef uint32_t u32;
|
||||||
|
typedef uint64_t u64;
|
||||||
|
|
||||||
|
// Floats not guaranteed per IEEE 754, but for use case it's fine
|
||||||
|
typedef float f32;
|
||||||
|
typedef double f64;
|
||||||
|
|
||||||
|
typedef size_t usize;
|
||||||
|
typedef ptrdiff_t isize;
|
||||||
|
|
||||||
|
#endif // PRIMITIVES_H
|
||||||
36
src/data_structure/dynamic_array.c
Normal file
36
src/data_structure/dynamic_array.c
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
#include <htd/primitives/primitives.h>
|
||||||
|
#include <htd/data_structure/dynamic_array.h>
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <memory.h>
|
||||||
|
|
||||||
|
const usize GROWTH_FACTOR = 2;
|
||||||
|
const usize START_LEN = 32;
|
||||||
|
|
||||||
|
void dynarr_init(DynamicArray* arr, usize data_size) {
|
||||||
|
arr->data_size = data_size;
|
||||||
|
arr->len = 0;
|
||||||
|
arr->capacity = START_LEN;
|
||||||
|
arr->data = (void*)malloc(arr->capacity * arr->data_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* dynarr_at(DynamicArray* arr, usize idx) {
|
||||||
|
return &((u8*)arr->data)[arr->data_size * idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
void dynarr_push(DynamicArray* arr, void* data) {
|
||||||
|
if (arr->len >= arr->capacity) {
|
||||||
|
arr->capacity *= GROWTH_FACTOR;
|
||||||
|
arr->data = (void*)realloc(arr->data, arr->capacity * arr->data_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(&((u8*)arr->data)[arr->data_size * arr->len], data, arr->data_size);
|
||||||
|
arr->len++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dynarr_free(DynamicArray* arr) {
|
||||||
|
free(arr->data);
|
||||||
|
arr->data = NULL;
|
||||||
|
arr->capacity = 0;
|
||||||
|
arr->len = 0;
|
||||||
|
}
|
||||||
142
src/data_structure/hash_map.c
Normal file
142
src/data_structure/hash_map.c
Normal file
|
|
@ -0,0 +1,142 @@
|
||||||
|
#include <htd/primitives/primitives.h>
|
||||||
|
#include <htd/data_structure/hash_map.h>
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <memory.h>
|
||||||
|
|
||||||
|
const usize START_LEN = 16;
|
||||||
|
const usize GROWTH_FACTOR = 2;
|
||||||
|
|
||||||
|
usize hash(const u8* key, usize len) {
|
||||||
|
const u8 *data = (const u8*)key;
|
||||||
|
u32 h = 0x811c9dc5;
|
||||||
|
const u32 c1 = 0xcc9e2d51;
|
||||||
|
const u32 c2 = 0x1b873593;
|
||||||
|
|
||||||
|
const usize nblocks = len / 4;
|
||||||
|
for (usize i = 0; i < nblocks; i++) {
|
||||||
|
u32 k = *((u32*)data);
|
||||||
|
data += 4;
|
||||||
|
|
||||||
|
k *= c1;
|
||||||
|
k = (k << 15) | (k >> 17);
|
||||||
|
k *= c2;
|
||||||
|
|
||||||
|
h ^= k;
|
||||||
|
h = (h << 13) | (h >> 19);
|
||||||
|
h = h * 5 + 0xe6546b64;
|
||||||
|
}
|
||||||
|
|
||||||
|
const usize tail_size = len & 3;
|
||||||
|
u32 k1 = 0;
|
||||||
|
if (tail_size > 0) {
|
||||||
|
for (usize i = 0; i < tail_size; ++i) {
|
||||||
|
k1 ^= data[i] << (i * 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
k1 *= c1;
|
||||||
|
k1 = (k1 << 15) | (k1 >> 17);
|
||||||
|
k1 *= c2;
|
||||||
|
h ^= k1;
|
||||||
|
}
|
||||||
|
|
||||||
|
h ^= len;
|
||||||
|
h = h ^ (h >> 16);
|
||||||
|
h *= 0x85ebca6b;
|
||||||
|
h = h ^ (h >> 13);
|
||||||
|
h *= 0xc2b2ae35;
|
||||||
|
h = h ^ (h >> 16);
|
||||||
|
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hmap_init(HashMap* hmap, usize key_size, usize val_size) {
|
||||||
|
hmap->len = 0;
|
||||||
|
hmap->capacity = START_LEN;
|
||||||
|
hmap->key_size = key_size;
|
||||||
|
hmap->val_size = val_size;
|
||||||
|
|
||||||
|
hmap->table = (HashMapEntry*)malloc(sizeof(HashMapEntry) * hmap->capacity);
|
||||||
|
for (usize i = 0; i < hmap->capacity; ++i) {
|
||||||
|
hmap->table[i].key = NULL;
|
||||||
|
hmap->table[i].val = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void resize(HashMap* hmap) {
|
||||||
|
usize old_capacity = hmap->capacity;
|
||||||
|
HashMapEntry* old_table = (HashMapEntry*)malloc(sizeof(HashMapEntry) * old_capacity);
|
||||||
|
memcpy(old_table, hmap->table, sizeof(HashMapEntry) * old_capacity);
|
||||||
|
|
||||||
|
hmap->capacity *= GROWTH_FACTOR;
|
||||||
|
hmap->table = (HashMapEntry*)realloc(hmap->table, sizeof(HashMapEntry) * hmap->capacity);
|
||||||
|
|
||||||
|
for (usize i = 0; i < hmap->capacity; i++) {
|
||||||
|
hmap->table[i].key = NULL;
|
||||||
|
hmap->table[i].val = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset length, re-adding
|
||||||
|
hmap->len = 0;
|
||||||
|
for (usize i = 0; i < old_capacity; i++) {
|
||||||
|
if (old_table[i].key != NULL) {
|
||||||
|
hmap_put(hmap, old_table[i].key, old_table[i].val);
|
||||||
|
|
||||||
|
free(old_table[i].key);
|
||||||
|
free(old_table[i].val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(old_table);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hmap_put(HashMap* hmap, const void* key, const void* val) {
|
||||||
|
if (hmap->len >= hmap->capacity) {
|
||||||
|
resize(hmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
usize idx = hash(key, hmap->key_size) % hmap->capacity;
|
||||||
|
|
||||||
|
while (hmap->table[idx].key != NULL) {
|
||||||
|
if (memcmp(hmap->table[idx].key, key, hmap->key_size) == 0) {
|
||||||
|
free(hmap->table[idx].val);
|
||||||
|
hmap->table[idx].val = malloc(hmap->val_size);
|
||||||
|
memcpy(hmap->table[idx].val, val, hmap->val_size);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
idx = (idx + 1) % hmap->capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
hmap->table[idx].key = malloc(hmap->key_size);
|
||||||
|
memcpy(hmap->table[idx].key, key, hmap->key_size);
|
||||||
|
hmap->table[idx].val = malloc(hmap->val_size);
|
||||||
|
memcpy(hmap->table[idx].val, val, hmap->val_size);
|
||||||
|
|
||||||
|
hmap->len++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* hmap_get(HashMap* hmap, const void* key) {
|
||||||
|
usize idx = hash(key, hmap->key_size) % hmap->capacity;
|
||||||
|
|
||||||
|
while (hmap->table[idx].key != NULL) {
|
||||||
|
if (memcmp(hmap->table[idx].key, key, hmap->key_size) == 0) {
|
||||||
|
return hmap->table[idx].val;
|
||||||
|
}
|
||||||
|
idx = (idx + 1) % hmap->capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hmap_free(HashMap* hmap) {
|
||||||
|
for (usize i = 0; i < hmap->capacity; ++i) {
|
||||||
|
if (hmap->table[i].key != NULL) {
|
||||||
|
free(hmap->table[i].key);
|
||||||
|
free(hmap->table[i].val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(hmap->table);
|
||||||
|
hmap->table = NULL;
|
||||||
|
hmap->capacity = 0;
|
||||||
|
hmap->len = 0;
|
||||||
|
}
|
||||||
20
tests/test_dynamic_array.c
Normal file
20
tests/test_dynamic_array.c
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
#include <htd/primitives/primitives.h>
|
||||||
|
#include <htd/data_structure/dynamic_array.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
DynamicArray i32_array;
|
||||||
|
dynarr_init(&i32_array, sizeof(i32));
|
||||||
|
|
||||||
|
for (i32 i = 0; i < 100; i++) {
|
||||||
|
dynarr_push(&i32_array, &i);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(i32_array.len == 100);
|
||||||
|
for (i32 i = 0; i < 100; i++) {
|
||||||
|
assert(*(i32*)dynarr_at(&i32_array, i) == i);
|
||||||
|
}
|
||||||
|
|
||||||
|
dynarr_free(&i32_array);
|
||||||
|
}
|
||||||
25
tests/test_hash_map.c
Normal file
25
tests/test_hash_map.c
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
#include <htd/primitives/primitives.h>
|
||||||
|
#include <htd/data_structure/hash_map.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
HashMap i32_i64_hmap;
|
||||||
|
hmap_init(&i32_i64_hmap, sizeof(i32), sizeof(i64));
|
||||||
|
|
||||||
|
for (i32 i = 0; i < 100; i++) {
|
||||||
|
i64 v = (i64)i * 2;
|
||||||
|
hmap_put(&i32_i64_hmap, &i, &v);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i32 i = 0; i < 100; i++) {
|
||||||
|
i64 expected_value = (i64)i * 2;
|
||||||
|
i64* retrieved_value = (i64*)hmap_get(&i32_i64_hmap, &i);
|
||||||
|
|
||||||
|
assert(retrieved_value != NULL && *retrieved_value == expected_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
hmap_free(&i32_i64_hmap);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue