First Commit of HATPC remake

Internal Changelog:
- Implement Basic ECS
- Tag system not yet implemented
- Use SC for common data structures
scene_man
En Yi 2022-11-26 18:11:51 +08:00
commit 17aae0617c
26 changed files with 2222 additions and 0 deletions

3
.gitignore vendored 100644
View File

@ -0,0 +1,3 @@
.cache/
build/
compile_commands.json

21
CMakeLists.txt 100644
View File

@ -0,0 +1,21 @@
set(PROJECT_NAME HATPC_remake)
cmake_minimum_required(VERSION 3.22.1)
project(${PROJECT_NAME} C)
set(CMAKE_C_STANDARD 99)
add_subdirectory(sc)
add_executable(${PROJECT_NAME}
main.c
entManager.c
mempool.c
)
target_include_directories(${PROJECT_NAME}
PUBLIC
${CMAKE_CURRENT_LIST_DIR}
)
target_link_libraries(${PROJECT_NAME}
sc_queue
sc_map
)

14
Conventions.txt 100644
View File

@ -0,0 +1,14 @@
Trivial Variables: i,n,c,etc... (Only one letter. If one letter isn't clear, then make it a Local Variable)
Local Variables: snake_case
Global Variables: g_snake_case
Const Variables: ALL_CAPS
Pointer Variables: add a p_ to the prefix. For global variables it would be gp_var, for local variables p_var, for const variables p_VAR. If far pointers are used then use an fp_ instead of p_.
Typedef Values: suffix _t
Structs: PascalCase
Enums: PascalCase
Struct Member Variables: camelCase
Enum Values: ALL_CAPS
Functions: snake_case
Macros: ALL_CAPS

20
components.h 100644
View File

@ -0,0 +1,20 @@
#ifndef __COMPONENTS_H
#define __COMPONENTS_H
// TODO: Look at sc to use macros to auto generate functions
#define N_COMPONENTS 1
enum ComponentEnum
{
CBBOX_COMP_T,
};
typedef enum ComponentEnum ComponentEnum_t;
typedef struct _CBBox_t
{
int x;
int y;
int width;
int height;
}CBBox_t;
#endif // __COMPONENTS_H

121
entManager.c 100644
View File

@ -0,0 +1,121 @@
#include "entManager.h"
void init_entity_manager(EntityManager_t *p_manager)
{
sc_map_init_64v(&p_manager->entities, MAX_COMP_POOL_SIZE, 0);
for (size_t i=0; i<N_COMPONENTS; ++i)
{
sc_map_init_64v(p_manager->component_map + i, MAX_COMP_POOL_SIZE, 0);
}
sc_queue_init(&p_manager->to_add);
sc_queue_init(&p_manager->to_remove);
}
;
void update_entity_manager(EntityManager_t *p_manager)
{
// This will only update the entity map of the manager
// It does not make new entities, but will free entity
// New entities are assigned during add_entity
unsigned long e_idx;
unsigned long comp_type_idx;
unsigned long comp_idx;
sc_queue_foreach (&p_manager->to_remove, e_idx)
{
Entity_t *p_entity = (Entity_t *)sc_map_get_64v(&p_manager->entities, e_idx);
if (!p_entity) continue;
sc_map_foreach (&p_entity->components, comp_type_idx, comp_idx)
{
free_component_to_mempool((ComponentEnum_t)comp_type_idx, comp_idx);
sc_map_del_64v(&p_manager->component_map[comp_type_idx], comp_idx);
}
free_entity_to_mempool(e_idx);
sc_map_del_64v(&p_manager->entities, e_idx);
}
sc_queue_clear(&p_manager->to_remove);
sc_queue_foreach (&p_manager->to_add, e_idx)
{
Entity_t *ent = get_entity_wtih_id(e_idx);
sc_map_put_64v(&p_manager->entities, e_idx, (void *)ent);
}
sc_queue_clear(&p_manager->to_add);
}
void clear_entity_manager(EntityManager_t *p_manager)
{
unsigned long e_id;
Entity_t *p_ent;
sc_map_foreach (&p_manager->entities, e_id, p_ent)
{
remove_entity(p_manager, e_id);
}
update_entity_manager(p_manager);
}
void free_entity_manager(EntityManager_t *p_manager)
{
clear_entity_manager(p_manager);
sc_map_term_64v(&p_manager->entities);
for (size_t i=0; i<N_COMPONENTS; ++i)
{
sc_map_term_64v(p_manager->component_map + i);
}
sc_queue_term(&p_manager->to_add);
sc_queue_term(&p_manager->to_remove);
}
Entity_t *add_entity(EntityManager_t *p_manager, const char *tag)
{
unsigned long e_idx = 0;
Entity_t * p_ent = new_entity_from_mempool(&e_idx);
if (p_ent)
{
sc_queue_add_last(&p_manager->to_add, e_idx);
}
return p_ent;
}
void remove_entity(EntityManager_t *p_manager, unsigned long id)
{
unsigned long comp_type, comp_id;
Entity_t *p_entity = sc_map_get_64v(&p_manager->entities, id);
if(!sc_map_found(&p_manager->entities)) return;
// This only marks the entity for deletion
// Does not free entity. This is done during the update
p_entity->m_alive = false;
sc_queue_add_last(&p_manager->to_remove, id);
}
// Components are not expected to be removed
// So, no need to extra steps to deal with iterator invalidation
void *add_component(EntityManager_t *p_manager, Entity_t *p_entity, ComponentEnum_t comp_type)
{
unsigned long comp_type_idx = (unsigned long)comp_type;
unsigned long comp_idx = 0;
void * p_comp = new_component_from_mempool(comp_type, &comp_idx);
if (p_comp)
{
sc_map_put_64(&p_entity->components, comp_type_idx, comp_idx);
sc_map_put_64v(&p_manager->component_map[comp_type_idx], comp_idx, p_comp);
}
return p_comp;
}
void *get_component(EntityManager_t *p_manager, Entity_t *p_entity, ComponentEnum_t comp_type)
{
unsigned long comp_type_idx = (unsigned long)comp_type;
unsigned long comp_idx = sc_map_get_64(&p_entity->components, comp_type_idx);
if (!sc_map_found(&p_entity->components)) return NULL;
return sc_map_get_64v(&p_manager->component_map[comp_type_idx], comp_idx);
}
void remove_component(EntityManager_t *p_manager, Entity_t *p_entity, ComponentEnum_t comp_type)
{
unsigned long comp_type_idx = (unsigned long)comp_type;
unsigned long comp_idx = sc_map_del_64(&p_entity->components, comp_type_idx);
if (!sc_map_found(&p_entity->components)) return;
sc_map_del_64v(&p_manager->component_map[comp_type_idx], comp_idx);
free_component_to_mempool(comp_type, comp_idx);
}

28
entManager.h 100644
View File

@ -0,0 +1,28 @@
#ifndef __ENTITY_MANAGER_H
#define __ENTITY_MANAGER_H
#include "sc/queue/sc_queue.h"
#include "sc/map/sc_map.h"
#include "mempool.h" // includes entity and components
typedef struct EntityManager
{
// All fields are Read-Only
struct sc_map_64v entities;
struct sc_map_64v component_map[N_COMPONENTS];
struct sc_queue_uint to_add;
struct sc_queue_uint to_remove;
}EntityManager_t;
void init_entity_manager(EntityManager_t *manager);
void update_entity_manager(EntityManager_t *manager);
void clear_entity_manager(EntityManager_t *manager);
void free_entity_manager(EntityManager_t *manager);
Entity_t *add_entity(EntityManager_t *manager, const char *tag);
void remove_entity(EntityManager_t *manager, unsigned long id);
void *add_component(EntityManager_t *manager, Entity_t *entity, ComponentEnum_t comp_type);
void *get_component(EntityManager_t *manager, Entity_t *entity, ComponentEnum_t comp_type);
void remove_component(EntityManager_t *manager, Entity_t *entity, ComponentEnum_t comp_type);
#endif // __ENTITY_MANAGER_H

13
entity.h 100644
View File

@ -0,0 +1,13 @@
#ifndef __ENTITY_H
#define __ENTITY_H
#include <stdbool.h>
#include "sc/map/sc_map.h"
#define MAX_TAG_LEN 32
typedef struct Entity
{
unsigned long m_id;
char m_tag[MAX_TAG_LEN];
bool m_alive;
struct sc_map_64 components;
}Entity_t;
#endif // __ENTITY_H

47
main.c 100644
View File

@ -0,0 +1,47 @@
#include "mempool.h"
#include "entManager.h"
#include <stdio.h>
int main(void)
{
init_memory_pools();
puts("Init-ing manager and memory pool");
EntityManager_t manager;
init_entity_manager(&manager);
puts("Creating two entities");
Entity_t *p_ent = add_entity(&manager, "player");
CBBox_t * p_bbox = (CBBox_t *)add_component(&manager, p_ent, CBBOX_COMP_T);
p_bbox->x = 15;
p_ent = add_entity(&manager, "enemy");
p_bbox = (CBBox_t *)add_component(&manager, p_ent, CBBOX_COMP_T);
p_bbox->x = 40;
update_entity_manager(&manager);
puts("Print and remove the entities");
unsigned long idx = 0;
sc_map_foreach(&manager.entities, idx, p_ent)
{
p_bbox = (CBBox_t *)get_component(&manager, p_ent, CBBOX_COMP_T);
printf("BBOX x: %d\n", p_bbox->x);
remove_entity(&manager, idx);
}
puts("");
update_entity_manager(&manager);
puts("Print again, should show nothing");
sc_map_foreach(&manager.entities, idx, p_ent)
{
p_bbox = (CBBox_t *)get_component(&manager, p_ent, CBBOX_COMP_T);
printf("BBOX x: %d\n", p_bbox->x);
remove_entity(&manager, idx);
}
puts("");
puts("Freeing manager and memory pool");
free_entity_manager(&manager);
free_memory_pools();
return 0;
}

144
mempool.c 100644
View File

@ -0,0 +1,144 @@
#include "mempool.h"
#include "sc/queue/sc_queue.h"
#include "sc/map/sc_map.h"
// Use hashmap as a Set
// Use list will be used to check if an object exist
// The alternative method to check the free list if idx is not there
// requires bound checking
// It's just easier on the mind overall
// If need to optimise memory, replace free_list with set and remove use_list
static struct EntityMemPool
{
Entity_t entity_buffer[MAX_COMP_POOL_SIZE];
struct sc_map_64 use_list;
struct sc_queue_uint free_list;
}entity_mem_pool;
static struct BBoxMemPool
{
CBBox_t bbox_buffer[MAX_COMP_POOL_SIZE];
struct sc_map_64 use_list;
struct sc_queue_uint free_list;
}bbox_mem_pool;
static bool pool_inited = false;
void init_memory_pools(void)
{
if (!pool_inited)
{
memset(bbox_mem_pool.bbox_buffer, 0, sizeof(bbox_mem_pool.bbox_buffer));
memset(entity_mem_pool.entity_buffer, 0, sizeof(entity_mem_pool.entity_buffer));
sc_queue_init(&bbox_mem_pool.free_list);
for (int i=0;i<MAX_COMP_POOL_SIZE;++i)
{
sc_queue_add_last(&(bbox_mem_pool.free_list), i);
}
sc_queue_init(&entity_mem_pool.free_list);
for (int i=0;i<MAX_COMP_POOL_SIZE;++i)
{
entity_mem_pool.entity_buffer[i].m_id = i;
sc_map_init_64(&entity_mem_pool.entity_buffer[i].components, 16 ,0);
sc_queue_add_last(&(entity_mem_pool.free_list), i);
}
sc_map_init_64(&bbox_mem_pool.use_list, MAX_COMP_POOL_SIZE ,0);
sc_map_init_64(&entity_mem_pool.use_list, MAX_COMP_POOL_SIZE ,0);
pool_inited = true;
}
}
void free_memory_pools(void)
{
if (pool_inited)
{
sc_map_term_64(&bbox_mem_pool.use_list);
sc_map_term_64(&entity_mem_pool.use_list);
sc_queue_term(&bbox_mem_pool.free_list);
sc_queue_term(&entity_mem_pool.free_list);
for (int i=0;i<MAX_COMP_POOL_SIZE;++i)
{
sc_map_term_64(&entity_mem_pool.entity_buffer[i].components);
}
pool_inited = false;
}
}
Entity_t* new_entity_from_mempool(unsigned long *p_e_idx)
{
if(sc_queue_empty(&entity_mem_pool.free_list)) return NULL;
unsigned long e_idx = sc_queue_del_first(&entity_mem_pool.free_list);
*p_e_idx = e_idx;
sc_map_put_64(&entity_mem_pool.use_list, e_idx, e_idx);
Entity_t * ent = entity_mem_pool.entity_buffer + e_idx;
sc_map_clear_64(&ent->components);
ent->m_alive = true;
memset(ent->m_tag, 0, MAX_TAG_LEN);
return ent;
}
Entity_t * get_entity_wtih_id(unsigned long idx)
{
sc_map_get_64(&entity_mem_pool.use_list, idx);
if (!sc_map_found(&entity_mem_pool.use_list)) return NULL;
return entity_mem_pool.entity_buffer + idx;
}
void free_entity_to_mempool(unsigned long idx)
{
sc_map_del_64(&entity_mem_pool.use_list, idx);
if (sc_map_found(&entity_mem_pool.use_list))
{
sc_queue_add_first(&entity_mem_pool.free_list, idx);
}
}
void* new_component_from_mempool(ComponentEnum_t comp_type, unsigned long *idx)
{
void * comp = NULL;
switch(comp_type)
{
case CBBOX_COMP_T:
if(sc_queue_empty(&bbox_mem_pool.free_list)) break;
*idx = sc_queue_del_first(&bbox_mem_pool.free_list);
sc_map_put_64(&bbox_mem_pool.use_list, *idx, *idx);
comp = bbox_mem_pool.bbox_buffer + *idx;
memset(comp, 0, sizeof(CBBox_t));
break;
default:
break;
}
return comp;
}
void* get_component_wtih_id(ComponentEnum_t comp_type, unsigned long idx)
{
void * comp = NULL;
switch(comp_type)
{
case CBBOX_COMP_T:
sc_map_get_64(&bbox_mem_pool.use_list, idx);
if (!sc_map_found(&bbox_mem_pool.use_list)) break;
comp = bbox_mem_pool.bbox_buffer + idx;
break;
default:
break;
}
return comp;
}
void free_component_to_mempool(ComponentEnum_t comp_type, unsigned long idx)
{
// This just free the component from the memory pool
switch(comp_type)
{
case CBBOX_COMP_T:
sc_map_del_64(&bbox_mem_pool.use_list, idx);
if (sc_map_found(&bbox_mem_pool.use_list))
{
sc_queue_add_first(&bbox_mem_pool.free_list, idx);
}
break;
default:
break;
}
}

16
mempool.h 100644
View File

@ -0,0 +1,16 @@
#ifndef __MEMPOOL_H
#define __MEMPOOL_H
#include "entity.h"
#include "components.h"
#define MAX_COMP_POOL_SIZE 256
void init_memory_pools(void);
void free_memory_pools(void);
Entity_t* new_entity_from_mempool(unsigned long *idx);
Entity_t* get_entity_wtih_id(unsigned long idx);
void free_entity_to_mempool(unsigned long idx);
void* new_component_from_mempool(ComponentEnum_t comp_type, unsigned long *idx);
void* get_component_wtih_id(ComponentEnum_t comp_type, unsigned long idx);
void free_component_to_mempool(ComponentEnum_t comp_type, unsigned long idx);
#endif //__MEMPOOL_H

34
sc/CMakeLists.txt 100644
View File

@ -0,0 +1,34 @@
cmake_minimum_required(VERSION 3.5.1)
project(sc_lib C)
include(CTest)
include(CheckCCompilerFlag)
if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release")
endif ()
message(STATUS "Build type ${CMAKE_BUILD_TYPE}")
add_subdirectory(array)
#add_subdirectory(buffer)
#add_subdirectory(condition)
#add_subdirectory(crc32)
add_subdirectory(heap)
#add_subdirectory(ini)
#add_subdirectory(linked-list)
#add_subdirectory(logger)
add_subdirectory(map)
#add_subdirectory(memory-map)
#add_subdirectory(mutex)
#add_subdirectory(option)
add_subdirectory(queue)
#add_subdirectory(perf)
#add_subdirectory(sc)
#add_subdirectory(signal)
#add_subdirectory(socket)
#add_subdirectory(string)
#add_subdirectory(time)
#add_subdirectory(timer)
#add_subdirectory(thread)
#add_subdirectory(uri)

29
sc/LICENSE 100644
View File

@ -0,0 +1,29 @@
BSD-3-Clause
Copyright 2021 Ozan Tezcan
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,15 @@
cmake_minimum_required(VERSION 3.5.1)
project(sc_array C)
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF)
add_library(sc_array STATIC sc_array.h)
set_target_properties(sc_array PROPERTIES LINKER_LANGUAGE C)
target_include_directories(sc_array PUBLIC ${CMAKE_CURRENT_LIST_DIR})
if (NOT CMAKE_C_COMPILER_ID MATCHES "MSVC")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -Wall -Wextra -pedantic -Werror")
endif ()

61
sc/array/README.md 100644
View File

@ -0,0 +1,61 @@
### Generic array
### Overview
- Growable array/vector.
- It comes with predefined types, check out predefined types at the bottom of
sc_array.h You can add more types there if you need.
### Usage
```c
#include "sc_array.h"
#include <stdio.h>
void example_str(void)
{
const char *it;
struct sc_array_str arr;
sc_array_init(&arr);
sc_array_add(&arr, "item0");
sc_array_add(&arr, "item1");
sc_array_add(&arr, "item2");
printf("\nDelete first element \n\n");
sc_array_del(&arr, 0);
sc_array_foreach (&arr, it) {
printf("Elem = %s \n", it);
}
sc_array_term(&arr);
}
void example_int(void)
{
struct sc_array_int arr;
sc_array_init(&arr);
sc_array_add(&arr, 0);
sc_array_add(&arr, 1);
sc_array_add(&arr, 2);
for (size_t i = 0; i < sc_array_size(&arr); i++) {
printf("Elem = %d \n", arr.elems[i]);
}
sc_array_term(&arr);
}
int main(void)
{
example_int();
example_str();
return 0;
}
```

223
sc/array/sc_array.h 100644
View File

@ -0,0 +1,223 @@
/*
* BSD-3-Clause
*
* Copyright 2021 Ozan Tezcan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SC_ARRAY_H
#define SC_ARRAY_H
#include <assert.h>
#include <string.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#define SC_ARRAY_VERSION "2.0.0"
#ifdef SC_HAVE_CONFIG_H
#include "config.h"
#else
#define sc_array_realloc realloc
#define sc_array_free free
#endif
#ifndef SC_ARRAY_MAX
#define SC_ARRAY_MAX SIZE_MAX
#endif
#define sc_array_def(T, name) \
struct sc_array_##name { \
bool oom; \
size_t cap; \
size_t size; \
/* NOLINTNEXTLINE */ \
T *elems; \
}
/**
* Init array
* @param a array
*/
#define sc_array_init(a) \
do { \
memset((a), 0, sizeof(*(a))); \
} while (0)
/**
* Term array
* @param a array
*/
#define sc_array_term(a) \
do { \
sc_array_free((a)->elems); \
sc_array_init(a); \
} while (0)
/**
* Add elem to array, call sc_array_oom(v) to see if 'add' failed because of out
* of memory.
*
* @param a array
* @param k elem
*/
#define sc_array_add(a, k) \
do { \
const size_t _max = SC_ARRAY_MAX / sizeof(*(a)->elems); \
size_t _cap; \
void *_p; \
\
if ((a)->cap == (a)->size) { \
if ((a)->cap > _max / 2) { \
(a)->oom = true; \
break; \
} \
_cap = (a)->cap == 0 ? 8 : (a)->cap * 2; \
_p = sc_array_realloc((a)->elems, \
_cap * sizeof(*((a)->elems))); \
if (_p == NULL) { \
(a)->oom = true; \
break; \
} \
(a)->cap = _cap; \
(a)->elems = _p; \
} \
(a)->oom = false; \
(a)->elems[(a)->size++] = k; \
} while (0)
/**
* Deletes items from the array without deallocating underlying memory
* @param a array
*/
#define sc_array_clear(a) \
do { \
(a)->size = 0; \
(a)->oom = false; \
} while (0)
/**
* @param a array
* @return true if last add operation failed, false otherwise.
*/
#define sc_array_oom(a) ((a)->oom)
/**
* Get element at index i, if 'i' is out of range, result is undefined.
*
* @param a array
* @param i index
* @return element at index 'i'
*/
#define sc_array_at(a, i) ((a)->elems[i])
/**
* @param a array
* @return element count
*/
#define sc_array_size(a) ((a)->size)
/**
* @param a array
* @param i element index, If 'i' is out of the range, result is undefined.
*/
#define sc_array_del(a, i) \
do { \
size_t idx = (i); \
assert(idx < (a)->size); \
\
const size_t _cnt = (a)->size - (idx) - 1; \
if (_cnt > 0) { \
memmove(&((a)->elems[idx]), &((a)->elems[idx + 1]), \
_cnt * sizeof(*((a)->elems))); \
} \
(a)->size--; \
} while (0)
/**
* Deletes the element at index i, replaces last element with the deleted
* element unless deleted element is the last element. This is faster than
* moving elements but elements will no longer be in the 'add order'
*
* arr[a,b,c,d,e,f] -> sc_array_del_unordered(arr, 2) - > arr[a,b,f,d,e]
*
* @param a array
* @param i index. If 'i' is out of the range, result is undefined.
*/
#define sc_array_del_unordered(a, i) \
do { \
size_t idx = (i); \
assert(idx < (a)->size); \
(a)->elems[idx] = (a)->elems[(--(a)->size)]; \
} while (0)
/**
* Deletes the last element. If current size is zero, result is undefined.
* @param a array
*/
#define sc_array_del_last(a) \
do { \
assert((a)->size != 0); \
(a)->size--; \
} while (0)
/**
* Sorts the array using qsort()
* @param a array
* @param cmp comparator, check qsort() documentation for details
*/
#define sc_array_sort(a, cmp) \
(qsort((a)->elems, (a)->size, sizeof(*(a)->elems), cmp))
/**
* Returns last element. If array is empty, result is undefined.
* @param a array
*/
#define sc_array_last(a) (a)->elems[(a)->size - 1]
/**
* @param a array
* @param elem elem
*/
#define sc_array_foreach(a, elem) \
for (size_t _k = 1, _i = 0; _k && _i != (a)->size; _k = !_k, _i++) \
for ((elem) = (a)->elems[_i]; _k; _k = !_k)
// (type, name)
sc_array_def(int, int);
sc_array_def(unsigned int, uint);
sc_array_def(long, long);
sc_array_def(unsigned long, ulong);
sc_array_def(unsigned long long, ull);
sc_array_def(uint32_t, 32);
sc_array_def(uint64_t, 64);
sc_array_def(double, double);
sc_array_def(const char *, str);
sc_array_def(void *, ptr);
#endif

View File

@ -0,0 +1,13 @@
cmake_minimum_required(VERSION 3.5.1)
project(sc_heap C)
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF)
add_library(
sc_heap STATIC
sc_heap.c
sc_heap.h)
target_include_directories(sc_heap PUBLIC ${CMAKE_CURRENT_LIST_DIR})

60
sc/heap/README.md 100644
View File

@ -0,0 +1,60 @@
### Heap
### Overview
- Min-heap implementation, it can be used as Max-heap/priority queue as well.
### Usage
```c
#include "sc_heap.h"
#include <stdio.h>
int main(int argc, char *argv[])
{
struct data {
int priority;
char *data;
};
struct data n[] = {{1, "first"},
{4, "fourth"},
{5, "fifth"},
{3, "third"},
{2, "second"}};
struct sc_heap_data *elem;
struct sc_heap heap;
sc_heap_init(&heap, 0);
// Min-heap usage
for (int i = 0; i < 5; i++) {
sc_heap_add(&heap, n[i].priority, n[i].data);
}
while ((elem = sc_heap_pop(&heap)) != NULL) {
printf("key = %d, data = %s \n",
(int) elem->key, (char*) elem->data);
}
printf("---------------- \n");
// Max-heap usage, negate when adding into heap and negate back after
// pop :
for (int i = 0; i < 5; i++) {
sc_heap_add(&heap, -(n[i].priority), n[i].data);
}
while ((elem = sc_heap_pop(&heap)) != NULL) {
printf("key = %d, data = %s \n",
(int) elem->key, (char*) elem->data);
}
sc_heap_term(&heap);
return 0;
}
```

152
sc/heap/sc_heap.c 100644
View File

@ -0,0 +1,152 @@
/*
* BSD-3-Clause
*
* Copyright 2021 Ozan Tezcan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "sc_heap.h"
#include <stdlib.h>
#ifndef SC_HEAP_MAX
#define SC_HEAP_MAX SIZE_MAX / sizeof(struct sc_heap_data)
#endif
bool sc_heap_init(struct sc_heap *h, size_t cap)
{
void *e;
const size_t sz = cap * sizeof(struct sc_heap_data);
*h = (struct sc_heap){0};
if (cap == 0) {
return true;
}
if (cap > SC_HEAP_MAX || (e = sc_heap_malloc(sz)) == NULL) {
return false;
}
h->elems = e;
h->cap = cap;
return true;
}
void sc_heap_term(struct sc_heap *h)
{
sc_heap_free(h->elems);
*h = (struct sc_heap){
.elems = NULL,
};
}
size_t sc_heap_size(struct sc_heap *h)
{
return h->size;
}
void sc_heap_clear(struct sc_heap *h)
{
h->size = 0;
}
bool sc_heap_add(struct sc_heap *h, int64_t key, void *data)
{
size_t i, cap, m;
void *exp;
if (++h->size >= h->cap) {
cap = h->cap != 0 ? h->cap * 2 : 4;
m = cap * 2 * sizeof(*h->elems);
if (h->cap >= SC_HEAP_MAX / 2 ||
(exp = sc_heap_realloc(h->elems, m)) == NULL) {
return false;
}
h->elems = exp;
h->cap = cap;
}
i = h->size;
while (i != 1 && key < h->elems[i / 2].key) {
h->elems[i] = h->elems[i / 2];
i /= 2;
}
h->elems[i].key = key;
h->elems[i].data = data;
return true;
}
struct sc_heap_data *sc_heap_peek(struct sc_heap *h)
{
if (h->size == 0) {
return NULL;
}
// Top element is always at heap->elems[1].
return &h->elems[1];
}
struct sc_heap_data *sc_heap_pop(struct sc_heap *h)
{
size_t i = 1, child = 2;
struct sc_heap_data last;
if (h->size == 0) {
return NULL;
}
// Top element is always at heap->elems[1].
h->elems[0] = h->elems[1];
last = h->elems[h->size--];
while (child <= h->size) {
if (child < h->size &&
h->elems[child].key > h->elems[child + 1].key) {
child++;
};
if (last.key <= h->elems[child].key) {
break;
}
h->elems[i] = h->elems[child];
i = child;
child *= 2;
}
h->elems[i] = last;
return &h->elems[0];
}

111
sc/heap/sc_heap.h 100644
View File

@ -0,0 +1,111 @@
/*
* BSD-3-Clause
*
* Copyright 2021 Ozan Tezcan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SC_HEAP_H
#define SC_HEAP_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#define SC_HEAP_VERSION "2.0.0"
#ifdef SC_HAVE_CONFIG_H
#include "config.h"
#else
#define sc_heap_malloc malloc
#define sc_heap_realloc realloc
#define sc_heap_free free
#endif
struct sc_heap_data {
int64_t key;
void *data;
};
struct sc_heap {
size_t cap;
size_t size;
struct sc_heap_data *elems;
};
/**
* @param h heap
* @param cap initial capacity, pass '0' for no initial memory allocation
* @return 'true' on success, 'false' on out of memory
*/
bool sc_heap_init(struct sc_heap *h, size_t cap);
/**
* Destroys heap, frees memory
* @param h heap
*/
void sc_heap_term(struct sc_heap *h);
/**
* @param h heap
* @return element count
*/
size_t sc_heap_size(struct sc_heap *h);
/**
* Clears elements from the queue, does not free the allocated memory.
* @param h heap
*/
void sc_heap_clear(struct sc_heap *h);
/**
* @param h heap
* @param key key
* @param data data
* @return 'false' on out of memory.
*/
bool sc_heap_add(struct sc_heap *h, int64_t key, void *data);
/**
* Read top element without removing from the heap.
*
* @param h heap
* @return pointer to data holder(valid until next heap operation)
* NULL if heap is empty.
*/
struct sc_heap_data *sc_heap_peek(struct sc_heap *h);
/**
* Read top element and remove it from the heap.
*
* @param h heap
* @return pointer to data holder(valid until next heap operation)
* NULL if heap is empty.
*/
struct sc_heap_data *sc_heap_pop(struct sc_heap *h);
#endif

View File

@ -0,0 +1,13 @@
cmake_minimum_required(VERSION 3.5.1)
project(sc_map C)
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF)
add_library(
sc_map STATIC
sc_map.c
sc_map.h)
target_include_directories(sc_map PUBLIC ${CMAKE_CURRENT_LIST_DIR})

108
sc/map/README.md 100644
View File

@ -0,0 +1,108 @@
### Hashmap
### Overview
- Open addressing hashmap with linear probing.
- Requires postfix naming, e.g sc_map_str, sc_map_int. It's ugly but necessary
for better performance.
- Comes with predefined key value pairs :
```
name key type value type
sc_map_of_scalar(32, uint32_t, uint32_t)
sc_map_of_scalar(64, uint64_t, uint64_t)
sc_map_of_scalar(64v, uint64_t, void *)
sc_map_of_scalar(64s, uint64_t, const char *)
sc_map_of_strkey(str, const char *, const char *)
sc_map_of_strkey(sv, const char *, void*)
sc_map_of_strkey(s64, const char *, uint64_t)
```
- This is a very fast hashmap.
- Single array allocation for all data.
- Linear probing over an array.
- Deletion without tombstones.
- Macros generate functions in sc_map.c. So, inlining is upto the compiler.
### Note
Key and value types can be integers(32bit/64bit) or pointers only.
Other types can be added but must be scalar types, not structs. This is a
design decision, I don't remember when was the last time I wanted to store
struct as a key or value. I use hashmap for fast look-ups and small key-value
pairs with linear probing play well with cache lines and hardware-prefetcher.
If you want to use structs anyway, you need to change the code a little.
### Usage
```c
#include "sc_map.h"
#include <stdio.h>
void example_str(void)
{
const char *key, *value;
struct sc_map_str map;
sc_map_init_str(&map, 0, 0);
sc_map_put_str(&map, "jack", "chicago");
sc_map_put_str(&map, "jane", "new york");
sc_map_put_str(&map, "janie", "atlanta");
sc_map_foreach (&map, key, value) {
printf("Key:[%s], Value:[%s] \n", key, value);
}
sc_map_term_str(&map);
}
void example_int_to_str(void)
{
uint32_t key;
const char *value;
struct sc_map_64s map;
sc_map_init_64s(&map, 0, 0);
sc_map_put_64s(&map, 100, "chicago");
sc_map_put_64s(&map, 200, "new york");
sc_map_put_64s(&map, 300, "atlanta");
value = sc_map_get_64s(&map, 200);
if (sc_map_found(&map)) {
printf("Found Value:[%s] \n", value);
}
value = sc_map_del_64s(&map, 100);
if (sc_map_found(&map)) {
printf("Deleted : %s \n", value);
}
sc_map_foreach (&map, key, value) {
printf("Key:[%d], Value:[%s] \n", key, value);
}
value = sc_map_del_64s(&map, 200);
if (sc_map_found(&map)) {
printf("Found : %s \n", value);
}
value = sc_map_put_64s(&map, 300, "los angeles");
if (sc_map_found(&map)) {
printf("overridden : %s \n", value);
}
sc_map_term_64s(&map);
}
int main(void)
{
example_str();
example_int_to_str();
return 0;
}
```

405
sc/map/sc_map.c 100644
View File

@ -0,0 +1,405 @@
/*
* BSD-3-Clause
*
* Copyright 2021 Ozan Tezcan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "sc_map.h"
#include <string.h>
#ifndef SC_MAP_MAX
#define SC_MAP_MAX UINT32_MAX
#endif
#define sc_map_def_strkey(name, K, V, cmp, hash_fn) \
bool sc_map_cmp_##name(struct sc_map_item_##name *t, K key, \
uint32_t hash) \
{ \
return t->hash == hash && cmp(t->key, key); \
} \
\
void sc_map_assign_##name(struct sc_map_item_##name *t, K key, \
V value, uint32_t hash) \
{ \
t->key = key; \
t->value = value; \
t->hash = hash; \
} \
\
uint32_t sc_map_hashof_##name(struct sc_map_item_##name *t) \
{ \
return t->hash; \
} \
\
sc_map_def(name, K, V, cmp, hash_fn)
#define sc_map_def_scalar(name, K, V, cmp, hash_fn) \
bool sc_map_cmp_##name(struct sc_map_item_##name *t, K key, \
uint32_t hash) \
{ \
(void) hash; \
return cmp(t->key, key); \
} \
\
void sc_map_assign_##name(struct sc_map_item_##name *t, K key, \
V value, uint32_t hash) \
{ \
(void) hash; \
t->key = key; \
t->value = value; \
} \
\
uint32_t sc_map_hashof_##name(struct sc_map_item_##name *t) \
{ \
return hash_fn(t->key); \
} \
\
sc_map_def(name, K, V, cmp, hash_fn)
#define sc_map_def(name, K, V, cmp, hash_fn) \
\
static const struct sc_map_item_##name empty_items_##name[2]; \
\
static const struct sc_map_##name sc_map_empty_##name = { \
.cap = 1, \
.mem = (struct sc_map_item_##name *) &empty_items_##name[1]}; \
\
static void *sc_map_alloc_##name(uint32_t *cap, uint32_t factor) \
{ \
uint32_t v = *cap; \
struct sc_map_item_##name *t; \
\
if (*cap > SC_MAP_MAX / factor) { \
return NULL; \
} \
\
/* Find next power of two */ \
v = v < 8 ? 8 : (v * factor); \
v--; \
for (uint32_t i = 1; i < sizeof(v) * 8; i *= 2) { \
v |= v >> i; \
} \
v++; \
\
*cap = v; \
t = sc_map_calloc(sizeof(*t), v + 1); \
return t ? &t[1] : NULL; \
} \
\
bool sc_map_init_##name(struct sc_map_##name *m, uint32_t cap, \
uint32_t load_fac) \
{ \
void *t; \
uint32_t f = (load_fac == 0) ? 75 : load_fac; \
\
if (f > 95 || f < 25) { \
return false; \
} \
\
if (cap == 0) { \
*m = sc_map_empty_##name; \
m->load_fac = f; \
return true; \
} \
\
t = sc_map_alloc_##name(&cap, 1); \
if (t == NULL) { \
return false; \
} \
\
m->mem = t; \
m->size = 0; \
m->used = false; \
m->cap = cap; \
m->load_fac = f; \
m->remap = (uint32_t) (m->cap * ((double) m->load_fac / 100)); \
\
return true; \
} \
\
void sc_map_term_##name(struct sc_map_##name *m) \
{ \
if (m->mem != sc_map_empty_##name.mem) { \
sc_map_free(&m->mem[-1]); \
*m = sc_map_empty_##name; \
} \
} \
\
uint32_t sc_map_size_##name(struct sc_map_##name *m) \
{ \
return m->size; \
} \
\
void sc_map_clear_##name(struct sc_map_##name *m) \
{ \
if (m->size > 0) { \
for (uint32_t i = 0; i < m->cap; i++) { \
m->mem[i].key = 0; \
} \
\
m->used = false; \
m->size = 0; \
} \
} \
\
static bool sc_map_remap_##name(struct sc_map_##name *m) \
{ \
uint32_t pos, cap, mod; \
struct sc_map_item_##name *new; \
\
if (m->size < m->remap) { \
return true; \
} \
\
cap = m->cap; \
new = sc_map_alloc_##name(&cap, 2); \
if (new == NULL) { \
return false; \
} \
\
mod = cap - 1; \
\
for (uint32_t i = 0; i < m->cap; i++) { \
if (m->mem[i].key != 0) { \
pos = sc_map_hashof_##name(&m->mem[i]) & mod; \
\
while (true) { \
if (new[pos].key == 0) { \
new[pos] = m->mem[i]; \
break; \
} \
\
pos = (pos + 1) & (mod); \
} \
} \
} \
\
if (m->mem != sc_map_empty_##name.mem) { \
new[-1] = m->mem[-1]; \
sc_map_free(&m->mem[-1]); \
} \
\
m->mem = new; \
m->cap = cap; \
m->remap = (uint32_t) (m->cap * ((double) m->load_fac / 100)); \
\
return true; \
} \
\
V sc_map_put_##name(struct sc_map_##name *m, K key, V value) \
{ \
V ret; \
uint32_t pos, mod, h; \
\
m->oom = false; \
\
if (!sc_map_remap_##name(m)) { \
m->oom = true; \
return 0; \
} \
\
if (key == 0) { \
ret = (m->used) ? m->mem[-1].value : 0; \
m->found = m->used; \
m->size += !m->used; \
m->used = true; \
m->mem[-1].value = value; \
\
return ret; \
} \
\
mod = m->cap - 1; \
h = hash_fn(key); \
pos = h & (mod); \
\
while (true) { \
if (m->mem[pos].key == 0) { \
m->size++; \
} else if (!sc_map_cmp_##name(&m->mem[pos], key, h)) { \
pos = (pos + 1) & (mod); \
continue; \
} \
\
m->found = m->mem[pos].key != 0; \
ret = m->found ? m->mem[pos].value : 0; \
sc_map_assign_##name(&m->mem[pos], key, value, h); \
\
return ret; \
} \
} \
\
/** NOLINTNEXTLINE */ \
V sc_map_get_##name(struct sc_map_##name *m, K key) \
{ \
const uint32_t mod = m->cap - 1; \
uint32_t h, pos; \
\
if (key == 0) { \
m->found = m->used; \
return m->used ? m->mem[-1].value : 0; \
} \
\
h = hash_fn(key); \
pos = h & mod; \
\
while (true) { \
if (m->mem[pos].key == 0) { \
m->found = false; \
return 0; \
} else if (!sc_map_cmp_##name(&m->mem[pos], key, h)) { \
pos = (pos + 1) & (mod); \
continue; \
} \
\
m->found = true; \
return m->mem[pos].value; \
} \
} \
\
/** NOLINTNEXTLINE */ \
V sc_map_del_##name(struct sc_map_##name *m, K key) \
{ \
const uint32_t mod = m->cap - 1; \
uint32_t pos, prev, it, p, h; \
V ret; \
\
if (key == 0) { \
m->found = m->used; \
m->size -= m->used; \
m->used = false; \
\
return m->found ? m->mem[-1].value : 0; \
} \
\
h = hash_fn(key); \
pos = h & (mod); \
\
while (true) { \
if (m->mem[pos].key == 0) { \
m->found = false; \
return 0; \
} else if (!sc_map_cmp_##name(&m->mem[pos], key, h)) { \
pos = (pos + 1) & (mod); \
continue; \
} \
\
m->found = true; \
ret = m->mem[pos].value; \
\
m->size--; \
m->mem[pos].key = 0; \
prev = pos; \
it = pos; \
\
while (true) { \
it = (it + 1) & (mod); \
if (m->mem[it].key == 0) { \
break; \
} \
\
p = sc_map_hashof_##name(&m->mem[it]) & (mod); \
\
if ((p > it && (p <= prev || it >= prev)) || \
(p <= prev && it >= prev)) { \
\
m->mem[prev] = m->mem[it]; \
m->mem[it].key = 0; \
prev = it; \
} \
} \
\
return ret; \
} \
}
static uint32_t sc_map_hash_32(uint32_t a)
{
return a;
}
static uint32_t sc_map_hash_64(uint64_t a)
{
return ((uint32_t) a) ^ (uint32_t) (a >> 32u);
}
// clang-format off
uint32_t murmurhash(const char *key)
{
const uint64_t m = UINT64_C(0xc6a4a7935bd1e995);
const size_t len = strlen(key);
const unsigned char* p = (const unsigned char*) key;
const unsigned char *end = p + (len & ~(uint64_t) 0x7);
uint64_t h = (len * m);
while (p != end) {
uint64_t k;
memcpy(&k, p, sizeof(k));
k *= m;
k ^= k >> 47u;
k *= m;
h ^= k;
h *= m;
p += 8;
}
switch (len & 7u) {
case 7: h ^= (uint64_t) p[6] << 48ul; // fall through
case 6: h ^= (uint64_t) p[5] << 40ul; // fall through
case 5: h ^= (uint64_t) p[4] << 32ul; // fall through
case 4: h ^= (uint64_t) p[3] << 24ul; // fall through
case 3: h ^= (uint64_t) p[2] << 16ul; // fall through
case 2: h ^= (uint64_t) p[1] << 8ul; // fall through
case 1: h ^= (uint64_t) p[0]; // fall through
h *= m;
default:
break;
};
h ^= h >> 47u;
h *= m;
h ^= h >> 47u;
return (uint32_t) h;
}
#define sc_map_eq(a, b) ((a) == (b))
#define sc_map_streq(a, b) (!strcmp(a, b))
// name, key type, value type, cmp hash
sc_map_def_scalar(32, uint32_t, uint32_t, sc_map_eq, sc_map_hash_32)
sc_map_def_scalar(64, uint64_t, uint64_t, sc_map_eq, sc_map_hash_64)
sc_map_def_scalar(64v, uint64_t, void *, sc_map_eq, sc_map_hash_64)
sc_map_def_scalar(64s, uint64_t, const char *, sc_map_eq, sc_map_hash_64)
sc_map_def_strkey(str, const char *, const char *, sc_map_streq, murmurhash)
sc_map_def_strkey(sv, const char *, void *, sc_map_streq, murmurhash)
sc_map_def_strkey(s64, const char *, uint64_t, sc_map_streq, murmurhash)
// clang-format on

225
sc/map/sc_map.h 100644
View File

@ -0,0 +1,225 @@
/*
* BSD-3-Clause
*
* Copyright 2021 Ozan Tezcan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SC_MAP_H
#define SC_MAP_H
#include <memory.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#define SC_MAP_VERSION "2.0.0"
#ifdef SC_HAVE_CONFIG_H
#include "config.h"
#else
#define sc_map_calloc calloc
#define sc_map_free free
#endif
#define sc_map_dec_strkey(name, K, V) \
struct sc_map_item_##name { \
K key; \
V value; \
uint32_t hash; \
}; \
\
sc_map_of(name, K, V)
#define sc_map_dec_scalar(name, K, V) \
struct sc_map_item_##name { \
K key; \
V value; \
}; \
\
sc_map_of(name, K, V)
#define sc_map_of(name, K, V) \
struct sc_map_##name { \
struct sc_map_item_##name *mem; \
uint32_t cap; \
uint32_t size; \
uint32_t load_fac; \
uint32_t remap; \
bool used; \
bool oom; \
bool found; \
}; \
\
/** \
* Create map \
* \
* @param map map \
* @param cap initial capacity, zero is accepted \
* @param load_factor must be >25 and <95. Pass 0 for default value. \
* @return 'true' on success, \
* 'false' on out of memory or if 'load_factor' value is \
* invalid. \
*/ \
bool sc_map_init_##name(struct sc_map_##name *map, uint32_t cap, \
uint32_t load_factor); \
\
/** \
* Destroy map. \
* \
* @param map map \
*/ \
void sc_map_term_##name(struct sc_map_##name *map); \
\
/** \
* Get map element count \
* \
* @param map map \
* @return element count \
*/ \
uint32_t sc_map_size_##name(struct sc_map_##name *map); \
\
/** \
* Clear map \
* \
* @param map map \
*/ \
void sc_map_clear_##name(struct sc_map_##name *map); \
\
/** \
* Put element to the map \
* \
* struct sc_map_str map; \
* sc_map_put_str(&map, "key", "value"); \
* \
* @param map map \
* @param K key \
* @param V value \
* @return previous value if exists \
* call sc_map_found() to see if the returned value is valid. \
*/ \
V sc_map_put_##name(struct sc_map_##name *map, K key, V val); \
\
/** \
* Get element \
* \
* @param map map \
* @param K key \
* @return current value if exists. \
* call sc_map_found() to see if the returned value is valid. \
*/ \
/** NOLINTNEXTLINE */ \
V sc_map_get_##name(struct sc_map_##name *map, K key); \
\
/** \
* Delete element \
* \
* @param map map \
* @param K key \
* @return current value if exists. \
* call sc_map_found() to see if the returned value is valid. \
*/ \
/** NOLINTNEXTLINE */ \
V sc_map_del_##name(struct sc_map_##name *map, K key);
/**
* @param map map
* @return - if put operation overrides a value, returns true
* - if get operation finds the key, returns true
* - if del operation deletes a key, returns true
*/
#define sc_map_found(map) ((map)->found)
/**
* @param map map
* @return true if put operation failed with out of memory
*/
#define sc_map_oom(map) ((map)->oom)
// clang-format off
/**
* Foreach loop
*
* char *key, *value;
* struct sc_map_str map;
*
* sc_map_foreach(&map, key, value) {
* printf("key = %s, value = %s \n");
* }
*/
#define sc_map_foreach(map, K, V) \
for (int64_t _i = -1, _b = 0; !_b && _i < (map)->cap; _i++) \
for ((V) = (map)->mem[_i].value, (K) = (map)->mem[_i].key, _b = 1; \
_b && ((_i == -1 && (map)->used) || (K) != 0) ? 1 : (_b = 0); \
_b = 0)
/**
* Foreach loop for keys
*
* char *key;
* struct sc_map_str map;
*
* sc_map_foreach_key(&map, key) {
* printf("key = %s \n");
* }
*/
#define sc_map_foreach_key(map, K) \
for (int64_t _i = -1, _b = 0; !_b && _i < (map)->cap; _i++) \
for ((K) = (map)->mem[_i].key, _b = 1; \
_b && ((_i == -1 && (map)->used) || (K) != 0) ? 1 : (_b = 0); \
_b = 0)
/**
* Foreach loop for values
*
* char *value;
* struct sc_map_str map;
*
* sc_map_foreach_value(&map, value) {
* printf("value = %s \n");
* }
*/
#define sc_map_foreach_value(map, V) \
for (int64_t _i = -1, _b = 0; !_b && _i < (map)->cap; _i++) \
for ((V) = (map)->mem[_i].value, _b = 1; \
_b && ((_i == -1 && (map)->used) || (map)->mem[_i].key != 0) ? 1 : (_b = 0); \
_b = 0)
// name key type value type
sc_map_dec_scalar(32, uint32_t, uint32_t)
sc_map_dec_scalar(64, uint64_t, uint64_t)
sc_map_dec_scalar(64v, uint64_t, void *)
sc_map_dec_scalar(64s, uint64_t, const char *)
sc_map_dec_strkey(str, const char *, const char *)
sc_map_dec_strkey(sv, const char *, void*)
sc_map_dec_strkey(s64, const char *, uint64_t)
// clang-format on
#endif

View File

@ -0,0 +1,14 @@
cmake_minimum_required(VERSION 3.5.1)
project(sc_queue C)
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF)
add_library(
sc_queue STATIC
sc_queue.h)
set_target_properties(sc_queue PROPERTIES LINKER_LANGUAGE C)
target_include_directories(sc_queue PUBLIC ${CMAKE_CURRENT_LIST_DIR})

45
sc/queue/README.md 100644
View File

@ -0,0 +1,45 @@
### Generic queue
### Overview
- Queue implementation which grows when you add elements.
- Add/remove from head/tail is possible so it can be used as list, stack,
queue, dequeue etc.
- It comes with predefined types, check out at the end of sc_queue.h, you can
add there (sc_queue_def) if you need more.
### Usage
```c
#include "sc_queue.h"
#include <stdio.h>
int main(int argc, char *argv[])
{
int elem;
struct sc_queue_int queue;
sc_queue_init(&queue);
sc_queue_add_last(&queue, 2);
sc_queue_add_last(&queue, 3);
sc_queue_add_last(&queue, 4);
sc_queue_add_first(&queue, 1);
sc_queue_foreach (&queue, elem) {
printf("elem = [%d] \n", elem);
}
elem = sc_queue_del_last(&queue);
printf("Last element was : [%d] \n", elem);
elem = sc_queue_del_first(&queue);
printf("First element was : [%d] \n", elem);
sc_queue_term(&queue);
return 0;
}
```

287
sc/queue/sc_queue.h 100644
View File

@ -0,0 +1,287 @@
/*
* BSD-3-Clause
*
* Copyright 2021 Ozan Tezcan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SC_QUEUE_H
#define SC_QUEUE_H
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#define SC_QUEUE_VERSION "2.0.0"
#ifdef SC_HAVE_CONFIG_H
#include "config.h"
#else
#define sc_queue_calloc calloc
#define sc_queue_free free
#endif
#ifndef SC_QUEUE_MAX
#define SC_QUEUE_MAX (SIZE_MAX)
#endif
#define sc_queue_def(T, name) \
struct sc_queue_##name { \
bool oom; \
size_t cap; \
size_t first; \
size_t last; \
/* NOLINTNEXTLINE */ \
T *elems; \
}
#define sc_queue_expand(q) \
do { \
size_t _cap, _len, _off; \
size_t _pos = ((q)->last + 1) & ((q)->cap - 1); \
void *_dst, *_src; \
\
if (_pos == (q)->first) { \
if ((q)->cap > SC_QUEUE_MAX / 2ul) { \
(q)->oom = true; \
break; \
} \
_cap = (q)->cap * 2; \
_dst = sc_queue_calloc(_cap, sizeof(*((q)->elems))); \
if (_dst == NULL) { \
(q)->oom = true; \
break; \
} \
_len = ((q)->cap - (q)->first) * sizeof(*(q)->elems); \
_off = ((q)->first * sizeof(*((q)->elems))); \
_src = ((char *) (q)->elems) + _off; \
\
memcpy(_dst, _src, _len); \
memcpy(((char *) _dst) + _len, (q)->elems, _off); \
(q)->oom = false; \
(q)->last = (q)->cap - 1; \
(q)->first = 0; \
(q)->cap = _cap; \
sc_queue_free((q)->elems); \
(q)->elems = _dst; \
} \
} while (0)
/**
* Init queue. Call sc_queue_oom(q) to see if memory allocation succeeded.
* @param q queue
*/
#define sc_queue_init(q) \
do { \
(q)->oom = false; \
(q)->cap = 8; \
(q)->first = 0; \
(q)->last = 0; \
(q)->elems = sc_queue_calloc(1, sizeof(*(q)->elems) * 8); \
if ((q)->elems == NULL) { \
(q)->oom = true; \
} \
} while (0)
/**
* Term queue
* @param q queue
*/
#define sc_queue_term(q) \
do { \
sc_queue_free((q)->elems); \
(q)->elems = NULL; \
(q)->cap = 0; \
(q)->first = 0; \
(q)->last = 0; \
(q)->oom = false; \
} while (0)
/**
* @param q queue
* @return true if last add operation failed, false otherwise.
*/
#define sc_queue_oom(q) ((q)->oom)
/**
* @param q queue
* @return element count
*/
#define sc_queue_size(q) (((q)->last - (q)->first) & ((q)->cap - 1))
/**
* Clear the queue without deallocating underlying memory.
* @param q queue
*/
#define sc_queue_clear(q) \
do { \
(q)->first = 0; \
(q)->last = 0; \
(q)->oom = false; \
} while (0)
/**
* @param q queue
* @return true if queue is empty
*/
#define sc_queue_empty(q) (((q)->last == (q)->first))
/**
* @param q queue
* @return index of the first element. If queue is empty, result is undefined.
*/
#define sc_queue_first(q) ((q)->first)
/**
* @param q queue
* @return index of the last element. If queue is empty, result is undefined.
*/
#define sc_queue_last(q) ((q)->last)
/**
* @param q queue
* @param i index
* @return index of the next element after i, if i is the last element,
* result is undefined.
*/
#define sc_queue_next(q, i) (((i) + 1) & ((q)->cap - 1))
/**
* Returns element at index 'i', so regular loops are possible :
*
* for (size_t i = 0; i < sc_queue_size(q); i++) {
* printf("%d" \n, sc_queue_at(q, i));
* }
*
* @param q queue
* @return element at index i
*/
#define sc_queue_at(q, i) (q)->elems[(((q)->first) + (i)) & ((q)->cap - 1)]
/**
* @param q queue
* @return peek first element, if queue is empty, result is undefined
*/
#define sc_queue_peek_first(q) ((q)->elems[(q)->first])
/**
* @param q queue
* @return peek last element, if queue is empty, result is undefined
*/
#define sc_queue_peek_last(q) (q)->elems[((q)->last - 1) & ((q)->cap - 1)]
/**
* Call sc_queue_oom(q) after this function to check out of memory condition.
*
* @param q queue
* @param elem elem to be added at the end of the list
*/
#define sc_queue_add_last(q, elem) \
do { \
sc_queue_expand(q); \
if ((q)->oom) { \
break; \
} \
(q)->oom = false; \
(q)->elems[(q)->last] = elem; \
(q)->last = ((q)->last + 1) & ((q)->cap - 1); \
} while (0)
/**
* @param q queue
* @return delete the last element from the queue and return its value.
* If queue is empty, result is undefined.
*/
#define sc_queue_del_last(q) \
((q)->elems[((q)->last = ((q)->last - 1) & ((q)->cap - 1))])
/**
* Call sc_queue_oom(q) after this function to check out of memory condition.
*
* @param q queue.
* @param elem elem to be added at the head of the list.
*/
#define sc_queue_add_first(q, elem) \
do { \
sc_queue_expand(q); \
if ((q)->oom) { \
break; \
} \
(q)->oom = false; \
(q)->first = ((q)->first - 1) & ((q)->cap - 1); \
(q)->elems[(q)->first] = elem; \
} while (0)
static inline size_t sc_queue_inc_first(size_t *first, size_t cap)
{
size_t tmp = *first;
*first = (*first + 1) & (cap - 1);
return tmp;
}
/**
* @param q queue
* @return delete the first element from the queue and return its value.
* If queue is empty, result is undefined.
*/
#define sc_queue_del_first(q) \
(q)->elems[sc_queue_inc_first(&(q)->first, (q)->cap)]
/**
* For each loop,
*
* int *queue;
* sc_queue_create(queue, 4);"
*
* int elem;
* sc_queue_foreach(queue, elem) {
* printf("Elem : %d \n, elem);
* }
*/
#define sc_queue_foreach(q, elem) \
for (size_t _k = 1, _i = sc_queue_first(q); \
_k && _i != sc_queue_last(q); \
_k = !_k, _i = sc_queue_next(q, _i)) \
for ((elem) = (q)->elems[_i]; _k; _k = !_k)
// (type, name)
sc_queue_def(int, int);
sc_queue_def(unsigned int, uint);
sc_queue_def(long, long);
sc_queue_def(unsigned long, ulong);
sc_queue_def(unsigned long long, ull);
sc_queue_def(uint32_t, 32);
sc_queue_def(uint64_t, 64);
sc_queue_def(double, double);
sc_queue_def(const char *, str);
sc_queue_def(void *, ptr);
#endif