652 lines
20 KiB
C
652 lines
20 KiB
C
/*
|
|
* Name: ZVector (Header)
|
|
* Purpose: Library to use Dynamic Arrays (Vectors) in C Language
|
|
* Author: Paolo Fabio Zaino
|
|
* Domain: General
|
|
* License: Copyright by Paolo Fabio Zaino, all rights reserved
|
|
* Distributed under MIT license
|
|
*
|
|
*/
|
|
|
|
#ifndef SRC_ZVECTOR_H_
|
|
#define SRC_ZVECTOR_H_
|
|
|
|
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
|
|
#pragma once
|
|
#endif
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
// Requires standard C libraries:
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
|
|
// First library included is zvector_checks,
|
|
// so we know on which platform and which features
|
|
// we can use:
|
|
#include "zvector_checks.h"
|
|
|
|
// Include vector configuration header
|
|
#include "zvector_config.h"
|
|
|
|
// Declare required structs:
|
|
typedef struct p_vector *vector;
|
|
|
|
// Declare required enums:
|
|
|
|
/*
|
|
* Vector Properties Flags can be used to tell ZVector
|
|
* which types of properties we want to enable for each
|
|
* given vector we are creating.
|
|
* Each vector can have multiple properties enabled at the
|
|
* same time, you can use the typical C form to specify multiple
|
|
* properties for the same vector:
|
|
*
|
|
* ZV_SEC_WIPE | ZV_BYREF
|
|
*
|
|
* The above will create a vector that supports passing items to
|
|
* it by reference (instead of copying them into the vector) and
|
|
* having Secure Wipe enabled, so that when an element is deleted
|
|
* its reference will also be fully zeroed out before freeing it.
|
|
*/
|
|
enum ZVECT_PROPERTIES {
|
|
ZV_NONE = 0, // Sets or Resets all vector's properties to 0.
|
|
ZV_SEC_WIPE = 1 << 0, // Sets the vector for automatic Secure Wipe of items.
|
|
ZV_BYREF = 1 << 1, // Sets the vector to store items by reference instead of copying them as per default.
|
|
ZV_CIRCULAR = 1
|
|
<< 2, // Sets the vector to be a circular vector (so it will not grow in capacity automatically). Elements will be overwritten as in typical circular buffers!
|
|
ZV_NOLOCKING = 1 << 3, // This Property means the vector will not use mutexes, be careful using it!
|
|
};
|
|
|
|
enum ZVECT_ERR {
|
|
ZVERR_VECTUNDEF = -1,
|
|
ZVERR_IDXOUTOFBOUND = -2,
|
|
ZVERR_OUTOFMEM = -3,
|
|
ZVERR_VECTCORRUPTED = -4,
|
|
ZVERR_RACECOND = -5,
|
|
ZVERR_VECTTOOSMALL = -6,
|
|
ZVERR_VECTDATASIZE = -7,
|
|
ZVERR_VECTEMPTY = -8,
|
|
ZVERR_OPNOTALLOWED = -9
|
|
};
|
|
|
|
/*****************************
|
|
** Public API declaration: **
|
|
*****************************/
|
|
|
|
// Vector construction/Destruction and memory control:
|
|
|
|
/*
|
|
* vect_create creates and returns a new vector
|
|
* of the specified "capacity", with a storage area that
|
|
* can store items of "item_size" size and, if we want to
|
|
* have an automatic secure erasing enabled (ZV_SEC_WIPE
|
|
* ), we can simply pass ZV_SAFE_WIPE (or other flags too)
|
|
* after item_size. Flags syntax is the usual C flag sets:
|
|
* ZV_SEC_WIPE | ZV_BYREF etc.
|
|
*/
|
|
vector vect_create(zvect_index capacity, size_t item_size, uint32_t properties);
|
|
|
|
/*
|
|
* vect_destroy destroys the specified vector and, if
|
|
* secure_wipe is enabled, also ensure erasing each single
|
|
* value in the vector before destroying it.
|
|
*/
|
|
void vect_destroy(vector);
|
|
|
|
/*
|
|
* vect_shrink is useful when operating on systems with
|
|
* small amount of RAM, and it basically allows to shrink
|
|
* the vector capacity to match the actual used size, to
|
|
* save unused memory locations.
|
|
*/
|
|
void vect_shrink(vector const v);
|
|
#define vect_shrink_to_fit(x) vect_shrink(x)
|
|
|
|
/*
|
|
* vect_set_wipefunct allows you to pass ZVector a pointer to a custom
|
|
* function (of your creation) to securely wipe data from the vector v
|
|
* when automatic safe wipe is called.
|
|
*/
|
|
void vect_set_wipefunct(vector const v, void (*f1)(const void *item, size_t size));
|
|
|
|
// Vector state checks:
|
|
|
|
/*
|
|
* vect_is_empty returns true if the vector is empty
|
|
* and false if the vector is NOT empty.
|
|
*/
|
|
bool vect_is_empty(vector const v);
|
|
|
|
/*
|
|
* vect_size returns the actual size (the number of)
|
|
* USED slots in the vector storage.
|
|
*/
|
|
zvect_index vect_size(vector const v);
|
|
|
|
/*
|
|
* vect_clear clears out a vector and also resizes it
|
|
* to its initial capacity.
|
|
*/
|
|
void vect_clear(vector const v);
|
|
|
|
bool vect_check_status(const vector v, zvect_index flag_id);
|
|
|
|
bool vect_set_status(const vector v, zvect_index flag_id);
|
|
|
|
bool vect_clear_status(const vector v, zvect_index flag_id);
|
|
|
|
#if (ZVECT_THREAD_SAFE == 1)
|
|
// Vector Thread Safe functions:
|
|
|
|
/*
|
|
* vect_lock_enable allows you to enable thread safe
|
|
* code at runtime. It doesn't lock anything, it just
|
|
* enables globally ZVector thread safe code at
|
|
* runtime.
|
|
*
|
|
* Example of use:
|
|
* vect_lock_enable;
|
|
*/
|
|
void vect_lock_enable(void);
|
|
|
|
/*
|
|
* vect_lock_disable allows you to disable thread safe
|
|
* code at runtime. It doesn't lock anything, it just
|
|
* disables globally ZVector thread safe code at
|
|
* runtime.
|
|
*
|
|
* Example of use:
|
|
* vect_lock_disable;
|
|
*/
|
|
void vect_lock_disable(void);
|
|
|
|
/*
|
|
* vect_lock allows you to lock the given vector to
|
|
* have exclusive write access from your own thread.
|
|
* When you lock a vector directly then ZVector will
|
|
* NOT use its internal locking mechanism for that
|
|
* specific vector.
|
|
*
|
|
* Example of use: To lock a vector called v
|
|
* vect_lock(v);
|
|
*/
|
|
zvect_retval vect_lock(vector const v);
|
|
|
|
/*
|
|
* vect_trylock will try to lock the given vector to
|
|
* have exclusive write access from your own thread.
|
|
* When you lock a vector directly then ZVector will
|
|
* NOT use its internal locking mechanism for that
|
|
* specific vector.
|
|
*
|
|
* Example of use: To lock a vector called v
|
|
* vect_trylock(v);
|
|
*/
|
|
zvect_retval vect_trylock(vector const v);
|
|
|
|
/*
|
|
* vect_lock allows you to unlock the given vector that
|
|
* you have previously locked with vect_lock.
|
|
*
|
|
* Example of use: To unlock a vector called v
|
|
* vect_unlock(v);
|
|
*/
|
|
zvect_retval vect_unlock(vector const v);
|
|
|
|
/* TODO:
|
|
* zvect_retval vect_wait_for_signal(const vector v);
|
|
*
|
|
* zvect_retval vect_lock_after_signal(const vector v);
|
|
*/
|
|
|
|
zvect_retval vect_move_on_signal(vector const v1,
|
|
vector v2,
|
|
const zvect_index s2,
|
|
const zvect_index e2,
|
|
zvect_retval (*f2)(void *, void *));
|
|
|
|
zvect_retval vect_send_signal(const vector v);
|
|
|
|
zvect_retval vect_broadcast_signal(const vector v);
|
|
|
|
zvect_retval vect_sem_wait(const vector v);
|
|
|
|
zvect_retval vect_sem_post(const vector v);
|
|
|
|
#endif // ( ZVECT_THREAD_SAFE == 1 )
|
|
|
|
/////////////////////////////////////////////////////
|
|
// Vector Data Storage functions:
|
|
|
|
/*
|
|
* vect_push and vect_pop are used to use the
|
|
* vector as a dynamic stack.
|
|
*
|
|
* int i = 3;
|
|
* vect_push(v, &i) pushes the element 3 at the
|
|
* back of the vector v, which
|
|
* corresponds to the top of a
|
|
* Stack.
|
|
*/
|
|
void vect_push(vector const v, const void *item);
|
|
|
|
/*
|
|
* vect_pop(v) "pops" (returns) the element
|
|
* at the back of the vector as
|
|
* a regular pop would do with
|
|
* an element at the top of a
|
|
* stack. Remember when you use
|
|
* vect_pop the element you
|
|
* receive is also removed from
|
|
* the vector!
|
|
*/
|
|
void *vect_pop(vector const v);
|
|
#define vect_pop_back(x) vect_pop(x)
|
|
|
|
/*
|
|
* vect_add adds a new item into the vector and,
|
|
* if required, will also reorganize the vector.
|
|
*
|
|
* int i = 3;
|
|
* vect_add(v, &i) will add the new item 3 in
|
|
* the vector v at the end
|
|
* (or back) of the vector v.
|
|
*/
|
|
void vect_add(vector const v, const void *item);
|
|
#define vect_push_back(x, y) vect_add(x, y)
|
|
|
|
/*
|
|
* int i = 4;
|
|
* vect_add_at(v, &i, 2) will add the new item 4 at
|
|
* position 2 in the vector v
|
|
* and move all the elements
|
|
* from the original 2nd onward
|
|
* of a position to make space
|
|
* for the new item 4.
|
|
*/
|
|
void vect_add_at(vector const v, const void *item, zvect_index index);
|
|
|
|
/*
|
|
* int i = 5;
|
|
* vect_add_front(v, &i) will add the new item 5 at
|
|
* the beginning of the vector
|
|
* v (or front) and will also
|
|
* move all the existing
|
|
* elements of one position in
|
|
* the vector to make space for
|
|
* the new item 5 at the front
|
|
* of vector v.
|
|
*/
|
|
void vect_add_front(vector const v, const void *item);
|
|
#define vect_push_front(x, y) vect_add_front(x, y)
|
|
|
|
/*
|
|
* vect_get returns an item from the specified vector
|
|
*
|
|
* vect_get(v) will return the ast element in
|
|
* the v vector (but will not remove
|
|
* the element as it happens in
|
|
* vect_pop(v)).
|
|
*/
|
|
void *vect_get(vector const v);
|
|
#define vect_back(v) vect_get(v)
|
|
|
|
/*
|
|
*
|
|
* vect_get_at(v, 3) will return the element at location
|
|
* 3 in the vector v.
|
|
*/
|
|
void *vect_get_at(vector const v, const zvect_index i);
|
|
#define vect_at(v, x) vect_get_at(v, x)
|
|
|
|
/*
|
|
* vect_get_front(v) will return the first element in
|
|
* the vector v.
|
|
*/
|
|
void *vect_get_front(vector const v);
|
|
#define vect_front(v) vect_get_front(v)
|
|
|
|
/*
|
|
*vect_put allows you to REPLACE an item
|
|
* in the vector.
|
|
*
|
|
* int i = 3;
|
|
* vect_put(v, &i) will replace the last element
|
|
* in the vector with 3.
|
|
*/
|
|
void vect_put(vector const v, const void *item);
|
|
|
|
/*
|
|
*
|
|
* int i = 4;
|
|
* vect_put_at(v, &i, 2) will replace the 3rd element
|
|
* (2 + 1, as vector's 1st item
|
|
* starts at v[0]) with the
|
|
* item 4.
|
|
*/
|
|
void vect_put_at(vector const v, const void *item, const zvect_index i);
|
|
|
|
/*
|
|
*
|
|
* int i = 5;
|
|
* vect_put_front(v, &i) will replace the 1st element
|
|
* of the vector with the item
|
|
* 5.
|
|
*/
|
|
void vect_put_front(vector const v, const void *item);
|
|
|
|
/*
|
|
* vect_remove removes an item from the vector
|
|
* and reorganise the vector. It also returns
|
|
* the item remove from the vector, so you can
|
|
* use it to simulate a stack behaviour as well.
|
|
*
|
|
* vect_remove(v) will remove and return the
|
|
* last item in the vector.
|
|
*/
|
|
void *vect_remove(vector const v);
|
|
|
|
/*
|
|
* vect_remove_at(v, 3) will remove the 3rd item in
|
|
* the vector and return it.
|
|
*/
|
|
void *vect_remove_at(vector const v, const zvect_index i);
|
|
|
|
/*
|
|
* vect_remove_front(v) will remove the 1st item in
|
|
* the vector and return it.
|
|
*/
|
|
void *vect_remove_front(vector const v);
|
|
|
|
/*
|
|
* vect_delete deletes an item from the vector
|
|
* and reorganise the vector. It does not return
|
|
* the item like remove.
|
|
*
|
|
* vect_delete(v) will delete and the last
|
|
* item in the vector.
|
|
*/
|
|
void vect_delete(vector const v);
|
|
|
|
/*
|
|
* vect_delete_at(v, 3) will delete the 3rd item in
|
|
* the vector.
|
|
*/
|
|
void vect_delete_at(vector const v, const zvect_index i);
|
|
|
|
/*
|
|
* vect_delete_range(v, 20, 30)
|
|
* will delete items from item
|
|
* 20 to item 30 in the vector
|
|
* v.
|
|
*/
|
|
void vect_delete_range(vector const v, const zvect_index first_element, const zvect_index last_element);
|
|
|
|
/*
|
|
*
|
|
* vect_delete_front(v) will delete the 1st item in
|
|
* the vector.
|
|
*/
|
|
void vect_delete_front(vector const v);
|
|
|
|
////////////
|
|
// Vector Data manipulation functions:
|
|
////////////
|
|
|
|
#ifdef ZVECT_DMF_EXTENSIONS
|
|
// Data Manipulation Functions extensions:
|
|
|
|
/*
|
|
* vect_swap is a function that allows you to swap two
|
|
* items in the same vector.
|
|
* You just pass the vector and the index of both the
|
|
* two items to swap.
|
|
*
|
|
* For example to swap item 3 with item 22 on vector v
|
|
* use:
|
|
* vect_swap(v, 3, 22);
|
|
*/
|
|
void vect_swap(vector const v, const zvect_index s, const zvect_index e);
|
|
|
|
/*
|
|
* vect_swap_range is a function that allows to swap
|
|
* a range of items in the same vector.
|
|
* You just pass the vector, the index of the first item
|
|
* to swap, the index of the last item to swap and the
|
|
* index of the first item to swap with.
|
|
*
|
|
* For example to swap items from 10 to 20 with items
|
|
* from 30 to 40 on vector v, use:
|
|
* vect_swap_range(v, 10, 20, 30);
|
|
*/
|
|
void vect_swap_range(vector const v, const zvect_index s1, const zvect_index e1, const zvect_index s2);
|
|
|
|
/*
|
|
* vect_rotate_left is a function that allows to rotate
|
|
* a vector of "i" positions to the left (or from the
|
|
* "front" to the "end").
|
|
*
|
|
* For example to rotate a vector called v of 5 positions
|
|
* to the left, use:
|
|
* vect_rotate_left(v, 5);
|
|
*/
|
|
void vect_rotate_left(vector const v, const zvect_index i);
|
|
|
|
/*
|
|
* vect_rotate_right is a function that allows to rotate
|
|
* a vector of "i" positions to the right (or from the
|
|
* "end" to the "front").
|
|
*
|
|
* For example to rotate a vector called v of 5 positions
|
|
* to the right, use:
|
|
* vect_rotate_right(v, 5);
|
|
*/
|
|
void vect_rotate_right(vector const v, const zvect_index i);
|
|
|
|
/*
|
|
* vect_qsort allows you to sort a given vector.
|
|
* The algorithm used to sort a vector is Quicksort with
|
|
* 3 ways partitioning which is generally much faster than
|
|
* traditional quicksort.
|
|
*
|
|
* To sort a vector you need to provide a custom function
|
|
* that allows vect_sort to determine the order and which
|
|
* elements of a vector are used to order it in the way
|
|
* you desire. It pretty much works as a regular C qsort
|
|
* function. It quite fast given that it only reorders
|
|
* pointers to your datastructures stored in the vector.
|
|
*
|
|
*/
|
|
void vect_qsort(vector const v, int (*compare_func)(const void *, const void *));
|
|
|
|
/*
|
|
* vect_bsearch is a function that allows to perform
|
|
* a binary search over the vector we pass to it to
|
|
* find the item "key" using the comparison function
|
|
* "f1".
|
|
*
|
|
* The specific algorithm used to implement vect_bsearch
|
|
* if my own re-implementation of the Adaptive Binary
|
|
* Search algorithm (from Igor van den Hoven) which has
|
|
* some improvements over the original one (look at the
|
|
* sources for more details).
|
|
*
|
|
* For example to search for the number 5 in a vector
|
|
* called v using a compare function called "my_compare"
|
|
* use:
|
|
* int i = 5;
|
|
* vect_bsearch(v, &i, my_compare);
|
|
*/
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
bool vect_bsearch(vector const v, const void *key, int (*f1)(const void *, const void *), zvect_index *item_index);
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
/*
|
|
* vect_add_ordered allows the insertion of new items in
|
|
* an ordered fashion. Please note that for this to work
|
|
* fine you should always use only ordered vectors or if
|
|
* an empty vector use vect_add_ordered only to add new
|
|
* values to it!
|
|
*
|
|
* As for any other ordered function you must provide
|
|
* your own compare function (syntax is the usual one,
|
|
* and it's the same as for regular CLib qsort function)
|
|
*
|
|
* To add item 3 to a vector called v using vect_add_ordered
|
|
* (assuming your compare function is called my_compare),
|
|
* use:
|
|
*
|
|
* vect_Add_ordered(v, 3, my_compare);
|
|
*/
|
|
void vect_add_ordered(vector const v, const void *value, int (*f1)(const void *, const void *));
|
|
|
|
#endif // ZVECT_DMF_EXTENSIONS
|
|
|
|
#ifdef ZVECT_SFMD_EXTENSIONS
|
|
// Single Function Multiple Data extensions:
|
|
|
|
/*
|
|
* vect_apply allows you to apply a C function to
|
|
* each item in the vector, so you just pass the vector,
|
|
* the function you want to execute against each item on
|
|
* a vector and make sure such function is declared and
|
|
* defined to accept a "void *" pointer which will be the
|
|
* pointer to the single item in the vector passed to your
|
|
* function. The function has no return value because it's
|
|
* not necessary if you receive the pointer to the item.
|
|
* You can simply update the content of the memory pointed
|
|
* by the item passed and that is much faster than having
|
|
* to deal with return values especially when you'll be
|
|
* using complex data structures as item of the vector.
|
|
*/
|
|
void vect_apply(vector const v, void (*f1)(void *));
|
|
|
|
/*
|
|
* vect_apply_if is a function that will apply "f1" C function
|
|
* to each and every item in vector v1, IF the return value of
|
|
* function f2 is true. So it allows what is known as conditional
|
|
* application. f2 will receive an item from v1 as first parameter
|
|
* and an item from v2 (at the same position of the item in v1) as
|
|
* second parameter. So, for example, if we want to increment all
|
|
* items in v1 of 10 if they are smaller than the corresponded item
|
|
* in v2 then we can simply use:
|
|
*
|
|
* vect_apply_if(v1, v2, increment_item, is_item_too_small);
|
|
*
|
|
* and make sure we have defined 'increment_item' and
|
|
* 'is_item_too_small' as:
|
|
*
|
|
* void increment_item(void *item1)
|
|
* {
|
|
* int *value = (int *)item1;
|
|
* *value +=10;
|
|
* }
|
|
*
|
|
* bool is_item_too_small(void *item1, void *item2)
|
|
* {
|
|
* if (*((int *)item1) < *((int *)item2))
|
|
* return true;
|
|
* return false;
|
|
* }
|
|
*/
|
|
void vect_apply_if(vector const v1, vector const v2, void (*f1)(void *), bool (*f2)(void *, void *));
|
|
|
|
// Operations with multiple vectors:
|
|
|
|
/*
|
|
* vect_copy is a function that allows to copy a specified
|
|
* set of elements from a vector to another.
|
|
* Please note: only vectors with the same data size (the
|
|
* parameter we pass during the creation of both vectors)
|
|
* can be copied into the other!
|
|
*
|
|
* vect_copy(v1, v2, 3, 5) will copy all the items in
|
|
* vector v2, from the 4th item
|
|
* till the 9th (3 + 5, remember
|
|
* vector items start from 0) in
|
|
* the vector v1. So at the end
|
|
* of the process you'll have such
|
|
* items copied at the end of v1.
|
|
*/
|
|
void vect_copy(vector const v1, vector const v2, zvect_index start, zvect_index max_elements);
|
|
|
|
/*
|
|
* vect_insert is a function that allows to copy a specified
|
|
* set of elements from a vector to another and "insert"
|
|
* them from a specified position in the destination vector.
|
|
* Please note: only vectors with the same data size (the
|
|
* parameter we pass during the creation of both vectors)
|
|
* can be copied into the other!
|
|
*
|
|
* vect_insert(v1, v2, 3, 5, 7) will copy all the items in
|
|
* vector v2, from the 4th item
|
|
* till the 9th (3 + 5, remember
|
|
* vector items start from 0) in
|
|
* the vector v1 from position 7.
|
|
* So at the end of the process
|
|
* you'll have such items "inserted"
|
|
* inside v1.
|
|
*/
|
|
void vect_insert(vector const v1, vector const v2, const zvect_index s2, const zvect_index e2, const zvect_index s1);
|
|
|
|
/*
|
|
* vect_move is a function that allows to move a specified
|
|
* set of items from one vector to another.
|
|
* It will also re-organise the source vector and (obviously)
|
|
* expand the destination vector if needed.
|
|
* Please note: only vectors of the same data size can be moved
|
|
* one into the other!
|
|
*
|
|
* vect_move(v1, v2, 2, 2) will move items in v2 from the
|
|
* 3rd item in v2 till the 5th at
|
|
* the end of v1.
|
|
*/
|
|
void vect_move(vector const v1, vector const v2, zvect_index start, zvect_index max_elements);
|
|
|
|
/*
|
|
* vect_move_if is a function that allows to move a specified
|
|
* set of items from one vector to another if the condition
|
|
* returned by the function pointed by f2 function pointer
|
|
* is true.
|
|
* It will also re-organise the source vector and (obviously)
|
|
* expand the destination vector if needed.
|
|
* Please note: only vectors of the same data size can be moved
|
|
* one into the other!
|
|
*
|
|
* vect_move(v1, v2, 2, 2, check_data) will move items in v2 from the
|
|
* 3rd item in v2 till the 5th at
|
|
* the end of v1 if check_data returns
|
|
* true.
|
|
*/
|
|
zvect_retval vect_move_if(vector const v1,
|
|
vector v2,
|
|
const zvect_index s2,
|
|
const zvect_index e2,
|
|
zvect_retval (*f2)(void *, void *));
|
|
|
|
/*
|
|
* vect_merge is a function that merges together 2 vectors of
|
|
* the same data size. At the end of the process, the source
|
|
* vector will be destroyed.
|
|
*
|
|
* vect_merge(v1, v2) will merge vector v2 to v1 and then
|
|
* destroy v2. So at the end of the job
|
|
* v1 will contain the old v1 items +
|
|
* all v2 items.
|
|
*/
|
|
void vect_merge(vector const v1, vector v2);
|
|
|
|
#endif // ZVECT_SFMD_EXTENSIONS
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif // SRC_ZVECTOR_H_
|