Compare commits
16 Commits
936b2835b2
...
276d38a292
| Author | SHA1 | Date | |
|---|---|---|---|
| 276d38a292 | |||
| 32de4e478f | |||
| 1d037dd31e | |||
| cd822fedfc | |||
| 1b57ebb027 | |||
| c97538265d | |||
| 2bf3f32bed | |||
| b1a7c21caa | |||
| 20507f73bb | |||
| a04490cbdd | |||
| 1a7ba8e393 | |||
| c8d733e410 | |||
| 0271248472 | |||
| 5247394d35 | |||
| 3aedc61da4 | |||
| 4003fa1b78 |
@@ -1,12 +1,18 @@
|
||||
cmake_minimum_required(VERSION 2.7)
|
||||
|
||||
if(TARGET ds)
|
||||
return()
|
||||
endif(TARGET ds)
|
||||
|
||||
project(libds)
|
||||
|
||||
set_property(GLOBAL PROPERTY C_STANDARD 90)
|
||||
ADD_COMPILE_OPTIONS(-pedantic -Wall)
|
||||
add_compile_options(-pedantic -Wall)
|
||||
|
||||
add_library(ds STATIC src/libds.c src/vec.c src/ht.c src/ll.c)
|
||||
add_library(ds STATIC src/libds.c src/vec.c src/ht.c src/ll.c src/sprs.c)
|
||||
add_executable(libds src/main.c)
|
||||
|
||||
set_property(TARGET ds PROPERTY C_STANDARD 90)
|
||||
set_property(TARGET libds PROPERTY C_STANDARD 90)
|
||||
target_include_directories(ds PUBLIC include)
|
||||
target_include_directories(libds PUBLIC include)
|
||||
|
||||
|
||||
70
README.md
Normal file
70
README.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# libds
|
||||
|
||||
A homemade data structure library for use in my personal projects.
|
||||
|
||||
## Rationale
|
||||
|
||||
There is very little rationale for programming libds, looking at it from a very general poin of view. I've been rolling my own data structures in my other projects for quite some time now, and I got tired of it. So I wrote this. libds provides a library for three commonly used data structures, linked lists, growable-array-backed vectors and hash tables.
|
||||
|
||||
## Usage
|
||||
|
||||
Because of how small libds is, it's possible to either include it as a subdirectory in a CMake project, or simply copy its header and source files into another project. The code for each data structure is self-contained, but all data structures depend on common `libds` header and source files that declare and implement "comparison" and "foreach" function types, as well as simple `compare_always` and `compare_pointer` comparison functions.
|
||||
|
||||
To use a data strcucture, it's only necessary to include the relevant header file, like "ll.h". Most data structures require the call to `<data structure name>_init(<data structure pointer>)`, although they differ in whether memory is allocated or not during these calls.
|
||||
|
||||
### Error Prone Functions
|
||||
|
||||
Some functions may be error prone - for instance, calling malloc inside a function COULD technically fail. Error-prone functions return an enum, `LIBDS_RESULT`, which is equal to `LIBDS_SUCCESS` if all goes well, or `LIBDS_MALLOC` if something went wrong. More erro codes will be implemented as necessary.
|
||||
|
||||
### Comparison Functions
|
||||
|
||||
In order to simplify searching and executing code for every element in the data structure, I've typedef'ed two functions:
|
||||
|
||||
```C
|
||||
typedef int (*compare_func)(void*, void*);
|
||||
typedef int (*foreach_func)(void*, va_list);
|
||||
```
|
||||
|
||||
The comparison function is used whenever a search or selection is to be made from a data structure. It should return 1 if the comparison returned true, or 0 if the comparison returned false. Below is a sample implementation of a hypothetical compare_string function.
|
||||
|
||||
```c
|
||||
int compare_string(void*a, void* b){
|
||||
return strcmp(a, b) == 0;
|
||||
}
|
||||
```
|
||||
|
||||
The first parameter is the data given to whatever function invokes the comparison, and the second parameter is the current element in the data structure being compared with. For instance, in a Linked List containing the strings "A", "B", and "C", when a `ll_find` is called with the data "D" and a comparison function that always fails, the calls to the comparison function would be as follows:
|
||||
|
||||
```c
|
||||
compare_func("D", "A");
|
||||
compare_func("D", "B");
|
||||
compare_func("D", "C");
|
||||
```
|
||||
|
||||
Comparison functions don't always have to be used on the same datatype - although the last parameter is always an element from the data structure, anything can be passed as the first parameter.
|
||||
|
||||
### Foreach Functions
|
||||
|
||||
Foreach functions are used to execute a piece of code for every element in the list. The functions return an integer, 0 when all went well and a nonzero error code if there was a problem. The functions are passed a `va_list ` together with the element being iterated over, so that any function calling foreach is able to pass any number of additional parameters. Functions that take a foreach function also take a piece of data and a comparison function, so that it is possible to select only certain elements for which the foreach function is to be performed. Example:
|
||||
|
||||
```c
|
||||
/* test_ll is a linked list with the words "if", "else" and "for".
|
||||
* some_foreach_function_a expects no additional parameters and does nothing.
|
||||
* some_foreach_function_b prints the element given to it (as a string) as many times as an integer parameter passed to it.
|
||||
*/
|
||||
|
||||
/* Will do nothing, but iterate through every element. */
|
||||
ll_foreach(&test_ll, NULL, compare_always, some_foreach_function_a);
|
||||
/* Will print every element 5 times. */
|
||||
ll_foreach(&test_ll, NULL, compare_always, some_foreach_function_b, 5);
|
||||
/* Will print only "else" 5 times. */
|
||||
ll_foreach(&test_ll, "else", compare_string, some_foreach_function_b);
|
||||
```
|
||||
|
||||
### Further Documentation
|
||||
|
||||
For further documentation, please consult the headers for each of the data structures!
|
||||
|
||||
## Tests
|
||||
|
||||
The `main.c` file contains all the tests written for the library, and running the libds executable will perform these tests.
|
||||
18
include/ht.h
18
include/ht.h
@@ -45,21 +45,21 @@ struct ht_s {
|
||||
* @param input the data being hashed.
|
||||
* @return the data's hash.
|
||||
*/
|
||||
unsigned int (*hash_func)(void* input);
|
||||
unsigned long (*hash_func)(const void* input);
|
||||
/**
|
||||
* Function to compare two keys.
|
||||
* @param given_key the key being used to retrieve data
|
||||
* @param current_key the key of a hash table node
|
||||
* @return nonzero value if the keys match.
|
||||
*/
|
||||
int (*cmp_func)(void* given_key, void* current_key);
|
||||
int (*cmp_func)(const void* given_key, const void* current_key);
|
||||
/**
|
||||
* Function used to duplicate the given key and
|
||||
* store it in a node.
|
||||
* @param key the key to copy
|
||||
* @return pointer to a new key.
|
||||
*/
|
||||
void* (*copy_func)(void* key);
|
||||
void* (*copy_func)(const void* key);
|
||||
/**
|
||||
* Function used to free a previously copied key.
|
||||
* @param key the key to free.
|
||||
@@ -76,7 +76,7 @@ typedef struct ht_s ht;
|
||||
* @param key the data to hash.
|
||||
* @return the produced hashed integer.
|
||||
*/
|
||||
unsigned int ht_default_hash_func(void* key);
|
||||
unsigned long ht_default_hash_func(const void* key);
|
||||
/**
|
||||
* The default key comparison function.
|
||||
* Assumes both keys are strings and uses strcmp.
|
||||
@@ -84,13 +84,13 @@ unsigned int ht_default_hash_func(void* key);
|
||||
* @param b the key compared against
|
||||
* @return true if the keys represent the same string.
|
||||
*/
|
||||
int ht_default_cmp_func(void* a, void* b);
|
||||
int ht_default_cmp_func(const void* a, const void* b);
|
||||
/**
|
||||
* The default key copy function.
|
||||
* @param key the key being copied
|
||||
* @return a pointer to the copy of the key.
|
||||
*/
|
||||
void* ht_default_copy_func(void* key);
|
||||
void* ht_default_copy_func(const void* key);
|
||||
/**
|
||||
* The default key free function. Simply calls free()
|
||||
* @param key the key to free.
|
||||
@@ -117,20 +117,20 @@ void ht_free(ht* ht);
|
||||
* @param value the value to store.
|
||||
* @return LIBDS_SUCCESS if all goes well, LIBDS_MALLOC if an allocation fails.
|
||||
*/
|
||||
libds_result ht_put(ht* ht, void* key, void* value);
|
||||
libds_result ht_put(ht* ht, const void* key, void* value);
|
||||
/**
|
||||
* Retrieves a value from the hash table.
|
||||
* @param ht the hash table to retrieve a value from.
|
||||
* @param key the key to use to find the data.
|
||||
* @return the data, or NULL if it is not found.
|
||||
*/
|
||||
void* ht_get(ht* ht, void* key);
|
||||
void* ht_get(ht* ht, const void* key);
|
||||
/**
|
||||
* Removes a value from the hash table.
|
||||
* @param ht the hash table to remove a value from.
|
||||
* @param key the key to use to find the data.
|
||||
*/
|
||||
void ht_remove(ht* ht, void* key);
|
||||
void ht_remove(ht* ht, const void* key);
|
||||
|
||||
/**
|
||||
* Runs through every element in the hash table, and compares it against the
|
||||
|
||||
@@ -15,7 +15,7 @@ typedef enum libds_result_e libds_result;
|
||||
* The function should take two elements, and, if they match, return 1. Otherwise, it should
|
||||
* return 0.
|
||||
*/
|
||||
typedef int (*compare_func)(void*, void*);
|
||||
typedef int (*compare_func)(const void*, const void*);
|
||||
/**
|
||||
* Foreach function type.
|
||||
* Foreach functions are passed to _foreach calls.
|
||||
@@ -29,13 +29,13 @@ typedef int (*foreach_func)(void*, va_list);
|
||||
* @param b The second piece of data being compared
|
||||
* @return always true, i.e 1
|
||||
*/
|
||||
int compare_always(void* a, void* b);
|
||||
int compare_always(const void* a, const void* b);
|
||||
/**
|
||||
* A compare_func implementation that compares two pointers using ==.
|
||||
* @param a the first piece of data being compared
|
||||
* @param b the second piece of data being compared
|
||||
* @return true if a and b are the same value.
|
||||
*/
|
||||
int compare_pointer(void* a, void* b);
|
||||
int compare_pointer(const void* a, const void* b);
|
||||
|
||||
#endif
|
||||
|
||||
143
include/sprs.h
Normal file
143
include/sprs.h
Normal file
@@ -0,0 +1,143 @@
|
||||
#ifndef LIBDS_SPRS_HEADER
|
||||
#define LIBDS_SPRS_HEADER
|
||||
|
||||
#include <stddef.h>
|
||||
#include "libds.h"
|
||||
|
||||
/**
|
||||
* Sparse set implementation with the capacity to store data.
|
||||
* Sparse sets are initially defined as having two arrays of integers,
|
||||
* one "dense" array, into which new elements are added sequentially, and another
|
||||
* "sparse" array into which the elements' indexes in the dense array are put at
|
||||
* their index.
|
||||
*
|
||||
* For instance, after adding the elements 1, 0, and 3 to a sparse array,
|
||||
* it may look like:
|
||||
* (Size) (Capacity)
|
||||
* | |
|
||||
* [1 ][0 ][3 ][..][..][..] (Dense)
|
||||
* [1 ][0 ][..][2 ][..][..] (Sparse)
|
||||
* As seen in the above representation, the element in the dense array at index 0 is 1,
|
||||
* and the element at index 1 in the sparse array points to its location in the dense array.
|
||||
* It's very quick to check whether an element is part of this sparse set:
|
||||
* if sparse[element] < size && dense[sparse[element]] == element.
|
||||
* Iteration is also O(n), thanks to the dense array, and no initial memory clearing is required.
|
||||
*
|
||||
* The below implementation adds an additional piece of data to the sparse array, a void* so that an item
|
||||
* can be associated with an integer index and stored / retrieved / ismember'ed together with it.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A single element in a sparse array.
|
||||
*/
|
||||
struct sprs_element_s {
|
||||
/**
|
||||
* The index of this element in the dense array.
|
||||
*/
|
||||
size_t index;
|
||||
/**
|
||||
* A piece of data (optional) associated with this index.
|
||||
*/
|
||||
void* data;
|
||||
};
|
||||
|
||||
/**
|
||||
* A sparse array / set.
|
||||
*/
|
||||
struct sprs_s {
|
||||
/**
|
||||
* The maximum size of the set. This is the limit for integer indices.
|
||||
*/
|
||||
size_t capacity;
|
||||
/**
|
||||
* The current size of the sparse set, and the next available index in the dense array.
|
||||
*/
|
||||
size_t size;
|
||||
|
||||
/**
|
||||
* The dense array.
|
||||
* This is a pointer so that an array of a certain size can be allocated as needed.
|
||||
*/
|
||||
size_t* dense;
|
||||
/**
|
||||
* The sparse array.
|
||||
* This is a pointer so that an array of a certain size can be allocated as needed.
|
||||
*/
|
||||
struct sprs_element_s* sparse;
|
||||
};
|
||||
|
||||
typedef struct sprs_s sprs;
|
||||
|
||||
/**
|
||||
* Initializes a sparse set with default values.
|
||||
* This does not allocate any memory for the arrays.
|
||||
* @param sprs the sparse set / array to initialize.
|
||||
*/
|
||||
void sprs_init(sprs* sprs);
|
||||
/**
|
||||
* Allocates the necessary memory for a sparse set's
|
||||
* dense and sparse arrays.
|
||||
* @param sprs the sparse set for which memory should be allocated.
|
||||
* @param size the maximum capacity of the sparse set to use.
|
||||
* @return LIBDS_SUCCESS if all goes well, or LIBDS_MALLOC if an allocation failed.
|
||||
*/
|
||||
libds_result sprs_setup(sprs* sprs, size_t size);
|
||||
/**
|
||||
* Frees memory allocated by the sparse set, and resets its capacity and size.
|
||||
* @param sprs the sparse set to free.
|
||||
*/
|
||||
void sprs_free(sprs* sprs);
|
||||
|
||||
/**
|
||||
* Stores a value in the sparse set under a given index.
|
||||
* @param sprs the sparse set into which to store the value.
|
||||
* @param index the index at which to store the value
|
||||
* @param data the value to store.
|
||||
*/
|
||||
void sprs_put(sprs* sprs, size_t index, void* data);
|
||||
/**
|
||||
* Checks if the sparse set contains data under a given index.
|
||||
* @param sprs the sparse set to check.
|
||||
* @param index the index for which to check.
|
||||
* @return 1 if the index exists, 0 if not.
|
||||
*/
|
||||
int sprs_contains(sprs* sprs, size_t index);
|
||||
/**
|
||||
* Gets the value stored under the given sparse set index.
|
||||
* This will check for whether the index is in the sparse set first,
|
||||
* and return NULL if it's not.
|
||||
* Please not that this exhibit the !exact same! behavior if a NULL was stored
|
||||
* under the index or if the index was never populated. use sprs_contains to
|
||||
* check whether the index is populated if this is important.
|
||||
* @param sprs the sparse set to check.
|
||||
* @param index the index from under which to retrieve the value.
|
||||
* @return the value stored under the index, or NULL if there is nothing there.
|
||||
*/
|
||||
void* sprs_get(sprs* sprs, size_t index);
|
||||
|
||||
/**
|
||||
* Runs through every element in the sparse set, and compares it against the
|
||||
* given data using the given comparison function. If the comparison function returns
|
||||
* true, returns the element that was passed to it. If the comparison function returns
|
||||
* true for no element, returns NULL.
|
||||
* @param sprs the sparse set to iterate through.
|
||||
* @param data the data to compare elements against
|
||||
* @param compare the comparison function
|
||||
* @return the first element that is matched by the comparison function, or NULL if none are matched.
|
||||
*/
|
||||
void* sprs_find(sprs* sprs, void* data, compare_func compare);
|
||||
/**
|
||||
* Runs through every element in the sparse set, and compares it against the
|
||||
* given data using the given comparison function. If the comparison function returns
|
||||
* true, calls the foreach function, passing it the element and the variable argument list.
|
||||
* Stops if any foreach function returns a nonzero code.
|
||||
* @param sprs the sparse set to perform the operation on
|
||||
* @param data the data to pass the comparison function
|
||||
* @param compare the comparison function operating on the data and each element in the list.
|
||||
* @param foreach the function to be called on every element recognized by the comparison function
|
||||
* @param ... variable arguments to be passed on to the foreach function
|
||||
* @return 0 if all goes well, or the first nonzero code returned by foreach.
|
||||
*/
|
||||
int sprs_foreach(sprs* sprs, void* data, compare_func compare, foreach_func foreach, ...);
|
||||
|
||||
#endif
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#define LIBDS_VEC_CAPACITY 4
|
||||
|
||||
#include <stddef.h>
|
||||
#include "libds.h"
|
||||
|
||||
/**
|
||||
@@ -18,11 +19,11 @@ struct vec_s {
|
||||
/**
|
||||
* The maximum size of the vector.
|
||||
*/
|
||||
int capacity;
|
||||
size_t capacity;
|
||||
/**
|
||||
* The current number of items in the vector.
|
||||
*/
|
||||
int size;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
typedef struct vec_s vec;
|
||||
@@ -87,6 +88,12 @@ int vec_foreach(vec* vec, void* data, compare_func compare, foreach_func foreach
|
||||
* @param index the index to retreive a value from
|
||||
* @return pointer to the value, or, if the index is out of bounds (or there is nothing there) NULL.
|
||||
*/
|
||||
void* vec_index(vec* vec, int index);
|
||||
void* vec_index(vec* vec, size_t index);
|
||||
/**
|
||||
* Clears the vector, removing all elements from it
|
||||
* but not resizing it down.
|
||||
* @param vec the vector to clear.
|
||||
*/
|
||||
void vec_clear(vec* vec);
|
||||
|
||||
#endif
|
||||
|
||||
60
src/ht.c
60
src/ht.c
@@ -2,11 +2,11 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
unsigned int ht_default_hash_func(void* data) {
|
||||
const unsigned int fnv_prime = 16777619;
|
||||
const unsigned int fnv_offset_basis = 2166136261;
|
||||
unsigned int hash;
|
||||
char* string;
|
||||
unsigned long ht_default_hash_func(const void* data) {
|
||||
const unsigned long fnv_prime = 16777619;
|
||||
const unsigned long fnv_offset_basis = 2166136261;
|
||||
unsigned long hash;
|
||||
const char* string;
|
||||
hash = fnv_offset_basis;
|
||||
string = data;
|
||||
while (*string) {
|
||||
@@ -15,8 +15,8 @@ unsigned int ht_default_hash_func(void* data) {
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
int ht_default_cmp_func(void* a, void* b) { return strcmp(a, b) == 0; }
|
||||
void* ht_default_copy_func(void* from) {
|
||||
int ht_default_cmp_func(const void* a, const void* b) { return strcmp(a, b) == 0; }
|
||||
void* ht_default_copy_func(const void* from) {
|
||||
void* copy = malloc(sizeof(char) * (strlen(from) + 1));
|
||||
if (copy) {
|
||||
strcpy(copy, from);
|
||||
@@ -35,48 +35,44 @@ void ht_init(ht* ht) {
|
||||
void ht_free(ht* ht) {
|
||||
int index = 0;
|
||||
for (; index < LIBDS_HT_SIZE; index++) {
|
||||
ht_node* head = ht->data[index];
|
||||
while (head) {
|
||||
ht_node* to_delete = head;
|
||||
head = head->next;
|
||||
while (ht->data[index]) {
|
||||
ht_node* to_delete = ht->data[index];
|
||||
ht->data[index] = to_delete->next;
|
||||
ht->free_func(to_delete->key);
|
||||
free(to_delete);
|
||||
}
|
||||
}
|
||||
ht->cmp_func = NULL;
|
||||
ht->copy_func = NULL;
|
||||
ht->free_func = NULL;
|
||||
ht->hash_func = NULL;
|
||||
}
|
||||
|
||||
libds_result ht_put(ht* ht, void* key, void* data) {
|
||||
libds_result ht_put(ht* ht, const void* key, void* data) {
|
||||
libds_result result = LIBDS_SUCCESS;
|
||||
ht_node* new_node = NULL;
|
||||
unsigned int key_int = ht->hash_func(key) % LIBDS_HT_SIZE;
|
||||
void* new_key = NULL;
|
||||
unsigned long key_int = ht->hash_func(key) % LIBDS_HT_SIZE;
|
||||
|
||||
new_node = malloc(sizeof(*new_node));
|
||||
if (new_node) {
|
||||
new_key = ht->copy_func(key);
|
||||
if (new_node && new_key) {
|
||||
new_node->data = data;
|
||||
new_node->key = ht->copy_func(key);
|
||||
if (new_node->key == NULL) {
|
||||
result = LIBDS_MALLOC;
|
||||
}
|
||||
} else {
|
||||
result = LIBDS_MALLOC;
|
||||
}
|
||||
|
||||
if (result != LIBDS_SUCCESS) {
|
||||
if (new_node) {
|
||||
free(new_node);
|
||||
new_node = NULL;
|
||||
}
|
||||
} else {
|
||||
new_node->key = new_key;
|
||||
new_node->next = ht->data[key_int];
|
||||
ht->data[key_int] = new_node;
|
||||
} else {
|
||||
free(new_node);
|
||||
free(new_key);
|
||||
result = LIBDS_MALLOC;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
void* ht_get(ht* ht, void* key) {
|
||||
void* ht_get(ht* ht, const void* key) {
|
||||
void* data = NULL;
|
||||
ht_node* search_node;
|
||||
unsigned int key_int = ht->hash_func(key) % LIBDS_HT_SIZE;
|
||||
unsigned long key_int = ht->hash_func(key) % LIBDS_HT_SIZE;
|
||||
|
||||
search_node = ht->data[key_int];
|
||||
while (search_node && data == NULL) {
|
||||
@@ -88,9 +84,9 @@ void* ht_get(ht* ht, void* key) {
|
||||
|
||||
return data;
|
||||
}
|
||||
void ht_remove(ht* ht, void* key) {
|
||||
void ht_remove(ht* ht, const void* key) {
|
||||
ht_node** search_ptr;
|
||||
unsigned int key_int = ht->hash_func(key) % LIBDS_HT_SIZE;
|
||||
unsigned long key_int = ht->hash_func(key) % LIBDS_HT_SIZE;
|
||||
|
||||
search_ptr = &ht->data[key_int];
|
||||
while (*search_ptr) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "libds.h"
|
||||
|
||||
int compare_always(void* a, void* b) { return 1; }
|
||||
int compare_pointer(void* a, void* b) { return a == b; }
|
||||
int compare_always(const void* a, const void* b) { return 1; }
|
||||
int compare_pointer(const void* a, const void* b) { return a == b; }
|
||||
|
||||
115
src/main.c
115
src/main.c
@@ -5,6 +5,7 @@
|
||||
#include "ht.h"
|
||||
#include "ll.h"
|
||||
#include "vec.h"
|
||||
#include "sprs.h"
|
||||
|
||||
int _test_vec_foreach_func(void* data, va_list args) {
|
||||
char* real_data = data;
|
||||
@@ -13,7 +14,7 @@ int _test_vec_foreach_func(void* data, va_list args) {
|
||||
*sum += *real_data;
|
||||
return 0;
|
||||
}
|
||||
int _test_vec_find_string(void* a, void* b) { return strcmp(a, b) == 0; }
|
||||
int _test_vec_find_string(const void* a, const void* b) { return strcmp(a, b) == 0; }
|
||||
|
||||
int test_vec_basic() {
|
||||
vec test_vec;
|
||||
@@ -37,12 +38,16 @@ int test_vec_add() {
|
||||
int test_vec_remove() {
|
||||
vec test_vec;
|
||||
char* test_string = "test";
|
||||
char* second_test_string = "test2";
|
||||
int return_code;
|
||||
|
||||
vec_init(&test_vec);
|
||||
vec_add(&test_vec, test_string);
|
||||
vec_add(&test_vec, test_string);
|
||||
vec_add(&test_vec, test_string);
|
||||
vec_add(&test_vec, second_test_string);
|
||||
vec_remove(&test_vec, test_string);
|
||||
return_code = *((void**)test_vec.data) == NULL;
|
||||
return_code = ((void**)test_vec.data)[0] == second_test_string;
|
||||
vec_free(&test_vec);
|
||||
|
||||
return return_code;
|
||||
@@ -88,6 +93,22 @@ int test_vec_index() {
|
||||
vec_free(&test_vec);
|
||||
return return_code;
|
||||
}
|
||||
int test_vec_clear(){
|
||||
vec test_vec;
|
||||
char* test_string = "test";
|
||||
void** vec_data = NULL;
|
||||
int return_code;
|
||||
vec_init(&test_vec);
|
||||
vec_data = test_vec.data;
|
||||
vec_add(&test_vec, test_string);
|
||||
vec_add(&test_vec, test_string);
|
||||
vec_add(&test_vec, test_string);
|
||||
vec_add(&test_vec, test_string);
|
||||
vec_clear(&test_vec);
|
||||
return_code = (vec_data[0] == vec_data[1]) && (vec_data[1] == vec_data[2]) && (vec_data[2] == vec_data[3]) && (test_vec.size == 0);
|
||||
vec_free(&test_vec);
|
||||
return return_code;
|
||||
}
|
||||
|
||||
int _test_ht_foreach_func(void* data, va_list args) {
|
||||
char* real_data = data;
|
||||
@@ -252,6 +273,82 @@ int test_ll_poptail(){
|
||||
return return_code;
|
||||
}
|
||||
|
||||
int _test_sprs_foreach_count(void* data, va_list args){
|
||||
(*(va_arg(args, int*)))++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_sprs_basic(){
|
||||
sprs sprs;
|
||||
sprs_init(&sprs);
|
||||
sprs_setup(&sprs, 1);
|
||||
sprs_free(&sprs);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int test_sprs_put(){
|
||||
int return_code;
|
||||
sprs sprs;
|
||||
sprs_init(&sprs);
|
||||
sprs_setup(&sprs, 4);
|
||||
sprs_put(&sprs, 2, NULL);
|
||||
return_code = sprs.dense[0] == 2 && sprs.sparse[2].index == 0 && sprs.sparse[2].data == NULL;
|
||||
sprs_free(&sprs);
|
||||
return return_code;
|
||||
}
|
||||
|
||||
int test_sprs_contains(){
|
||||
int return_code;
|
||||
sprs sprs;
|
||||
sprs_init(&sprs);
|
||||
sprs_setup(&sprs, 4);
|
||||
sprs_put(&sprs, 2, NULL);
|
||||
sprs_put(&sprs, 3, NULL);
|
||||
return_code = (sprs_contains(&sprs, 2) == 1) && (sprs_contains(&sprs, 3) == 1) && (sprs_contains(&sprs, 0) == 0);
|
||||
sprs_free(&sprs);
|
||||
return return_code;
|
||||
}
|
||||
|
||||
int test_sprs_get(){
|
||||
int return_code;
|
||||
sprs sprs;
|
||||
char* test_string = "test";
|
||||
sprs_init(&sprs);
|
||||
sprs_setup(&sprs, 4);
|
||||
sprs_put(&sprs, 2, test_string);
|
||||
sprs_put(&sprs, 3, NULL);
|
||||
return_code = sprs_get(&sprs, 2) == test_string && sprs_get(&sprs, 3) == NULL && sprs_get(&sprs, 0) == NULL;
|
||||
sprs_free(&sprs);
|
||||
return return_code;
|
||||
}
|
||||
|
||||
int test_sprs_find(){
|
||||
int return_code;
|
||||
sprs sprs;
|
||||
char* test_string = "test";
|
||||
sprs_init(&sprs);
|
||||
sprs_setup(&sprs, 4);
|
||||
sprs_put(&sprs, 3, test_string);
|
||||
return_code = sprs_find(&sprs, test_string, compare_pointer) == test_string;
|
||||
sprs_free(&sprs);
|
||||
return return_code;
|
||||
}
|
||||
|
||||
int test_sprs_foreach(){
|
||||
int return_code;
|
||||
sprs sprs;
|
||||
int count = 0;
|
||||
sprs_init(&sprs);
|
||||
sprs_setup(&sprs, 4);
|
||||
sprs_put(&sprs, 0, NULL);
|
||||
sprs_put(&sprs, 2, NULL);
|
||||
sprs_put(&sprs, 3, NULL);
|
||||
sprs_foreach(&sprs, NULL, compare_always, _test_sprs_foreach_count, &count);
|
||||
return_code = count == 3;
|
||||
sprs_free(&sprs);
|
||||
return return_code;
|
||||
}
|
||||
|
||||
int run_test(char* test_name, int (*test_func)()) {
|
||||
int success = test_func();
|
||||
printf("Running test %-15s . . . ", test_name);
|
||||
@@ -260,18 +357,18 @@ int run_test(char* test_name, int (*test_func)()) {
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
char* test_names[19] = {"vec_basic", "vec_add", "vec_remove", "vec_find",
|
||||
"vec_foreach", "vec_index", "ht_basic", "ht_put",
|
||||
"ht_get", "ht_remove", "ht_foreach", "ll_basic", "ll_append", "ll_prepend", "ll_remove", "ll_find" , "ll_foreach", "ll_pophead", "ll_poptail"};
|
||||
char* test_names[26] = {"vec_basic", "vec_add", "vec_remove", "vec_find",
|
||||
"vec_foreach", "vec_index", "vec_clear", "ht_basic", "ht_put",
|
||||
"ht_get", "ht_remove", "ht_foreach", "ll_basic", "ll_append", "ll_prepend", "ll_remove", "ll_find" , "ll_foreach", "ll_pophead", "ll_poptail", "sprs_basic", "sprs_put", "sprs_contains", "sprs_get", "sprs_find", "sprs_foreach"};
|
||||
|
||||
int (*test_functions[19])() = {
|
||||
int (*test_functions[26])() = {
|
||||
test_vec_basic, test_vec_add, test_vec_remove, test_vec_find,
|
||||
test_vec_foreach, test_vec_index, test_ht_basic, test_ht_put,
|
||||
test_ht_get, test_ht_remove, test_ht_foreach, test_ll_basic, test_ll_append, test_ll_prepend, test_ll_remove, test_ll_find, test_ll_foreach, test_ll_pophead, test_ll_poptail};
|
||||
test_vec_foreach, test_vec_index, test_vec_clear, test_ht_basic, test_ht_put,
|
||||
test_ht_get, test_ht_remove, test_ht_foreach, test_ll_basic, test_ll_append, test_ll_prepend, test_ll_remove, test_ll_find, test_ll_foreach, test_ll_pophead, test_ll_poptail, test_sprs_basic, test_sprs_put, test_sprs_contains, test_sprs_get, test_sprs_find, test_sprs_foreach};
|
||||
|
||||
int test_index = 0;
|
||||
int result = 1;
|
||||
for (; test_index < 19 && result; test_index++) {
|
||||
for (; test_index < 26 && result; test_index++) {
|
||||
result = run_test(test_names[test_index], test_functions[test_index]);
|
||||
}
|
||||
return result ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
|
||||
72
src/sprs.c
Normal file
72
src/sprs.c
Normal file
@@ -0,0 +1,72 @@
|
||||
#include "sprs.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
void sprs_init(sprs* sprs){
|
||||
sprs->capacity = 0;
|
||||
sprs->size = 0;
|
||||
sprs->dense = NULL;
|
||||
sprs->sparse = NULL;
|
||||
}
|
||||
libds_result sprs_setup(sprs* sprs, size_t size){
|
||||
libds_result result = LIBDS_SUCCESS;
|
||||
sprs->sparse = malloc(sizeof(*(sprs->sparse)) * size);
|
||||
sprs->dense = malloc(sizeof(*(sprs->dense)) * size);
|
||||
if(sprs->sparse == NULL || sprs->dense == NULL){
|
||||
free(sprs->sparse);
|
||||
free(sprs->dense);
|
||||
sprs->sparse = NULL;
|
||||
sprs->dense = NULL;
|
||||
result = LIBDS_MALLOC;
|
||||
} else {
|
||||
sprs->capacity = size;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
void sprs_free(sprs* sprs){
|
||||
free(sprs->sparse);
|
||||
free(sprs->dense);
|
||||
sprs->capacity = 0;
|
||||
sprs->size = 0;
|
||||
sprs->dense = NULL;
|
||||
sprs->sparse = NULL;
|
||||
}
|
||||
void sprs_put(sprs* sprs, size_t index, void* data){
|
||||
if(index < sprs->capacity && sprs->size < sprs->capacity){
|
||||
sprs->dense[sprs->size++] = index;
|
||||
sprs->sparse[index].index = sprs->size - 1;
|
||||
sprs->sparse[index].data = data;
|
||||
}
|
||||
}
|
||||
int sprs_contains(sprs* sprs, size_t index){
|
||||
return sprs->sparse[index].index < sprs->size && sprs->dense[sprs->sparse[index].index] == index;
|
||||
}
|
||||
void* sprs_get(sprs* sprs, size_t index){
|
||||
void* data = NULL;
|
||||
if(sprs_contains(sprs, index)){
|
||||
data = sprs->sparse[index].data;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
void* sprs_find(sprs* sprs, void* data, compare_func compare){
|
||||
int index = 0;
|
||||
void* to_return = NULL;
|
||||
for(; index < sprs->size && to_return == NULL; index++){
|
||||
if(compare(data, sprs->sparse[sprs->dense[index]].data)) {
|
||||
to_return = sprs->sparse[sprs->dense[index]].data;
|
||||
}
|
||||
}
|
||||
return to_return;
|
||||
}
|
||||
int sprs_foreach(sprs* sprs, void* data, compare_func compare, foreach_func foreach, ...){
|
||||
int index = 0;
|
||||
int return_code = 0;
|
||||
va_list args;
|
||||
for(; index < sprs->size && return_code == 0; index++){
|
||||
if(compare(data, sprs->sparse[sprs->dense[index]].data)) {
|
||||
va_start(args, foreach);
|
||||
return_code = foreach(sprs->sparse[sprs->dense[index]].data, args);
|
||||
va_end(args);
|
||||
}
|
||||
}
|
||||
return return_code;
|
||||
}
|
||||
63
src/vec.c
63
src/vec.c
@@ -8,7 +8,7 @@ libds_result vec_init(vec* v) {
|
||||
|
||||
v->size = 0;
|
||||
v->capacity = LIBDS_VEC_CAPACITY;
|
||||
v->data = calloc(LIBDS_VEC_CAPACITY, sizeof(void*));
|
||||
v->data = malloc(LIBDS_VEC_CAPACITY * sizeof(void*));
|
||||
if (v->data == NULL) {
|
||||
result = LIBDS_MALLOC;
|
||||
}
|
||||
@@ -27,7 +27,7 @@ void vec_free(vec* v) {
|
||||
libds_result vec_add(vec* v, void* data) {
|
||||
libds_result result = LIBDS_SUCCESS;
|
||||
if (v->size >= v->capacity) {
|
||||
void* new_mem = calloc(v->capacity *= 2, sizeof(void*));
|
||||
void* new_mem = malloc((v->capacity *= 2) * sizeof(void*));
|
||||
if (new_mem) {
|
||||
memcpy(new_mem, v->data, sizeof(void*) * v->capacity / 2);
|
||||
free(v->data);
|
||||
@@ -38,78 +38,65 @@ libds_result vec_add(vec* v, void* data) {
|
||||
}
|
||||
|
||||
if (result == LIBDS_SUCCESS) {
|
||||
int index = 0;
|
||||
while (index < v->capacity) {
|
||||
void** data_array = v->data;
|
||||
if (data_array[index] == NULL) {
|
||||
data_array[index] = data;
|
||||
v->size++;
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
((void**) v->data)[v->size++] = data;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
void vec_remove(vec* v, void* data) {
|
||||
int elements = v->size;
|
||||
int index = 0;
|
||||
size_t index = 0;
|
||||
void** data_array = v->data;
|
||||
while (elements > 0) {
|
||||
if (data_array[index]) {
|
||||
elements--;
|
||||
if (data_array[index] == data) {
|
||||
data_array[index] = NULL;
|
||||
v->size--;
|
||||
while (index < v->size && data_array[index]) {
|
||||
size_t search_ahead = 0;
|
||||
while(data_array[index + search_ahead] == data){
|
||||
search_ahead++;
|
||||
}
|
||||
|
||||
if(search_ahead){
|
||||
size_t remaining_elements = v->size - (index + search_ahead);
|
||||
memmove(data_array + index, data_array + index + search_ahead, remaining_elements);
|
||||
v->size -= search_ahead;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
void* vec_find(vec* v, void* data, compare_func compare) {
|
||||
int elements = v->size;
|
||||
int index = 0;
|
||||
size_t index = 0;
|
||||
void* found = NULL;
|
||||
void** data_array = v->data;
|
||||
while (elements > 0 && found == NULL) {
|
||||
if (data_array[index]) {
|
||||
if (compare(data, data_array[index])) {
|
||||
while (index < v->size && found == NULL) {
|
||||
if (data_array[index] && compare(data, data_array[index])) {
|
||||
found = data_array[index];
|
||||
}
|
||||
elements--;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return found;
|
||||
}
|
||||
int vec_foreach(vec* v, void* data, compare_func compare, foreach_func foreach,
|
||||
...) {
|
||||
int elements = v->size;
|
||||
int index = 0;
|
||||
size_t index = 0;
|
||||
int return_code = 0;
|
||||
void** data_array = v->data;
|
||||
va_list args;
|
||||
while (elements > 0 && return_code == 0) {
|
||||
if (data_array[index]) {
|
||||
if (compare(data, data_array[index])) {
|
||||
while (index < v->size && return_code == 0) {
|
||||
if (data_array[index] && compare(data, data_array[index])) {
|
||||
va_start(args, foreach);
|
||||
return_code = foreach (data_array[index], args);
|
||||
va_end(args);
|
||||
}
|
||||
elements--;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return return_code;
|
||||
}
|
||||
|
||||
void* vec_index(vec* v, int index) {
|
||||
void* vec_index(vec* v, size_t index) {
|
||||
void* to_return = NULL;
|
||||
if (index < v->capacity && index >= 0) {
|
||||
void** data_array = v->data;
|
||||
to_return = data_array[index];
|
||||
if (index < v->capacity) {
|
||||
to_return = ((void**) v->data)[index];
|
||||
}
|
||||
return to_return;
|
||||
}
|
||||
void vec_clear(vec* vec) {
|
||||
vec->size = 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user