First Commit of HATPC remake
Internal Changelog: - Implement Basic ECS - Tag system not yet implemented - Use SC for common data structuresscene_man
commit
17aae0617c
|
@ -0,0 +1,3 @@
|
|||
.cache/
|
||||
build/
|
||||
compile_commands.json
|
|
@ -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
|
||||
)
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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)
|
|
@ -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.
|
|
@ -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 ()
|
|
@ -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;
|
||||
}
|
||||
```
|
|
@ -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
|
|
@ -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})
|
|
@ -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;
|
||||
}
|
||||
```
|
|
@ -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];
|
||||
}
|
|
@ -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
|
|
@ -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})
|
|
@ -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;
|
||||
}
|
||||
```
|
|
@ -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
|
|
@ -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
|
|
@ -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})
|
|
@ -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;
|
||||
}
|
||||
```
|
|
@ -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
|
Loading…
Reference in New Issue