From 0b8e3382efc8adcaa74b09019fbbaa561c5b8bd8 Mon Sep 17 00:00:00 2001 From: "Magomed Kostoev (mkostoevr)" Date: Mon, 22 Feb 2021 08:19:58 +0000 Subject: [PATCH] Add one more clink's dependency git-svn-id: svn://kolibrios.org@8601 a494cfbc-eb01-0410-851d-a64ba20cac60 --- programs/develop/clink/cdict/LICENSE | 21 ++ programs/develop/clink/cdict/README.md | 88 +++++++ programs/develop/clink/cdict/cdict.h | 350 +++++++++++++++++++++++++ 3 files changed, 459 insertions(+) create mode 100644 programs/develop/clink/cdict/LICENSE create mode 100644 programs/develop/clink/cdict/README.md create mode 100644 programs/develop/clink/cdict/cdict.h diff --git a/programs/develop/clink/cdict/LICENSE b/programs/develop/clink/cdict/LICENSE new file mode 100644 index 0000000000..b1520f0de1 --- /dev/null +++ b/programs/develop/clink/cdict/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Magomed Kostoev + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/programs/develop/clink/cdict/README.md b/programs/develop/clink/cdict/README.md new file mode 100644 index 0000000000..fef8f3a18f --- /dev/null +++ b/programs/develop/clink/cdict/README.md @@ -0,0 +1,88 @@ +# CDict - a simple dictionary implementation in C +## Ready to use! + +It may be used out of the box with key and value of type `CStr` (which is `char *` - zero-terminated string). Once it instantiated in some file using `CDICT_INST` definition: + +```C +#define CDICT_INST +#include "cdict.h" +``` + +It may be then declared just using `#include`: + +```C +#include "cdict.h" + +int main() { + CDict_CStr_CStr dict; + if (!cdict_CStr_CStr_init(&dict)) { + printf("CDict returned error #%d", dict.error_code); + return 0; + } + cdict_CStr_CStr_add_vv(&dict, "key_a", "value_a", CDICT_REPLACE_EXIST); + printf("[key_a] = \"%s\"\n", cdict_CStr_CStr_get_v(&dict, "key_a")); +} +``` + +## Easy to configure! + +If you want to create a dictionary for other key types you should provide your own keys hashing and comparsion functions, in such case the instantiation of the library will look like this: + +```C +#define CDICT_INST +#define CDICT_KEY_T MyType +#define CDICT_HASH_FN(pkey) my_hash(pkey) +#define CDICT_CMP_FN(pkey0, pkey1) my_cmp(pkey0, pkey1) +#include "cdict.h" + +int my_cmp(MyType *pkey0, MyType *pkey1) { + // Return `whatever_negative` if `key0 < key1`, `0` if `key0 == key1` and `whatever_positive` if `key0 > key1` +} + +unsigned long my_hash(MyType *key) { + // Return the hash of the key +} +``` + +Then to use the new dictionary you only need to define key type before the header inclusion: + +```C +#define CDICT_KEY_T MyType +#include "cdict.h" + +// ... + CDict_MyType_CStr dict; + cdict_MyType_CStr_init(&dict); +// ... +``` + +If you want to specify the type of values - just define the type: + +```C +#define CDICT_VAL_T MyValueType +``` + +And so on. + +## Dependency-free! + +Every single used dependency may be redefined: + +```C +#define CDICT_ASSERT_FN(x) my_assert(x); +``` + +## Flexible! + +May define user data to be used in overriden functions (for example - custom allocators): + +```C +#define CDICT_USER_DATA_T UserData +#define CDICT_HASHTAB_ITEM_ALLOC_FN(cdict, size) item_alloc(cdict, size) +#define CDICT_HASHTAB_ITEM_FREE_FN(cdict, ptr) item_free(cdict, ptr) +#define CDICT_HASHTAB_ALLOC_FN(cdict, size) hashtab_alloc(cdict, size) +``` + +## Checkout + +[The library](cdict.h). diff --git a/programs/develop/clink/cdict/cdict.h b/programs/develop/clink/cdict/cdict.h new file mode 100644 index 0000000000..4d375492bf --- /dev/null +++ b/programs/develop/clink/cdict/cdict.h @@ -0,0 +1,350 @@ +// Copyright (c) 2020 Magomed Kostoev +// +// You may use, distribute and modify this code under the terms of the MIT license. +// +// You should have received a copy of the MIT license with this file. If not, please visit +// https://opensource.org/licenses/MIT or boppan.org/MIT for full license details. + +// cdict.h - simple dictionaty implementation in C. +// +// Uses hash table with fixed (but configurable) size. Functions named using given type names: +// cdict___. +// +// See configuration below (in public "Input macros" section), also: +// CDICT_INST: Instantiate the functions if defined. +// +// Minimal definitions for declaration: CDICT_KEY_T, CDICT_VAL_T +// Minimal definitions for instantiation: CDICT_INST, CDICT_KEY_T, CDICT_VAL_T, CDICT_HASH_FN, +// CDICT_CMP_FN +// +// WARNING: All used definitions will be undefined on header exit. +// +// Dependencies: +// or another source of size_t +// or another source of malloc, and calloc +// or another source of assert + +// +// Internal macros for external declarations +// + +#define CDICT_CAT2_IMPL(x, y) x ## _ ## y +#define CDICT_CAT2(x, y) CDICT_CAT2_IMPL(x, y) + +/// Creates method name according to CDICT_KEY_T and CDICT_VAL_T +#define CDICT_FUN(name) CDICT_CAT2(cdict, CDICT_CAT2(CDICT_KEY_T, CDICT_CAT2(CDICT_VAL_T, name))) + +#define cdict_init CDICT_FUN(init) +#define cdict_init_ud CDICT_FUN(init_ud) +#define cdict_init_pud CDICT_FUN(init_pud) +#define cdict_add_pp CDICT_FUN(add_pp) +#define cdict_add_vv CDICT_FUN(add_vv) +#define cdict_get_p CDICT_FUN(get_p) +#define cdict_get_v CDICT_FUN(get_v) +#define cdict_error_message CDICT_FUN(error_message) + +#define cdict_hash CDICT_FUN(hash) +#define cdict_keycmp CDICT_FUN(keycmp) +#define cdict_chain_begin CDICT_FUN(begin) +#define cdict_chain_next CDICT_FUN(next) + +#define CDictItem CDICT_CAT2(CDictItem, CDICT_CAT2(CDICT_KEY_T, CDICT_VAL_T)) +#define CDictItem_s CDICT_CAT2(CDictItem_s, CDICT_CAT2(CDICT_KEY_T, CDICT_VAL_T)) +#define CDict CDICT_CAT2(CDict, CDICT_CAT2(CDICT_KEY_T, CDICT_VAL_T)) + +// +// Input macros +// + +/// Type of the dictionary's keys, named type only - used in functions names, default: CStr +#ifndef CDICT_KEY_T +typedef char *CStr; +#define CDICT_CSTR_IS_THERE +#define CDICT_KEY_T CStr +#endif + +/// Type of the dictionary's values, named type only - used in functions names, default: CStr +#ifndef CDICT_VAL_T +#ifndef CDICT_CSTR_IS_THERE +typedef char *CStr; +#endif +#define CDICT_VAL_T CStr +#endif + +/// Type of user data +#ifndef CDICT_USER_DATA_T +#define CDICT_USER_DATA_T void * +#endif + +// +// Public interface +// + +#define CDICT_NO_CHECK 0 +#define CDICT_REPLACE_EXIST 1 +#define CDICT_LEAVE_EXIST 2 + +#define CDICT_ERR_SUCCESS 0 +#define CDICT_ERR_OUT_OF_MEMORY 1 +#define CDICT_ERR_THIS_IS_NULL 2 +#define CDICT_ERR_HASH_TABLE_IS_NULL 3 +#define CDICT_ERR_PKEY0_IS_NULL 4 +#define CDICT_ERR_PKEY1_IS_NULL 5 +#define CDICT_ERR_PKEY_IS_NULL 6 +#define CDICT_ERR_PVAL_IS_NULL 7 + +typedef struct CDictItem_s { + struct CDictItem_s *next_collision; + CDICT_KEY_T key; + CDICT_VAL_T val; +} CDictItem; + +typedef struct { + CDictItem **hash_table; + CDICT_USER_DATA_T user_data; + int error_code; +} CDict; + +/// Initializes dictionary structure +int cdict_init(CDict *s); + +/// Initializes dictionary structure with non-standard user data +int cdict_init_ud(CDict *s, CDICT_USER_DATA_T user_data); + +/// Initializes dictionary structure with non-standard user data +int cdict_init_pud(CDict *s, CDICT_USER_DATA_T *user_data); + +/// Inserts a value by key (receives pointers to val and key) +CDictItem *cdict_add_pp(CDict *s, CDICT_KEY_T *pkey, CDICT_VAL_T *pval, int if_exists); + +/// Inserts a value by key (receives values of val and key) +CDictItem *cdict_add_vv(CDict *s, CDICT_KEY_T key, CDICT_VAL_T val, int if_exists); + +/// Gives a value by key (receives a pointer to key) +CDICT_VAL_T cdict_get_p(CDict *s, CDICT_KEY_T *pkey); + +/// Gives a vaule by key (receives a value of key) +CDICT_VAL_T cdict_get_v(CDict *s, CDICT_KEY_T key); + +#ifdef CDICT_INST + +// +// Input macros (instantiation edition) +// + +/// The value returned on some error +#ifndef CDICT_VAL_DEFAULT +#define CDICT_VAL_DEFAULT (CDICT_VAL_T){ 0 } +#endif + +/// Hashing function for the key type +#ifndef CDICT_HASH_FN +#include +#define CDICT_HASH_FN(pkey) strlen(*pkey) ^ (*pkey)[0] +#else +#define CDICT_HASH_FN_OVERRIDEN +#endif + +/// Ammount of pointers to elements in hash table +#ifndef CDICT_HASHTAB_SZ +#define CDICT_HASHTAB_SZ 1024 +#endif + +/// Hash table allocator MUST return zeroed buffer +#ifndef CDICT_HASHTAB_ALLOC_FN +#include +#define CDICT_HASHTAB_ALLOC_FN(cdict, size) calloc(1, size) +#else +#define CDICT_HASHTAB_ALLOCATORS_OVERRIDDEN +#endif + +/// New hash table items allocator (should be just a function call) +#ifndef CDICT_HASHTAB_ITEM_ALLOC_FN +#include +#define CDICT_HASHTAB_ITEM_ALLOC_FN(cdict, size) malloc(size) +#define CDICT_HASHTAB_ITEM_FREE_FN(cdict, ptr) free(ptr) +#else +#ifndef CDICT_HASHTAB_ITEM_FREE_FN +#error "CDICT_HASHTAB_ITEM_FREE_FN should be defiend along with CDICT_HASHTAB_ITEM_ALLOC_FN" +#endif +#define CDICT_HASHTAB_ITEM_ALLOCATORS_OVERRIDDEN +#endif + +#ifdef CDICT_HASHTAB_ITEM_ALLOCATORS_OVERRIDDEN +#error "FUCK!" +#endif + +/// Replacement for assert from +#ifndef CDICT_ASSERT_FN +#include +#define CDICT_ASSERT_FN(x) if (!x) { printf(__FILE__":%d: Disasserted", __LINE__); } assert(x) +#endif + +/// Function for comparsion of keys, return should be the same as memcmp +#ifndef CDICT_CMP_FN +#include +#define CDICT_CMP_FN(pkey0, pkey1) strcmp(*pkey0, *pkey1) +#else +#define CDICT_CMP_FN_OVERRIDDEN +#endif + +// +// Internal macros (instantiation edition) +// + +#define CDICT_ASSERT(x) ({ CDICT_ASSERT_FN(x); x; }) +// Should write the error code into strucure, but should not rewrite it's already set +#define CDICT_IF_NULL_SET_ERR_RETURN(x, ec, res) if (x == NULL) { \ + if (s->error_code == CDICT_ERR_SUCCESS) s->error_code = ec; \ + return res; \ + } +#define CDICT_IF_NULL_RETURN(x, res) if (x == NULL) return res + +// +// Predeclarations +// + +#ifdef CDICT_HASHTAB_ITEM_ALLOCATORS_OVERRIDDEN +void *CDICT_HASHTAB_ITEM_ALLOC_FN(CDict *s, size_t size); +void CDICT_HASHTAB_ITEM_FREE_FN(CDict *s, CDictItem *ptr); +#endif + +#ifdef CDICT_HASHTAB_ALLOCATORS_OVERRIDDEN +void *CDICT_HASHTAB_ALLOC_FN(CDict *s, size_t size); +#endif + +#ifdef CDICT_HASH_FN_OVERRIDEN +unsigned long CDICT_HASH_FN(CDICT_KEY_T *pkey); +#endif + +#ifdef CDICT_CMP_FN_OVERRIDDEN +int CDICT_CMP_FN(CDICT_KEY_T *pkey0, CDICT_KEY_T *pkey1); +#endif + +// +// The code +// + +static size_t cdict_hash(CDICT_KEY_T *pkey) { + return CDICT_HASH_FN(CDICT_ASSERT(pkey)) & (CDICT_HASHTAB_SZ - 1); +} + +static int cdict_keycmp(CDICT_KEY_T *pkey0, CDICT_KEY_T *pkey1) { + return CDICT_CMP_FN(CDICT_ASSERT(pkey0), CDICT_ASSERT(pkey1)); +} + +static CDictItem **cdict_chain_begin(CDict *s, CDICT_KEY_T *pkey) { + CDICT_ASSERT(s); + size_t hash = cdict_hash(CDICT_ASSERT(pkey)); + CDICT_IF_NULL_SET_ERR_RETURN(s->hash_table, CDICT_ERR_HASH_TABLE_IS_NULL, NULL); + return &s->hash_table[hash]; +} + +static CDictItem **cdict_chain_next(CDictItem **ppit) { + CDICT_ASSERT(ppit); + CDICT_ASSERT(*ppit); + return &(*ppit)->next_collision; +} + +int cdict_init(CDict *s) { + return cdict_init_pud(s, NULL); +} + +int cdict_init_ud(CDict *s, CDICT_USER_DATA_T user_data) { + return cdict_init_pud(s, &user_data); +} + +int cdict_init_pud(CDict *s, CDICT_USER_DATA_T *user_data) { + CDICT_IF_NULL_RETURN(s, 0); + s->user_data = user_data ? *user_data : (CDICT_USER_DATA_T){ 0 }; + s->error_code = CDICT_ERR_SUCCESS; + s->hash_table = CDICT_HASHTAB_ALLOC_FN(s, sizeof(*s->hash_table) * CDICT_HASHTAB_SZ); + CDICT_IF_NULL_SET_ERR_RETURN(s->hash_table, CDICT_ERR_OUT_OF_MEMORY, 0); + return 1; +} + +CDictItem *cdict_add_pp(CDict *s, CDICT_KEY_T *pkey, CDICT_VAL_T *pval, int if_exists) { + CDICT_IF_NULL_RETURN(s, NULL); + CDICT_IF_NULL_SET_ERR_RETURN(pkey, CDICT_ERR_PKEY_IS_NULL, NULL); + CDICT_IF_NULL_SET_ERR_RETURN(pval, CDICT_ERR_PVAL_IS_NULL, NULL); + CDictItem *next_collision = NULL; + CDictItem **ppit = cdict_chain_begin(s, pkey); + CDICT_IF_NULL_RETURN(ppit, NULL); + while (*ppit) { + int exists = if_exists == CDICT_NO_CHECK ? 0 : !cdict_keycmp(pkey, &(*ppit)->key); + if (exists) { + if (if_exists == CDICT_LEAVE_EXIST) { + return *ppit; + } else if (if_exists == CDICT_REPLACE_EXIST) { + next_collision = (*ppit)->next_collision; + CDICT_HASHTAB_ITEM_FREE_FN(s, *ppit); + break; + } + } + ppit = cdict_chain_next(ppit); + } + *ppit = CDICT_HASHTAB_ITEM_ALLOC_FN(s, sizeof(**ppit)); + CDictItem *pit = *ppit; + pit->key = *pkey; + pit->val = *pval; + pit->next_collision = next_collision; + return pit; +} + +CDictItem *cdict_add_vv(CDict *s, CDICT_KEY_T key, CDICT_VAL_T val, int if_exists) { + return cdict_add_pp(s, &key, &val, if_exists); +} + +CDICT_VAL_T cdict_get_p(CDict *s, CDICT_KEY_T *pkey) { + CDICT_IF_NULL_RETURN(s, CDICT_VAL_DEFAULT); + CDICT_IF_NULL_SET_ERR_RETURN(pkey, CDICT_ERR_PKEY_IS_NULL, CDICT_VAL_DEFAULT); + CDictItem **ppit = cdict_chain_begin(s, pkey); + CDICT_IF_NULL_RETURN(ppit, CDICT_VAL_DEFAULT); + while (*ppit) { + if (!cdict_keycmp(pkey, &(*ppit)->key)) { + return (*ppit)->val; + } + ppit = cdict_chain_next(ppit); + } + return CDICT_VAL_DEFAULT; +} + +CDICT_VAL_T cdict_get_v(CDict *s, CDICT_KEY_T key) { + return cdict_get_p(s, &key); +} + +#endif + +#undef CDICT_CAT2_IMPL +#undef CDICT_CAT2 +#undef CDICT_FUN + +#undef cdict_init +#undef cdict_init_ud +#undef cdict_init_pud +#undef cdict_add_pp +#undef cdict_add_vv +#undef cdict_get_p +#undef cdict_get_v + +#undef CDICT_KEY_T +#undef CDICT_VAL_T +#undef CDICT_USER_DATA_T + +#ifdef CDICT_INST +#undef CDICT_INST +#undef CDICT_HASH_FN +#undef CDICT_HASHTAB_SZ +#undef CDICT_HASHTAB_ALLOC_FN +#undef CDICT_HASHTAB_ITEM_ALLOC_FN +#undef CDICT_HASHTAB_ITEM_FREE_FN +#undef CDICT_ASSERT_FN +#undef CDICT_CMP_FN +#undef CDICT_ASSERT +#undef CDICT_IF_NULL_RETURN +#undef CDICT_IF_NULL_SET_ERR_RETURN +#undef CDICT_HASHTAB_ITEM_ALLOCATORS_OVERRIDDEN +#undef CDICT_HASHTAB_ALLOCATORS_OVERRIDDEN +#undef CDICT_HASH_FN_OVERRIDEN +#undef CDICT_CMP_FN_OVERRIDDEN +#endif