3426 lines
93 KiB
C
3426 lines
93 KiB
C
/*
|
|
* 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 <assert.h>
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
/* Include vector.h header */
|
|
#include "zvector/zvector.h"
|
|
#include "common.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 <malloc.h>
|
|
#endif
|
|
#if (CPU_TYPE == x86_64)
|
|
// # include <xmmintrin.h>
|
|
#endif
|
|
#endif // OS_TYPE
|
|
#if (ZVECT_THREAD_SAFE == 1)
|
|
#if MUTEX_TYPE == 1
|
|
#if (!defined(macOS))
|
|
#include <semaphore.h>
|
|
#else
|
|
#include <dispatch/dispatch.h>
|
|
#endif
|
|
#include <pthread.h>
|
|
#elif MUTEX_TYPE == 2
|
|
#include <windows.h>
|
|
#include <psapi.h>
|
|
#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))
|
|
#ifndef UNUSED
|
|
#define UNUSED(x) (void)x
|
|
#endif
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* 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;
|
|
|
|
// Internal representation of a vector
|
|
typedef struct p_vector *const ivector;
|
|
|
|
// 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
|
|
|
|
// Local declaration of private functions
|
|
void p_init_zvect(void);
|
|
zvect_retval p_vect_clear(ivector v);
|
|
|
|
// 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, 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;
|
|
size_t 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)
|
|
#else
|
|
(dispatch_semaphore_t *sem, int value)
|
|
#endif
|
|
{
|
|
#if !defined(macOS)
|
|
return sem_init(sem, 0, value);
|
|
#else
|
|
*sem = dispatch_semaphore_create(value);
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
static inline int semaphore_destroy
|
|
#if !defined(macOS)
|
|
(sem_t *sem)
|
|
#else
|
|
(dispatch_semaphore_t *sem)
|
|
#endif
|
|
{
|
|
#if !defined(macOS)
|
|
return sem_destroy(sem);
|
|
#else
|
|
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 c_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 c_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 c_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 c_vector v, const int32_t
|
|
lock_type, bool (*f1)(const c_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 c_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 c_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 c_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 c_vector x) {
|
|
return (x == NULL) ? ZVERR_VECTUNDEF : 0;
|
|
}
|
|
|
|
ZVECT_ALWAYSINLINE
|
|
static inline zvect_index p_vect_capacity(const c_vector v) {
|
|
return (v->cap_left + v->cap_right);
|
|
}
|
|
|
|
ZVECT_ALWAYSINLINE
|
|
static inline zvect_index p_vect_size(const c_vector v) {
|
|
return (v->end > v->begin) ? (v->end - v->begin) : (v->begin - v->end);
|
|
}
|
|
|
|
static inline void p_item_safewipe(ivector 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 c_vector v, zvect_index flag_id) {
|
|
return (v->status >> flag_id) & 1U;
|
|
}
|
|
|
|
bool vect_set_status(const c_vector v, zvect_index flag_id) {
|
|
zvect_retval rval = p_usr_status(flag_id);
|
|
|
|
if (!rval) {
|
|
v->status |= 1 << flag_id;
|
|
}
|
|
|
|
return (bool)rval;
|
|
}
|
|
|
|
bool vect_clear_status(const c_vector v, zvect_index flag_id) {
|
|
zvect_retval rval = p_usr_status(flag_id);
|
|
|
|
if (!rval) {
|
|
v->status &= ~(1 << flag_id);
|
|
}
|
|
|
|
return (bool)rval;
|
|
}
|
|
|
|
bool vect_toggle_status(const c_vector v, zvect_index flag_id) {
|
|
zvect_retval rval = p_usr_status(flag_id);
|
|
|
|
if (!rval) {
|
|
v->status ^= (1 << flag_id);
|
|
}
|
|
|
|
return rval;
|
|
}
|
|
|
|
static void p_free_items(ivector 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(ivector 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;
|
|
}
|
|
|
|
memset(new_data, 0, sizeof(void *) * (new_capacity + v->cap_right));
|
|
|
|
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(ivector 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(ivector 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(ivector 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(ivector 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(c_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 zvect_retval p_vect_put_at(ivector 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(ivector 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(ivector 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(ivector 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(ivector 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:
|
|
|
|
int vect_is_empty(ivector v) {
|
|
return !p_vect_check(v) ? ((p_vect_size(v) == 0) ? TRUE : FALSE) : FALSE;
|
|
//return !p_vect_check(v) ? (p_vect_size(v) == 0) : (bool)ZVERR_VECTUNDEF;
|
|
}
|
|
|
|
zvect_index vect_size(ivector v) {
|
|
return !p_vect_check(v) ? p_vect_size(v) : 0;
|
|
}
|
|
|
|
zvect_index vect_max_size(ivector v) {
|
|
return (!p_vect_check(v) ? zvect_index_max : 0);
|
|
}
|
|
|
|
void *vect_begin(ivector v) {
|
|
return !p_vect_check(v) ? v->data[v->begin] : NULL;
|
|
}
|
|
|
|
void *vect_end(ivector v) {
|
|
return !p_vect_check(v) ? v->data[v->end] : NULL;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
// Vector Creation and Destruction:
|
|
|
|
c_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:
|
|
c_vector v = (c_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(c_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(ivector v) {
|
|
return (locking_disabled || (v->flags & ZV_NOLOCKING)) ? 0 : get_mutex_lock(v, 3);
|
|
}
|
|
|
|
zvect_retval vect_lock(ivector v) {
|
|
return p_vect_lock(v);
|
|
}
|
|
|
|
static inline zvect_retval p_vect_unlock(ivector v) {
|
|
return (locking_disabled || (v->flags & ZV_NOLOCKING)) ? 0 : get_mutex_unlock(v, 3);
|
|
}
|
|
|
|
zvect_retval vect_unlock(ivector v) {
|
|
return p_vect_unlock(v);
|
|
}
|
|
|
|
static inline zvect_retval p_vect_trylock(ivector v) {
|
|
return (locking_disabled || (v->flags & ZV_NOLOCKING)) ? 0 : check_mutex_trylock(v, 3);
|
|
}
|
|
|
|
zvect_retval vect_trylock(ivector v) {
|
|
return p_vect_trylock(v);
|
|
}
|
|
|
|
zvect_retval vect_sem_wait(const c_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 c_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 c_vector v) {
|
|
return (locking_disabled || (v->flags & ZV_NOLOCKING)) ? 1 :
|
|
wait_for_signal(v, 3);
|
|
}
|
|
|
|
inline zvect_retval vect_wait_for_signal(const c_vector v) {
|
|
return p_vect_wait_for_signal(v);
|
|
}
|
|
*/
|
|
|
|
static inline zvect_retval p_vect_lock_after_signal(const c_vector v) {
|
|
return (locking_disabled || (v->flags & ZV_NOLOCKING)) ? 1 : lock_after_signal(v, 3);
|
|
}
|
|
|
|
zvect_retval vect_lock_after_signal(const c_vector v) {
|
|
return p_vect_lock_after_signal(v);
|
|
}
|
|
|
|
zvect_retval vect_send_signal(const c_vector v) {
|
|
return send_signal(v, 3);
|
|
}
|
|
|
|
zvect_retval vect_broadcast_signal(const c_vector v) {
|
|
return broadcast_signal(v, 3);
|
|
}
|
|
|
|
#endif
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
// Vector Data Storage functions:
|
|
|
|
void vect_clear(ivector 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 c_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
|
|
void vect_push(const c_vector v, const void *value) {
|
|
zvect_index vsize = 0;
|
|
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:
|
|
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 c_vector v, const void *value) {
|
|
vect_push(v, value);
|
|
}
|
|
|
|
// Add an item at position "i" of the vector
|
|
void vect_add_at(const c_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(ivector 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(ivector 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(ivector 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(ivector 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(ivector 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(ivector 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(ivector 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(ivector 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);
|
|
}
|
|
}
|
|
|
|
void *vect_pop(ivector 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(ivector 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(ivector 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(ivector 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(ivector 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 c_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(ivector 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(ivector 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(ivector 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(ivector 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(ivector 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(ivector 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(c_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(c_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 c_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 c_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(c_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(ivector 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(ivector 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(ivector 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(ivector 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(ivector 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(ivector v1, ivector 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(ivector v1, ivector 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(ivector v1, ivector 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(ivector v1, c_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(ivector v1, c_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(ivector v1,
|
|
c_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(ivector v1,
|
|
c_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)) {
|
|
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(ivector v1, c_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
|
|
|
|
/*---------------------------------------------------------------------------*/
|