diff --git a/config/vcpe.cfg b/config/vcpe.cfg index 04b474c..51574cd 100644 --- a/config/vcpe.cfg +++ b/config/vcpe.cfg @@ -76,23 +76,23 @@ application: }; # DHCP Server Config - #dhcp_server: { - # listen_on = ["192.168.30.1", "192.168.100.1"]; - # http_client = ["192.168.30.1", "192.168.30.110", "192.168.100.1", "192.168.100.110"]; - # - # range_set: ( - # { dhcp_range = "192.168.30.110-192.168.30.120"; - # subnet_mask = "255.255.255.0"; - # domain_server = "114.114.114.114, 8.8.8.8"; - # gateway = "192.168.30.1"; - # lease_time = 360; - # }, - # { dhcp_range = "192.168.100.110-192.168.100.200"; - # subnet_mask = "255.255.255.0"; - # domain_server = "114.114.114.114, 8.8.8.8"; - # gateway = "192.168.100.1"; - # lease_time = 360; - # } - # ); - #}; + dhcp_server: { + listen_on = ["192.168.30.1", "192.168.100.1"]; + http_client = ["192.168.30.1", "192.168.30.110", "192.168.100.1", "192.168.100.110"]; + + range_set: ( + { dhcp_range = "192.168.30.110-192.168.30.120"; + subnet_mask = "255.255.255.0"; + domain_server = "114.114.114.114,8.8.8.8"; + gateway = "192.168.30.1"; + lease_time = 360; + }, + { dhcp_range = "192.168.100.110-192.168.100.200"; + subnet_mask = "255.255.255.0"; + domain_server = "114.114.114.114,8.8.8.8"; + gateway = "192.168.100.1"; + lease_time = 360; + } + ); + }; } \ No newline at end of file diff --git a/srcs/libs/CMakeLists.txt b/srcs/libs/CMakeLists.txt index 7acbff6..b380b06 100644 --- a/srcs/libs/CMakeLists.txt +++ b/srcs/libs/CMakeLists.txt @@ -8,7 +8,7 @@ INCLUDE_DIRECTORIES(include ./ ./include ../lwip/src/include ../lwip/src/arch_linux/include ../include ../httpserver/include ../httpserver/src/haywire ../httpserver/src/haywire/configuration ${COMMON_INCLUDE}) -FILE(GLOB C_HEADS include/*.h include/uthash/*.h include/s2j/*.h) +FILE(GLOB C_HEADS include/*.h include/uthash/*.h include/s2j/*.h vector/*.h) AUX_SOURCE_DIRECTORY(json C_SRC) @@ -24,9 +24,12 @@ AUX_SOURCE_DIRECTORY(mq C_SRC) AUX_SOURCE_DIRECTORY(cmdline C_SRC) AUX_SOURCE_DIRECTORY(crypto C_SRC) AUX_SOURCE_DIRECTORY(hardware C_SRC) +AUX_SOURCE_DIRECTORY(vector C_SRC) SET(CMAKE_C_STANDARD 99) +SET_SOURCE_FILES_PROPERTIES(vector/zvector.c PROPERTIES COMPILE_FLAGS "-Wall -Wextra -flto") + ADD_DEFINITIONS(-DBUILD_VERSION="${GIT_VERSION}" ${COMMON_DEFINE}) diff --git a/srcs/libs/configure/config.c b/srcs/libs/configure/config.c index d3843d4..2b9ec1c 100644 --- a/srcs/libs/configure/config.c +++ b/srcs/libs/configure/config.c @@ -14,6 +14,7 @@ #include "user_errno.h" #include "crypto.h" #include "hardware.h" +#include "zvector/zvector.h" #define CFG_INT_VALUE(p) (p->value.longValue) #define CFG_BOOL_VALUE(p) (p->value.longValue == FALSE ? FALSE : TRUE) @@ -65,13 +66,24 @@ do { ADD_CFG_ITEM(CFG_HTTP_SVR_ADDR, "application.http_svr.listen_addr", VALUE_TYPE_STRING, "0.0.0.0", "Network address to listen on"); \ ADD_CFG_ITEM(CFG_HTTP_SVR_PORT, "application.http_svr.listen_port", VALUE_TYPE_INTEGRAL, "6789", "Network port to listen on"); \ ADD_CFG_ITEM(CFG_HTTP_SVR_TCP_NODELAY, "application.http_svr.tcp_nodelay", VALUE_TYPE_BOOL, "1", "TCP delay switch"); \ + ADD_CFG_ITEM(CFG_DHCP_LISTEN_ON, "application.dhcp_server.listen_on", VALUE_TYPE_ARRAY_STR, "", "DHCP listen interface"); \ + ADD_CFG_ITEM(CFG_DHCP_HTTP_CLIENT, "application.dhcp_server.http_client", VALUE_TYPE_ARRAY_STR, "", "DHCP http server client write list"); \ + ADD_CFG_ITEM(CFG_DHCP_RANGE_SET, "application.dhcp_server.range_set", VALUE_TYPE_ARRAY_OBJ, "", "DHCP IP pool"); \ } while (0)// clang-format on +typedef struct { + char rangAddr[256]; + char subnet[20]; + char dnsSvr[256]; + char gateway[20]; + unsigned int lease; +} OBJ_DHCP_RNG, *POBJ_DHCP_RNG; typedef union { long long longValue; char *strValue; long double floatValue; + vector array; } CFG_VALUE, *PCFG_VALUE; typedef struct { @@ -173,6 +185,103 @@ static double load_float_value(const char *pKeyName, double defValue) { return val; } +static int cmp_str(const void *a, const void *b) { + return strcmp((const char *)a, (const char *)b); +} + +static int load_array_str(const char *pKeyName, PCONFIG_ITEM pValue) { + int i; + config_setting_t *pItem = config_lookup(&g_cfgContent, pKeyName); + + if (pItem) { + for (i = 0; i < config_setting_length(pItem); i++) { + const char *pVal = config_setting_get_string_elem(pItem, i); + if (pVal) { + zvect_index idx = 0; + if (!vect_bsearch(pValue->value.array, pVal, cmp_str, &idx)) { + vect_push(pValue->value.array, pVal); + pValue->isChanged = TRUE; + } + } + } + + vect_shrink(pValue->value.array); + return ERR_SUCCESS; + } + + return -ERR_ITEM_UNEXISTS; +} + +static int cmp_dhcp_obj(const void *a, const void *b) { + POBJ_DHCP_RNG pV1 = (POBJ_DHCP_RNG)a; + POBJ_DHCP_RNG pV2 = (POBJ_DHCP_RNG)b; + + if (strcmp(pV1->rangAddr, pV2->rangAddr) == 0 && strcmp(pV1->subnet, pV2->subnet) == 0 + && strcmp(pV1->dnsSvr, pV2->dnsSvr) == 0 && strcmp(pV1->gateway, pV2->gateway) == 0 + && pV1->lease == pV2->lease) { + return 0; + } + + return -1; +} + +static int load_array_obj(const char *pKeyName, PCONFIG_ITEM pValue) { + int i; + config_setting_t *pItem = config_lookup(&g_cfgContent, pKeyName); + + if (!pItem) { + return -ERR_ITEM_UNEXISTS; + } + for (i = 0; i < config_setting_length(pItem); i++) { + OBJ_DHCP_RNG v, k; + config_setting_t *pObj = config_setting_get_elem(pItem, i); + + switch (pValue->cfgId) { + case CFG_DHCP_RANGE_SET: { + zvect_index idx = -1; + const char *rangAddr, *subnet, *dnsSvr, *gateway; + memset(&v, 0, sizeof(OBJ_DHCP_RNG)); + + if (config_setting_lookup_string(pObj, "dhcp_range", &rangAddr)) { + strcpy(v.rangAddr, rangAddr); + } + + if (config_setting_lookup_string(pObj, "subnet_mask", &subnet)) { + strcpy(v.subnet, subnet); + } + + if (config_setting_lookup_string(pObj, "domain_server", &dnsSvr)) { + strcpy(v.dnsSvr, dnsSvr); + } + + if (config_setting_lookup_string(pObj, "gateway", &gateway)) { + strcpy(v.gateway, gateway); + } + + if (!config_setting_lookup_int(pObj, "lease_time", (int *)&v.lease)) { + v.lease = 0xFFFFFFFF; + } + + if (pValue->value.array == NULL) { + pValue->value.array = vect_create(128, sizeof(OBJ_DHCP_RNG), ZV_SEC_WIPE); + } + + if (!vect_bsearch(pValue->value.array, &v, cmp_dhcp_obj, &idx)) { + memcpy(&k, &v, sizeof(OBJ_DHCP_RNG)); + vect_push(pValue->value.array, &k); + pValue->isChanged = TRUE; + printf("++++Add %s\n", k.rangAddr); + } + } break; + + default: + break; + } + } + + return ERR_SUCCESS; +} + static int setConfigItemValue(PCONFIG_ITEM pItem, const char *pValue) { errno = 0; @@ -200,6 +309,11 @@ static int setConfigItemValue(PCONFIG_ITEM pItem, const char *pValue) { } pItem->value.floatValue = val; + } else if (pItem->valType == VALUE_TYPE_ARRAY_STR) { + pItem->value.array = vect_create(128, 512, ZV_SEC_WIPE); + } else if (pItem->valType == VALUE_TYPE_ARRAY_OBJ) { + pItem->value.array = NULL; + vect_create(128, sizeof(int), ZV_SEC_WIPE | ZV_BYREF); } else { return -ERR_UN_SUPPORT; } @@ -323,6 +437,14 @@ static void refreshCfgFileCb() { } break; + case VALUE_TYPE_ARRAY_STR: + load_array_str(pItem->pcfgKey, pItem); + break; + + case VALUE_TYPE_ARRAY_OBJ: + load_array_obj(pItem->pcfgKey, pItem); + break; + default: break; } @@ -338,9 +460,11 @@ static void refreshCfgFileCb() { } const char *config_item_dump_fmt(const char *titleMessage) { + int i; const char *pResp; PCONFIG_ITEM pItem, pTmp; - sds tmp, tmp2; + char tmp2[256]; + sds tmp; sds s = sdsempty(); if (titleMessage && strlen(titleMessage) > 0) { @@ -349,23 +473,23 @@ const char *config_item_dump_fmt(const char *titleMessage) { s = sdscat(s, "--------------------------------------------------------------------------------" - "---------------------------------------------------\n"); + "-----------------------------------------------------------------------\n"); s = sdscat(s, "| id | Key Name | Configuration file key |" - " value |\n"); + " value |\n"); s = sdscat(s, "--------------------------------------------------------------------------------" - "---------------------------------------------------\n"); + "-----------------------------------------------------------------------\n"); HASH_ITER(hh, g_pConfigItem, pItem, pTmp) { - tmp2 = sdsempty(); - tmp2 = sdscatprintf(tmp2, "%s%s", cfg_is_upgrade(pItem) ? "*" : " ", pItem->pStrId); + memset(tmp2, 0, 256); + sprintf(tmp2, "%s%s", cfg_is_upgrade(pItem) ? "*" : " ", pItem->pStrId); switch (pItem->valType) { case VALUE_TYPE_BOOL: s = sdscatprintf(s, - "|%4d | %-25s | %-45s | %-44s |\n", + "|%4d | %-25s | %-45s | %-64s |\n", pItem->cfgId, tmp2, pItem->pcfgKey, @@ -373,11 +497,11 @@ const char *config_item_dump_fmt(const char *titleMessage) { break; case VALUE_TYPE_INTEGRAL: s = sdscatprintf( - s, "|%4d | %-25s | %-45s | %-44lld |\n", pItem->cfgId, tmp2, pItem->pcfgKey, CFG_INT_VALUE(pItem)); + s, "|%4d | %-25s | %-45s | %-64lld |\n", pItem->cfgId, tmp2, pItem->pcfgKey, CFG_INT_VALUE(pItem)); break; case VALUE_TYPE_FLOAT: s = sdscatprintf(s, - "|%4d | %-25s | %-45s | %-44Lf |\n", + "|%4d | %-25s | %-45s | %-64Lf |\n", pItem->cfgId, tmp2, pItem->pcfgKey, @@ -386,22 +510,74 @@ const char *config_item_dump_fmt(const char *titleMessage) { case VALUE_TYPE_STRING: tmp = sdsempty(); tmp = sdscatprintf(tmp, "\"%s\"", CFG_STRING_VALUE(pItem)); - if (sdslen(tmp) > 43) { - sdsrange(tmp, 0, 39); - sdscat(tmp, "...\""); + if (sdslen(tmp) > 63) { + sdsrange(tmp, 0, 59); + tmp = sdscat(tmp, "...\""); } - s = sdscatprintf(s, "|%4d | %-25s | %-45s | %-44s |\n", pItem->cfgId, tmp2, pItem->pcfgKey, tmp); + s = sdscatprintf(s, "|%4d | %-25s | %-45s | %-64s |\n", pItem->cfgId, tmp2, pItem->pcfgKey, tmp); sdsfree(tmp); break; + case VALUE_TYPE_ARRAY_STR: + tmp = sdsempty(); + tmp = sdscat(tmp, "["); + for (i = 0; i < vect_size(pItem->value.array); i++) { + const char *p = (const char *)vect_get_at(pItem->value.array, i); + if (p) { + tmp = sdscatfmt(tmp, "\"%s\",", p); + } + } + sdsrange(tmp, 0, -2); + tmp = sdscat(tmp, "]"); + //tmp = sdscatprintf(tmp, "\"%s\"", CFG_STRING_VALUE(pItem)); + if (sdslen(tmp) > 63) { + sdsrange(tmp, 0, 58); + tmp = sdscat(tmp, "...\"]"); + } + s = sdscatprintf(s, "|%4d | %-25s | %-45s | %-64s |\n", pItem->cfgId, tmp2, pItem->pcfgKey, tmp); + sdsfree(tmp); + break; + case VALUE_TYPE_ARRAY_OBJ: + for (i = 0; i < vect_size(pItem->value.array); i++) { + POBJ_DHCP_RNG p = (POBJ_DHCP_RNG)vect_get_at(pItem->value.array, i); + if (!p) { + continue; + } + + sds title = sdsempty(); + sds keyRang = sdsempty(); + sds keySubnet = sdsempty(); + sds keyDns = sdsempty(); + sds keyGateway = sdsempty(); + + title = sdscatprintf(title, "%s[%d]", pItem->pcfgKey, i); + keyRang = sdscatprintf(keyRang, "\"%s\"", p->rangAddr); + keySubnet = sdscatprintf(keySubnet, "\"%s\"", p->subnet); + keyDns = sdscatprintf(keyDns, "\"%s\"", p->dnsSvr); + keyGateway = sdscatprintf(keyGateway, "\"%s\"", p->gateway); + + s = sdscatprintf(s, "|%4d | %-25s | %-45s | %-64s |\n", pItem->cfgId, tmp2, title, ""); + + s = sdscatprintf(s, "| | %-25s | %-45s | %-64s |\n", "", " .dhcp_range", keyRang); + s = sdscatprintf(s, "| | %-25s | %-45s | %-64s |\n", "", " .subnet_mask", keySubnet); + s = sdscatprintf(s, "| | %-25s | %-45s | %-64s |\n", "", " .domain_server", keyDns); + s = sdscatprintf(s, "| | %-25s | %-45s | %-64s |\n", "", " .gateway", keyGateway); + s = sdscatprintf(s, "| | %-25s | %-45s | %-64u |\n", "", " .lease_time", p->lease); + + sdsfree(title); + sdsfree(keyRang); + sdsfree(keySubnet); + sdsfree(keyDns); + sdsfree(keyGateway); + } + break; default: break; } - sdsfree(tmp2); } s = sdscat(s, "--------------------------------------------------------------------------------" - "---------------------------------------------------\n"); + "-----------------------------------------------------------------------\n"); pResp = strdup(s); sdsfree(s); @@ -494,7 +670,6 @@ const char *cfg_get_string_value(CONFIG_ITEM_ID id) { } } - int init_config_system(const char *pCfgFile, const char *pKey) { if (!file_exists(pCfgFile)) { dzlog_error("Configuration file [%s] not exists\n", pCfgFile); diff --git a/srcs/libs/include/config.h b/srcs/libs/include/config.h index 0ca917c..e3e86d3 100644 --- a/srcs/libs/include/config.h +++ b/srcs/libs/include/config.h @@ -11,11 +11,12 @@ extern "C" { #define DEFAULT_INTEGRAL_ERR_VALUE (0x0AFFFFAA) typedef enum { - VALUE_TYPE_INTEGRAL = 0, - VALUE_TYPE_FLOAT = 1, - VALUE_TYPE_STRING = 2, - VALUE_TYPE_BOOL = 3, - VALUE_TYPE_ARRAY = 4, + VALUE_TYPE_INTEGRAL = 0, + VALUE_TYPE_FLOAT = 1, + VALUE_TYPE_STRING = 2, + VALUE_TYPE_BOOL = 3, + VALUE_TYPE_ARRAY_STR = 4, + VALUE_TYPE_ARRAY_OBJ = 5, } CONFIG_VALUE_TYPE; typedef enum { @@ -50,6 +51,9 @@ typedef enum { CFG_HTTP_SVR_ADDR = 29, CFG_HTTP_SVR_PORT = 30, CFG_HTTP_SVR_TCP_NODELAY = 31, + CFG_DHCP_LISTEN_ON = 32, + CFG_DHCP_HTTP_CLIENT = 33, + CFG_DHCP_RANGE_SET = 34, CONFIG_ITEM_ID_MAX } CONFIG_ITEM_ID; diff --git a/srcs/libs/include/zvector/zvector.h b/srcs/libs/include/zvector/zvector.h new file mode 100644 index 0000000..5b73ff6 --- /dev/null +++ b/srcs/libs/include/zvector/zvector.h @@ -0,0 +1,642 @@ +/* + * Name: ZVector (Header) + * Purpose: Library to use Dynamic Arrays (Vectors) in C Language + * Author: Paolo Fabio Zaino + * Domain: General + * License: Copyright by Paolo Fabio Zaino, all rights reserved + * Distributed under MIT license + * + */ + +#ifndef SRC_ZVECTOR_H_ +#define SRC_ZVECTOR_H_ + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +#ifdef _cplusplus +extern "C" { +#endif + +// Requires standard C libraries: +#include +#include + +// First library included is zvector_checks, +// so we know on which platform and which features +// we can use: +#include "zvector_checks.h" + +// Include vector configuration header +#include "zvector_config.h" + +// Declare required structs: +typedef struct p_vector * vector; + +// Declare required enums: + +/* + * Vector Properties Flags can be used to tell ZVector + * which types of properties we want to enable for each + * given vector we are creating. + * Each vector can have multiple properties enabled at the + * same time, you can use the typical C form to specify multiple + * properties for the same vector: + * + * ZV_SEC_WIPE | ZV_BYREF + * + * The above will create a vector that supports passing items to + * it by reference (instead of copying them into the vector) and + * having Secure Wipe enabled, so that when an element is deleted + * its reference will also be fully zeroed out before freeing it. + */ +enum ZVECT_PROPERTIES { + ZV_NONE = 0, // Sets or Resets all vector's properties to 0. + ZV_SEC_WIPE = 1 << 0, // Sets the vector for automatic Secure Wipe of items. + ZV_BYREF = 1 << 1, // Sets the vector to store items by reference instead of copying them as per default. + ZV_CIRCULAR = 1 << 2, // Sets the vector to be a circular vector (so it will not grow in capacity automatically). Elements will be overwritten as in typical circular buffers! + ZV_NOLOCKING = 1 << 3, // This Property means the vector will not use mutexes, be careful using it! +}; + +enum ZVECT_ERR { + ZVERR_VECTUNDEF = -1, + ZVERR_IDXOUTOFBOUND = -2, + ZVERR_OUTOFMEM = -3, + ZVERR_VECTCORRUPTED = -4, + ZVERR_RACECOND = -5, + ZVERR_VECTTOOSMALL = -6, + ZVERR_VECTDATASIZE = -7, + ZVERR_VECTEMPTY = -8, + ZVERR_OPNOTALLOWED = -9 +}; + +/***************************** + ** Public API declaration: ** + *****************************/ + +// Vector construction/Destruction and memory control: + +/* + * vect_create creates and returns a new vector + * of the specified "capacity", with a storage area that + * can store items of "item_size" size and, if we want to + * have an automatic secure erasing enabled (ZV_SEC_WIPE + * ), we can simply pass ZV_SAFE_WIPE (or other flags too) + * after item_size. Flags syntax is the usual C flag sets: + * ZV_SEC_WIPE | ZV_BYREF etc. + */ +vector vect_create(zvect_index capacity, size_t item_size, uint32_t properties); + +/* + * vect_destroy destroys the specified vector and, if + * secure_wipe is enabled, also ensure erasing each single + * value in the vector before destroying it. + */ +void vect_destroy(vector); + +/* + * vect_shrink is useful when operating on systems with + * small amount of RAM, and it basically allows to shrink + * the vector capacity to match the actual used size, to + * save unused memory locations. + */ +void vect_shrink(vector const v); +#define vect_shrink_to_fit(x) vect_shrink(x) + +/* + * vect_set_wipefunct allows you to pass ZVector a pointer to a custom + * function (of your creation) to securely wipe data from the vector v + * when automatic safe wipe is called. + */ +void vect_set_wipefunct(vector const v, void (*f1)(const void *item, size_t size)); + +// Vector state checks: + +/* + * vect_is_empty returns true if the vector is empty + * and false if the vector is NOT empty. + */ +bool vect_is_empty(vector const v); + +/* + * vect_size returns the actual size (the number of) + * USED slots in the vector storage. + */ +zvect_index vect_size(vector const v); + +/* + * vect_clear clears out a vector and also resizes it + * to its initial capacity. + */ +void vect_clear(vector const v); + +bool vect_check_status(const vector v, zvect_index flag_id); + +bool vect_set_status(const vector v, zvect_index flag_id); + +bool vect_clear_status(const vector v, zvect_index flag_id); + +#if ( ZVECT_THREAD_SAFE == 1 ) +// Vector Thread Safe functions: + +/* + * vect_lock_enable allows you to enable thread safe + * code at runtime. It doesn't lock anything, it just + * enables globally ZVector thread safe code at + * runtime. + * + * Example of use: + * vect_lock_enable; + */ +void vect_lock_enable(void); + +/* + * vect_lock_disable allows you to disable thread safe + * code at runtime. It doesn't lock anything, it just + * disables globally ZVector thread safe code at + * runtime. + * + * Example of use: + * vect_lock_disable; + */ +void vect_lock_disable(void); + +/* + * vect_lock allows you to lock the given vector to + * have exclusive write access from your own thread. + * When you lock a vector directly then ZVector will + * NOT use its internal locking mechanism for that + * specific vector. + * + * Example of use: To lock a vector called v + * vect_lock(v); + */ +zvect_retval vect_lock(vector const v); + +/* + * vect_trylock will try to lock the given vector to + * have exclusive write access from your own thread. + * When you lock a vector directly then ZVector will + * NOT use its internal locking mechanism for that + * specific vector. + * + * Example of use: To lock a vector called v + * vect_trylock(v); + */ +zvect_retval vect_trylock(vector const v); + +/* + * vect_lock allows you to unlock the given vector that + * you have previously locked with vect_lock. + * + * Example of use: To unlock a vector called v + * vect_unlock(v); + */ +zvect_retval vect_unlock(vector const v); + +/* TODO: + * zvect_retval vect_wait_for_signal(const vector v); + * + * zvect_retval vect_lock_after_signal(const vector v); + */ + +zvect_retval vect_move_on_signal(vector const v1, vector v2, + const zvect_index s2, + const zvect_index e2, + zvect_retval (*f2)(void *, void *)); + +zvect_retval vect_send_signal(const vector v); + +zvect_retval vect_broadcast_signal(const vector v); + +zvect_retval vect_sem_wait(const vector v); + +zvect_retval vect_sem_post(const vector v); + +#endif // ( ZVECT_THREAD_SAFE == 1 ) + +///////////////////////////////////////////////////// +// Vector Data Storage functions: + +/* + * vect_push and vect_pop are used to use the + * vector as a dynamic stack. + * + * int i = 3; + * vect_push(v, &i) pushes the element 3 at the + * back of the vector v, which + * corresponds to the top of a + * Stack. + */ +void vect_push(vector const v, const void *item); + +/* + * vect_pop(v) "pops" (returns) the element + * at the back of the vector as + * a regular pop would do with + * an element at the top of a + * stack. Remember when you use + * vect_pop the element you + * receive is also removed from + * the vector! + */ +void *vect_pop(vector const v); +#define vect_pop_back(x) vect_pop(x) + +/* + * vect_add adds a new item into the vector and, + * if required, will also reorganize the vector. + * + * int i = 3; + * vect_add(v, &i) will add the new item 3 in + * the vector v at the end + * (or back) of the vector v. + */ +void vect_add(vector const v, const void *item); +#define vect_push_back(x, y) vect_add(x, y) + +/* + * int i = 4; + * vect_add_at(v, &i, 2) will add the new item 4 at + * position 2 in the vector v + * and move all the elements + * from the original 2nd onward + * of a position to make space + * for the new item 4. + */ +void vect_add_at(vector const v, const void *item, zvect_index index); + +/* + * int i = 5; + * vect_add_front(v, &i) will add the new item 5 at + * the beginning of the vector + * v (or front) and will also + * move all the existing + * elements of one position in + * the vector to make space for + * the new item 5 at the front + * of vector v. + */ +void vect_add_front(vector const v, const void *item); +#define vect_push_front(x, y) vect_add_front(x, y) + +/* + * vect_get returns an item from the specified vector + * + * vect_get(v) will return the ast element in + * the v vector (but will not remove + * the element as it happens in + * vect_pop(v)). + */ +void *vect_get(vector const v); +#define vect_back(v) vect_get(v) + +/* + * + * vect_get_at(v, 3) will return the element at location + * 3 in the vector v. + */ +void *vect_get_at(vector const v, const zvect_index i); +#define vect_at(v, x) vect_get_at(v, x) + +/* + * vect_get_front(v) will return the first element in + * the vector v. + */ +void *vect_get_front(vector const v); +#define vect_front(v) vect_get_front(v) + +/* + *vect_put allows you to REPLACE an item + * in the vector. + * + * int i = 3; + * vect_put(v, &i) will replace the last element + * in the vector with 3. + */ +void vect_put(vector const v, const void *item); + +/* + * + * int i = 4; + * vect_put_at(v, &i, 2) will replace the 3rd element + * (2 + 1, as vector's 1st item + * starts at v[0]) with the + * item 4. + */ +void vect_put_at(vector const v, const void *item, const zvect_index i); + +/* + * + * int i = 5; + * vect_put_front(v, &i) will replace the 1st element + * of the vector with the item + * 5. + */ +void vect_put_front(vector const v, const void *item); + +/* + * vect_remove removes an item from the vector + * and reorganise the vector. It also returns + * the item remove from the vector, so you can + * use it to simulate a stack behaviour as well. + * + * vect_remove(v) will remove and return the + * last item in the vector. + */ +void *vect_remove(vector const v); + +/* + * vect_remove_at(v, 3) will remove the 3rd item in + * the vector and return it. + */ +void *vect_remove_at(vector const v, const zvect_index i); + +/* + * vect_remove_front(v) will remove the 1st item in + * the vector and return it. + */ +void *vect_remove_front(vector const v); + +/* + * vect_delete deletes an item from the vector + * and reorganise the vector. It does not return + * the item like remove. + * + * vect_delete(v) will delete and the last + * item in the vector. + */ +void vect_delete(vector const v); + +/* + * vect_delete_at(v, 3) will delete the 3rd item in + * the vector. + */ +void vect_delete_at(vector const v, const zvect_index i); + +/* + * vect_delete_range(v, 20, 30) + * will delete items from item + * 20 to item 30 in the vector + * v. + */ +void vect_delete_range(vector const v, const zvect_index first_element, const zvect_index last_element); + +/* + * + * vect_delete_front(v) will delete the 1st item in + * the vector. + */ +void vect_delete_front(vector const v); + +//////////// +// Vector Data manipulation functions: +//////////// + +#ifdef ZVECT_DMF_EXTENSIONS +// Data Manipulation Functions extensions: + +/* + * vect_swap is a function that allows you to swap two + * items in the same vector. + * You just pass the vector and the index of both the + * two items to swap. + * + * For example to swap item 3 with item 22 on vector v + * use: + * vect_swap(v, 3, 22); + */ +void vect_swap(vector const v, const zvect_index s, const zvect_index e); + +/* + * vect_swap_range is a function that allows to swap + * a range of items in the same vector. + * You just pass the vector, the index of the first item + * to swap, the index of the last item to swap and the + * index of the first item to swap with. + * + * For example to swap items from 10 to 20 with items + * from 30 to 40 on vector v, use: + * vect_swap_range(v, 10, 20, 30); + */ +void vect_swap_range(vector const v, const zvect_index s1, const zvect_index e1, const zvect_index s2); + +/* + * vect_rotate_left is a function that allows to rotate + * a vector of "i" positions to the left (or from the + * "front" to the "end"). + * + * For example to rotate a vector called v of 5 positions + * to the left, use: + * vect_rotate_left(v, 5); + */ +void vect_rotate_left(vector const v, const zvect_index i); + +/* + * vect_rotate_right is a function that allows to rotate + * a vector of "i" positions to the right (or from the + * "end" to the "front"). + * + * For example to rotate a vector called v of 5 positions + * to the right, use: + * vect_rotate_right(v, 5); + */ +void vect_rotate_right(vector const v, const zvect_index i); + +/* + * vect_qsort allows you to sort a given vector. + * The algorithm used to sort a vector is Quicksort with + * 3 ways partitioning which is generally much faster than + * traditional quicksort. + * + * To sort a vector you need to provide a custom function + * that allows vect_sort to determine the order and which + * elements of a vector are used to order it in the way + * you desire. It pretty much works as a regular C qsort + * function. It quite fast given that it only reorders + * pointers to your datastructures stored in the vector. + * + */ +void vect_qsort(vector const v, int (*compare_func)(const void *, const void*)); + +/* + * vect_bsearch is a function that allows to perform + * a binary search over the vector we pass to it to + * find the item "key" using the comparison function + * "f1". + * + * The specific algorithm used to implement vect_bsearch + * if my own re-implementation of the Adaptive Binary + * Search algorithm (from Igor van den Hoven) which has + * some improvements over the original one (look at the + * sources for more details). + * + * For example to search for the number 5 in a vector + * called v using a compare function called "my_compare" + * use: + * int i = 5; + * vect_bsearch(v, &i, my_compare); + */ +bool vect_bsearch(vector const v, const void *key, int (*f1)(const void *, const void *), zvect_index *item_index); + +/* + * vect_add_ordered allows the insertion of new items in + * an ordered fashion. Please note that for this to work + * fine you should always use only ordered vectors or if + * an empty vector use vect_add_ordered only to add new + * values to it! + * + * As for any other ordered function you must provide + * your own compare function (syntax is the usual one, + * and it's the same as for regular CLib qsort function) + * + * To add item 3 to a vector called v using vect_add_ordered + * (assuming your compare function is called my_compare), + * use: + * + * vect_Add_ordered(v, 3, my_compare); + */ +void vect_add_ordered(vector const v, const void *value, int (*f1)(const void *, const void *)); + +#endif // ZVECT_DMF_EXTENSIONS + +#ifdef ZVECT_SFMD_EXTENSIONS +// Single Function Multiple Data extensions: + +/* + * vect_apply allows you to apply a C function to + * each item in the vector, so you just pass the vector, + * the function you want to execute against each item on + * a vector and make sure such function is declared and + * defined to accept a "void *" pointer which will be the + * pointer to the single item in the vector passed to your + * function. The function has no return value because it's + * not necessary if you receive the pointer to the item. + * You can simply update the content of the memory pointed + * by the item passed and that is much faster than having + * to deal with return values especially when you'll be + * using complex data structures as item of the vector. + */ +void vect_apply(vector const v, void (*f1)(void *)); + +/* + * vect_apply_if is a function that will apply "f1" C function + * to each and every item in vector v1, IF the return value of + * function f2 is true. So it allows what is known as conditional + * application. f2 will receive an item from v1 as first parameter + * and an item from v2 (at the same position of the item in v1) as + * second parameter. So, for example, if we want to increment all + * items in v1 of 10 if they are smaller than the corresponded item + * in v2 then we can simply use: + * + * vect_apply_if(v1, v2, increment_item, is_item_too_small); + * + * and make sure we have defined 'increment_item' and + * 'is_item_too_small' as: + * + * void increment_item(void *item1) + * { + * int *value = (int *)item1; + * *value +=10; + * } + * + * bool is_item_too_small(void *item1, void *item2) + * { + * if (*((int *)item1) < *((int *)item2)) + * return true; + * return false; + * } + */ +void vect_apply_if(vector const v1, vector const v2, void (*f1)(void *), bool (*f2)(void *, void *)); + +// Operations with multiple vectors: + +/* + * vect_copy is a function that allows to copy a specified + * set of elements from a vector to another. + * Please note: only vectors with the same data size (the + * parameter we pass during the creation of both vectors) + * can be copied into the other! + * + * vect_copy(v1, v2, 3, 5) will copy all the items in + * vector v2, from the 4th item + * till the 9th (3 + 5, remember + * vector items start from 0) in + * the vector v1. So at the end + * of the process you'll have such + * items copied at the end of v1. + */ +void vect_copy(vector const v1, vector const v2, zvect_index start, zvect_index max_elements); + +/* + * vect_insert is a function that allows to copy a specified + * set of elements from a vector to another and "insert" + * them from a specified position in the destination vector. + * Please note: only vectors with the same data size (the + * parameter we pass during the creation of both vectors) + * can be copied into the other! + * + * vect_insert(v1, v2, 3, 5, 7) will copy all the items in + * vector v2, from the 4th item + * till the 9th (3 + 5, remember + * vector items start from 0) in + * the vector v1 from position 7. + * So at the end of the process + * you'll have such items "inserted" + * inside v1. + */ +void vect_insert(vector const v1, vector const v2, const zvect_index s2, + const zvect_index e2, const zvect_index s1); + +/* + * vect_move is a function that allows to move a specified + * set of items from one vector to another. + * It will also re-organise the source vector and (obviously) + * expand the destination vector if needed. + * Please note: only vectors of the same data size can be moved + * one into the other! + * + * vect_move(v1, v2, 2, 2) will move items in v2 from the + * 3rd item in v2 till the 5th at + * the end of v1. + */ +void vect_move(vector const v1, vector const v2, zvect_index start, zvect_index max_elements); + +/* + * vect_move_if is a function that allows to move a specified + * set of items from one vector to another if the condition + * returned by the function pointed by f2 function pointer + * is true. + * It will also re-organise the source vector and (obviously) + * expand the destination vector if needed. + * Please note: only vectors of the same data size can be moved + * one into the other! + * + * vect_move(v1, v2, 2, 2, check_data) will move items in v2 from the + * 3rd item in v2 till the 5th at + * the end of v1 if check_data returns + * true. + */ +zvect_retval vect_move_if(vector const v1, vector v2, const zvect_index s2, + const zvect_index e2, zvect_retval (*f2)(void *, void *)); + +/* + * vect_merge is a function that merges together 2 vectors of + * the same data size. At the end of the process, the source + * vector will be destroyed. + * + * vect_merge(v1, v2) will merge vector v2 to v1 and then + * destroy v2. So at the end of the job + * v1 will contain the old v1 items + + * all v2 items. + */ +void vect_merge(vector const v1, vector v2); + +#endif // ZVECT_SFMD_EXTENSIONS + +#ifdef _cplusplus +} +#endif + +#endif // SRC_ZVECTOR_H_ diff --git a/srcs/libs/include/zvector/zvector_checks.h b/srcs/libs/include/zvector/zvector_checks.h new file mode 100644 index 0000000..2479b57 --- /dev/null +++ b/srcs/libs/include/zvector/zvector_checks.h @@ -0,0 +1,144 @@ +/* + * Name: ZVector_Checks + * Purpose: Header used by ZVector Library to identify for which + * platform ZVector is being compiled. + * Author: Paolo Fabio Zaino + * Domain: General + * License: Copyright 2021 by Paolo Fabio Zaino, all rights reserved + * Distributed under MIT license + * + */ + +#ifndef SRC_ZVECTOR_CHECKS_H_ +#define SRC_ZVECTOR_CHECKS_H_ +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +// Try to determine the Operating System being used: +#if defined(__APPLE__) && defined(__MACH__) +# define macOS +#endif + +#if ( defined(__GNU__) || defined(__gnu_linux__) || \ + defined(__linux__) || defined(macOS) ) +# define OS_TYPE 1 +#elif ( defined(__WIN32__) || defined(WIN32) || defined(_WIN32) ) +# define OS_TYPE 2 +#else +# define OS_TYPE 0 +#endif + +// Try to determine compiler being used: +#if defined(__GNUC__) +# define compiler gcc +# define ZVECT_COMPTYPE 1 +# define COMP_MAJRELEASE (__GNUC__) +# define COMP_MINRELEASE (__GNUC_MINOR__) +# define COMP_PATRELEASE (__GNUC_PATCHLEVEL__) +#elif defined(_MSC_VER) +# define compiler msc +# define ZVECT_COMPTYPE 2 +# define COMP_MAJRELEASE (_MSC_VER) +# define COMP_MINRELEASE 0 +# define COMP_PATRELEASE 0 +#elif defined(__clang__) +# define compiler clang +# define ZVECT_COMPTYPE 3 +# define COMP_MAJRELEASE (__clang_major__) +# define COMP_MINRELEASE (__clang_minor__) +# define COMP_PATRELEASE (__clang_patchlevel__) +#elif defined(__INTEL_COMPILER) || defined(__ICC) || \ + defined(__ECC) || defined(__ICL) +// For intel c compiler please remember to specify: +// /Qstd=c99 (on Windows) +// -std=c99 on Linux and/or macOS +# define compiler intelc +# define ZVECT_COMPTYPE 4 +# define COMP_MAJRELEASE (__INTEL_COMPILER) +# define COMP_MINRELEASE 0 +# define COMP_PATRELEASE 0 +#elif defined (__LCC__) +# define compiler lcc +# define ZVECT_COMPTYPE 5 +# define COMP_MAJRELEASE (__LCC) +# define COMP_MINRELEASE 0 +# define COMP_PATRELEASE 0 +#elif defined(__NORCROFT_C__) || defined(__CC_NORCROFT) || \ + defined(__ARMCC_VERSION) +// For Norcroft C please remember to specify: +// -c99 +# define compiler norcroftc +# define ZVECT_COMPTYPE 6 +# define COMP_MAJRELEASE (__ARMCC_VERSION) +#elif defined(_CRAYC) +// For Cray C please remember to specify: +// -hc99 +# define compiler crayc +# define ZVECT_COMPTYPE 10 +# define COMP_MAJRELEASE (_RELEASE) +# define COMP_MINRELEASE (_RELEASE_MINOR) +# define COMP_PATRELEASE 0 +#elif defined(__HP_cc) +// For HP CC please remember to specify: +// -ansi -std=c99 +# define compiler hpc +# define ZVECT_COMPTYPE 11 +# define COMP_MAJRELEASE 1 +# define COMP_MINRELEASE 21 +# define COMP_PATRELEASE 0 +#elif defined(__IBMC__) +// For IBM C please remember to specify: +// C99 flags +# define compiler ibmc +# define ZVECT_COMPTYPE 12 +#elif defined(__TINYC__) +# define compiler tinyc +# define ZVECT_COMPTYPE 6 +# define COMP_MAJRELEASE 0 +# define COMP_MINRELEASE 0 +# define COMP_PATRELEASE 0 +#else +# define compiler unknown +# define ZVECT_COMPTYPE 0 +#endif + +// Try to determine CPU Architecture: +#if defined(__aarch64__) +# define CPU_TYPE ARM64 +# define Arch64 +#elif defined(__aarch32__) +# define CPU_TYPE ARM32 +# define Arch32 +#elif defined(__amd64__) || defined(__x86_64__) || \ + defined(__ia64__) || defined(_M_IA64) || \ + defined(_M_AMD64) || defined(_M_X64) +# define CPU_TYPE x86_64 +# define Arch64 +#else +# define CPU_TYPE unknown +# define Arch32 +#endif + +// Start setting up macros based on the platform we detected +// above. + +#if ( OS_TYPE == 1 ) + // We are on a Unix-like OS so we can use pthreads! +# define MUTEX_TYPE 1 +# elif ( OS_TYPE == 2 ) && ( defined(__CYGWIN__) || \ + defined(__MINGW32__) || defined(__MINGW64__) ) + // We are on MS Windows using CIGWIN so we can use pthreads! +# define MUTEX_TYPE 1 +# elif ( OS_TYPE == 2 ) && ( !defined(__CYGWIN__) && \ + !defined(__MINGW32__) && !defined(__MINGW64__) ) + // We are on MS Windows, so we need to use + // Windows stuff: +# define MUTEX_TYPE 2 +#else + // I have no idea on which platform are we, + // hence I have to use fake mutexes and go with the flow! +# define MUTEX_TYPE 0 +#endif + +#endif // SRC_ZVECTOR_CHECKS_H_ diff --git a/srcs/libs/include/zvector/zvector_config.h b/srcs/libs/include/zvector/zvector_config.h new file mode 100644 index 0000000..b458bb1 --- /dev/null +++ b/srcs/libs/include/zvector/zvector_config.h @@ -0,0 +1,84 @@ +/* + * Name: Vector_config + * Purpose: Base configuration for the ZVector library + * Author: Paolo Fabio Zaino + * Domain: General + * License: Copyright by Paolo Fabio Zaino, all rights reserved + * Distributed under MIT license + * + */ + +#ifndef SRC_ZVECTOR_CONFIG_H_ +#define SRC_ZVECTOR_CONFIG_H_ +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +// Include some standard C lib header +#include +#include + +// Data alignment configuration +#if ( ZVECT_COMPTYPE == 1 ) +#define ZVECT_DATAALIGN __attribute__((aligned)) +#define ZVECT_PACKING __attribute__((__packed__)) +#define ZVECT_ALWAYSINLINE __attribute__ ((__always_inline__)) +#else +#define ZVECT_DATAALIGN +#define ZVECT_PACKING +#define ZVECT_ALWAYSINLINE +#endif + +// Default vector Index type +// This is set to unsigned int of 32bit +// size (so all different architectures +// and OS behaves in a similar way) +// If you want a larger index you can +// change it to, for example, uint64_t +typedef uint32_t zvect_index; +#define zvect_index_max 4294967295 // If you change zvect_index type make sure you update this value + // it's the maximum number that can be stored in a zvect_index. + +// Default vector return type for +// error codes. +// Generally negative numbers identify +// an error. +// 0 identify completed successful +// Positive numbers identify return +// attributes, like 1 is generally true +typedef int32_t zvect_retval; + +// Default vector storage size +// This will be used when the user +// specifies 0 (zero) as data type +// size. +#define ZVECT_DEFAULT_DATA_SIZE sizeof(int) + +// Default vector capacity +// This will be used when the user +// does NOT specify an Initial Capacity +// or set it to 0 (zero): +#define ZVECT_INITIAL_CAPACITY 8 + +// The following options are handled by Make +// So you should not need to modify them here. + +// Choose which type of memory functions you want +// to use for your case: +// 0 = Use Standard memcpy and memmove +// 1 = Use Optimized memcpy and memmove +#define ZVECT_MEMX_METHOD 0 + +// Enable/Disable thread safe code: +#define ZVECT_THREAD_SAFE 1 + +// Enable/Disbale reentrant code: +#define ZVECT_FULL_REENTRANT 0 + +// Enable/Disable DMF Extensions: +#define ZVECT_DMF_EXTENSIONS 1 + +// Enable/Disable SFMD Extensions: +#define ZVECT_SFMD_EXTENSIONS 1 + +#endif // SRC_ZVECTOR_CONFIG_H_ diff --git a/srcs/libs/vector/zvector.c b/srcs/libs/vector/zvector.c new file mode 100644 index 0000000..bdd9d12 --- /dev/null +++ b/srcs/libs/vector/zvector.c @@ -0,0 +1,3124 @@ +/* + * Name: ZVector + * Purpose: Library to use Dynamic Arrays (Vectors) in C Language + * Author: Paolo Fabio Zaino + * Domain: General + * License: Copyright by Paolo Fabio Zaino, all rights reserved. + * Distributed under MIT license + * + * Credits: This Library was inspired by the work of quite few, + * apologies if I forgot to mention them all! + * + * Gnome Team (GArray demo) + * Dimitros Michail (Dynamic Array in C presentation) + * + */ + +/* + * Few code standard notes: + * + * p_ <- indicate a PRIVATE method or variable. Not reachable outside this module. + * + */ + +// Include standard C libs headers +#include +#include +#include +#include + +// Include vector.h header +#include "zvector/zvector.h" + +#if (OS_TYPE == 1) +# if (!defined(macOS)) +/* Improve PThreads on Linux. + * macOS seems to be handling pthreads + * in a slightly different way than + * Linux, so, avoid using the same trick + * on macOS. + */ +# ifndef _POSIX_C_SOURCE +# define _POSIX_C_SOURCE 200112L +# endif // _POSIX_C_SOURCE +# define __USE_UNIX98 +# endif // macOS +#endif // OS_TYPE + +// Include non-ANSI Libraries +// only if the user has requested +// special extensions: +#if (OS_TYPE == 1) +# if ( !defined(macOS) ) +# include +# endif +# if (CPU_TYPE == x86_64) +//# include +# endif +#endif // OS_TYPE +#if (ZVECT_THREAD_SAFE == 1) +# if MUTEX_TYPE == 1 +# if ( !defined(macOS) ) +# include +# else +# include +# endif +# include +# elif MUTEX_TYPE == 2 +# include +# include +# endif // MUTEX_TYPE +#endif // ZVECT_THREAD_SAFE + +// Local Defines/Macros: + +// Declare Vector status flags: +enum { + ZVS_NONE = 0, // - Set or Reset vector's status + // register. + ZVS_CUST_WIPE_ON = 1 << 0, // - Set the bit to indicate a custom + // secure wipe + // function has been set. + ZVS_USR1_FLAG = 1 << 1, // - This is a "user" available flag, + // a user code can set it to 1 or + // 0 for its own need. + // Can be useful when signaling + // between threads. + ZVS_USR2_FLAG = 1 << 2 + +}; + +#ifndef ZVECT_MEMX_METHOD +# define ZVECT_MEMX_METHOD 1 +#endif + +#if defined(Arch32) +# define ADDR_TYPE1 uint32_t +# define ADDR_TYPE2 uint32_t +# define ADDR_TYPE3 uint16_t +#else +# define ADDR_TYPE1 uint64_t +# define ADDR_TYPE2 uint64_t +# define ADDR_TYPE3 uint32_t +#endif // Arch32 + +/*---------------------------------------------------------------------------*/ +// Useful macros +//# define min(x, y) (((x) < (y)) ? (x) : (y)) +#define max(x, y) (((x) > (y)) ? (x) : (y)) +#define UNUSED(x) (void)x + +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +// Define the vector data structure: + +// This is ZVector core data structure, it is the structure of a ZVector vector :) +// Please note: fields order is based on "most used fields" to help a bit with cache + +struct ZVECT_PACKING p_vector { + zvect_index cap_left; // - Max capacity allocated on the + // left. + zvect_index cap_right; // - Max capacity allocated on the + // right. + zvect_index begin; // - First vector's Element Pointer + zvect_index end; // - Current Array size. size - 1 gives + // us the pointer to the last element + // in the vector. + uint32_t flags; // - This flag set is used to store all + // Vector's properties. + // It contains bits that set Secure + // Wipe, Auto Shrink, Pass Items By + // Ref etc. + size_t data_size; // - User DataType size. + // This should be 2 bytes size on a + // 16-bit system, 4 bytes on a 32 + // bit, 8 bytes on a 64 bit. But + // check your compiler for the + // actual size, it's implementation + // dependent. +#if (ZVECT_THREAD_SAFE == 1) +# if MUTEX_TYPE == 0 + void *lock ZVECT_DATAALIGN; // - Vector's mutex for thread safe + // micro-transactions or user locks. + // This should be 2 bytes size on a + // 16 bit machine, 4 bytes on a 32 + // bit 4 bytes on a 64 bit. + void *cond; // - Vector's mutex condition variable +# elif MUTEX_TYPE == 1 + pthread_mutex_t lock ZVECT_DATAALIGN; + // - Vector's mutex for thread safe + // micro-transactions or user locks. + // This should be 24 bytes on a 32bit + // machine and 40 bytes on a 64bit. + pthread_cond_t cond; // - Vector's mutex condition variable + +# elif MUTEX_TYPE == 2 + CRITICAL_SECTION lock ZVECT_DATAALIGN; + // - Vector's mutex for thread safe + // micro-transactions or user locks. + // Check your WINNT.H to calculate + // the size of this one. + CONDITION_VARIABLE cond; // - Vector's mutex condition variable +# endif // MUTEX_TYPE +#endif // ZVECT_THREAD_SAFE + void **data ZVECT_DATAALIGN; // - Vector's storage. + zvect_index init_capacity; // - Initial Capacity (this is set at + // creation time). + // For the size of zvect_index check + // zvector_config.h. + void (*SfWpFunc)(const void *item, size_t size); + // - Pointer to a CUSTOM Safe Wipe + // function (optional) needed only + // for Secure Wiping special + // structures. +#ifdef ZVECT_DMF_EXTENSIONS + zvect_index balance; // - Used by the Adaptive Binary Search + // to improve performance. + zvect_index bottom; // - Used to optimise Adaptive Binary + // Search. +#endif // ZVECT_DMF_EXTENSIONS + volatile uint32_t status; // - Internal vector Status Flags +#if (ZVECT_THREAD_SAFE == 1) +# if ( !defined(macOS) ) + sem_t semaphore; // - Vector semaphore +# else + dispatch_semaphore_t semaphore; // - Vector semaphore +# endif + volatile int32_t lock_type; // - This field contains the lock type + // used for this Vector. +#endif // ZVECT_THREAD_SAFE +} ZVECT_DATAALIGN; + +// Initialisation state: +static uint32_t p_init_state = 0; + +/*---------------------------------------------------------------------------*/ + +/***************************************************************************** + ** ZVector Support Functions ** + *****************************************************************************/ + +/*---------------------------------------------------------------------------*/ +// Errors and messages handling: + +enum ZVECT_LOGPRIORITY { + ZVLP_NONE = 0, // No priority + ZVLP_INFO = 1 << 0, // This is an informational priority + // message. + ZVLP_LOW = 1 << 1, // " low " + ZVLP_MEDIUM = 1 << 2, // " medium " + ZVLP_HIGH = 1 << 3, // " high " + ZVLP_ERROR = 1 << 4 // This is an error message. +}; + +// Set the message priority at which we log it: +#ifdef DEBUG +unsigned int LOG_PRIORITY = (ZVLP_ERROR | ZVLP_HIGH | ZVLP_MEDIUM | + ZVLP_LOW | ZVLP_INFO); +#endif +#ifndef DEBUG +unsigned int LOG_PRIORITY = (ZVLP_ERROR | ZVLP_HIGH | ZVLP_MEDIUM); +#endif + +// This is a vprintf wrapper nothing special: +static void log_msg(int const priority, const char * const format, ...) +{ + va_list args; + va_start(args, format); + + if ( priority & LOG_PRIORITY ) + vprintf(format, args); + +#ifdef DEBUG + fflush(stdout); +#endif +#ifndef DEBUG + if (priority & (ZVLP_ERROR | ZVLP_HIGH)) + fflush(stdout); +#endif + + va_end(args); +} + +static size_t safe_strlen(const char *str, size_t max_len) +{ + const char *end = (const char *)memchr(str, '\0', max_len); + if (end == NULL) + return max_len; + else + return end - str; +} + +static void *safe_strncpy(const char * const str_src, + size_t max_len) +{ + void *str_dst = NULL; + char tmp_dst[max_len]; + tmp_dst[sizeof(tmp_dst) - 1] = 0; + + strncpy(tmp_dst, str_src, sizeof(tmp_dst)); + + if ( tmp_dst[sizeof(tmp_dst) - 1] != 0 ) + { + tmp_dst[sizeof(tmp_dst) - 1] = 0; + } + + str_dst = (void *)malloc(sizeof(char *) * (sizeof(tmp_dst) + 1)); + if ( str_dst == NULL ) + { + log_msg(ZVLP_ERROR, "Error: %*i, %s\n", 8, -1000, "Out of memory!"); + } else { + strncpy((char *)str_dst, tmp_dst, sizeof(char) * max_len); + } + return str_dst; +} + +#if (ZVECT_COMPTYPE == 1) || (ZVECT_COMPTYPE == 3) +__attribute__((noreturn)) +#endif +static void p_throw_error(const zvect_retval error_code, + const char *error_message) { + int32_t locally_allocated = 0; + char *message = NULL; + unsigned long msg_len = 0; + + if ( error_message == NULL ) + { + msg_len = sizeof(char *) * 255; + locally_allocated=-1; + } else { + msg_len = safe_strlen(error_message, 255); + } + + if ( locally_allocated ) { + switch (error_code) + { + case ZVERR_VECTUNDEF: + message=(char *)safe_strncpy("Undefined or uninitialized vector.\n\0", msg_len); + break; + case ZVERR_IDXOUTOFBOUND: + message=(char *)safe_strncpy("Index out of bound.\n\0", msg_len); + break; + case ZVERR_OUTOFMEM: + message=(char *)safe_strncpy("Not enough memory to allocate space for the vector.\n\0", msg_len); + break; + case ZVERR_VECTCORRUPTED: + message=(char *)safe_strncpy("Vector corrupted.\n\0", msg_len); + break; + case ZVERR_RACECOND: + message=(char *)safe_strncpy("Race condition detected, cannot continue.\n\0", msg_len); + break; + case ZVERR_VECTTOOSMALL: + message=(char *)safe_strncpy("Destination vector is smaller than source.\n\0", msg_len); + break; + case ZVERR_VECTDATASIZE: + message=(char *)safe_strncpy("This operation requires two (or more vectors) with the same data size.\n\0", msg_len); + break; + case ZVERR_VECTEMPTY: + message=(char *)safe_strncpy("Vector is empty.\n\0", msg_len); + break; + case ZVERR_OPNOTALLOWED: + message=(char *)safe_strncpy("Operation not allowed.\n\0", msg_len); + break; + default: + message=(char *)safe_strncpy("Unknown error.\n\0", msg_len); + break; + } + } else + message=(char *)safe_strncpy(error_message, msg_len); + + log_msg(ZVLP_ERROR, "Error: %*i, %s\n", 8, error_code, message); + if (locally_allocated) + free((void *)message); + + exit(error_code); +} + +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +// Memory management: + +#if (ZVECT_MEMX_METHOD == 0) +ZVECT_ALWAYSINLINE +static inline +#endif // ZVECT_MEMX_METHOD +void *p_vect_memcpy(const void *__restrict dst, const void *__restrict src, size_t size) { +#if (ZVECT_MEMX_METHOD == 0) + // Using regular memcpy + // If you are using ZVector on Linux/macOS/BSD/Windows + // you should stick to this one! + return memcpy((void *)dst, src, size); +#elif (ZVECT_MEMX_METHOD == 1) + // Using improved memcpy (where improved means for + // embedded systems only!): + if (size > 0) { + if (((uintptr_t)dst % sizeof(ADDR_TYPE1) == 0) && + ((uintptr_t)src % sizeof(ADDR_TYPE1) == 0) && + (size % sizeof(ADDR_TYPE1) == 0)) { + ADDR_TYPE1 *pExDst = (ADDR_TYPE1 *)dst; + ADDR_TYPE1 const *pExSrc = (ADDR_TYPE1 const *)src; + size_t end = size / sizeof(ADDR_TYPE1); + for (register size_t i = 0; i < end; i++) { + // The following should be compiled as: (-O2 on x86_64) + // mov rdi, QWORD PTR [rsi+rcx] + // mov QWORD PTR [rax+rcx], rdi + *pExDst++ = *pExSrc++; + } + } else { + return memcpy(dst, src, size); + } + } + return dst; +#endif // ZVECT_MEMX_METHOD +} + +ZVECT_ALWAYSINLINE +static inline void *p_vect_memmove(const void *__restrict dst, + const void *__restrict src, size_t size) { +#ifdef DEBUG + log_msg(ZVLP_INFO, "p_vect_memmove: dst %*p\n", 14, dst); + log_msg(ZVLP_INFO, "p_vect_memmove: src %*p\n", 14, src); + log_msg(ZVLP_INFO, "p_vect_memmove: size %*u\n", 14, size); +#endif + return memmove((void *)dst, src, size); +} + +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +// Thread Safe functions: + +#if (ZVECT_THREAD_SAFE == 1) +static volatile bool locking_disabled = false; +# if MUTEX_TYPE == 0 +# define ZVECT_THREAD_SAFE 0 +# elif MUTEX_TYPE == 1 + +ZVECT_ALWAYSINLINE +static inline int mutex_lock(pthread_mutex_t *lock) { + return pthread_mutex_lock(lock); +} + +ZVECT_ALWAYSINLINE +static inline int mutex_trylock(pthread_mutex_t *lock) { + return pthread_mutex_trylock(lock); +} + +ZVECT_ALWAYSINLINE +static inline int mutex_unlock(pthread_mutex_t *lock) { + return pthread_mutex_unlock(lock); +} + +static inline void mutex_init(pthread_mutex_t *lock) { +# if (!defined(macOS)) + pthread_mutexattr_t Attr; + pthread_mutexattr_init(&Attr); + pthread_mutexattr_settype(&Attr, PTHREAD_MUTEX_RECURSIVE_NP); + pthread_mutexattr_setpshared(&Attr, PTHREAD_PROCESS_PRIVATE); + pthread_mutexattr_setprotocol(&Attr, PTHREAD_PRIO_INHERIT); + pthread_mutex_init(lock, &Attr); +# else + pthread_mutex_init(lock, NULL); +# endif // macOS +} + +static inline void mutex_cond_init(pthread_cond_t *cond) { +# if (!defined(macOS)) + pthread_condattr_t Attr; + pthread_condattr_init(&Attr); + pthread_condattr_setpshared(&Attr, PTHREAD_PROCESS_PRIVATE); + pthread_cond_init(cond, &Attr); +# else + pthread_cond_init(cond, NULL); +# endif // macOS +} + +static inline void mutex_destroy(pthread_mutex_t *lock) { + pthread_mutex_destroy(lock); +} + +static inline int semaphore_init +# if !defined(macOS) + (sem_t *sem, int value) { + return sem_init(sem, 0, value); +# else + (dispatch_semaphore_t *sem, int value) { + *sem = dispatch_semaphore_create(value); + return 0; +# endif +} + +static inline int semaphore_destroy +# if !defined(macOS) + (sem_t *sem) { + return sem_destroy(sem); +# else + (dispatch_semaphore_t *sem) { + return 0; // dispatch_semaphore_destroy(sem); + (void)sem; +# endif +} + +# elif MUTEX_TYPE == 2 + +static inline void mutex_lock(CRITICAL_SECTION *lock) { + EnterCriticalSection(lock); +} + +static inline void mutex_unlock(CRITICAL_SECTION *lock) { + LeaveCriticalSection(lock); +} + +static inline void mutex_alloc(CRITICAL_SECTION *lock) { + // InitializeCriticalSection(lock); + InitializeCriticalSectionAndSpinCount(lock, 32); +} + +static inline void mutex_destroy(CRITICAL_SECTION *lock) { + DeleteCriticalSection(lock); +} +# endif // MUTEX_TYPE +#endif // ZVECT_THREAD_SAFE + +#if (ZVECT_THREAD_SAFE == 1) +// The following two functions are generic locking functions + +/* + * ZVector uses the concept of Priorities for locking. + * A user lock has the higher priority while ZVector itself + * uses two different levels of priorities (both lower than + * the user lock priority). + * level 1 is the lower priority, and it's used just by the + * primitives in ZVector. + * level 2 is the priority used by the ZVector functions that + * uses ZVector primitives. + * level 3 is the priority of the User's locks. + */ +static inline zvect_retval get_mutex_lock(const vector v, const int32_t lock_type) { + if (lock_type >= v->lock_type) { + mutex_lock(&(v->lock)); + v->lock_type = lock_type; + return 1; + } + return 0; +} + +static inline zvect_retval check_mutex_trylock(const vector v, const int32_t lock_type) { + if ((lock_type >= v->lock_type) && (!mutex_trylock(&(v->lock)))) { + v->lock_type = lock_type; + return 1; + } + return 0; +} + +static inline zvect_retval lock_after_signal(const vector v, const int32_t lock_type) { + if (lock_type >= v->lock_type) { + //if (!mutex_trylock(&(v->lock))) { + while (!pthread_cond_wait(&(v->cond), &(v->lock))) { + // wait until we get a signal + } + //mutex_lock(&(v->lock)); + v->lock_type = lock_type; + return 1; + //} + } + return 0; +} + +/* + * TODO: Write a generic function to allow user to use signals: + +static inline zvect_retval wait_for_signal(const vector v, const int32_t lock_type, bool (*f1)(const vector v), ) { + if (lock_type >= v->lock_type) { + //if (!mutex_trylock(&(v->lock))) { + while(!(*f1)(v)) { + // wait until we get a signal + pthread_cond_wait(&(v->cond), &(v->lock)) + } + return 1; + //} + } + return 0; +} +*/ + +static inline zvect_retval send_signal(const vector v, const int32_t lock_type) { + return (lock_type >= v->lock_type) ? pthread_cond_signal(&(v->cond)) : 0; +} + +static inline zvect_retval broadcast_signal(const vector v, const int32_t lock_type) { + return (lock_type >= v->lock_type) ? pthread_cond_broadcast(&(v->cond)) : 0; +} + +static inline zvect_retval get_mutex_unlock(const vector v, const int32_t lock_type) { + if (lock_type == v->lock_type) { + v->lock_type = 0; + mutex_unlock(&(v->lock)); + return 1; + } + return 0; +} +#endif // ZVECT_THREAD_SAFE +/*---------------------------------------------------------------------------*/ + +/***************************************************************************** + ** ZVector Primitives ** + *****************************************************************************/ + +/*---------------------------------------------------------------------------*/ +// Library Initialisation: + +void p_init_zvect(void) { +#if (OS_TYPE == 1) +# if ( !defined(macOS) ) + //mallopt(M_MXFAST, 196*sizeof(size_t)/4); +# endif +#endif // OS_TYPE == 1 + + // We are done initialising ZVector so set the following + // to one, so this function will no longer be called: + p_init_state = 1; +} + +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +// Vector's Utilities: + +ZVECT_ALWAYSINLINE +static inline zvect_retval p_vect_check(const vector x) { + return (x == NULL) ? ZVERR_VECTUNDEF : 0; +} + +ZVECT_ALWAYSINLINE +static inline zvect_index p_vect_capacity(const vector v) { + return ( v->cap_left + v->cap_right ); +} + +ZVECT_ALWAYSINLINE +static inline zvect_index p_vect_size(const vector v) { + return ( v->end > v->begin ) ? ( v->end - v->begin ) : ( v->begin - v->end ); +} + +static inline void p_item_safewipe(vector const v, void *const item) { + if (item != NULL) { + if (!(v->status & ZVS_CUST_WIPE_ON)) { + memset(item, 0, v->data_size); + } else { + (*(v->SfWpFunc))(item, v->data_size); + } + } +} + +ZVECT_ALWAYSINLINE +static inline zvect_retval p_usr_status(zvect_index flag_id) +{ + switch (flag_id) { + case 1: return 0; + break; + default: return 1; + } +} + +bool vect_check_status(const vector v, zvect_index flag_id) +{ + return (v->status >> flag_id) & 1U; +} + +bool vect_set_status(const vector v, zvect_index flag_id) +{ + zvect_retval rval = p_usr_status(flag_id); + + if (!rval) + v->status |= 1UL << flag_id; + + return (bool)rval; +} + +bool vect_clear_status(const vector v, zvect_index flag_id) +{ + zvect_retval rval = p_usr_status(flag_id); + + if (!rval) + v->status &= ~(1UL << flag_id); + + return (bool)rval; +} + +bool vect_toggle_status(const vector v, zvect_index flag_id) +{ + zvect_retval rval = p_usr_status(flag_id); + + if (!rval) + v->status ^= (1UL << flag_id); + + return rval; +} + +static void p_free_items(vector const v, zvect_index first, zvect_index offset) { + if (p_vect_size(v) == 0) + return; + + for (register zvect_index j = (first + offset); j >= first; j--) { + if (v->data[v->begin + j] != NULL) { + if (v->flags & ZV_SEC_WIPE) + p_item_safewipe(v, v->data[v->begin + j]); + if (!(v->flags & ZV_BYREF)) + free(v->data[v->begin + j]); + } + if (j == first) + break; // this is required if we are using + // uint and the first element is element + // 0, because on GCC an uint will fail + // then check in the for loop if j >= first + // in this particular case! + } +} + +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +// Vector Size and Capacity management functions: + +/* + * Set vector capacity to a specific new_capacity value + */ +static zvect_retval p_vect_set_capacity(vector const v, const zvect_index direction, const zvect_index new_capacity_) +{ + zvect_index new_capacity = new_capacity_; + + if ( new_capacity <= v->init_capacity ) + new_capacity = v->init_capacity; + + void **new_data = NULL; + if (!direction) + { + + // Set capacity on the left side of the vector to new_capacity: + new_data = (void **)malloc(sizeof(void *) * (new_capacity + v->cap_right)); + if (new_data == NULL) + return ZVERR_OUTOFMEM; + + zvect_index nb; + zvect_index ne; + nb = v->cap_left; + ne = ( nb + (v->end - v->begin) ); + if (v->end != v->begin) + p_vect_memcpy(new_data + nb, v->data + v->begin, sizeof(void *) * (v->end - v->begin) ); + + // Reconfigure vector: + v->cap_left = new_capacity; + v->end = ne; + v->begin = nb; + free(v->data); + + } else { + + // Set capacity on the right side of the vector to new_capacity: + new_data = (void **)realloc(v->data, sizeof(void *) * (v->cap_left + new_capacity)); + if (new_data == NULL) + return ZVERR_OUTOFMEM; + + // Reconfigure Vector: + v->cap_right = new_capacity; + } + + // Apply changes + v->data = new_data; + + // done + return 0; +} + +/* + * This function increase the CAPACITY of a vector. + */ +static zvect_retval p_vect_increase_capacity(vector const v, const zvect_index direction) { + zvect_index new_capacity; + + if (!direction) + { + + // Increase capacity on the left side of the vector: + + // Get actual left capacity and double it + // Note: "<< 1" is the same as "* 2" + // this is an optimisation for old + // compilers. + new_capacity = v->cap_left << 1; + + } else { + + // Increase capacity on the right side of the vector: + + // Get actual left capacity and double it + // Note: "<< 1" is the same as "* 2" + // this is an optimisation for old + // compilers. + new_capacity = v->cap_right << 1; + + } + + zvect_retval rval = p_vect_set_capacity(v, direction, new_capacity); + + // done + return rval; +} + +/* + * This function decrease the CAPACITY of a vector. + */ +static zvect_retval p_vect_decrease_capacity(vector const v, const zvect_index direction) { + // Check if new capacity is smaller than initial capacity + if ( p_vect_capacity(v) <= v->init_capacity) + return 0; + + zvect_index new_capacity; + + void **new_data = NULL; + if (!direction) + { + // Decreasing on the left: + + // Note: ">> 1" is the same as "/ 2" + // this is an optimisation for old + // compilers. + new_capacity = v->cap_left >> 1; + + if (new_capacity < ( v->init_capacity >> 1 )) + new_capacity = v->init_capacity >> 1; + + new_capacity = max( (p_vect_size(v) >> 1), new_capacity); + + new_data = (void **)malloc(sizeof(void *) * (new_capacity + v->cap_right)); + if (new_data == NULL) + return ZVERR_OUTOFMEM; + + zvect_index nb; + zvect_index ne; + nb = ( new_capacity >> 1); + ne = ( nb + (v->end - v->begin) ); + p_vect_memcpy(new_data + nb, v->data + v->begin, sizeof(void *) * (v->end - v->begin) ); + + // Reconfigure Vector: + v->cap_left = new_capacity; + v->end = ne; + v->begin = nb; + free(v->data); + + } else { + + // Decreasing on the right: + // Note: ">> 1" is the same as "/ 2" + // this is an optimisation for old + // compilers. + new_capacity = v->cap_right >> 1; + + if (new_capacity < ( v->init_capacity >> 1 )) + new_capacity = v->init_capacity >> 1; + + new_capacity = max( (p_vect_size(v) >> 1), new_capacity); + + new_data = (void **)realloc(v->data, sizeof(void *) * (v->cap_left + new_capacity)); + if (new_data == NULL) + return ZVERR_OUTOFMEM; + + // Reconfigure vector: + v->cap_right = new_capacity; + } + + // Apply changes + v->data = new_data; + + // done: + return 0; +} + +/* + * This function shrinks the CAPACITY of a vector + * not its size. To reduce the size of a vector we + * need to remove items from it. + */ +static zvect_retval p_vect_shrink(vector const v) { + if (v->init_capacity < 2) + v->init_capacity = 2; + + if (p_vect_capacity(v) == v->init_capacity || p_vect_capacity(v) <= p_vect_size(v)) + return 0; + + // Determine the correct shrunk size: + zvect_index new_capacity; + if (p_vect_size(v) < v->init_capacity) + new_capacity = v->init_capacity; + else + new_capacity = p_vect_size(v) + 2; + + // shrink the vector: + // Given that zvector supports vectors that can grow on the left and on the right + // I cannot use realloc here. + void **new_data = (void **)malloc(sizeof(void *) * new_capacity); + if (new_data == NULL) + return ZVERR_OUTOFMEM; + + zvect_index ne; + zvect_index nb; + // Note: ">> 1" is the same as "/ 2" + // this is an optimisation for old + // compilers. + nb = ( new_capacity >> 1 ); + ne = ( nb + (v->end - v->begin) ); + p_vect_memcpy(new_data + nb, v->data + v->begin, sizeof(void *) * (v->end - v->begin) ); + + // Apply changes: + free(v->data); + v->data = new_data; + v->end = ne; + v->begin = nb; + v->cap_left = new_capacity >> 1; + v->cap_right = new_capacity >> 1; + + // done: + return 0; +} + +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +// Creation and destruction primitives: + +zvect_retval p_vect_clear(vector const v) { + // Clear the vector: + if (!vect_is_empty(v)) + p_free_items(v, 0, (p_vect_size(v) - 1)); + + // Reset interested descriptors: + v->begin = v->end = 0; + + // Shrink Vector's capacity: + // p_vect_shrink(v); // commented this out to make vect_clear behave more like the clear method in C++ + + return 0; +} + +static zvect_retval p_vect_destroy(vector v, uint32_t flags) { + // p_destroy is an exception to the rule of handling + // locking from the public methods. This because + // p_destroy has to destroy the vector mutex too and + // so it needs to control the lock as well! +#if (ZVECT_THREAD_SAFE == 1) + zvect_retval lock_owner = (locking_disabled || (v->flags & ZV_NOLOCKING)) ? 0 : get_mutex_lock(v, 1); + if (!lock_owner && (!locking_disabled) && !(v->flags & ZV_NOLOCKING)) + return ZVERR_RACECOND; +#endif + + // Clear the vector (if LSB of flags is set to 1): + if ((p_vect_size(v) > 0) && (flags & 1)) { + // Clean the vector: + p_vect_clear(v); + + // Reset interested descriptors: + v->end = 0; + } + + // Destroy the vector: + v->init_capacity = v->cap_left = v->cap_right = 0; + + // Destroy it: + if (v->status & ZVS_CUST_WIPE_ON) + v->SfWpFunc = NULL; + + if (v->data != NULL) { + free(v->data); + v->data = NULL; + } + + // Clear vector status flags: + v->status = v->flags = v->begin = v->end = v->data_size = v->balance = v->bottom = 0; + +#if (ZVECT_THREAD_SAFE == 1) + if ( lock_owner ) + get_mutex_unlock(v, v->lock_type); + mutex_destroy(&(v->lock)); + semaphore_destroy(&(v->semaphore)); +#endif + + // All done and freed, so we can safely + // free the vector itself: + free(v); + + return 0; +} + +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +// Vector data storage primitives: + +// inline implementation for all put: +static inline zvect_retval p_vect_put_at(vector const v, const void *value, + const zvect_index i) { + // Check if the index passed is out of bounds: + zvect_index idx = i; + if (!(v->flags & ZV_CIRCULAR)) + { + if (idx >= p_vect_size(v)) + return ZVERR_IDXOUTOFBOUND; + } else { + if (idx >= p_vect_size(v)) + idx = i % v->init_capacity; + } + + // Add value at the specified index, considering + // if the vector has ZV_BYREF property enabled: + if ( v->flags & ZV_BYREF ) { + void *temp = v->data[v->begin + idx]; + v->data[v->begin + idx] = (void *)value; + if ( v->flags & ZV_SEC_WIPE ) + memset((void *)temp, 0, v->data_size); + } else { + p_vect_memcpy(v->data[v->begin + idx], value, v->data_size); + } + + // done + return 0; +} + +// inline implementation for all add(s): +static inline zvect_retval p_vect_add_at(vector const v, const void *value, + const zvect_index i) { + zvect_index idx = i; + + // Get vector size: + zvect_index vsize = p_vect_size(v); + +#if (ZVECT_FULL_REENTRANT == 1) + // If we are in FULL_REENTRANT MODE prepare for potential + // array copy: + void **new_data = NULL; + if (idx < vsize) { + new_data = (void **)malloc(sizeof(void *) * p_vect_capacity(v)); + if (new_data == NULL) + return ZVERR_OUTOFMEM; + } +#endif + + // Allocate memory for the new item: + zvect_index base = v->begin; + if (!idx) { + // Prepare left side of the vector: + if ( base ) + base--; + if (!(v->flags & ZV_BYREF)) { + v->data[base] = (void *)malloc(v->data_size); + if (v->data[base] == NULL) + return ZVERR_OUTOFMEM; + } + } else if (idx == vsize && !(v->flags & ZV_BYREF)) { + // Prepare right side of the vector: + v->data[base + vsize] = (void *)malloc(v->data_size); + if (v->data[base + vsize] == NULL) + return ZVERR_OUTOFMEM; + } + + // "Shift" right the array of one position to make space for the new item: + int16_t array_changed = 0; + + if ((idx < vsize) && (idx != 0)) { + array_changed = 1; +#if (ZVECT_FULL_REENTRANT == 1) + // Algorithm to try to copy an array of pointers as fast as possible: + if (idx > 0) + p_vect_memcpy(new_data + base, v->data + base, sizeof(void *) * idx); + p_vect_memcpy(new_data + base + (idx + 1), v->data + base + idx, + sizeof(void *) * (vsize - idx)); +#else + // We can't use the vect_memcpy when not in full reentrant code + // because it's not safe to use it on the same src and dst. + p_vect_memmove(v->data + base + (idx + 1), v->data + base + idx, + sizeof(void *) * (vsize - idx)); +#endif // (ZVECT_FULL_REENTRANT == 1) + } + + // Add new value in (at the index i): +#if (ZVECT_FULL_REENTRANT == 1) + if (array_changed) { + if (v->flags & ZV_BYREF) { + new_data[base + idx] = (void *)value; + } else { + new_data[base + idx] = (void *)malloc(v->data_size); + if (new_data[base + idx] == NULL) + return ZVERR_OUTOFMEM; + p_vect_memcpy(new_data[base + idx], value, v->data_size); + } + } else { + if (v->flags & ZV_BYREF) + v->data[base + idx] = (void *)value; + else + p_vect_memcpy(v->data[base + idx], value, v->data_size); + } +#else + if (array_changed && !(v->flags & ZV_BYREF)) { + // We moved chunks of memory, so we need to + // allocate new memory for the item at position i: + v->data[base + idx] = malloc(v->data_size); + if (v->data[base + idx] == NULL) + return ZVERR_OUTOFMEM; + } + if (v->flags & ZV_BYREF) + v->data[base + idx] = (void *)value; + else + p_vect_memcpy(v->data[base + idx], value, v->data_size); +#endif // (ZVECT_FULL_REENTRANT == 1) + + // Apply changes: +#if (ZVECT_FULL_REENTRANT == 1) + if (array_changed) { + free(v->data); + v->data = new_data; + } +#endif + // Increment vector size + if (!idx) { + if (v->begin == base) + v->end++; + else + v->begin = base; + } else { + v->end++; + } + + // done + return 0; + +#if (ZVECT_FULL_REENTRANT == 1) + UNUSED(new_data); +#endif +} + +// This is the inline implementation for all the remove and pop +static inline zvect_retval p_vect_remove_at(vector const v, const zvect_index i, void **item) { + zvect_index idx = i; + + // Get the vector size: + zvect_index vsize = p_vect_size(v); + + // Check if the index is out of bounds: + if (idx >= vsize) { + if (!(v->flags & ZV_CIRCULAR)) { + return ZVERR_IDXOUTOFBOUND; + } else { + idx = idx % vsize; + } + } + + // Check if the vector got corrupted + if ((v->end != 0) && (v->begin > v->end)) + return ZVERR_VECTCORRUPTED; + + // Start processing the vector: +#if (ZVECT_FULL_REENTRANT == 1) + // Allocate memory for support Data Structure: + void **new_data = (void **)malloc(sizeof(void *) * p_vect_capacity(v)); + if (new_data == NULL) + return ZVERR_OUTOFMEM; +#endif + + // Get the value we are about to remove: + // If the vector is set as ZV_BYREF, then just copy the pointer to the item + // If the vector is set as regular, then copy the item + zvect_index base = v->begin; + if (v->flags & ZV_BYREF) { + *item = v->data[base + idx]; + } else { + *item = (void **)malloc(v->data_size); + if ( v->data[base + idx] != NULL ) + { + p_vect_memcpy(*item, v->data[base + idx], v->data_size); + + // If the vector is set for secure wipe, and we copied the item + // then we need to wipe the old copy: + if (v->flags & ZV_SEC_WIPE) + p_item_safewipe(v, v->data[base + idx]); + } /* else { + memset(item, 0, v->data_size - 1); + } */ + } + + // "shift" left the array of one position: + uint16_t array_changed; + array_changed = 0; + if ( idx != 0 ) { + if ((idx < (vsize - 1)) && (vsize > 0)) { + array_changed = 1; + free(v->data[base + idx]); +#if (ZVECT_FULL_REENTRANT == 1) + p_vect_memcpy(new_data + base, v->data + base, sizeof(void *) * idx); + p_vect_memcpy(new_data + base + idx, v->data + base + (idx + 1), + sizeof(void *) * (vsize - idx)); +#else + // We can't use the vect_memcpy when not in full reentrant code + // because it's not safe to use it on the same src and dst. + p_vect_memmove(v->data + (base + idx), v->data + (base + (idx + 1)), + sizeof(void *) * (vsize - idx)); + + // Clear leftover item pointers: + memset(v->data[(v->begin + vsize) - 1], 0, 1); +#endif + } + } else { + if ( base < v->end ) { + array_changed = 1; + if (v->data[base] != NULL) + free(v->data[base]); + } + } + + // Reduce vector size: +#if (ZVECT_FULL_REENTRANT == 0) + if (!(v->flags & ZV_BYREF) && !array_changed) + p_free_items(v, vsize - 1, 0); +#else + // Apply changes + if (array_changed) { + free(v->data); + v->data = new_data; + } +#endif + if (!(v->flags & ZV_CIRCULAR)) { + if ( idx != 0 ) { + if (v->end > v->begin) { + v->end--; + } else { + v->end = v->begin; + } + } else { + if (v->begin < v->end) { + v->begin++; + } else { + v->begin = v->end; + } + } + // Check if we need to shrink vector's capacity: + if ((4 * vsize) < p_vect_capacity(v) ) + p_vect_decrease_capacity(v, idx); + } + + // All done, return control: + return 0; +} + +// This is the inline implementation for all the "delete" methods +static inline zvect_retval p_vect_delete_at(vector const v, const zvect_index start, + const zvect_index offset, uint32_t flags) { + + zvect_index vsize = p_vect_size(v); + + // If the vector is empty just return + if (vsize == 0) + return ZVERR_VECTEMPTY; + + // Check if the index is out of bounds: + if ((start + offset) > vsize) + return ZVERR_IDXOUTOFBOUND; + + uint16_t array_changed = 0; + + // "shift" left the data of one position: + zvect_index tot_items = start + offset; +#ifdef DEBUG + /* + log_msg(ZVLP_INFO, "p_vect_delete_at: start %*u\n", 14, start); + log_msg(ZVLP_INFO, "p_vect_delete_at: offset %*u\n", 14, offset); + log_msg(ZVLP_INFO, "p_vect_delete_at: tot_items %*u\n", 14, tot_items); + log_msg(ZVLP_INFO, "p_vect_delete_at: v->begin %*u\n", 14, v->begin); + log_msg(ZVLP_INFO, "p_vect_delete_at: v->end %*u\n", 14, v->end); + log_msg(ZVLP_INFO, "p_vect_delete_at: vsize %*u\n", 14, vsize); + log_msg(ZVLP_INFO, "p_vect_delete_at: data %*p\n", 14, v->data); + */ +#endif + if ( (vsize > 1) && (start < (vsize - 1)) && (tot_items < vsize) && (v->data != NULL) ) { + array_changed = 1; +#ifdef DEBUG + /* for (zvect_index ptrID = start; ptrID < start + offset; ptrID++) + { + log_msg(ZVLP_INFO, "p_vect_delete_at: data ptr %*p\n", 14, v->data + (v->begin + ptrID)); + }*/ +#endif + // Safe erase items? + if ( flags & 1 ) + p_free_items(v, start, offset); + + // Move remaining items pointers up: + if ( tot_items ) + p_vect_memmove(v->data + (v->begin + start), v->data + (v->begin + tot_items + 1), + sizeof(void *) * ((vsize - start) - offset)); + + // Clear leftover item pointers: + if ( offset && !(flags & 1)) + memset(v->data + ((v->begin + vsize) - (offset + 1)), 0, offset); + } + + // Reduce vector size: + if (!(v->flags & ZV_BYREF) && (flags & 1) && !array_changed) + p_free_items(v, ((vsize - 1) - offset), offset); + + // Check if we need to increment begin or decrement end + // depending on the direction of the "delete" (left or right) + if ( start != 0 || array_changed ) { + if ((v->end - (offset + 1)) > v->begin) { + v->end -= (offset + 1); + } else { + v->end = v->begin; + } + } else { + if ((v->begin + (offset + 1)) < v->end) { + v->begin += (offset + 1); + } else { + v->begin = v->end; + } + } + + if (v->begin == v->end) + { + v->begin = 0; + v->end = 0; + } + + // Check if we need to shrink the vector: + if ((4 * vsize) < p_vect_capacity(v)) + p_vect_decrease_capacity(v, start); + + // All done, return control: + return 0; +} + +/*---------------------------------------------------------------------------*/ + + + + +/***************************************************************************** + ** ZVector API ** + *****************************************************************************/ + +/*---------------------------------------------------------------------------*/ +// Vector Size and Capacity management functions: + +/* + * Public method to request ZVector to + * shrink a vector. + */ +void vect_shrink(vector const v) { +#if (ZVECT_THREAD_SAFE == 1) + zvect_retval lock_owner = get_mutex_lock(v, 1); +#endif + + zvect_retval rval = p_vect_shrink(v); + +#if (ZVECT_THREAD_SAFE == 1) + if (lock_owner) + get_mutex_unlock(v, 1); +#endif + if (rval) + p_throw_error(rval, NULL); +} + +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +// Vector Structural Information report: + +bool vect_is_empty(vector const v) { + return !p_vect_check(v) ? (p_vect_size(v) == 0) : (bool)ZVERR_VECTUNDEF; +} + +zvect_index vect_size(vector const v) { + return !p_vect_check(v) ? p_vect_size(v) : 0; +} + +zvect_index vect_max_size(vector const v) { + return !p_vect_check(v) ? zvect_index_max : 0; +} + +void *vect_begin(vector const v) { + return !p_vect_check(v) ? v->data[v->begin] : NULL; +} + +void *vect_end(vector const v) { + return !p_vect_check(v) ? v->data[v->end] : NULL; +} + +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +// Vector Creation and Destruction: + +vector vect_create(const zvect_index init_capacity, const size_t item_size, + const uint32_t properties) { + // If ZVector has not been initialised yet, then initialise it + // when creating the first vector: + if (p_init_state == 0) + p_init_zvect(); + + // Create the vector first: + vector v = (vector)malloc(sizeof(struct p_vector)); + if (v == NULL) + p_throw_error(ZVERR_OUTOFMEM, NULL); + + // Initialize the vector: + v->end = 0; + if (item_size == 0) + v->data_size = ZVECT_DEFAULT_DATA_SIZE; + else + v->data_size = item_size; + + zvect_index capacity = init_capacity; + if (init_capacity == 0) + { + v->cap_left = ZVECT_INITIAL_CAPACITY / 2; + v->cap_right= ZVECT_INITIAL_CAPACITY / 2; + } else { + if (init_capacity <= 4) + capacity = 4; + + v->cap_left = capacity / 2; + v->cap_right= capacity / 2; + } + v->begin = v->end = 0; + + v->init_capacity = v->cap_left + v->cap_right; + v->flags = properties; + v->SfWpFunc = NULL; + v->status = 0; + if (v->flags & ZV_CIRCULAR) + { + // If the vector is circular then + // we need to pre-allocate all the elements + // the vector will not grow: + v->end = v->cap_right - 1; + v->begin = v->cap_left - 1; + } +#ifdef ZVECT_DMF_EXTENSIONS + v->balance = v->bottom = 0; +#endif // ZVECT_DMF_EXTENSIONS + + v->data = NULL; + +#if (ZVECT_THREAD_SAFE == 1) + v->lock_type = 0; + mutex_init(&(v->lock)); + mutex_cond_init(&(v->cond)); + semaphore_init(&(v->semaphore), 0); +#endif + + // Allocate memory for the vector storage area + v->data = (void **)calloc(p_vect_capacity(v), sizeof(void *)); + if (v->data == NULL) + p_throw_error(ZVERR_OUTOFMEM, NULL); + + // Return the vector to the user: + return v; +} + +void vect_destroy(vector v) { + // Call p_vect_destroy with flags set to 1 + // to destroy data according to the vector + // properties: + zvect_retval rval = p_vect_check(v); + if (rval) + goto JOB_DONE; + + rval = p_vect_destroy(v, 1); + +JOB_DONE: + if (rval) + p_throw_error(rval, NULL); +} + +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +// Vector Thread Safe user functions: + +#if (ZVECT_THREAD_SAFE == 1) +void vect_lock_enable(void) { + locking_disabled = false; +} + +void vect_lock_disable(void) { + locking_disabled = true; +} + +static inline zvect_retval p_vect_lock(vector const v) { + return (locking_disabled || (v->flags & ZV_NOLOCKING)) ? 0 : get_mutex_lock(v, 3); +} + +zvect_retval vect_lock(vector const v) { + return p_vect_lock(v); +} + +static inline zvect_retval p_vect_unlock(vector const v) { + return (locking_disabled || (v->flags & ZV_NOLOCKING)) ? 0 : get_mutex_unlock(v, 3); +} + +zvect_retval vect_unlock(vector const v) { + return p_vect_unlock(v); +} + +static inline zvect_retval p_vect_trylock(vector const v) { + return (locking_disabled || (v->flags & ZV_NOLOCKING)) ? 0 : check_mutex_trylock(v, 3); +} + +zvect_retval vect_trylock(vector const v) { + return p_vect_trylock(v); +} + +zvect_retval vect_sem_wait(const vector v) { +#if !defined(macOS) + return sem_wait(&(v->semaphore)); +#else + return dispatch_semaphore_wait(v->semaphore, DISPATCH_TIME_FOREVER); +#endif +} + +zvect_retval vect_sem_post(const vector v) { +# if !defined(macOS) + return sem_post(&(v->semaphore)); +# else + return dispatch_semaphore_signal(v->semaphore); +# endif +} + +/* +static inline zvect_retval p_vect_wait_for_signal(const vector v) { + return (locking_disabled || (v->flags & ZV_NOLOCKING)) ? 1 : wait_for_signal(v, 3); +} + +inline zvect_retval vect_wait_for_signal(const vector v) { + return p_vect_wait_for_signal(v); +} +*/ + +static inline zvect_retval p_vect_lock_after_signal(const vector v) { + return (locking_disabled || (v->flags & ZV_NOLOCKING)) ? 1 : lock_after_signal(v, 3); +} + +zvect_retval vect_lock_after_signal(const vector v) { + return p_vect_lock_after_signal(v); +} + +zvect_retval vect_send_signal(const vector v) { + return send_signal(v, 3); +} + +zvect_retval vect_broadcast_signal(const vector v) { + return broadcast_signal(v, 3); +} + +#endif + +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +// Vector Data Storage functions: + +void vect_clear(vector const v) { + // check if the vector exists: + zvect_retval rval = p_vect_check(v); + if (rval) + goto JOB_DONE; + +#if (ZVECT_THREAD_SAFE == 1) + zvect_retval lock_owner = (locking_disabled || (v->flags & ZV_NOLOCKING)) ? 0 : get_mutex_lock(v, 1); +#endif + + rval = p_vect_clear(v); + +//DONE_PROCESSING: +#if (ZVECT_THREAD_SAFE == 1) + if (lock_owner) + get_mutex_unlock(v, 1); +#endif + +JOB_DONE: + if (rval) + p_throw_error(rval, NULL); +} + +void vect_set_wipefunct(const vector v, void (*f1)(const void *, size_t)) { + v->SfWpFunc = (void *)malloc(sizeof(void *)); + if (v->SfWpFunc == NULL) + p_throw_error(ZVERR_OUTOFMEM, NULL); + + // Set custom Safe Wipe function: + v->SfWpFunc = f1; + // p_vect_memcpy(v->SfWpFunc, f1, sizeof(void *)); + v->status |= ZVS_CUST_WIPE_ON; +} + +// Add an item at the END (top) of the vector +inline void vect_push(const vector v, const void *value) { + zvect_retval rval = p_vect_check(v); + if (rval) + goto JOB_DONE; + + // If the vector is circular then use vect_put_at + // instead: + if (v->flags & ZV_CIRCULAR) { + rval = p_vect_put_at(v, value, p_vect_size(v)); + goto JOB_DONE; + } + +#if (ZVECT_THREAD_SAFE == 1) + zvect_retval lock_owner = (locking_disabled || (v->flags & ZV_NOLOCKING)) ? 0 : get_mutex_lock(v, 1); +#endif + + // The very first time we do a push, if the vector is + // declared very small, we may need to expand its + // capacity on the left. This because the very first + // push will use index 0 and the rule in ZVector is + // when we use index 0 we may need to expand on the + // left. So let's check if we do for this vector: + zvect_index vsize = p_vect_size(v); + + // Check if we need to expand on thr right side: + if ( (v->end >= v->cap_right) && ((rval = p_vect_increase_capacity(v, 1)) != 0) ) + goto DONE_PROCESSING; + + rval = p_vect_add_at(v, value, vsize); + +DONE_PROCESSING: +#if (ZVECT_THREAD_SAFE == 1) + if (lock_owner) + get_mutex_unlock(v, 1); +#endif + +JOB_DONE: + if (rval) + p_throw_error(rval, NULL); +} + +// Add an item at the END of the vector +void vect_add(const vector v, const void *value) { + vect_push(v, value); +} + +// Add an item at position "i" of the vector +void vect_add_at(const vector v, const void *value, const zvect_index i) { + zvect_retval rval = p_vect_check(v); + if (rval) + goto JOB_DONE; + + // If the vector is circular then use vect_put_at + // instead: + if (v->flags & ZV_CIRCULAR) { + rval = p_vect_put_at(v, value, i); + goto JOB_DONE; + } + +#if (ZVECT_THREAD_SAFE == 1) + zvect_retval lock_owner = (locking_disabled || (v->flags & ZV_NOLOCKING)) ? 0 : get_mutex_lock(v, 1); +#endif + + // Check if the provided index is out of bounds: + if (i > p_vect_size(v)) + { + rval = ZVERR_IDXOUTOFBOUND; + goto DONE_PROCESSING; + } + + if (!i) { + // Check if we need to expand on the left side: + if ( (v->begin == 0 || v->cap_left <= 1) && ( (rval = p_vect_increase_capacity(v, 0)) != 0 ) ) + goto DONE_PROCESSING; + } else { + // Check if we need to expand on thr right side: + if ( (v->end >= v->cap_right ) && ( (rval = p_vect_increase_capacity(v, 1)) != 0 ) ) + goto DONE_PROCESSING; + } + rval = p_vect_add_at(v, value, i); + +DONE_PROCESSING: +#if (ZVECT_THREAD_SAFE == 1) + if (lock_owner) + get_mutex_unlock(v, 1); +#endif + +JOB_DONE: + if (rval) + p_throw_error(rval, NULL); +} + +// Add an item at the FRONT of the vector +void vect_add_front(vector const v, const void *value) { + zvect_retval rval = p_vect_check(v); + if (rval) + goto JOB_DONE; + + // If the vector is circular then use vect_put_at + // instead: + if (v->flags & ZV_CIRCULAR) { + rval = p_vect_put_at(v, value, 0); + goto JOB_DONE; + } + +#if (ZVECT_THREAD_SAFE == 1) + zvect_retval lock_owner = (locking_disabled || (v->flags & ZV_NOLOCKING)) ? 0 : get_mutex_lock(v, 1); +#endif + + // Check if we need to expand on the left side: + if ( (v->begin == 0 || v->cap_left <= 1 ) && ( (rval = p_vect_increase_capacity(v, 0)) != 0 ) ) + goto DONE_PROCESSING; + + rval = p_vect_add_at(v, value, 0); + +DONE_PROCESSING: +#if (ZVECT_THREAD_SAFE == 1) + if (lock_owner) + get_mutex_unlock(v, 1); +#endif + +JOB_DONE: + if (rval) + p_throw_error(rval, NULL); +} + +// inline implementation for all get(s): +static inline void *p_vect_get_at(vector const v, const zvect_index i) { + // Check if passed index is out of bounds: + if (i >= p_vect_size(v)) + p_throw_error(ZVERR_IDXOUTOFBOUND, NULL); + + // Return found element: + return v->data[v->begin + i]; +} + +void *vect_get(vector const v) { + // check if the vector exists: + zvect_retval rval = p_vect_check(v); + if (!rval) + return p_vect_get_at(v, p_vect_size(v) - 1); + else + p_throw_error(rval, NULL); + + return NULL; +} + +void *vect_get_at(vector const v, const zvect_index i) { + // check if the vector exists: + zvect_retval rval = p_vect_check(v); + if (!rval) + return p_vect_get_at(v, i); + else + p_throw_error(rval, NULL); + + return NULL; +} + +void *vect_get_front(vector const v) { + // check if the vector exists: + zvect_retval rval = p_vect_check(v); + if (!rval) + return p_vect_get_at(v, 0); + else + p_throw_error(rval, NULL); + + return NULL; +} + +void vect_put(vector const v, const void *value) { + zvect_retval rval = p_vect_check(v); + if (rval) + goto JOB_DONE; + +#if (ZVECT_THREAD_SAFE == 1) + zvect_retval lock_owner = (locking_disabled || (v->flags & ZV_NOLOCKING)) ? 0 : get_mutex_lock(v, 1); +#endif + + rval = p_vect_put_at(v, value, p_vect_size(v) - 1); + +#if (ZVECT_THREAD_SAFE == 1) + if (lock_owner) + get_mutex_unlock(v, 1); +#endif + +JOB_DONE: + if (rval) + p_throw_error(rval, NULL); +} + +void vect_put_at(vector const v, const void *value, const zvect_index i) { + zvect_retval rval = p_vect_check(v); + if (rval) + goto JOB_DONE; + +#if (ZVECT_THREAD_SAFE == 1) + zvect_retval lock_owner = (locking_disabled || (v->flags & ZV_NOLOCKING)) ? 0 : get_mutex_lock(v, 1); +#endif + + rval = p_vect_put_at(v, value, i); + +#if (ZVECT_THREAD_SAFE == 1) + if (lock_owner) + get_mutex_unlock(v, 1); +#endif + +JOB_DONE: + if (rval) + p_throw_error(rval, NULL); +} + +void vect_put_front(vector const v, const void *value) { + zvect_retval rval = p_vect_check(v); + if (rval) + goto JOB_DONE; + +#if (ZVECT_THREAD_SAFE == 1) + zvect_retval lock_owner = (locking_disabled || (v->flags & ZV_NOLOCKING)) ? 0 : get_mutex_lock(v, 1); +#endif + + rval = p_vect_put_at(v, value, 0); + +#if (ZVECT_THREAD_SAFE == 1) + if (lock_owner) + get_mutex_unlock(v, 1); +#endif + +JOB_DONE: + if (rval) + p_throw_error(rval, NULL); +} + +inline void *vect_pop(vector const v) { + void *item = NULL; + zvect_retval rval = p_vect_check(v); + if (rval) + goto JOB_DONE; + +#if (ZVECT_THREAD_SAFE == 1) + zvect_retval lock_owner = (locking_disabled || (v->flags & ZV_NOLOCKING)) ? 0 : get_mutex_lock(v, 1); +#endif + + zvect_index vsize = p_vect_size(v); + if (vsize != 0) + rval = p_vect_remove_at(v, vsize - 1, &item); + +#if (ZVECT_THREAD_SAFE == 1) + if (lock_owner) + get_mutex_unlock(v, 1); +#endif + +JOB_DONE: + if (rval) + p_throw_error(rval, NULL); + + return item; +} + +void *vect_remove(vector const v) { + void *item = NULL; + zvect_retval rval = p_vect_check(v); + if (rval) + goto JOB_DONE; + +#if (ZVECT_THREAD_SAFE == 1) + zvect_retval lock_owner = (locking_disabled || (v->flags & ZV_NOLOCKING)) ? 0 : get_mutex_lock(v, 1); +#endif + + zvect_index vsize = p_vect_size(v); + if (vsize != 0) + rval = p_vect_remove_at(v, vsize - 1, &item); + +#if (ZVECT_THREAD_SAFE == 1) + if (lock_owner) + get_mutex_unlock(v, 1); +#endif + +JOB_DONE: + if (rval) + p_throw_error(rval, NULL); + + return item; +} + +void *vect_remove_at(vector const v, const zvect_index i) { + void *item = NULL; + zvect_retval rval = p_vect_check(v); + if (rval) + goto JOB_DONE; + +#if (ZVECT_THREAD_SAFE == 1) + zvect_retval lock_owner = (locking_disabled || (v->flags & ZV_NOLOCKING)) ? 0 : get_mutex_lock(v, 1); +#endif + + if (p_vect_size(v) != 0) + rval = p_vect_remove_at(v, i, &item); + +#if (ZVECT_THREAD_SAFE == 1) + if (lock_owner) + get_mutex_unlock(v, 1); +#endif + +JOB_DONE: + if (rval) + p_throw_error(rval, NULL); + + return item; +} + +void *vect_remove_front(vector const v) { + void *item = NULL; + zvect_retval rval = p_vect_check(v); + if (rval) + goto JOB_DONE; + +#if (ZVECT_THREAD_SAFE == 1) + zvect_retval lock_owner = (locking_disabled || (v->flags & ZV_NOLOCKING)) ? 0 : get_mutex_lock(v, 1); +#endif + + if (p_vect_size(v) != 0) + rval = p_vect_remove_at(v, 0, &item); + +#if (ZVECT_THREAD_SAFE == 1) + if (lock_owner) + get_mutex_unlock(v, 1); +#endif + +JOB_DONE: + if (rval) + p_throw_error(rval, NULL); + + return item; +} + +// Delete an item at the END of the vector +void vect_delete(vector const v) { + zvect_retval rval = p_vect_check(v); + if (rval) + goto JOB_DONE; + +#if (ZVECT_THREAD_SAFE == 1) + zvect_retval lock_owner = (locking_disabled || (v->flags & ZV_NOLOCKING)) ? 0 : get_mutex_lock(v, 1); +#endif + + rval = p_vect_delete_at(v, p_vect_size(v) - 1, 0, 1); + +#if (ZVECT_THREAD_SAFE == 1) + if (lock_owner) + get_mutex_unlock(v, 1); +#endif + +JOB_DONE: + if (rval) + p_throw_error(rval, NULL); +} + +// Delete an item at position "i" on the vector +void vect_delete_at(const vector v, const zvect_index i) { + zvect_retval rval = p_vect_check(v); + if (rval) + goto JOB_DONE; + +#if (ZVECT_THREAD_SAFE == 1) + zvect_retval lock_owner = (locking_disabled || (v->flags & ZV_NOLOCKING)) ? 0 : get_mutex_lock(v, 1); +#endif + + rval = p_vect_delete_at(v, i, 0, 1); + +#if (ZVECT_THREAD_SAFE == 1) + if (lock_owner) + get_mutex_unlock(v, 1); +#endif + +JOB_DONE: + if (rval) + p_throw_error(rval, NULL); +} + +// Delete a range of items from "first_element" to "last_element" on the vector v +void vect_delete_range(vector const v, const zvect_index first_element, + const zvect_index last_element) { + zvect_retval rval = p_vect_check(v); + if (rval) + goto JOB_DONE; + +#if (ZVECT_THREAD_SAFE == 1) + zvect_retval lock_owner = (locking_disabled || (v->flags & ZV_NOLOCKING)) ? 0 : get_mutex_lock(v, 1); +#endif + + zvect_index end = (last_element - first_element); + rval = p_vect_delete_at(v, first_element, end, 1); + +#if (ZVECT_THREAD_SAFE == 1) + if (lock_owner) + get_mutex_unlock(v, 1); +#endif + +JOB_DONE: + if (rval) + p_throw_error(rval, NULL); +} + +// Delete an item at the BEGINNING of a vector v +void vect_delete_front(vector const v) { + zvect_retval rval = p_vect_check(v); + if (rval) + goto JOB_DONE; + +#if (ZVECT_THREAD_SAFE == 1) + zvect_retval lock_owner = (locking_disabled || (v->flags & ZV_NOLOCKING)) ? 0 : get_mutex_lock(v, 1); +#endif + + rval = p_vect_delete_at(v, 0, 0, 1); + +#if (ZVECT_THREAD_SAFE == 1) + if (lock_owner) + get_mutex_unlock(v, 1); +#endif + +JOB_DONE: + if (rval) + p_throw_error(rval, NULL); +} + +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +// Vector Data Manipulation functions +#ifdef ZVECT_DMF_EXTENSIONS + +void vect_swap(vector const v, const zvect_index i1, const zvect_index i2) { + // Check parameters: + if (i1 == i2) + return; + + // check if the vector exists: + zvect_retval rval = p_vect_check(v); + if (rval) + goto JOB_DONE; + +#if (ZVECT_THREAD_SAFE == 1) + zvect_retval lock_owner = (locking_disabled || (v->flags & ZV_NOLOCKING)) ? 0 : get_mutex_lock(v, 1); +#endif + + // Check parameters: + zvect_index vsize = p_vect_size(v); + if (i1 > vsize || i2 > vsize) { + rval = ZVERR_IDXOUTOFBOUND; + goto DONE_PROCESSING; + } + + // Let's swap items: + void *temp = v->data[v->begin + i2]; + v->data[v->begin + i2] = v->data[v->begin + i1]; + v->data[v->begin + i1] = temp; + +DONE_PROCESSING: +#if (ZVECT_THREAD_SAFE == 1) + if (lock_owner) + get_mutex_unlock(v, 1); +#endif + +JOB_DONE: + if (rval) + p_throw_error(rval, NULL); +} + +void vect_swap_range(vector const v, const zvect_index s1, const zvect_index e1, + const zvect_index s2) { + // Check parameters: + if (s1 == s2) + return; + + zvect_index end = e1; + if (e1 != 0) + end = e1 - s1; + + // check if the vector exists: + zvect_retval rval = p_vect_check(v); + if (rval) + goto JOB_DONE; + +#if (ZVECT_THREAD_SAFE == 1) + zvect_retval lock_owner = (locking_disabled || (v->flags & ZV_NOLOCKING)) ? 0 : get_mutex_lock(v, 1); +#endif + + zvect_index vsize = p_vect_size(v); + if ((s1 + end) > vsize || (s2 + end) > vsize || s2 < (s1 + end)) { + rval = ZVERR_IDXOUTOFBOUND; + goto DONE_PROCESSING; + } + + // Let's swap items: + register zvect_index i; + for (register zvect_index j = s1; j <= (s1 + end); j++) { + i = j - s1; + void *temp = v->data[v->begin + j]; + v->data[v->begin + j] = v->data[v->begin + (s2 + i)]; + v->data[v->begin + (s2 + i)] = temp; + } + +DONE_PROCESSING: +#if (ZVECT_THREAD_SAFE == 1) + if (lock_owner) + get_mutex_unlock(v, 1); +#endif + +JOB_DONE: + if (rval) + p_throw_error(rval, NULL); +} + +void vect_rotate_left(vector const v, const zvect_index i) { + + // check if the vector exists: + zvect_retval rval = p_vect_check(v); + if (rval) + goto JOB_DONE; + +#if (ZVECT_THREAD_SAFE == 1) + zvect_retval lock_owner = (locking_disabled || (v->flags & ZV_NOLOCKING)) ? 0 : get_mutex_lock(v, 1); +#endif + + // Check parameters: + zvect_index vsize = p_vect_size(v); + if (i == 0 || i == vsize) + goto DONE_PROCESSING; + + if (i > vsize) { + rval = ZVERR_IDXOUTOFBOUND; + goto DONE_PROCESSING; + } + + // Process the vector + if (i == 1) { + // Rotate left the vector of 1 position: + void *temp = v->data[0]; + p_vect_memmove(v->data, v->data + 1, sizeof(void *) * (vsize - 1)); + v->data[vsize - 1] = temp; + } else { + void **new_data = (void **)malloc(sizeof(void *) * p_vect_capacity(v)); + if (new_data == NULL) { + rval = ZVERR_OUTOFMEM; + goto DONE_PROCESSING; + } + // Rotate left the vector of "i" positions: + p_vect_memcpy(new_data + v->begin, v->data + v->begin, sizeof(void *) * i); + p_vect_memmove(v->data + v->begin, v->data + v->begin + i, sizeof(void *) * (vsize - i)); + p_vect_memcpy(v->data + v->begin + (vsize - i), new_data + v->begin, sizeof(void *) * i); + + free(new_data); + } + +DONE_PROCESSING: +#if (ZVECT_THREAD_SAFE == 1) + if (lock_owner) + get_mutex_unlock(v, 1); +#endif + +JOB_DONE: + if (rval) + p_throw_error(rval, NULL); +} + +void vect_rotate_right(vector const v, const zvect_index i) { + // check if the vector exists: + zvect_retval rval = p_vect_check(v); + if (rval) + goto JOB_DONE; + +#if (ZVECT_THREAD_SAFE == 1) + zvect_retval lock_owner = (locking_disabled || (v->flags & ZV_NOLOCKING)) ? 0 : get_mutex_lock(v, 1); +#endif + + // Check parameters: + zvect_index vsize = p_vect_size(v); + if (i == 0 || i == vsize) + goto DONE_PROCESSING; + + if (i > vsize) { + rval = ZVERR_IDXOUTOFBOUND; + goto DONE_PROCESSING; + } + + // Process the vector + if (i == 1) { + // Rotate right the vector of 1 position: + void *temp = v->data[v->begin + p_vect_size(v) - 1]; + p_vect_memmove(v->data + v->begin + 1, v->data + v->begin, sizeof(void *) * (vsize - 1)); + v->data[v->begin] = temp; + } else { + void **new_data = (void **)malloc(sizeof(void *) * p_vect_capacity(v)); + if (new_data == NULL) { + rval = ZVERR_OUTOFMEM; + goto DONE_PROCESSING; + } + + // Rotate right the vector of "i" positions: + p_vect_memcpy(new_data + v->begin, v->data + v->begin + (p_vect_size(v) - i), sizeof(void *) * i); + p_vect_memmove(v->data + v->begin + i, v->data + v->begin, sizeof(void *) * (vsize - i)); + p_vect_memcpy(v->data + v->begin, new_data + v->begin, sizeof(void *) * i); + + free(new_data); + } + +DONE_PROCESSING: +#if (ZVECT_THREAD_SAFE == 1) + if (lock_owner) + get_mutex_unlock(v, 1); +#endif + +JOB_DONE: + if (rval) + p_throw_error(rval, NULL); +} + +#ifdef TRADITIONAL_QSORT +static inline zvect_index +p_partition(vector v, zvect_index low, zvect_index high, + int (*compare_func)(const void *, const void *)) { + if (high >= p_vect_size(v)) + high = p_vect_size(v) - 1; + + void *pivot = v->data[v->begin + high]; + zvect_index i = (low - 1); + + for (register zvect_index j = low; j <= (high - 1); j++) { + // v->data[j] <= pivot + if ((*compare_func)(v->data[v->begin + j], pivot) <= 0) + vect_swap(v, ++i, j); + } + + vect_swap(v, i + 1, high); + return (i + 1); +} + +static void p_vect_qsort(vector v, zvect_index low, zvect_index high, + int (*compare_func)(const void *, const void *)) { + if (low < high) { + zvect_index pi = p_partition(v, low, high, compare_func); + p_vect_qsort(v, low, pi - 1, compare_func); + p_vect_qsort(v, pi + 1, high, compare_func); + } +} +#endif // TRADITIONAL_QSORT + +#ifndef TRADITIONAL_QSORT +// This is my much faster implementation of a quicksort algorithm +// it fundamentally uses the 3 ways partitioning adapted and improved +// to deal with arrays of pointers together with having a custom +// compare function: +static void p_vect_qsort(const vector v, zvect_index l, zvect_index r, + int (*compare_func)(const void *, const void *)) { + if (r <= l) + return; + + zvect_index i; + zvect_index p; + zvect_index j; + zvect_index q; + void *ref_val = NULL; + + // l = left (also low) + if (l > 0) + i = l - 1; + else + i = l; + // r = right (also high) + j = r; + p = i; + q = r; + ref_val = v->data[v->begin + r]; + + for (;;) { + while ((*compare_func)(v->data[v->begin + i], ref_val) < 0) + i++; + while ((*compare_func)(ref_val, v->data[(--j) + v->begin]) < 0) + if (j == l) + break; + if (i >= j) + break; + vect_swap(v, i, j); + if ((*compare_func)(v->data[v->begin + i], ref_val) == 0) { + p++; + vect_swap(v, p, i); + } + if ((*compare_func)(ref_val, v->data[v->begin + j]) == 0) { + q--; + vect_swap(v, q, j); + } + } + vect_swap(v, i, r); + j = i - 1; + i = i + 1; + register zvect_index k; + for (k = l; k < p; k++, j--) + vect_swap(v, k, j); + for (k = r - 1; k > q; k--, i++) + vect_swap(v, k, i); + p_vect_qsort(v, l, j, compare_func); + p_vect_qsort(v, i, r, compare_func); +} +#endif // ! TRADITIONAL_QSORT + +void vect_qsort(const vector v, int (*compare_func)(const void *, const void *)) { + // Check parameters: + if ( compare_func == NULL ) + return; + + zvect_retval rval = p_vect_check(v); + if (rval) + goto JOB_DONE; + +#if (ZVECT_THREAD_SAFE == 1) + zvect_retval lock_owner = (locking_disabled || (v->flags & ZV_NOLOCKING)) ? 0 : get_mutex_lock(v, 1); +#endif + + zvect_index vsize = p_vect_size(v); + if (vsize <= 1) + goto DONE_PROCESSING; + + // Process the vector: + p_vect_qsort(v, 0, vsize - 1, compare_func); + +DONE_PROCESSING: +#if (ZVECT_THREAD_SAFE == 1) + if (lock_owner) + get_mutex_unlock(v, 1); +#endif + +JOB_DONE: + if (rval) + p_throw_error(rval, NULL); +} + +#ifdef TRADITIONAL_BINARY_SEARCH +static bool p_standard_binary_search(vector v, const void *key, + zvect_index *item_index, + int (*f1)(const void *, const void *)) { + zvect_index bot, top; + + bot = 0; + top = p_vect_size(v) - 1; + + while (bot < top) { + zvect_index mid // mid-point + mid = top - (top - bot) / 2; + + // key < array[mid] + if ((*f1)(key, v->data[v->begin + mid]) < 0) { + top = mid - 1; + } else { + bot = mid; + } + } + + // key == array[top] + if ((*f1)(key, v->data[v->begin + top]) == 0) { + *item_index = top; + return true; + } + + *item_index = top; + return false; +} +#endif // TRADITIONAL_BINARY_SEARCH + +#ifndef TRADITIONAL_BINARY_SEARCH +// This is my re-implementation of Igor van den Hoven's Adaptive +// Binary Search algorithm. It has few improvements over the +// original design, most notably the use of custom compare +// function that makes it suitable also to search through strings +// and other types of vectors. +static bool p_adaptive_binary_search(vector const v, const void *key, + zvect_index *item_index, + int (*f1)(const void *, const void *)) { + zvect_index bot; + zvect_index top; + zvect_index mid; + + if ((v->balance >= 32) || (p_vect_size(v) <= 64)) { + bot = 0; + top = p_vect_size(v); + goto MONOBOUND; + } + bot = v->bottom; + top = 32; + + // key >= array[bot] + if ((*f1)(key, v->data[v->begin + bot]) >= 0) { + while (1) { + if ((bot + top) >= p_vect_size(v)) { + top = p_vect_size(v) - bot; + break; + } + bot += top; + + // key < array[bot] + if ((*f1)(key, v->data[v->begin + bot]) < 0) { + bot -= top; + break; + } + top *= 2; + } + } else { + while (1) { + if (bot < top) { + top = bot; + bot = 0; + break; + } + bot -= top; + + // key >= array[bot] + if ((*f1)(key, v->data[v->begin + bot]) >= 0) + break; + top *= 2; + } + } + + MONOBOUND: + while (top > 3) { + mid = top / 2; + // key >= array[bot + mid] + if ((*f1)(key, v->data[v->begin + (bot + mid)]) >= 0) + bot += mid; + top -= mid; + } + + v->balance = v->bottom > bot ? v->bottom - bot : bot - v->bottom; + v->bottom = bot; + + while (top) { + // key == array[bot + --top] + int test = (*f1)(key, v->data[v->begin + (bot + (--top))]); + if (test == 0) { + *item_index = bot + top; + return true; + } else if (test > 0) { + *item_index = bot + (top + 1); + return false; + } + } + + *item_index = bot + top; + return false; +} +#endif // ! TRADITIONAL_BINARY_SEARCH + +bool vect_bsearch(vector const v, const void *key, + int (*f1)(const void *, const void *), + zvect_index *item_index) { + // Check parameters: + if ((key == NULL) || (f1 == NULL) || (p_vect_size(v) == 0)) + return false; + + *item_index = 0; + // check if the vector exists: + zvect_retval rval = p_vect_check(v); + if (rval) + goto JOB_DONE; + + // TODO: Add mutex locking + +#ifdef TRADITIONAL_BINARY_SEARCH + if (p_standard_binary_search(v, key, item_index, f1)) { + return true; + } else { + *item_index = 0; + return false; + } +#endif // TRADITIONAL_BINARY_SEARCH +#ifndef TRADITIONAL_BINARY_SEARCH + if (p_adaptive_binary_search(v, key, item_index, f1)) { + return true; + } else { + *item_index = 0; + return false; + } +#endif // ! TRADITIONAL_BINARY_SEARCH + +// DONE_PROCESSING: + // TODO: Add mutex unlock + +JOB_DONE: + if (rval) + p_throw_error(rval, NULL); + + return false; +} + +/* + * Although if the vect_add_* doesn't belong to this group of + * functions, the vect_add_ordered is an exception because it + * requires vect_bserach and vect_qsort to be available. + */ +void vect_add_ordered(vector const v, const void *value, + int (*f1)(const void *, const void *)) { + // Check parameters: + if (value == NULL) + return; + + // check if the vector exists: + zvect_retval rval = p_vect_check(v); + if (rval) + goto JOB_DONE; + +#if (ZVECT_THREAD_SAFE == 1) + zvect_retval lock_owner = (locking_disabled || (v->flags & ZV_NOLOCKING)) ? 0 : get_mutex_lock(v, 2); +#endif + + // Few tricks to make it faster: + zvect_index vsize = p_vect_size(v); + if (vsize == 0) { + // If the vector is empty clearly we can just + // use vect_add and add the value normally! + vect_add(v, value); + goto DONE_PROCESSING; + } + + if ((*f1)(value, v->data[v->begin + (vsize - 1)]) > 0) { + // If the compare function returns that + // the value passed should go after the + // last value in the vector, just do so! + vect_add(v, value); + goto DONE_PROCESSING; + } + + // Ok previous checks didn't help us, so we need + // to get "heavy weapons" out and find where in + // the vector we should add "value": + zvect_index item_index = 0; + + // Here is another trick: + // I improved adaptive binary search to ALWAYS + // return an index (even when it doesn't find a + // searched item), this works for both: regular + // searches which will also use the bool to + // know if we actually found the item in that + // item_index or not and the vect_add_ordered + // which will use item_index (which will be the + // place where value should have been) to insert + // value as an ordered item :) + p_adaptive_binary_search(v, value, &item_index, f1); + + vect_add_at(v, value, item_index); + +DONE_PROCESSING: +#if (ZVECT_THREAD_SAFE == 1) + if (lock_owner) + get_mutex_unlock(v, 2); +#endif + +JOB_DONE: + if(rval) + p_throw_error(rval, NULL); +} + +#endif // ZVECT_DMF_EXTENSIONS + +#ifdef ZVECT_SFMD_EXTENSIONS +// Single Function Call Multiple Data operations extensions: + +void vect_apply(vector const v, void (*f)(void *)) { + // Check Parameters: + if (f == NULL) + return; + + zvect_retval rval = p_vect_check(v); + if (rval) + goto JOB_DONE; + +#if (ZVECT_THREAD_SAFE == 1) + zvect_retval lock_owner = (locking_disabled || (v->flags & ZV_NOLOCKING)) ? 0 : get_mutex_lock(v, 1); +#endif + + // Process the vector: + for (register zvect_index i = p_vect_size(v); i--;) + (*f)(v->data[v->begin + i]); + +//DONE_PROCESSING: +#if (ZVECT_THREAD_SAFE == 1) + if (lock_owner) + get_mutex_unlock(v, 1); +#endif + +JOB_DONE: + if(rval) + p_throw_error(rval, NULL); +} + +void vect_apply_range(vector const v, void (*f)(void *), const zvect_index x, + const zvect_index y) { + // Check parameters: + if ( f == NULL ) + return; + + // check if the vector exists: + zvect_retval rval = p_vect_check(v); + if (rval) + goto JOB_DONE; + +#if (ZVECT_THREAD_SAFE == 1) + zvect_retval lock_owner = (locking_disabled || (v->flags & ZV_NOLOCKING)) ? 0 : get_mutex_lock(v, 1); +#endif + + if (x > p_vect_size(v) || y > p_vect_size(v)) + { + rval = ZVERR_IDXOUTOFBOUND; + goto DONE_PROCESSING; + } + + zvect_index start; + zvect_index end; + if (x > y) { + start = x; + end = y; + } else { + start = y; + end = x; + } + + // Process the vector: + for (register zvect_index i = start; i <= end; i++) + (*f)(v->data[v->begin + i]); + +DONE_PROCESSING: +#if (ZVECT_THREAD_SAFE == 1) + if (lock_owner) + get_mutex_unlock(v, 1); +#endif + +JOB_DONE: + if(rval) + p_throw_error(rval, NULL); +} + +void vect_apply_if(vector const v1, vector const v2, void (*f1)(void *), + bool (*f2)(void *, void *)) { + // Check parameters: + if (f1 == NULL || f2 == NULL) + return; + + // check if the vector exists: + zvect_retval rval = p_vect_check(v1) | p_vect_check(v2); + if (rval) + goto JOB_DONE; + +#if (ZVECT_THREAD_SAFE == 1) + zvect_retval lock_owner = (locking_disabled || (v1->flags & ZV_NOLOCKING)) ? 0 : get_mutex_lock(v1, 1); +#endif + + // Check parameters: + if (p_vect_size(v1) > p_vect_size(v2)) { + rval = ZVERR_VECTTOOSMALL; + goto DONE_PROCESSING; + } + + // Process vectors: + for (register zvect_index i = p_vect_size(v1); i--;) + if ((*f2)(v1->data[v1->begin + i], v2->data[v2->begin + i])) + (*f1)(v1->data[v1->begin + i]); + +DONE_PROCESSING: +#if (ZVECT_THREAD_SAFE == 1) + if (lock_owner) + get_mutex_unlock(v1, 1); +#endif + +JOB_DONE: + if(rval) + p_throw_error(rval, NULL); +} + +void vect_copy(vector const v1, vector const v2, const zvect_index s2, + const zvect_index e2) { + // check if the vectors v1 and v2 exist: + zvect_retval rval = p_vect_check(v1) | p_vect_check(v2); + if (rval) + goto JOB_DONE; + +#if (ZVECT_THREAD_SAFE == 1) + zvect_retval lock_owner = (locking_disabled || (v1->flags & ZV_NOLOCKING)) ? 0 : get_mutex_lock(v1, 2); +#endif + + // We can only copy vectors with the same data_size! + if (v1->data_size != v2->data_size) { + rval = ZVERR_VECTDATASIZE; + goto DONE_PROCESSING; + } + + // Let's check if the indexes provided are correct for + // v2: + if (e2 > p_vect_size(v2) || s2 > p_vect_size(v2)) { + rval = ZVERR_IDXOUTOFBOUND; + goto DONE_PROCESSING; + } + + // If the user specified 0 max_elements then + // copy the entire vector from start position + // till the last item in the vector 2: + zvect_index ee2; + if (e2 == 0) + ee2 = (p_vect_size(v2) - 1) - s2; + else + ee2 = e2; + + // Set the correct capacity for v1 to get the whole v2: + while ( p_vect_capacity(v1) <= (p_vect_size(v1) + ee2)) + p_vect_increase_capacity(v1, 1); + + // Copy v2 (from s2) in v1 at the end of v1: + p_vect_memcpy(v1->data + v1->begin + p_vect_size(v1), v2->data + v2->begin + s2, sizeof(void *) * ee2); + + // Update v1 size: + v1->end += ee2; + +DONE_PROCESSING: +#if (ZVECT_THREAD_SAFE == 1) + if (lock_owner) + get_mutex_unlock(v1, 2); +#endif + +JOB_DONE: + if(rval) + p_throw_error(rval, NULL); +} + +/* + * vect_insert inserts the specified number of elements + * from vector v2 (from position s2) in vector v1 (from + * position s1). + * + */ +void vect_insert(vector const v1, vector const v2, const zvect_index s2, + const zvect_index e2, const zvect_index s1) { + // check if the vectors v1 and v2 exist: + zvect_retval rval = p_vect_check(v1) | p_vect_check(v2); + if (rval) + goto JOB_DONE; +#if (ZVECT_THREAD_SAFE == 1) + zvect_retval lock_owner = (locking_disabled || (v1->flags & ZV_NOLOCKING)) ? 0 : get_mutex_lock(v1, 2); +#endif + + // We can only copy vectors with the same data_size! + if (v1->data_size != v2->data_size) { + rval = ZVERR_VECTDATASIZE; + goto DONE_PROCESSING; + } + + // Let's check if the indexes provided are correct for + // v2: + if ((e2 > p_vect_size(v2)) || (s2 > p_vect_size(v2))) { + rval = ZVERR_IDXOUTOFBOUND; + goto DONE_PROCESSING; + } + + // If the user specified 0 max_elements then + // copy the entire vector from start position + // till the last item in the vector 2: + zvect_index ee2; + if (e2 == 0) + ee2 = (p_vect_size(v2) - 1) - s2; + else + ee2 = e2; + + // Process vectors: + if (ee2 > 1) + { + // Number of rows to insert is large, so let's use + // memmove: + + // Set the correct capacity for v1 to get data from v2: + if (p_vect_capacity(v1) <= (p_vect_size(v1) + ee2)) + p_vect_set_capacity(v1, 1, p_vect_capacity(v1) + ee2); + + const void *rptr = NULL; + + // Reserve appropriate space in the destination vector + rptr = p_vect_memmove(v1->data + (v1->begin + s1 + ee2), v1->data + (v1->begin + s1), sizeof(void *) * (p_vect_size(v1) - s1)); + + if (rptr == NULL) + { + rval = ZVERR_VECTCORRUPTED; + goto DONE_PROCESSING; + } + + // Copy items from v2 to v1 at location s1: + rptr = p_vect_memcpy(v1->data + (v1->begin + s1), v2->data + (v2->begin + s2), sizeof(void *) * ee2); + + if (rptr == NULL) + { + rval = ZVERR_VECTCORRUPTED; + goto DONE_PROCESSING; + } + + // Update v1 size: + v1->end += ee2; + } else { + // Number of rows to insert is small + // so let's use vect_Add_at: + register zvect_index j = 0; + + // Copy v2 items (from s2) in v1 (from s1): + for (register zvect_index i = s2; i <= s2 + ee2; i++, j++) + vect_add_at(v1, v2->data[v2->begin + i], s1 + j); + + goto DONE_PROCESSING; + } + + rval = p_vect_delete_at(v2, s2, ee2 - 1, 0); + +DONE_PROCESSING: +#if (ZVECT_THREAD_SAFE == 1) + if (lock_owner) + get_mutex_unlock(v1, 2); +#endif + +JOB_DONE: + if(rval) + p_throw_error(rval, NULL); +} + +/* + * vect_move moves the specified number of elements + * from vector v2 (from position s2) in vector v1 (at + * the end of it). + * + */ +static inline zvect_retval p_vect_move(vector const v1, vector v2, const zvect_index s2, + const zvect_index e2) { + zvect_retval rval = 0; + +#ifdef DEBUG + log_msg(ZVLP_INFO, "p_vect_move: move pointers set from v2 to v1\n"); +#endif + // We can only copy vectors with the same data_size! + if (v1->data_size != v2->data_size) + return ZVERR_VECTDATASIZE; + + // Let's check if the indexes provided are correct for + // v2: + if ((e2 > p_vect_size(v2)) || (s2 > p_vect_size(v2))) + return ZVERR_IDXOUTOFBOUND; + + // If the user specified 0 max_elements then + // move the entire vector from start position + // till the last item in the vector 2: + zvect_index ee2; + if (e2 == 0) + ee2 = (p_vect_size(v2) - 1) - s2; + else + ee2 = e2; + +#ifdef DEBUG + log_msg(ZVLP_INFO, "p_vect_move: v2 capacity = %*u, begin = %*u, end = %*u, size = %*u, s2 = %*u, ee2 = %*u\n", 10, p_vect_capacity(v2), 10, v2->begin, 10, v2->end, 10, p_vect_size(v2), 10, s2, 10, ee2); + + log_msg(ZVLP_INFO, "p_vect_move: v1 capacity = %*u, begin = %*u, end = %*u, size = %*u\n", 10, p_vect_capacity(v1), 10, v1->begin, 10, v1->end, 10, p_vect_size(v1)); +#endif + + // Set the correct capacity for v1 to get the whole v2: + if (p_vect_capacity(v1) <= (p_vect_size(v1) + ee2)) + p_vect_set_capacity(v1, 1, p_vect_capacity(v1)+ee2); + +#ifdef DEBUG + log_msg(ZVLP_INFO, "p_vect_move: v1 capacity = %*u, begin = %*u, end = %*u, size = %*u\n", 10, p_vect_capacity(v1), 10, v1->begin, 10, v1->end, 10, p_vect_size(v1)); + + log_msg(ZVLP_INFO, "p_vect_move: ready to copy pointers set\n"); +#endif + // Move v2 (from s2) in v1 at the end of v1: + const void *rptr = NULL; + if (v1 != v2) + rptr = p_vect_memcpy(v1->data + (v1->begin + p_vect_size(v1)), v2->data + (v2->begin + s2), sizeof(void *) * ee2); + else + rptr = p_vect_memmove(v1->data + (v1->begin + p_vect_size(v1)), v2->data + (v2->begin + s2), sizeof(void *) * ee2); + + if (rptr == NULL) + { + rval = ZVERR_VECTCORRUPTED; + goto DONE_PROCESSING; + } + + // Update v1 size: + v1->end += ee2; + +#ifdef DEBUG + log_msg(ZVLP_INFO, "p_vect_move: done copy pointers set\n"); +#endif + + // Clean up v2 memory slots that no longer belong to v2: + //rval = p_vect_delete_at(v2, s2, ee2 - 1, 0); + +DONE_PROCESSING: +#ifdef DEBUG + log_msg(ZVLP_INFO, "p_vect_move: v1 capacity = %*u, begin = %*u, end = %*u, size = %*u\n", 10, p_vect_capacity(v1), 10, v1->begin, 10, v1->end, 10, p_vect_size(v1)); + log_msg(ZVLP_INFO, "p_vect_move: v2 capacity = %*u, begin = %*u, end = %*u, size = %*u\n", 10, p_vect_capacity(v2), 10, v2->begin, 10, v2->end, 10, p_vect_size(v2)); +#endif + + return rval; +} +// END of p_vect_move +////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////// +// vect_move moves a portion of a vector (v2) into another (v1): +void vect_move(vector const v1, vector v2, const zvect_index s2, + const zvect_index e2) { + // check if the vectors v1 and v2 exist: + zvect_retval rval = p_vect_check(v1) | p_vect_check(v2); + if (rval) + goto JOB_DONE; + +#if (ZVECT_THREAD_SAFE == 1) + // vect_move modifies both vectors, so has to lock them both (if needed) + zvect_retval lock_owner2 = (locking_disabled || (v2->flags & ZV_NOLOCKING)) ? 0 : get_mutex_lock(v2, 1); + zvect_retval lock_owner1 = (locking_disabled || (v1->flags & ZV_NOLOCKING)) ? 0 : get_mutex_lock(v1, 1); +#endif +#ifdef DEBUG + log_msg(ZVLP_INFO, "vect_move: --- begin ---\n"); +# if (ZVECT_THREAD_SAFE == 1) + log_msg(ZVLP_INFO, "vect_move: lock_owner1 for vector v1: %*u\n",10,lock_owner1); + log_msg(ZVLP_INFO, "vect_move: lock_owner2 for vector v2: %*u\n",10,lock_owner2); +# endif // ZVECT_THREAD_SAFE +#endif + + // We can only copy vectors with the same data_size! + if (v1->data_size != v2->data_size) { + rval = ZVERR_VECTDATASIZE; + goto DONE_PROCESSING; + } + + rval = p_vect_move(v1, v2, s2, e2); + +DONE_PROCESSING: +#if (ZVECT_THREAD_SAFE == 1) + if (lock_owner1) + get_mutex_unlock(v1, 1); + + rval = p_vect_delete_at(v2, s2, e2 - 1, 0); + + if (lock_owner2) + get_mutex_unlock(v2, 1); +#endif +#ifdef DEBUG + log_msg(ZVLP_INFO, "vect_move: --- end ---\n"); +#endif + +JOB_DONE: + if(rval) + p_throw_error(rval, NULL); +} +///////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////// +// vect_move_if moves a portion of a vector (v2) into another (v1) if f2 is true: +zvect_retval vect_move_if(vector const v1, vector v2, const zvect_index s2, + const zvect_index e2, zvect_retval (*f2)(void *, void *)) { + // check if the vectors v1 and v2 exist: + zvect_retval rval = p_vect_check(v1) | p_vect_check(v2); + if (rval) + goto JOB_DONE; + +#if (ZVECT_THREAD_SAFE == 1) + // vect_move modifies both vectors, so has to lock them both (if needed) + zvect_retval lock_owner2 = (locking_disabled || (v2->flags & ZV_NOLOCKING)) ? 0 : get_mutex_lock(v2, 1); + zvect_retval lock_owner1 = (locking_disabled || (v1->flags & ZV_NOLOCKING)) ? 0 : get_mutex_lock(v1, 1); +#endif +#ifdef DEBUG + log_msg(ZVLP_INFO, "vect_move_if: --- begin ---\n"); +# if (ZVECT_THREAD_SAFE == 1) + log_msg(ZVLP_INFO, "vect_move_if: lock_owner1 for vector v1: %*u\n",10,lock_owner1); + log_msg(ZVLP_INFO, "vect_move_if: lock_owner2 for vector v2: %*u\n",10,lock_owner2); +# endif // ZVECT_THREAD_SAFE +#endif + + // We can only move vectors with the same data_size! + if (v1->data_size != v2->data_size) { + rval = ZVERR_VECTDATASIZE; + goto DONE_PROCESSING; + } + + rval = (*f2)(v1, v2) ? p_vect_move(v1, v2, s2, e2) : 1; + +DONE_PROCESSING: +#if (ZVECT_THREAD_SAFE == 1) + if (lock_owner1) + get_mutex_unlock(v1, 1); + + rval = p_vect_delete_at(v2, s2, e2 - 1, 0); + + if (lock_owner2) + get_mutex_unlock(v2, 1); +#endif +#ifdef DEBUG + log_msg(ZVLP_INFO, "vect_move_if: --- end ---\n"); +#endif + +JOB_DONE: + if(rval && (rval != 1)) + p_throw_error(rval, NULL); + + return rval; +} +///////////////////////////////////////////////////////////////// + +#if (ZVECT_THREAD_SAFE == 1) +///////////////////////////////////////////////////////////////// +// vect_move_on_signal + +zvect_retval vect_move_on_signal(vector const v1, vector v2, const zvect_index s2, + const zvect_index e2, zvect_retval (*f2)(void *, void *)) +{ + // check if the vectors v1 and v2 exist: + zvect_retval rval = p_vect_check(v1) | p_vect_check(v2); + if (rval) + goto JOB_DONE; + + // vect_move modifies both vectors, so has to lock them both (if needed) + zvect_retval lock_owner2 = (locking_disabled || (v2->flags & ZV_NOLOCKING)) ? 0 : get_mutex_lock(v2, 1); + + zvect_retval lock_owner1 = (locking_disabled || (v1->flags & ZV_NOLOCKING)) ? 0 : get_mutex_lock(v1, 1); + +#ifdef DEBUG + log_msg(ZVLP_MEDIUM, "vect_move_on_signal: --- start waiting ---\n"); +#endif + // wait until we get a signal + while(!(*f2)(v1, v2) && !(v2->status && (bool)ZVS_USR1_FLAG)) { + pthread_cond_wait(&(v2->cond), &(v2->lock)); + } + v2->status &= ~(ZVS_USR1_FLAG); + //v2->status |= ZVS_USR_FLAG; + +#ifdef DEBUG + log_msg(ZVLP_MEDIUM, "Set status flag: %*i\n", 10, vect_check_status(v2, 1)); + + log_msg(ZVLP_MEDIUM, "vect_move_on_signal: --- received signal ---\n"); + + log_msg(ZVLP_MEDIUM, "vect_move_on_signal: --- begin ---\n"); +#endif + // Proceed with move items: + rval = p_vect_move(v1, v2, s2, e2); + +#ifdef DEBUG + log_msg(ZVLP_MEDIUM, "vect_move_on_signal: --- end ---\n"); +#endif + + v2->status &= ~(ZVS_USR1_FLAG); +#ifdef DEBUG + log_msg(ZVLP_MEDIUM, "Reset status flag: %*i\n", 10, vect_check_status(v2, 1)); +#endif + +//DONE_PROCESSING: +#ifdef DEBUG + log_msg(ZVLP_MEDIUM, "v1 owner? %*i\n", 10, lock_owner1); +#endif + if (lock_owner1) + get_mutex_unlock(v1, 1); + +#ifdef DEBUG + log_msg(ZVLP_MEDIUM, "v2 owner? %*i\n", 10, lock_owner2); +#endif + rval = p_vect_delete_at(v2, s2, e2 - 1, 0); + if (lock_owner2) + get_mutex_unlock(v2, 1); + +JOB_DONE: + if(rval && (rval != 1)) + p_throw_error(rval, NULL); + + return rval; +} +#endif // (ZVECT_THREAD_SAFE == 1) +///////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////// +// vect_merge merges a vector (v2) into another (v1) +void vect_merge(vector const v1, vector v2) { + // check if the vector v1 exists: + zvect_retval rval = p_vect_check(v1) | p_vect_check(v2); + if (rval) + goto JOB_DONE; + +#if (ZVECT_THREAD_SAFE == 1) + zvect_retval lock_owner1 = (locking_disabled || (v1->flags & ZV_NOLOCKING)) ? 0 : get_mutex_lock(v1, 1); + zvect_retval lock_owner2 = (locking_disabled || (v2->flags & ZV_NOLOCKING)) ? 0 : get_mutex_lock(v2, 1); +#endif + +#ifdef DEBUG + log_msg(ZVLP_INFO, "vect_merge: --- begin ---\n"); +# if (ZVECT_THREAD_SAFE == 1) + log_msg(ZVLP_INFO, "vect_merge: lock_owner1 for vector v1: %*u\n",10,lock_owner1); + log_msg(ZVLP_INFO, "vect_merge: lock_owner2 for vector v2: %*u\n",10,lock_owner2); +# endif // ZVECT_THREAD_SAFE +#endif // DEBUG + + // We can only copy vectors with the same data_size! + if (v1->data_size != v2->data_size) { + rval = ZVERR_VECTDATASIZE; + goto DONE_PROCESSING; + } + + // Check if the user is trying to merge a vector to itself: + if (v1 == v2) { + rval = ZVERR_OPNOTALLOWED; + goto DONE_PROCESSING; + } + +#ifdef DEBUG + log_msg(ZVLP_INFO, "vect_merge: v2 capacity = %*u, begin = %*u, end: %*u, size = %*u\n", 10, p_vect_capacity(v2), 10, v2->begin, 10, v2->end, 10, p_vect_size(v2)); + log_msg(ZVLP_INFO, "vect_merge: v1 capacity = %*u, begin = %*u, end: %*u, size = %*u\n", 10, p_vect_capacity(v1), 10, v1->begin, 10, v1->end, 10, p_vect_size(v1)); +#endif + + // Set the correct capacity for v1 to get the whole v2: + if (p_vect_capacity(v1) <= (p_vect_size(v1) + p_vect_size(v2))) + p_vect_set_capacity(v1, 1, p_vect_capacity(v1) + p_vect_size(v2)); + +#ifdef DEBUG + log_msg(ZVLP_INFO, "vect_merge: v1 capacity = %*u, begin = %*u, end: %*u, size = %*u\n", 10, p_vect_capacity(v1), 10, v1->begin, 10, v1->end, 10, p_vect_size(v1)); +#endif + + // Copy the whole v2 in v1 at the end of v1: + p_vect_memcpy(v1->data + (v1->begin + p_vect_size(v1)), v2->data + v2->begin, sizeof(void *) * p_vect_size(v2)); + + // Update v1 size: + v1->end += p_vect_size(v2); + +#ifdef DEBUG + log_msg(ZVLP_INFO, "vect_merge: v1 capacity = %*u, begin = %*u, end: %*u, size = %*u\n", 10, p_vect_capacity(v1), 10, v1->begin, 10, v1->end, 10, p_vect_size(v1)); +#endif + +DONE_PROCESSING: +#if (ZVECT_THREAD_SAFE == 1) + if (lock_owner2) + get_mutex_unlock(v2, 1); + + if (!rval) + rval = p_vect_destroy(v2, 0); + + if (lock_owner1) + get_mutex_unlock(v1, 1); +#else + rval = p_vect_destroy(v2, 0); + // ^ Because we are merging two vectors in one + // after merged v2 to v1 there is no need for + // v2 to still exists, so let's destroy it to + // free memory correctly. +#endif +#ifdef DEBUG + log_msg(ZVLP_INFO, "vect_merge: --- end ---\n"); +#endif + +JOB_DONE: + if(rval) + p_throw_error(rval, NULL); +} +// END of vect_merge + +#endif + +/*---------------------------------------------------------------------------*/ diff --git a/srcs/vcpe_main.c b/srcs/vcpe_main.c index 7006f10..51aee84 100644 --- a/srcs/vcpe_main.c +++ b/srcs/vcpe_main.c @@ -15,6 +15,7 @@ #ifdef OPENDHCPD_ON #include "user_errno.h" +#include "libs/misc/sdsalloc.h" #endif #ifdef OPENDHCPDDNS_ON @@ -46,7 +47,7 @@ static void lwip_init_env() { int main(int argc, char **argv) { int ret; - + hiredisResetAllocators(); #ifdef OPENDHCPDDNS_ON return dual_server_main(argc, argv); #else