v0.26.0
This commit is contained in:
1 parent
6f8496d39e
commit
feb5dab0ce
129 files changed
+3608
-3636
No files matched your search
@@ -0,0 +1,152 @@
|
||||
# Minimal Example
|
||||
|
||||
(TODO this example is currently outdated)
|
||||
|
||||
Lets create a list of type `int`, add some elements to it and get the sum of all the elements printed on the screen.
|
||||
|
||||
### Include headers
|
||||
|
||||
```c
|
||||
#include <cmc/list.h>
|
||||
```
|
||||
|
||||
First you need to include only the necessary headers. It is not recommended that you include everything with `macro_collections.h`, only in cases where you might be using almost all features from the library.
|
||||
|
||||
### Generating a list
|
||||
|
||||
```c
|
||||
// CMC_GENERATE_LIST(PFX, SNAME, V)
|
||||
CMC_GENERATE_LIST(il, int_list, int)
|
||||
```
|
||||
|
||||
The List is also known as `vector`, `ArrayList` and dynamic array. In fact the latter is the best description for this collection. It is a dynamic array that can grow and shrink as needed. To generate it we can simply call the macro `CMC_GENERATE_LIST` after our includes. It has three parameters:
|
||||
|
||||
| Parameter name | Purpose |
|
||||
| :------------: | ------- |
|
||||
| __PFX__ | Functions prefix (namespace) |
|
||||
| __SNAME__ | Struct name |
|
||||
| __V__ | The list value type (can be any valid data type) |
|
||||
|
||||
<aside class="notice">
|
||||
All collections can be generated by using the macro CMC_GENERATE_ and then the name of the include (in this case list.h) but in uppercase without the `.h`.
|
||||
</aside>
|
||||
|
||||
### Initializing and adding elements
|
||||
|
||||
```c
|
||||
// Allocate an int_list with an initial capacity of 100
|
||||
int_list *list = il_new(100);
|
||||
|
||||
// Adding elements to the back of the list
|
||||
for (int i = 0; i <= 1000; i++)
|
||||
{
|
||||
if (!il_push_back(list, i))
|
||||
{
|
||||
// Something went wrong (check out documentation)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To initialize any collection we call a function `_new`. Since the `PFX` parameter is set to `il` all functions related to our list of integer will be prefixed by `il`, so `_new` becomes `il_new`.
|
||||
|
||||
The list name is `int_list` as defined by `SNAME`. This type is a typedef like:
|
||||
|
||||
`typedef struct SNAME##_s SNAME;`
|
||||
|
||||
Which expands to:
|
||||
|
||||
`typedef struct int_list_s int_list`
|
||||
|
||||
To add elements to the back of a list we can use `il_push_back`. This function will first check if the list has enough space for a new element. If not, reallocate the internal buffer with doubled size and then add the new element.
|
||||
|
||||
### Iteration
|
||||
|
||||
> It is recommended that the iterator is allocated on the stack
|
||||
|
||||
```c
|
||||
// Resulting sum will be stored here
|
||||
int sum = 0;
|
||||
|
||||
// Declare our iterator (allocating it on the stack)
|
||||
int_list_iter it;
|
||||
|
||||
// Loop for each element
|
||||
for (il_iter_init(&it, list); !il_iter_end(&it); il_iter_next(&it))
|
||||
{
|
||||
// Accessing the value
|
||||
int elem = il_iter_value(&it);
|
||||
|
||||
sum += elem;
|
||||
}
|
||||
|
||||
printf("%d\n", sum);
|
||||
```
|
||||
|
||||
There are 6 (2 optional) steps to iterate over the elements of any collection:
|
||||
|
||||
| Step | Description | Function |
|
||||
| :--: | ----------- | -------- |
|
||||
| 1 | Allocate the iterator (optional) | `il_iter_new` |
|
||||
| 2 | Initialize the iterator given a target list | `il_iter_init` |
|
||||
| 3 | Access the iterator's elements (value, index, etc) | `il_iter_value` |
|
||||
| 4 | Go to the next element | `il_iter_next` |
|
||||
| 5 | Check if we haven't reached the end of the list | `il_iter_end` |
|
||||
| 6 | Free the iterator if it was allocated on the heap | `il_iter_free` |
|
||||
|
||||
Note that all iterator functions are prefixed by the user defined prefix and then by `iter` (`il + iter = il_iter`). Also, the steps __1__ and __6__ are optional so `il_iter_new` and `il_iter_free` are not used in the example.
|
||||
|
||||
By the end of the example we have the variable `sum` with the sum of all integers inside our list. We can then print it and this example is almost done.
|
||||
|
||||
### Freeing resources
|
||||
|
||||
```c
|
||||
// void il_free(int_list *_list_, void(*deallocator)(int))
|
||||
il_free(list, NULL);
|
||||
```
|
||||
|
||||
The list is currently allocated in memory. To deallocate it we call `il_free`. This function takes a pointer to the allocated list and a pointer to a `deallocator` function that will be responsible for deallocating each element in the list. This last parameter is optional and we won't be using it since our data type `int` is not allocated in the heap. This optional parameter can be set to `NULL` to be ignored by the `il_free` function.
|
||||
|
||||
### Compile and run
|
||||
|
||||
> source.c
|
||||
|
||||
```c
|
||||
#include <cmc/list.h>
|
||||
|
||||
CMC_GENERATE_LIST(il, int_list, int)
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int_list *list = il_new(100);
|
||||
|
||||
for (int i = 0; i <= 1000; i++)
|
||||
{
|
||||
if (!il_push_back(list, i))
|
||||
{
|
||||
// Something went wrong (check out documentation)
|
||||
}
|
||||
}
|
||||
|
||||
int sum = 0;
|
||||
|
||||
int_list_iter it;
|
||||
|
||||
for (il_iter_init(&it, list); !il_iter_end(&it); il_iter_next(&it))
|
||||
{
|
||||
int elem = il_iter_value(&it);
|
||||
sum += elem;
|
||||
}
|
||||
|
||||
printf("%d\n", sum);
|
||||
|
||||
il_free(list, NULL);
|
||||
}
|
||||
```
|
||||
|
||||
If the source file is `source.c`, all you have to do is to include the `src` folder to the include path and compile. The following example uses `gcc`:
|
||||
|
||||
`gcc source.c -I path/to/library/src`
|
||||
|
||||
### Conclusion
|
||||
|
||||
The C Macro Collections Library is prepared to accept any data type and is highly customizable. It is also very easy to integrate and no previous compilation is required. The library also comes with many utilities in the `utl` folder that are designed to work well with all collections.
|
||||
@@ -0,0 +1,21 @@
|
||||
# How to install
|
||||
|
||||
To start using the library you need first to download the source code. You can either fork the repository or download a zip file containing everything. Either way, after you unzip everything you will end up with the root folder (probably something called `C-Macro-Collections`) and inside it is the `src` folder.
|
||||
|
||||
With the `src` folder you can start including the library headers to your project with `-I path/to/library/src`.
|
||||
|
||||
The library has no external dependencies other than the C standard library.
|
||||
|
||||
```c
|
||||
// Include header files like this
|
||||
#include <cmc/list.h>
|
||||
#include <dev/deque.h>
|
||||
#include <utl/assert.h>
|
||||
```
|
||||
|
||||
```c
|
||||
// Not like this
|
||||
#include <list.h>
|
||||
#include <deque.h> // dev or cmc?
|
||||
#include <assert.h> // this will import from the standard library
|
||||
```
|
||||
@@ -0,0 +1,5 @@
|
||||
# How to Use the Library
|
||||
|
||||
The C Macro Collections Library comes with very powerful data structures and they are only one macro away from you.
|
||||
|
||||
TODO
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
@@ -0,0 +1,30 @@
|
||||
# Introduction
|
||||
|
||||
<p align="center">
|
||||
<img src="img/logo.png" alt="C Macro Collections Logo" width="300"/>
|
||||
</p>
|
||||
|
||||
<p align="center">Easy to use, modular, header only, macro based, generic and type-safe Data Structures in C</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/LeoVen/C-Macro-Collections"><img src="https://img.shields.io/badge/GitHub-C%20Macro%20Collections-2195F3.svg?logo=github" alt="LinkToRepo"/></a>
|
||||
<a href="https://leoven.github.io/C-Macro-Collections/"><img style="color: #ffffff;" src="https://img.shields.io/badge/Read%20the%20Docs-D13636.svg?logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHN0eWxlPSIiPjxyZWN0IGlkPSJiYWNrZ3JvdW5kcmVjdCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgeD0iMCIgeT0iMCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8%2BPHRpdGxlPmljbi9kb2MtdGV4dDwvdGl0bGU%2BPGcgY2xhc3M9ImN1cnJlbnRMYXllciIgc3R5bGU9IiI%2BPHRpdGxlPkxheWVyIDE8L3RpdGxlPjxwYXRoIGQ9Ik01IDFoNC4yNDRhMiAyIDAgMCAxIDEuNDM0LjYwNmwyLjc1NiAyLjgzNEEyIDIgMCAwIDEgMTQgNS44MzVWMTJhMyAzIDAgMCAxLTMgM0g1YTMgMyAwIDAgMS0zLTNWNGEzIDMgMCAwIDEgMy0zem0wIDJhMSAxIDAgMCAwLTEgMXY4YTEgMSAwIDAgMCAxIDFoNmExIDEgMCAwIDAgMS0xVjUuODM1TDkuMjQ0IDNINXptMS41IDdoM2EuNS41IDAgMSAxIDAgMWgtM2EuNS41IDAgMSAxIDAtMXptMC0yaDJhLjUuNSAwIDAgMSAwIDFoLTJhLjUuNSAwIDAgMSAwLTF6TTggMmwzIDEuOTk1TDEzIDdIOWExIDEgMCAwIDEtMS0xVjJ6IiBpZD0iYSIgY2xhc3M9IiIgZmlsbD0iI2ZmZmZmZiIgZmlsbC1vcGFjaXR5PSIxIi8%2BPC9nPjwvc3ZnPg%3D%3D" alt="LinkToDocs"/></a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/LeoVen/C-Macro-Collections/blob/master/LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License"/></a>
|
||||
<a href="https://github.com/LeoVen/C-Macro-Collections/releases"><img src="https://img.shields.io/badge/Version-v0.26.0-orange.svg" alt="Version"/></a>
|
||||
<a href="https://travis-ci.org/LeoVen/C-Macro-Collections"><img src="https://travis-ci.org/LeoVen/C-Macro-Collections.svg?branch=master" alt="travis-ci"/></a>
|
||||
<a href="https://codecov.io/gh/LeoVen/C-Macro-Collections"><img src="https://codecov.io/gh/LeoVen/C-Macro-Collections/branch/master/graph/badge.svg" alt="codecov"/></a>
|
||||
<a href="https://github.com/LeoVen/C-Macro-Collections/actions"><img src="https://github.com/LeoVen/C-Macro-Collections/workflows/Test%20Suit/badge.svg?branch=master" alt="test_suit"/></a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/LeoVen/C-Macro-Collections/stargazers"><img src="https://img.shields.io/github/stars/LeoVen/C-Macro-Collections?style=flat&color=ffa000" alt="Stars"/></a>
|
||||
<a href="https://github.com/XAMPPRocky/tokei"><img src="https://img.shields.io/badge/Lines%20of%20Code-28513-007596.svg" alt="loc"/></a>
|
||||
<a href="https://github.com/XAMPPRocky/tokei"><img src="https://img.shields.io/badge/Files-130-961c00.svg" alt="loc"/></a>
|
||||
</p>
|
||||
|
||||
The C Macro Collections Library is a compilation of macros that can be used to generate common data structures for any desired type. These data structures are [type safe](https://en.wikipedia.org/wiki/Type_safety) and are powered with many features (see Features section).
|
||||
|
||||
The documentation is currently being written in hopes that in the future it can be used to help you get to know better this amazing C library.
|
||||
@@ -0,0 +1,29 @@
|
||||
# Library Structure
|
||||
|
||||
The `src` folder is subdivided in 5 other folders and one file:
|
||||
|
||||
* __cmc__ - The main C Macro Collections Library
|
||||
* __dev__ - The main C Macro Collections Library for development
|
||||
* __sac__ - Statically Allocated Collections
|
||||
* __utl__ - Utility like ForEach macros, logging, etc
|
||||
* __macro\_collections.h__ - Master header containing all collections and utilities
|
||||
|
||||
## cmc
|
||||
|
||||
This is where the C Macro Collections are hosted.
|
||||
|
||||
## dev
|
||||
|
||||
In this folder is an exact copy of the `cmc` Collections with the added logging utility (`./utl/log.h`). These are useful to debug your code because everything that is happening inside the data structure can be seen.
|
||||
|
||||
## sac
|
||||
|
||||
This is where the Statically Allocated Collections are hosted. These collections are just like the `cmc` Collections but they have a constant size, a C array, instead of a dynamically allocated array (yes, even for Linked List).
|
||||
|
||||
## utl
|
||||
|
||||
Utility. Here you will find things like assert macros, foreach macros, logging utility, unit test and timer.
|
||||
|
||||
## macro_collections.h
|
||||
|
||||
This is the master header. Include this big boy and all functionalities of the library will be in your hands.
|
||||
@@ -0,0 +1,57 @@
|
||||
# Understanding the Library
|
||||
|
||||
Every macro within the library is prefixed by `CMC` or `cmc`. If you have [intellisense](https://code.visualstudio.com/docs/editor/intellisense) you can easily check all the features of the library once you include the necessary headers.
|
||||
|
||||
When you generate code for a certain collection you will need to pass four very important parameters: `SNAME`, `PFX`, `K` and `V`.
|
||||
|
||||
## SNAME
|
||||
|
||||
`SNAME` is short for struct name. This parameter will define your collection so that its type name becomes:
|
||||
|
||||
```c
|
||||
struct SNAME;
|
||||
```
|
||||
|
||||
No `typedef`s are used within the code that is generated in order to not pollute the global namespace. If you wish to `typedef` feel free to give your own naming conventions.
|
||||
__Every other `struct` that is generated as part of the implementation will be prefixed by `SNAME`__. So iterators and nodes will become:
|
||||
|
||||
```c
|
||||
struct SNAME##_iter;
|
||||
struct SNAME##_node;
|
||||
```
|
||||
|
||||
Note that the `##` is a [Token Pasting Operator](https://en.wikipedia.org/wiki/C_preprocessor#Token_concatenation).
|
||||
|
||||
## PFX
|
||||
|
||||
`PFX` is a short for prefix. __Every function generated will be within this namespace__. When using [Intellisense](https://code.visualstudio.com/docs/editor/intellisense), write whichever prefix you gave to your functions and everything will be in there.
|
||||
|
||||
Functions that are part of the implementation and shouldn't be called directly are prefixed by `PFX` and then by `_impl_`. Every iterator function will be prefixed by `PFX` and then by `_iter_`. For example:
|
||||
|
||||
```c
|
||||
// A function that you shouldn't use directly
|
||||
size_t PFX##_impl_quick_sort(struct SNAME *list);
|
||||
```
|
||||
|
||||
```c
|
||||
// A function that is associated with the iterator
|
||||
struct SNAME##_iter *PFX##_iter_new(struct SNAME *target);
|
||||
```
|
||||
|
||||
## K
|
||||
|
||||
`K`, short for Key, is the data type that is only used by associative collections to map a key to a value.
|
||||
|
||||
## V
|
||||
|
||||
`V`, short for Value, is the primary data type for most collections.
|
||||
|
||||
## Summarizing
|
||||
|
||||
With `SNAME` and `PFX` a common function definition looks like:
|
||||
|
||||
`size_t PFX##_count(struct SNAME *list);`
|
||||
|
||||
So if input `SNAME` as `i32list` and `PFX` as `i32l` you will have the following function definition:
|
||||
|
||||
`size_t i32l_count(struct i32list *list);`
|
||||
@@ -0,0 +1,18 @@
|
||||
# Why this library
|
||||
|
||||
C is a low level language and lacks all the data structures that we commonly use. Many high level languages already come with these collections so that we don't have to implement everything ourselves. A lot of third party libraries that implement these missing data structures for the C language usually make use of a void pointers and a lot of macros. __This is why the C Macro Collections Library was created.__ All you need to do is to write down one macro and which data type you wish to work with. The library currently provides many data structures, such as:
|
||||
|
||||
* __Linear Collections__ : List, LinkedList, Deque, Stack, Queue, SortedList
|
||||
* __Sets__ : HashSet, TreeSet, MultiSet
|
||||
* __Maps__ : HashMap, TreeMap, MultiMap, BidiMap
|
||||
* __Heaps__ : Heap, IntervalHeap
|
||||
|
||||
All with the following features:
|
||||
|
||||
* __Type-safe__ - No `void *` pointers are used. A collection of type `int` will only accept integers;
|
||||
* __Customization__ - Custom struct name and function namespace;
|
||||
* __Bidirectional Iterators__ - Full support for iterators;
|
||||
* __Nesting__ - Collections can be nested (List of Lists, HashSet of Stacks, etc);
|
||||
* __One macro to rule them all__ - Only one macro to generate everything and that's it.
|
||||
|
||||
and many other powerful features (see [Features](../Overview/features_overview.html)).
|
||||
@@ -0,0 +1,19 @@
|
||||
# Collections Overview
|
||||
|
||||
| Collection | Abstract Data Type | Data Structure | Details |
|
||||
| :--------------------------------: | :---------------------------------: | :-----------------------------: | :-----------------------------------: |
|
||||
| BidiMap <br> _bidimap.h_ | Bidirectional Map | Two Hashtables | A bijection between two sets of unique keys and unique values `K <-> V` using two hashtables |
|
||||
| Deque <br> _deque.h_ | Double-Ended Queue | Dynamic Circular Array | A circular array that allows `push` and `pop` on both ends (only) at constant time |
|
||||
| HashMap <br> _hashmap.h_ | Map | Hashtable | A unique set of keys associated with a value `K -> V` with constant time look up using a hashtable with open addressing and robin hood hashing |
|
||||
| HashSet <br> _hashset.h_ | Set | Hashtable | A unique set of values with constant time look up using a hashtable with open addressing and robin hood hashing |
|
||||
| Heap <br> _heap.h_ | Priority Queue | Dynamic Array | A binary heap as a dynamic array as an implicit data structure |
|
||||
| IntervalHeap <br> _intervalheap.h_ | Double-Ended Priority Queue | Custom Dynamic Array | A dynamic array of nodes, each hosting one value from the MinHeap and one from the MaxHeap |
|
||||
| LinkedList <br> _linkedlist.h_ | List | Doubly-Linked List | A default doubly-linked list |
|
||||
| List <br> _list.h_ | List | Dynamic Array | A dynamic array with `push` and `pop` anywhere on the array |
|
||||
| MultiMap <br> _multimap.h_ | Multimap | Custom Hashtable | A mapping of multiple keys with one node per key using a hashtable with separate chaining |
|
||||
| Multiset <br> _multiset.h_ | Multiset | Hashtable | A mapping of a value and its multiplicity using a hashtable with open addressing and robin hood hashing |
|
||||
| Queue <br> _queue.h_ | FIFO | Dynamic Circular Array | A queue using a circular array with `enqueue` at the `back` index and `dequeue` at the `front` index |
|
||||
| SortedList <br> _sortedlist.h_ | Sorted List | Sorted Dynamic Array | A lazily sorted dynamic array that is sorted only when necessary |
|
||||
| Stack <br> _stack.h_ | FILO | Dynamic Array | A stack with push and pop at the end of a dynamic array |
|
||||
| TreeMap <br> _treemap.h_ | Sorted Map | AVL Tree | A unique set of keys associated with a value `K -> V` using an AVL tree with `log(n)` look up and sorted iteration |
|
||||
| TreeSet <br> _treeset.h_ | Sorted Set | AVL Tree | A unique set of keys using an AVL tree with `log(n)` look up and sorted iteration |
|
||||
@@ -0,0 +1,11 @@
|
||||
# Features Overview
|
||||
|
||||
The C Macro Collections library comes with some core features available to all collections.
|
||||
|
||||
| Feature | Description |
|
||||
| ------- | ----------- |
|
||||
| [Callbacks](../cor/callbacks/index.html) | Callbacks are functions that are called when an operation is successful. These operations are divided in 5 categories (create, read, update, delete, resize). |
|
||||
| [Custom Allocation](../cor/custom_allocation/index.html) | Allows you to use your own custom dynamic memory allocation functions inside the collections. |
|
||||
| [Error Codes](../cor/error_codes/index.md) | Error codes that can be used to treat certain common errors when operating on a collection. |
|
||||
| [Functions Table](../cor/functions_table/index.html) | A standard way to access required behaviors from the custom data types. Things like hash, comparison, freeing from memory if needed, etc. |
|
||||
| [Iterators](../cor/iterators/index.html) | Iterators are a simplified access to the values of a collection. |
|
||||
@@ -0,0 +1,30 @@
|
||||
# Files Overview
|
||||
|
||||
An overview of the source files (all `.h`) of the C Macro Collections library.
|
||||
|
||||
* `cmc` - The main C Macro Collections library
|
||||
* `cor` - Core functionalities of the C Macro Collections libraries
|
||||
* `core.h` - Core functionalities of the library
|
||||
* `hashtable.h` - Common things used by hash table based collections
|
||||
* `utl` - Utilities
|
||||
* `assert.h` - Non-abortive assert macros
|
||||
* `foreach.h` - For Each macros
|
||||
* `futils.h` - Common functions used by Functions Table
|
||||
* `log.h` - Logging utility with levels of severity
|
||||
* `test.h` - Simple Unit Test building with macros
|
||||
* `test.h` - Timing code execution utility
|
||||
* `bidimap.h` - A bi-directional map based on a hash table
|
||||
* `deque.h` - A double-ended queue
|
||||
* `hashmap.h` - A map based on a hash table
|
||||
* `hashset.h` - A set based on a hash table
|
||||
* `heap.h` - A binary heap based on a dynamic array
|
||||
* `intervalheap.h` - A heap that is both Min and Max based on a dynamic array
|
||||
* `linkedlist.h` - A doubly-linked list
|
||||
* `list.h` - A dynamic array
|
||||
* `multimap.h` - A map that accepts multiple keys based on a hash table
|
||||
* `multiset.h` - A multiset based on a hash table
|
||||
* `queue.h` - A FIFO based on a circular dynamic array
|
||||
* `sortedlist.h` - A sorted list based on a dynamic array
|
||||
* `stack.h` - A LIFO based on a dynamic array
|
||||
* `treemap.h` - A sorted map based on an AVL tree
|
||||
* `treeset.h` - A sorted set based on an AVL tree
|
||||
@@ -0,0 +1,3 @@
|
||||
# Overview
|
||||
|
||||
In this section you will be able to quickly glance at what are the core functionalities that the C Macro Collections library can bring you.
|
||||
@@ -0,0 +1,38 @@
|
||||
# Summary
|
||||
|
||||
- [Introduction](./Introduction/introduction.md)
|
||||
- [Why this library](./Introduction/why_this_library.md)
|
||||
- [How to install](./Introduction/how_to_install.md)
|
||||
- [Library structure](./Introduction/library_structure.md)
|
||||
- [Understanding the library](./Introduction/understanding_the_library.md)
|
||||
- [How to use the library](./Introduction/how_to_use_the_library.md)
|
||||
- [Overview](./Overview/index.md)
|
||||
- [Collections Overview](./Overview/collections_overview.md)
|
||||
- [Features Overview](./Overview/features_overview.md)
|
||||
- [Files Overview](./Overview/files_overview.md)
|
||||
- [cor](./cor/index.md)
|
||||
- [Callbacks](./cor/callbacks/index.md)
|
||||
- [Custom allocation](./cor/custom_allocation/index.md)
|
||||
- [Error Codes](./cor/error_codes/index.md)
|
||||
- [Functions Table](./cor/functions_table/index.md)
|
||||
- [Iterators](./cor/iterators/index.md)
|
||||
- [cmc](./cmc/index.md)
|
||||
- [bitset.h](./cmc/bitset.h/index.md)
|
||||
- [deque.h](./cmc/deque.h/index.md)
|
||||
- [Functions.h](./cmc/deque.h/functions.md)
|
||||
- [dev](./dev/index.md)
|
||||
- [sac](./sac/index.md)
|
||||
- [utl](./utl/index.md)
|
||||
- [assert.h](./utl/assert.h/index.md)
|
||||
- [Overview](./utl/assert.h/overview.md)
|
||||
- [Valued Assertions](./utl/assert.h/valued_assertions.md)
|
||||
- [Generic Assertions](./utl/assert.h/generic_assertions.md)
|
||||
- [foreach.h](./utl/foreach.h/index.md)
|
||||
- [futils.h](./utl/futils.h/index.md)
|
||||
- [log.h](./utl/log.h/index.md)
|
||||
- [Log Functions](./utl/log.h/log_functions.md)
|
||||
- [Configuration](./utl/log.h/configuration.md)
|
||||
- [Meanings](./utl/log.h/meanings.md)
|
||||
- [test.h](./utl/test.h/index.md)
|
||||
- [timer.h](./utl/timer.h/index.md)
|
||||
- [Examples](./Examples/examples.md)
|
||||
@@ -0,0 +1 @@
|
||||
# Chapter 1
|
||||
@@ -0,0 +1,17 @@
|
||||
# bidimap.h
|
||||
|
||||
A bidirectional map is a map that allows you to create a bijection in both directions between two sets of elements (K <-> V). It is possible to retrieve the value using a key or retrieve a key using a value. The naming is simply used to differentiate between one set of elements and the other set of elements. There is no difference between key and value since both can be keys and/or values.
|
||||
|
||||
## BidiMap Implementation
|
||||
|
||||
This implementation uses two arrays of pointers to an entry containing both the key and the value. Robin Hood hashing is used to minimize worst case scenarios.
|
||||
|
||||
## BidiMap Generation Macro
|
||||
|
||||
## BidiMap Structures
|
||||
|
||||
### Members
|
||||
|
||||
## BidiMap Functions
|
||||
|
||||
## BidiMap Callback Table
|
||||
@@ -0,0 +1,77 @@
|
||||
# bitset.h
|
||||
|
||||
A Bit Set is an array where each bit can be individually modified and queried by using bitwise operators such as `|`, `&`, `^`, `~`, `>>` and `<<` (`or`, `and`, `xor`, `not`, `right shift`, `left shift` respectively).
|
||||
|
||||
## BitSet Implementation
|
||||
|
||||
This BitSet implementation uses an array of type `cmc_bitset_word` which can be `typedef`ed to any unsigned type such as `uint8_t`, `uint16_t`, `uint32_t`, `uint64_t`, `size_t`, etc. The BitSet does not make use of `K` or `V`. Because of that, it also doesn't have Functions Tables.
|
||||
|
||||
The BitSet is initialized with a custom capacity but, if a bit index accessed is greater than the total capacity, the BitSet will resize. This means that the BitSet will try to guarantee that every bit index is accessible, as long as there is enough memory.
|
||||
|
||||
## BitSet Generation Macros
|
||||
|
||||
* `CMC_GENERATE_BITSET(PFX, SNAME)` - Generate full definition.
|
||||
* `CMC_GENERATE_BITSET_HEADER(PFX, SNAME)` - Generate only the header portion
|
||||
* `CMC_GENERATE_BITSET_SOURCE(PFX, SNAME)` - Generate only the source portion
|
||||
|
||||
| Parameter | Description |
|
||||
| --------- | ----------------------------- |
|
||||
| `PFX` | Functions namespace or prefix |
|
||||
| `SNAME` | Structure name |
|
||||
|
||||
## BitSet Structures
|
||||
|
||||
| Declared structs | Description |
|
||||
| ------------------------- | ----------------------------------- |
|
||||
| `struct SNAME` | A bit set data structure |
|
||||
| `struct SNAME##_iter` | A bit set iterator |
|
||||
|
||||
## struct SNAME
|
||||
|
||||
| `struct SNAME` | Description |
|
||||
| --------------------------------- | ------------------------------------------ |
|
||||
| `cmc_bitset_word *buffer` | Dynamic array of bits. |
|
||||
| `size_t capacity` | Current array capacity . |
|
||||
| `int flag` | Flag indicating errors or success. |
|
||||
| `struct cmc_alloc_node *alloc` | Custom allocation functions. |
|
||||
| `struct cmc_callbacks *callbacks` | Callback functions. |
|
||||
|
||||
## struct SNAME##_iter
|
||||
|
||||
| `struct SNAME##_iter` | Description |
|
||||
| ---------------------- | --------------------------------------------------- |
|
||||
| `struct SNAME *target` | BitSet being iterated over. |
|
||||
| `size_t cursor` | Cursor's current position (index). |
|
||||
| `bool start` | If the iterator reached the start of the iteration. |
|
||||
| `bool end` | If the iterator reached the end of iteration. |
|
||||
|
||||
## cmc_bitset_word
|
||||
|
||||
The `typedef`ed data type o the underlying bit set buffer.
|
||||
|
||||
```c
|
||||
typedef uint32_t cmc_bitset_word;
|
||||
```
|
||||
|
||||
This `typedef` can be changed to any unsigned integer data type and the bit set will still work properly.
|
||||
|
||||
## Callbacks
|
||||
|
||||
This list associates which functions calls which callbacks.
|
||||
|
||||
* `create`
|
||||
* `PFX##_set()`
|
||||
* `PFX##_set_range()`
|
||||
* `read`
|
||||
* `update`
|
||||
* `delete`
|
||||
* `PFX##_clear()`
|
||||
* `PFX##_clear_range()`
|
||||
* `resize`
|
||||
* `PFX##_resize()`
|
||||
|
||||
## Functions Table
|
||||
|
||||
| CMP | CPY | STR | FREE | HASH | PRI |
|
||||
| :-: | :-: | :-: | :--: | :--: | :-: |
|
||||
|  |  |  |  |  |  |
|
||||
@@ -0,0 +1,79 @@
|
||||
# Index
|
||||
|
||||
__Deque__
|
||||
|
||||
* [`_new()`](#_new)
|
||||
* [`_new_custom()`](#_new_custom)
|
||||
* `_clear()`
|
||||
* `_free()`
|
||||
* `_customize()`
|
||||
* `_push_front()`
|
||||
* `_push_back()`
|
||||
* `_pop_front()`
|
||||
* `_front()`
|
||||
* `_back()`
|
||||
* `_contains()`
|
||||
* `_empty()`
|
||||
* `_full()`
|
||||
* `_count()`
|
||||
* `_capacity()`
|
||||
* `_flag()`
|
||||
* `_resize()`
|
||||
* `_copy_of()`
|
||||
* `_equals()`
|
||||
* `_to_string()`
|
||||
* `_print()`
|
||||
|
||||
__Deque Iterator__
|
||||
|
||||
WIP.
|
||||
|
||||
## [<span id="_new"> \_new() </span>](#index)
|
||||
|
||||
Allocates on the heap and returns a new Deque with an internal capacity of the specified value. The Deque will be empty, using the default allocator `cmc_alloc_node_default` and no callbacks will be set, that is, `callbacks` will be set to `NULL`. Its `flag` will be initially `cmc_flags.OK`.
|
||||
|
||||
__Declaration__
|
||||
|
||||
`struct SNAME *PFX##_new(size_t capacity, struct SNAME##_fval *f_val);`
|
||||
|
||||
__Parameters__
|
||||
|
||||
| Parameter | Required | Description |
|
||||
| ---------------------------- | -------- | ------------------------------------ |
|
||||
| `size_t capacity` | Yes | The initial Deque capacity |
|
||||
| `struct SNAME##_fval *f_val` | Yes | A Functions Table for `V` |
|
||||
|
||||
__Returns__
|
||||
|
||||
| Returns | When |
|
||||
| ---------------- | ---------------------------------------------------------------- |
|
||||
| `struct SNAME *` | If operation was successful |
|
||||
| `NULL` | If allocation failed, if capacity is `0` or if `f_val` is `NULL` |
|
||||
|
||||
## [<span id="_new_custom"> \_new\_custom() </span>](#index)
|
||||
|
||||
Like [\_new()](#_new) but allows for custom allocation node and callbacks. The allocation node is optional. If present, the function will immediately use it to allocate the new Deque. If `NULL`, `cmc_alloc_node_default` will instead be used.
|
||||
|
||||
__Declaration__
|
||||
|
||||
```
|
||||
struct SNAME *PFX##_new_custom(size_t capacity, struct SNAME##_fval *f_val, struct cmc_alloc_node *alloc, struct cmc_callbacks *callbacks);
|
||||
```
|
||||
|
||||
__Parameters__
|
||||
|
||||
| Parameter | Required | Description |
|
||||
| --------------------------------- | -------- | ------------------------------------ |
|
||||
| `size_t capacity` | Yes | The initial Deque capacity |
|
||||
| `struct SNAME##_fval *f_val` | Yes | A Functions Table for `V` |
|
||||
| `struct cmc_alloc_node *alloc` | No | A custom allocation node |
|
||||
| `struct cmc_callbacks *callbacks` | No | A custom callbacks struct |
|
||||
|
||||
__Returns__
|
||||
|
||||
| Returns | When |
|
||||
| ---------------- | ---------------------------------------------------------------- |
|
||||
| `struct SNAME *` | If operation was successful |
|
||||
| `NULL` | If allocation failed, if capacity is `0` or if `f_val` is `NULL` |
|
||||
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
# deque.h
|
||||
|
||||
A Deque, also known as a double-ended queue, is a linear data structure that is able to add and remove elements from both ends. It can also be thought of a double-ended stack since you can push and pop elements from two ends. The Deque can also be used as a Queue. The only elements accessible from a Deque ar the two elements at its ends (front element and back element).
|
||||
|
||||
## Deque Implementation
|
||||
|
||||
This implementation uses a circular buffer (ring buffer or cyclic buffer) in order to operate on O(1) for push and pop on either ends. The only case where it takes longer than O(1) is when the buffer is reallocated. If it was implemented as a regular array, adding or removing elements from the front would take O(N) due to the need to shift all elements in the Deque.
|
||||
|
||||
Two indices are kept track of. The `front` index and the `rear` index. These represent the both ends of the Deque. If an index reaches one end of the real buffer, they wrap around to the other end and an element is added there. This abstracts the real buffer as a circular buffer with the cost os constantly checking for boundaries and using the modulo operator.
|
||||
|
||||
## Deque Generation Macros
|
||||
|
||||
* `CMC_GENERATE_DEQUE(PFX, SNAME, V)` - Generate full definition.
|
||||
* `CMC_GENERATE_DEQUE_HEADER(PFX, SNAME, V)` - Generate only the header portion
|
||||
* `CMC_GENERATE_DEQUE_SOURCE(PFX, SNAME, V)` - Generate only the source portion
|
||||
|
||||
| Parameter | Description |
|
||||
| --------- | ----------------------------- |
|
||||
| `PFX` | Functions namespace or prefix |
|
||||
| `SNAME` | Structure name |
|
||||
| `V` | Value type |
|
||||
|
||||
## Deque Structures
|
||||
|
||||
| Declared structs | Description |
|
||||
| ------------------------- | ----------------------------------- |
|
||||
| `struct SNAME` | A double-ended queue data structure |
|
||||
| `struct SNAME##_fval` | A function table for the `V` type |
|
||||
| `struct SNAME##_iter` | A double-ended queue iterator |
|
||||
|
||||
## struct SNAME
|
||||
|
||||
| `struct SNAME` | Description |
|
||||
| --------------------------------- | ------------------------------------------ |
|
||||
| `V *buffer` | Dynamic circular array of elements. |
|
||||
| `size_t capacity` | Current circular array capacity. |
|
||||
| `size_t count` | Current amount of elements. |
|
||||
| `size_t front` | Index representing the front of the Deque. |
|
||||
| `size_t rear` | Index representing the back of the Deque. |
|
||||
| `int flag` | Flag indicating errors or success. |
|
||||
| `struct SNAME##_fval *f_val` | Functions table for the Value type. |
|
||||
| `struct cmc_alloc_node *alloc` | Custom allocation functions. |
|
||||
| `struct cmc_callbacks *callbacks` | Callback functions. |
|
||||
|
||||
## struct SNAME##_fval
|
||||
|
||||
The Functions Table for `V`. Check out the Functions Table documentation [here](../../cor/functions_table/index.html).
|
||||
|
||||
## struct SNAME##_iter
|
||||
|
||||
| `struct SNAME##_iter` | Description |
|
||||
| ---------------------- | --------------------------------------------------- |
|
||||
| `struct SNAME *target` | Deque being iterated over. |
|
||||
| `size_t cursor` | Cursor's current position (index). |
|
||||
| `size_t index` | Relative index to all elements in the iteration. |
|
||||
| `bool start` | If the iterator reached the start of the iteration. |
|
||||
| `bool end` | If the iterator reached the end of iteration. |
|
||||
|
||||
## Callbacks
|
||||
|
||||
This list associates which functions calls which callbacks.
|
||||
|
||||
* `create`
|
||||
* `PFX##_push_front()`
|
||||
* `PFX##_push_back()`
|
||||
* `read`
|
||||
* `PFX##_front()`
|
||||
* `PFX##_back()`
|
||||
* `PFX##_contains()`
|
||||
* `update`
|
||||
* `delete`
|
||||
* `PFX##_pop_front()`
|
||||
* `PFX##_pop_back()`
|
||||
* `resize`
|
||||
* `PFX##_resize()`
|
||||
|
||||
## Functions Table
|
||||
|
||||
| CMP | CPY | STR | FREE | HASH | PRI |
|
||||
| :-: | :-: | :-: | :--: | :--: | :-: |
|
||||
|  |  |  |  |  |  |
|
||||
@@ -0,0 +1,53 @@
|
||||
# deque.h
|
||||
|
||||
A Deque, also known as a double-ended queue, is a linear data structure that is able to add and remove elements from both ends. It can also be thought of a double-ended stack since you can push and pop elements from two ends. The Deque can also be used as a Queue. The only elements accessible from a Deque ar the two elements at its ends (front element and back element).
|
||||
|
||||
## Deque Implementation
|
||||
|
||||
This implementation uses a circular buffer (ring buffer or cyclic buffer) in order to operate on O(1) for push and pop on either ends. The only case where it takes longer than O(1) is when the buffer is reallocated. If it was implemented as a regular array, adding or removing elements from the front would take O(N) due to the need to shift all elements in the Deque.
|
||||
|
||||
Two indices are kept track of. The `front` index and the `rear` index. These represent the both ends of the Deque. If an index reaches one end of the real buffer, they wrap around to the other end and an element is added there. This abstracts the real buffer as a circular buffer with the cost os constantly checking for boundaries and using the modulo operator.
|
||||
|
||||
## Deque Generation Macro
|
||||
|
||||
`CMC_GENERATE_DEQUE(PFX, SNAME, V)`
|
||||
|
||||
| | |
|
||||
| ------- | ----------------------------- |
|
||||
| `PFX` | Functions namespace or prefix |
|
||||
| `SNAME` | Structure name |
|
||||
| `V` | Element type |
|
||||
|
||||
## Deque Structures
|
||||
|
||||
| | |
|
||||
| --------------------- | ----------------------------------- |
|
||||
| `struct SNAME` | A double-ended queue data structure |
|
||||
| `struct SNAME##_iter` | A double-ended queue iterator |
|
||||
|
||||
### Members
|
||||
|
||||
| `struct SNAME` | |
|
||||
| --------------------------- | ------------------------------------------------------------ |
|
||||
| `V *buffer` | Dynamic circular array of elements. |
|
||||
| `size_t capacity` | Current circular array capacity. |
|
||||
| `size_t count` | Current amount of elements. |
|
||||
| `size_t front` | Index representing the front of the deque. |
|
||||
| `size_t rear` | Index representing the back of the deque. |
|
||||
| `it_start` <sup>\[1\]</sup> | Function that returns an iterator to the start of the deque. |
|
||||
| `it_end` <sup>\[2\]</sup> | Function that returns an iterator to the end of the deque. |
|
||||
|
||||
1. Full definition: `struct SNAME##_iter (*it_start)(struct SNAME *);`.
|
||||
2. Full definition: `struct SNAME##_iter (*it_end)(struct SNAME *);`.
|
||||
|
||||
| `struct SNAME##_iter` | |
|
||||
| ---------------------- | --------------------------------------------------- |
|
||||
| `struct SNAME *target` | Deque being iterated over. |
|
||||
| `size_t cursor` | Cursor's current position (index). |
|
||||
| `size_t index` | Relative index to all elements in the iteration. |
|
||||
| `bool start` | If the iterator reached the start of the iteration. |
|
||||
| `bool end` | If the iterator reached the end of iteration. |
|
||||
|
||||
## Deque Functions
|
||||
|
||||
## Deque Callback Table
|
||||
@@ -0,0 +1,9 @@
|
||||
# hashmap.h
|
||||
|
||||
A HashMap is an implementation of a Map with unique keys, where every key is mapped to a value (K -> V). The keys are not sorted. To access a value you must specify a key. This key is hashed to a number that uniquely represents it and through this it is possible to find in which position of an array of buckets the value is.
|
||||
|
||||
## HashMap Implementation
|
||||
|
||||
The HashMap is implemented as a flat HashTable meaning that every entry is allocated when the collection is initialized, but they are all empty.
|
||||
|
||||
The HashTable uses [Open Addressing](https://en.wikipedia.org/wiki/Open_addressing) and [Linear Probing](https://en.wikipedia.org/wiki/Linear_probing) to resolve collisions along with [Robin Hood Hashing](https://en.wikipedia.org/wiki/Hash_table) to minimize the worst case scenarios.
|
||||
@@ -0,0 +1,9 @@
|
||||
# hashset.h
|
||||
|
||||
A HashSet is an implementation of a Set with unique keys. The keys are not sorted. It is implemented as a flat hashtable with linear probing and robin hood hashing.
|
||||
|
||||
## HashSet Implementation
|
||||
|
||||
The HashSet is implemented as a flat HashTable meaning that every entry is allocated when the collection is initialized, but they are all empty.
|
||||
|
||||
The HashTable uses [Open Addressing](https://en.wikipedia.org/wiki/Open_addressing) and [Linear Probing](https://en.wikipedia.org/wiki/Linear_probing) to resolve collisions along with [Robin Hood Hashing](https://en.wikipedia.org/wiki/Hash_table) to minimize the worst case scenarios.
|
||||
@@ -0,0 +1,19 @@
|
||||
# heap.h
|
||||
|
||||
The Heap is a specialized tree-based data structure which is essentially a complete tree that satisfies the heap property:
|
||||
|
||||
__MaxHeap__
|
||||
|
||||
- \- The root node is the greatest element of the unique set of keys.
|
||||
- \- A node key is always greater than or equal to its children keys.
|
||||
|
||||
__Minheap__
|
||||
|
||||
- \- The root node is the smallest element of the unique set of keys.
|
||||
- \- A node Key is always greater than or equal to its children keys.
|
||||
|
||||
The heap is mostly used to implement priority queues. Sometimes it can also be used to sort elements. There are three main functions:
|
||||
|
||||
- insert : Adds an element to the heap
|
||||
- remove_(min/max): Removes the min/max element from the heap
|
||||
- min/max : Accesses the min/max element from the heap
|
||||
@@ -0,0 +1,27 @@
|
||||
# CMC
|
||||
|
||||
The main C Macro Collections.
|
||||
|
||||
The C Macro Collections library was created out of necessity. To create powerful data structures of any type without the constant use of macros and `void *` pointers, but at the same time being [type-safe](https://en.wikipedia.org/wiki/Type_safety). These collections are generated by macros containing a template. These templates allow for some much needed customizations including the data type you wish to handle.
|
||||
|
||||
In the `cmc` folder is the default implementation of all collections. There is also a `dev` folder that pretty much has the same macros that expand to the same code, except that they have an extensive use of logging that might be useful for debugging or getting to know what is happening in your program. There is also the `sac` collections that are statically allocated (have a fixed sized buffer).
|
||||
|
||||
## Quick Example
|
||||
|
||||
Let's say you need a HashMap mapping keys of type `char *` to `int`. All you need to do is:
|
||||
|
||||
```c
|
||||
#include "cmc/hashmap"
|
||||
|
||||
// CMC_GENERATE_HASHMAP(PFX, SNAME, K, V)
|
||||
CMC_GENERATE_HASHMAP(map, hashmap, char *, int)
|
||||
|
||||
int main(void)
|
||||
{
|
||||
// Here you have full functionalities of a HashMap
|
||||
// No more macros needed!
|
||||
// Every function can be found in the 'map_' prefix and are all type-safe
|
||||
}
|
||||
```
|
||||
|
||||
You can customize the functions prefix (`PFX`) and the `struct` name (`SNAME`). The macro `CMC_GENERATE_HASHMAP` will expand to all the code that you will need to operate on a HashMap with `char *` keys and `int` values. The only thing left to do is to define a hash function and a comparator functions for the keys of this map. In case of `char *` you can find these functions in `utl/futils.h` or make your own. To know which collections require which functions check out [Functions Table](../cor/functions_table/index.html) or the documentation for each collection.
|
||||
@@ -0,0 +1,9 @@
|
||||
# intervalheap.h
|
||||
|
||||
An interval heap is a specialized heap with:
|
||||
|
||||
- O(1) - Find Min
|
||||
- O(1) - Find Max
|
||||
- O(log n) - Insert
|
||||
- O(log n) - Remove Min
|
||||
- O(log n) - Remove Max
|
||||
@@ -0,0 +1,7 @@
|
||||
# linkedlist.h
|
||||
|
||||
A LinkedList is a linear collection of elements, whose order is not given by their physical placement in memory. Instead, each element points to the next (singly-linked list) and to a previous one (doubly-linked list). It consists of nodes connected by these next or previous pointers. Unlike an array, the LinkedList can indefinitely grow.
|
||||
|
||||
## LinkedList Implementation
|
||||
|
||||
The LinkedList is implemented as a doubly-linked list and allows insertions and removals at both ends in O(1) and in a given index in O(N). The list has a head and tail pointer. The head points to the first element in the sequence and tail points to the last.
|
||||
@@ -0,0 +1,11 @@
|
||||
# list.h
|
||||
|
||||
A List is a Dynamic Array that store its elements contiguously. It has an internal buffer that is pre-allocated with a greater capacity than necessary in order to foresee new elements being added. The items can be added at both ends of the list and at the middle (with a given index).
|
||||
|
||||
## List Implementation
|
||||
|
||||
Insertions at the start are more costly as it is necessary to shift all elements one position to the right to give space to a new element being added at index 0. Insertions at the end are instantaneous as long as there is enough space in the buffer. Insertions at the middle will be based on an index. The index represents where the element being added will be located once the operation ends. This operation might also need to shift elements. It is also possible to add arrays of elements in the list. When the buffer is filled, it is reallocated with a greater capacity, usually being doubled.
|
||||
|
||||
Removing elements follows the same principle. Removing the front element will require to shift all other elements one position to the left, thus being slower than removing from the end in which is done in constant time. Removing elements in the middle of the list will also require shifting elements to the left. Is is also possible to remove a range of elements or extract them, creating a new list with the removed items.
|
||||
|
||||
The iterator is a simple structure that is capable of going back and forwards. Any modifications to the target list during iteration is considered undefined behavior. Its sole purpose is to facilitate navigation through a list.
|
||||
@@ -0,0 +1,17 @@
|
||||
# multimap.h
|
||||
|
||||
The MultiMap is a Map that allows for multiple keys. This is a data structure that has a very narrow usage. A Map (either TreeMap or HashMap) can also work like a MultiMap if a certain key is mapped to another collection.
|
||||
|
||||
`Map<K = int, V = List<int>>` maps an integer to a list of integer.
|
||||
|
||||
`MultiMap<K = int, V = int>` maps many integer keys to integer values.
|
||||
|
||||
The difference is that in a MultiMap you can store keys that are the same but might be different instances of the same value. This is also relevant if your data type is more complex like a struct where its ID is the same but some other members of this data type are different.
|
||||
|
||||
## MultiMap Implementation
|
||||
|
||||
The map uses separate chaining and robin hood hashing. Its internal buffer is made of a pair of pointers to entries that represent a linked list. So accessing map->buffer[0] would give the first pair of pointers. Each entry has both a pointer to a previous entry and a next entry. This design choice was made so that every collection in this library has a two-way iterator with a very few exceptions.
|
||||
|
||||
Each entry is composed of a Key and a Value. Entries with the same key should always hash to the same linked list. Also, keys that hash to the same bucket will also be in the same linked list.
|
||||
|
||||
The order of inserting and removing the same keys will behave like a FIFO. So the first key added will be the first to be removed.
|
||||
@@ -0,0 +1,3 @@
|
||||
# multiset.h
|
||||
|
||||
In mathematics, a multiset is a modification of the concept of a set that, unlike a set, allows for multiple instances for each of its elements. The positive integer number of instances, given for each element is called the multiplicity of this element in the multiset. A MultiSet also has a cardinality which equals the sum of the multiplicities of its elements.
|
||||
@@ -0,0 +1,13 @@
|
||||
# queue.h
|
||||
|
||||
A Queue is a First-In First-out (or Last-in Last-out) data structure. It is a Dynamic Circular Array where elements are added from one end of the array and removed from the other end. The circular array here (also known as circular buffer or ring buffer) is very important so that both adding and removing elements from the Queue are done instantly. The array is linear but with the modulo operator it is treated as a circular sequence of elements.
|
||||
|
||||
## Queue Implementation
|
||||
|
||||
If the Queue was implemented as a regular Dynamic Array, when adding or removing an element at the front, it would be necessary to shift all elements currently present in the Queue and this would add up a lot of computing time. Shifting `100000` elements in memory by one position every time an element is added to the Queue is simply not efficient.
|
||||
|
||||
The Queue has two ends. The `front` and `back`. In this implementation all elements are added to the back of the Queue and removed from the front, which is more or less how queues work in real life. Unlike a Stack that only has operations at one end of the buffer, the Queue needs to be implemented as a circular array in order to quickly add or remove elements.
|
||||
|
||||
The Queue has three main functions: `enqueue` which adds an element to the Queue; `dequeue` which removes an element from the Queue; and `peek` which return the element at the front of the Queue, that is, the next element to be removed from it.
|
||||
|
||||
The Queue is used in many applications where a resource is shared among multiple consumers and the Queue is responsible for scheduling the access to the resource.
|
||||
@@ -0,0 +1,3 @@
|
||||
# sortedlist.h
|
||||
|
||||
A SortedList is a dynamic array, meaning that you can store as many elements as you like and when its capacity is full, the buffer is reallocated. The elements are only sorted when a certain action requires that the array is sorted like accessing min() or max(). This prevents the array from being sorted after every insertion or removal. The array is sorted using a variation of quick sort that uses insertion sort for small partitions.
|
||||
@@ -0,0 +1,9 @@
|
||||
# stack.h
|
||||
|
||||
A Stack is a Last-in First-out (or First-in Last-out) data structure used in a variety of algorithms. It is a Dynamic Array that can only add or remove its elements at the end of the buffer, in this case, it represents the top of the stack.
|
||||
|
||||
## Stack Implementation
|
||||
|
||||
It has three main functions: `push` which adds an element at the top of the stack; `pop` which removes the top element from the stack; and `top` which returns the top element without removing it (it is also sometimes called `peek`).
|
||||
|
||||
A Stack is used in algorithms like backtracking, depth-first search, expression evaluation, syntax parsing and many more.
|
||||
@@ -0,0 +1,3 @@
|
||||
# treemap.h
|
||||
|
||||
A TreeMap is an implementation of a Map that keeps its keys sorted. Like a Map, it has only unique keys. This implementation uses a balanced binary tree called AVL Tree that uses the height of nodes to keep its keys balanced.
|
||||
@@ -0,0 +1,3 @@
|
||||
# treeset.h
|
||||
|
||||
A TreeSet is an implementation of a Set that keeps its elements sorted. Like a Set it has only unique keys. This implementation uses a balanced binary tree called AVL Tree that uses the height of nodes to keep its keys balanced.
|
||||
@@ -0,0 +1,60 @@
|
||||
# Callbacks
|
||||
|
||||
Every collection can have an optional callback node. In this node there are five functions:
|
||||
|
||||
| Callback | Description |
|
||||
| -------- | ----------------------------------------------------------------------- |
|
||||
| `create` | Is called when an element was successfully added to the collection |
|
||||
| `read` | Is called when the collection was successfully queried about an element |
|
||||
| `update` | Is called when an element in the collection was successfully updated |
|
||||
| `delete` | Is called when an element was successfully removed from the collection |
|
||||
| `resize` | Is called when the collection was full and successfully resized |
|
||||
|
||||
Check the documentation for each collection to see which functions call which callbacks.
|
||||
|
||||
## cmc_callbacks
|
||||
|
||||
Every collection has a pointer to this `struct`. If it is `NULL` then the collection has no callbacks. Also, individual callbacks can be optional if set to `NULL`.
|
||||
|
||||
```c
|
||||
struct cmc_callbacks
|
||||
{
|
||||
void (*create)(void);
|
||||
void (*read)(void);
|
||||
void (*update)(void);
|
||||
void (*delete)(void);
|
||||
void (*resize)(void);
|
||||
};
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
```c
|
||||
#include "cmc/heap.h"
|
||||
#include "utl/futils.h" // cmc_i32_cmp
|
||||
|
||||
// Generate a heap of integers
|
||||
CMC_GENERATE_HEAP(i32h, i32heap, int)
|
||||
|
||||
void heap_on_create(void)
|
||||
{
|
||||
printf("An element was added to the heap\n");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
// create() is set but the other callbacks are not
|
||||
struct cmc_callbacks my_callbacks = { .create = heap_on_create, NULL };
|
||||
|
||||
// Create a max heap with the callbacks
|
||||
struct i32heap *heap = i32h_new_custom(
|
||||
100, cmc_max_heap, &(struct i32heap_fval){ .cmp = cmc_i32_cmp }, NULL,
|
||||
&my_callbacks);
|
||||
|
||||
i32h_insert(heap, 10);
|
||||
i32h_insert(heap, 11);
|
||||
i32h_insert(heap, 12);
|
||||
|
||||
i32h_free(heap);
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,91 @@
|
||||
# Custom Allocation
|
||||
|
||||
All collections have an allocation node. This node can be modified so that every allocation and de-allocation can be done with custom functions. Every custom allocation function must follow the same prototype of the `stdlib.h` functions.
|
||||
|
||||
## cmc_alloc_node
|
||||
|
||||
```c
|
||||
struct cmc_alloc_node
|
||||
{
|
||||
void *(*malloc)(size_t);
|
||||
void *(*calloc)(size_t, size_t);
|
||||
void *(*realloc)(void *, size_t);
|
||||
void (*free)(void *);
|
||||
};
|
||||
```
|
||||
|
||||
## cmc_alloc_node_default
|
||||
|
||||
The default allocation node. If a collections is not initialized with a custom allocation node, this will be the default one using the functions from the C standard library.
|
||||
|
||||
```c
|
||||
static struct cmc_alloc_node cmc_alloc_node_default = { malloc, calloc,
|
||||
realloc, free };
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
```c
|
||||
#include "cmc/heap.h"
|
||||
#include "utl/futils.h" // cmc_i32_cmp
|
||||
|
||||
// Total bytes allocated
|
||||
size_t total = 0;
|
||||
|
||||
void *my_malloc(size_t size)
|
||||
{
|
||||
void *result = malloc(size);
|
||||
|
||||
if (!result)
|
||||
return result;
|
||||
|
||||
total += size;
|
||||
return result;
|
||||
}
|
||||
|
||||
void *my_calloc(size_t count, size_t size)
|
||||
{
|
||||
void *result = calloc(count, size);
|
||||
|
||||
if (!result)
|
||||
return result;
|
||||
|
||||
total += count * size;
|
||||
return result;
|
||||
}
|
||||
|
||||
void *my_realloc(void *block, size_t size)
|
||||
{
|
||||
void *result = realloc(block, size);
|
||||
|
||||
if (!result)
|
||||
return result;
|
||||
|
||||
total += size;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Generate a heap of integers
|
||||
CMC_GENERATE_HEAP(i32h, i32heap, int)
|
||||
|
||||
int main(void)
|
||||
{
|
||||
// My custom allocation node
|
||||
struct cmc_alloc_node node = { .malloc = my_malloc,
|
||||
.calloc = my_calloc,
|
||||
.realloc = my_realloc,
|
||||
.free = free };
|
||||
|
||||
// Create a max heap with the custom allocation node
|
||||
struct i32heap *heap = i32h_new_custom(
|
||||
100, cmc_max_heap, &(struct i32heap_fval){ .cmp = cmc_i32_cmp }, &node,
|
||||
NULL);
|
||||
|
||||
for (int i = 0; i < 100000; i++)
|
||||
i32h_insert(heap, i);
|
||||
|
||||
i32h_free(heap);
|
||||
|
||||
printf("Total bytes allocated : %" PRIuMAX "\n", total);
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,76 @@
|
||||
# Error Codes
|
||||
|
||||
Error codes are ways to signal errors or success. It is accessed through the `flag` component of a `struct` or through a function that is present in every collection:
|
||||
|
||||
```c
|
||||
int PFX##_flag(struct SNAME *_collection_);
|
||||
```
|
||||
|
||||
Some functions have a return type of `bool` indicating if it exited successfully or not. These error codes can be used for further error handling.
|
||||
|
||||
## cmc_flags
|
||||
|
||||
It is a global `struct` with default values for error codes.
|
||||
|
||||
```c
|
||||
static struct
|
||||
{
|
||||
int OK;
|
||||
int ALLOC;
|
||||
int EMPTY;
|
||||
int NOT_FOUND;
|
||||
int INVALID;
|
||||
int RANGE;
|
||||
int DUPLICATE;
|
||||
int ERROR;
|
||||
} cmc_flags = { 0, 1, 2, 3, 4, 5, 6, 7 };
|
||||
```
|
||||
|
||||
* `OK` - No errors. The operation exited successfully.
|
||||
* `ALLOC` - Allocation failed.
|
||||
* `EMPTY` - The collection is empty when it shouldn't.
|
||||
* `NOT_FOUND` - Key or value not found.
|
||||
* `INVALID` - Invalid argument or operation given the collection's state
|
||||
* `RANGE` - Index out of range.
|
||||
* `DUPLICATE` - Duplicate key or value.
|
||||
* `ERROR` - Generic error. Usually caused by algorithm errors.
|
||||
|
||||
## cmc_flags_to_str
|
||||
|
||||
Maps the integer representation of error codes to their character representation.
|
||||
|
||||
```c
|
||||
const char *cmc_flags_to_str[8] = { "OK", "ALLOC", "EMPTY",
|
||||
"NOT_FOUND", "INVALID", "RANGE",
|
||||
"DUPLICATE", "ERROR" };
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
```c
|
||||
#include "cmc/treeset.h"
|
||||
#include "utl/futils.h" // cmc_i32_cmp
|
||||
|
||||
// Generate a sorted set of integers
|
||||
CMC_GENERATE_TREESET(tset, sortedset, int)
|
||||
|
||||
int main(void)
|
||||
{
|
||||
struct sortedset *set =
|
||||
tset_new(&(struct sortedset_fval){ .cmp = cmc_i32_cmp });
|
||||
|
||||
if (!tset_insert(set, 10))
|
||||
printf("Error! %s\n", cmc_flags_to_str[tset_flag(set)]);
|
||||
|
||||
if (!tset_insert(set, 10))
|
||||
printf("Error! %s\n", cmc_flags_to_str[tset_flag(set)]);
|
||||
|
||||
if (!tset_remove(set, 10))
|
||||
printf("Error! %s\n", cmc_flags_to_str[tset_flag(set)]);
|
||||
|
||||
if (!tset_remove(set, 10))
|
||||
printf("Error! %s\n", cmc_flags_to_str[tset_flag(set)]);
|
||||
|
||||
tset_free(set);
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,127 @@
|
||||
# Functions Table
|
||||
|
||||
Functions Table is a `struct` with function pointers that are used to extract behavior from a custom data type. This exists because an `int` or a `char *` or `struct my_data_type` don't have default hash functions or ways to compare between them. This could be achieved with [operator overloading](https://en.wikipedia.org/wiki/Operator_overloading) but C doesn't have that. Hence, Functions Table was created to provide a single access point to extract these behaviors for __any__ data type.
|
||||
|
||||
Some functions are optional and others are needed in order for a collection to operate. Say you want a HashMap. There are two required functions: `hash` and `cmp` (comparator). Without them it is impossible to know how to hash your custom that type (it could be anything). This means you can also customize the collection by using different hash functions.
|
||||
|
||||
Many required functions are simply mundane. Like a comparator function between integers or between floats. The header [`utl/futils.h`](../../utl/futils.h/index.html) defines many of these functions for basic C data types.
|
||||
|
||||
Some collections generate two functions table. One for `K` and one for `V` (maps). Others simply generate one for `V`. A Functions Table `struct` is always going to be called:
|
||||
|
||||
* `struct SNAME##_fkey` for the `K` type
|
||||
* `struct SNAME##_fval` for the `V` type
|
||||
|
||||
Their definition is defined as follows:
|
||||
|
||||
```c
|
||||
\
|
||||
struct SNAME##_fkey \
|
||||
{ \
|
||||
/* Comparator function */ \
|
||||
int (*cmp)(K, K); \
|
||||
\
|
||||
/* Copy function */ \
|
||||
K (*cpy)(K); \
|
||||
\
|
||||
/* To string function */ \
|
||||
bool (*str)(FILE *, K); \
|
||||
\
|
||||
/* Free from memory function */ \
|
||||
void (*free)(K); \
|
||||
\
|
||||
/* Hash function */ \
|
||||
size_t (*hash)(K); \
|
||||
\
|
||||
/* Priority function */ \
|
||||
int (*pri)(K, K); \
|
||||
}; \
|
||||
\
|
||||
```
|
||||
|
||||
### CMP
|
||||
|
||||
* `K` - `int (*cmp)(K, K)`
|
||||
* `V` - `int (*cmp)(V, V)`
|
||||
|
||||
A comparator function is used in sorted collections or when an equality is being checked like when trying to find a certain element in a list. It is responsible for taking two arguments of the same data type and comparing them. The return value is an `int` with the following definitions:
|
||||
|
||||
* Return `1` if the first argument is greater than the second;
|
||||
* Return `0` if the first argument equals the second;
|
||||
* Return `-1` if the first argument is less than the second.
|
||||
|
||||
Note that certain collections require a total ordering of its elements (sorted collections) while others only require to know if one data equals another. Check out the documentation for each collection to know more.
|
||||
|
||||
### CPY
|
||||
|
||||
* `K` - `K (*cpy)(K)`
|
||||
* `V` - `V (*cpy)(V)`
|
||||
|
||||
A copy function is used when a collection is being copied. It can be used to make a deep copy of of your custom data type. It must take a single parameter and return a new copy of that same data type. If this function is absent (`NULL`) the data type will be copied by assignment (for pointers this is a shallow copy).
|
||||
|
||||
Note that it doesn't makes sense to define a `cpy` function for data types that are not pointers. This function is useful for deep copies of a pointer in case you don't want two collections holding the same reference to the data.
|
||||
|
||||
### STR
|
||||
|
||||
* `K` - `bool (*str)(FILE *, K)`
|
||||
* `V` - `bool (*str)(FILE *, V)`
|
||||
|
||||
A string function is responsible for taking a `FILE` pointer and a custom data type and outputting the string representation of that data returning a `bool` indication success or failure. It is useful for debugging and doesn't necessarily needs to print all the data that the specific type holds.
|
||||
|
||||
### FREE
|
||||
|
||||
* `K` - `void (*free)(K)`
|
||||
* `V` - `void (*free)(V)`
|
||||
|
||||
The free function is called when a collection is cleared (all elements removed) or freed (all elements removed and the collection is freed from memory) and it is responsible for completely freeing all resources that are usually acquired by your data type.
|
||||
|
||||
This function only makes sense to be used when the data type has dynamically allocated memory. It is the only completely optional function.
|
||||
|
||||
### HASH
|
||||
|
||||
* `K` - `size_t (*hash)(K)`
|
||||
* `V` - `size_t (*hash)(V)`
|
||||
|
||||
This function receives a custom data type as parameter and returns a `size_t` [hash](https://en.wikipedia.org/wiki/Hash_function) that represents that data. Used by collections with an underlying hash tables.
|
||||
|
||||
### PRI
|
||||
|
||||
* `K` - `int (*cmp)(K, K)`
|
||||
* `V` - `int (*cmp)(V, V)`
|
||||
|
||||
A priority function works much like the comparator function except that it compares the priority between two elements. It is used in collections whose structure is based on the priority of elements and not in their general comparison (heaps).
|
||||
|
||||
* Return `1` if the first argument has a greater priority than the second;
|
||||
* Return `0` if the first argument has the same priority as second;
|
||||
* Return `-1` if the first argument has a lower priority than the second.
|
||||
|
||||
Why use both `cmp` and `pri`? Heaps have their internal structure based in the priority of elements. This priority is not necessarily how each element is compared to each other. Maybe their equality is defined differently for an equality of priorities. Maybe the rules for their priorities is different for when comparing an element against another.
|
||||
|
||||
The following table shows which functions are required, optional or never used for each Collection:
|
||||
|
||||
| Collection | CMP | CPY | STR | FREE | HASH | PRI |
|
||||
| ---------- | :-: | :-: | :-: | :--: | :--: | :-: |
|
||||
| BidiMap |  |  |  |  |  |  |
|
||||
| Deque |  |  |  |  |  |  |
|
||||
| HashMap |  |  |  |  |  |  |
|
||||
| HashSet |  |  |  |  |  |  |
|
||||
| Heap |  |  |  |  |  |  |
|
||||
| IntervalHeap |  |  |  |  |  |  |
|
||||
| List |  |  |  |  |  |  |
|
||||
| LinkedList |  |  |  |  |  |  |
|
||||
| MultiMap |  |  |  |  |  |  |
|
||||
| MultiSet |  |  |  |  |  |  |
|
||||
| Queue |  |  |  |  |  |  |
|
||||
| SortedList |  |  |  |  |  |  |
|
||||
| Stack |  |  |  |  |  |  |
|
||||
| TreeMap |  |  |  |  |  |  |
|
||||
| TreeSet |  |  |  |  |  |  |
|
||||
|
||||
<br>
|
||||
|
||||
| Color | Label |
|
||||
| :---: | ----- |
|
||||
|  | Required for basic functionality. |
|
||||
|  | Required for specific functions. |
|
||||
|  | Required for non-core specific functions. |
|
||||
|  | Optional. |
|
||||
|  | Not Used. |
|
||||
@@ -0,0 +1,4 @@
|
||||
# COR
|
||||
|
||||
Core functionalities of the C Macro Collections Library.
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# Iterators
|
||||
|
||||
Every collection comes with an interface of iterators where you can easily access the elements of a collection. These can move at any direction, can start at any 'end' of a collection and can move any amount of steps per iteration. Every collection generated comes with an iterator.
|
||||
|
||||
By design choice these iterators do not support modifications to the collection. If a collection is modified, all iterators that have been initialized will most likely be invalidated and may cause undefined behavior if used afterwards.
|
||||
@@ -0,0 +1,3 @@
|
||||
# DEV
|
||||
|
||||
The development collections. These collections are an exact copy of the `cmc` collections but they come with many logging throughout the generated code. Useful for debugging or during development.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
@@ -0,0 +1,23 @@
|
||||
# SAC
|
||||
|
||||
The Statically Allocated Collections. These collections have a fixed size. Its size is one of the parameters during macro expansion, producing a C array.
|
||||
|
||||
## CMC vs. SAC
|
||||
|
||||
The `cmc` collections hold their data either with nodes or a buffer that can be reallocated. The `sac` collections only have an array with fixed size and don't perform any allocation on the heap.
|
||||
|
||||
```c
|
||||
// A CMC List
|
||||
struct List
|
||||
{
|
||||
T *buffer;
|
||||
/* Other fields */
|
||||
};
|
||||
|
||||
// A SAC List
|
||||
struct List
|
||||
{
|
||||
T buffer[SIZE];
|
||||
/* Other fields */
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,73 @@
|
||||
# Generic Assertion Macros
|
||||
|
||||
## Contents
|
||||
|
||||
* [cmc_assert_array_equals_any](#cmc_assert_array_equals_any)
|
||||
* [cmc_assert_array_within_any](#cmc_assert_array_within_any)
|
||||
* [cmc_assert_array_outside_any](#cmc_assert_array_outside_any)
|
||||
* [cmc_assert_array_sorted_any](#cmc_assert_array_sorted_any)
|
||||
|
||||
## cmc_assert_array_equals_any
|
||||
|
||||
Assert that two arrays are equal element by element. `from_index` and `to_index` are both inclusive.
|
||||
|
||||
```c
|
||||
#define cmc_assert_array_equals_any(dtype, array1, array2, comparator, \
|
||||
from_index, to_index)
|
||||
```
|
||||
|
||||
* `dtype` - The type of the array. Can be of any data type.
|
||||
* `array1` - First array to be compared
|
||||
* `array2` - Second array to be compared
|
||||
* `comparator` - A comparator function pointer that has two `dtype` arguments and returns -1 if the first is less then the second, 0 if both are equal or 1 if the first is greater then the second
|
||||
* `from_index` - First index from both arrays to compare (inclusive)
|
||||
* `to_index` - Last index from both arrays to compare (inclusive)
|
||||
|
||||
## cmc_assert_array_within_any
|
||||
|
||||
Assert that each element in the array are within a certain boundary. Both ends are inclusive.
|
||||
|
||||
```c
|
||||
#define cmc_assert_array_within_any(dtype, array, comparator, lower_bound, \
|
||||
upper_bound, from_index, to_index)
|
||||
```
|
||||
|
||||
* `dtype` - The type of the array. Can be of any data type
|
||||
* `array` - Array to be checked if its elements are within a given range
|
||||
* `comparator` - A comparator function pointer that has two `dtype` arguments and returns -1 if the first is less then the second, 0 if both are equal or 1 if the first is greater then the second
|
||||
* `lower_bound` - Smallest value of the given range
|
||||
* `upper_bound` - Highest value of the given range
|
||||
* `from_index` - First index from the array to compare (inclusive)
|
||||
* `to_index` - Last index from the array to compare (inclusive)
|
||||
|
||||
## cmc_assert_array_outside_any
|
||||
|
||||
Assert that each element in the array are outside of a certain boundary. Both ends are inclusive.
|
||||
|
||||
```c
|
||||
#define cmc_assert_array_outside_any(dtype, array, comparator, lower_bound, \
|
||||
upper_bound, from_index, to_index)
|
||||
```
|
||||
|
||||
* `dtype` - The type of the array. Can be of any data type
|
||||
* `array` - Array to be checked if its elements are within a given range
|
||||
* `comparator` - A comparator function pointer that has two `dtype` arguments and returns -1 if the first is less then the second, 0 if both are equal or 1 if the first is greater then the second
|
||||
* `lower_bound` - Smallest value of the given range
|
||||
* `upper_bound` - Highest value of the given range
|
||||
* `from_index` - First index from the array to compare (inclusive)
|
||||
* `to_index` - Last index from the array to compare (inclusive)
|
||||
|
||||
## cmc_assert_array_sorted_any
|
||||
|
||||
Asserts that an array is sorted.
|
||||
|
||||
```c
|
||||
#define cmc_assert_array_sorted_any(dtype, array, comparator, from_index, \
|
||||
to_index)
|
||||
```
|
||||
|
||||
* `dtype` - The type of the array. Can be of any data type
|
||||
* `array` - Array to be checked if it is sorted
|
||||
* `comparator` - A comparator function pointer that has two `dtype` arguments and returns -1 if the first is less then the second, 0 if both are equal or 1 if the first is greater then the second
|
||||
* `from_index` - First index from the array to compare (inclusive)
|
||||
* `to_index` - Last index from the array to compare (inclusive)
|
||||
@@ -0,0 +1,83 @@
|
||||
# assert.h
|
||||
|
||||
Definition of many typed non-abortive assertions. Every assertion that fails will print a message to `stderr` but will not abort the program execution. The macro parameters are first assigned to a temporary variable of the specified `dtype` in order to generate warnings from the compiler in case of type mismatch.
|
||||
|
||||
The advantage of these typed macros is that when an assertion fails, it is possible to print to `stderr` the actual value that was passed to the function and check the expected result(s) all in one message.
|
||||
|
||||
There are assertions that work with [predefined values](valued_assertions.html) and others that work with [generic values](generic_assertions.html). Valued assertions work with a selected list of `dtype` (see further below), while the generic assertions work with any data type.
|
||||
|
||||
The macro parameters can be an *lvalue* or an *rvalue* because the parameters will be first assigned to local variables of the selected data type. Then, these local variables will be tested for whichever test that needs to be checked.
|
||||
|
||||
Assigning the macro parameter to a local variable can be useful. These local variables will be of type `dtype` and if the parameters don't match that type a warning will pop up.
|
||||
|
||||
Ever assertion is inside a block of `do { } while (0)`.
|
||||
|
||||
## Printed Messages
|
||||
|
||||
Whenever an assertion fails, a message will be printed to `stderr`. The message will look something like this:
|
||||
|
||||
```
|
||||
Assertion Failed at FILE:FUNCTION:LINE for { ACTUAL }: DETAILS
|
||||
```
|
||||
|
||||
The first information is the file where that assertion failed. Then the function name and the line number. Then in place of `ACTUAL`, a stringified version of the `actual` parameter, which is the variable being checked in the assertion, will be printed.
|
||||
|
||||
In `DETAILS` is where more information about the assertion will be printed. This is the most useful part of these macros. In here, the actual value of the variable will be printed along with the expected value or range or lower bound, etc. __In here you will be able to debug your application to check the expected value(s) against the actual one.__
|
||||
|
||||
__Example__
|
||||
|
||||
```c
|
||||
#include "utl/assert.h"
|
||||
|
||||
void is_one_hundred(int variable)
|
||||
{
|
||||
cmc_assert_equals(int32_t, 100, variable);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int my_var = 9;
|
||||
is_one_hundred(my_var);
|
||||
}
|
||||
```
|
||||
|
||||
This will print the following message:
|
||||
|
||||
`Assertion Failed at main.c:is_one_hundred:5 for { variable }: Expected: 100 Actual: 9`
|
||||
|
||||
## cmc_assert_state
|
||||
|
||||
This is a global variable that can be used to test if all assertions passed. If any assertion fails, this variable will be set to `false`. If an assertion passes this variable will __not__ be set back to `true`. If you wish to, you can set the state back to `true` manually.
|
||||
|
||||
```c
|
||||
static bool cmc_assert_state = true;
|
||||
```
|
||||
|
||||
This variable is used in [test.h](../test.h/index.html) utility to automatically pass or fail a unit test. Once the unit test finishes, this variable is set back to `true` so that another unit test may follow.
|
||||
|
||||
## dtype
|
||||
|
||||
These are the data types supported by the Valued Assertion macros:
|
||||
|
||||
* `int8_t`
|
||||
* `int16_t`
|
||||
* `int32_t`
|
||||
* `int64_t`
|
||||
* `uint8_t`
|
||||
* `uint16_t`
|
||||
* `uint32_t`
|
||||
* `uint64_t`
|
||||
* `intmax_t`
|
||||
* `uintmax_t`
|
||||
* `size_t`
|
||||
* `float`
|
||||
* `double`
|
||||
|
||||
The following two are only accepted by `cmc_assert_equals` and `cmc_assert_not_equals`:
|
||||
|
||||
* `ptr`
|
||||
* `bool`
|
||||
|
||||
Note that `ptr` is used for any pointers. They are first casted to `void *` and then their address is compared.
|
||||
|
||||
Note that bool will be expanded to `_Bool`.
|
||||
@@ -0,0 +1,20 @@
|
||||
# Overview
|
||||
|
||||
## Single Value Assertion Macros
|
||||
|
||||
* __cmc_assert__ - Asserts that an expression evaluates to true;
|
||||
* __cmc_assert_equals__ - Asserts that a value from type `dtype` equals another;
|
||||
* __cmc_assert_not_equals__ - Asserts that value from type `dtype` does not equals another;
|
||||
* __cmc_assert_greater__ - Asserts that a value from type `dtype` is greater than a lower bound;
|
||||
* __cmc_assert_greater_equals__ - Asserts that a value from type `dtpype` is greater than or equal to a lower bound;
|
||||
* __cmc_assert_lesser__ - Asserts that a value from type `dtpype` is lesser than an upper bound;
|
||||
* __cmc_assert_lesser_equals__ - Asserts that a value from type `dtpype` is lesser than or equal to an upper bound;
|
||||
* __cmc_assert_in_range__ - Asserts that a value from type `dtpype` is within a certain range;
|
||||
* __cmc_assert_not_in_range__ - Asserts that a value from type `dtpype` is outside of a certain range.
|
||||
|
||||
## Array Assertion Macros
|
||||
|
||||
* __cmc_assert_array_equals_any__ - Assert that two arrays are equal element by element;
|
||||
* __cmc_assert_array_within_any__ - Assert that each element in the array are within a bounds;
|
||||
* __cmc_assert_array_outside_any__ - Assert that each element in the array are out of bounds;
|
||||
* __cmc_assert_array_sorted_any__ - Assert that the array is sorted.
|
||||
@@ -0,0 +1,127 @@
|
||||
# Valued Assertion Macros
|
||||
|
||||
## Contents
|
||||
|
||||
* [cmc_assert](#cmc_assert)
|
||||
* [cmc_assert_equals](#cmc_assert_equals)
|
||||
* [cmc_assert_not_equals](#cmc_assert_not_equals)
|
||||
* [cmc_assert_greater](#cmc_assert_greater)
|
||||
* [cmc_assert_greater_equals](#cmc_assert_greater_equals)
|
||||
* [cmc_assert_lesser](#cmc_assert_lesser)
|
||||
* [cmc_assert_lesser_equals](#cmc_assert_lesser_equals)
|
||||
* [cmc_assert_in_range](#cmc_assert_in_range)
|
||||
* [cmc_assert_not_in_range](#cmc_assert_not_in_range)
|
||||
|
||||
## cmc_assert
|
||||
|
||||
Asserts that an `expression` evaluates to `true`.
|
||||
|
||||
```c
|
||||
#define cmc_assert(expression)
|
||||
```
|
||||
|
||||
__Parameters__
|
||||
|
||||
* `expression` - An expression that needs to be evaluated to true.
|
||||
|
||||
## cmc_assert_equals
|
||||
|
||||
Asserts that a value equals another expected value.
|
||||
|
||||
```c
|
||||
#define cmc_assert_equals(dtype, expected, actual)
|
||||
```
|
||||
|
||||
__Parameters__
|
||||
|
||||
* `dtype` - One of the possible data types listed [here](index.html#dtype)
|
||||
* `expected` - The expected value for this assertion
|
||||
* `actual` - A variant that is checked against the expected value
|
||||
|
||||
## cmc_assert_not_equals
|
||||
|
||||
Asserts that a value does not equal another.
|
||||
|
||||
```c
|
||||
#define cmc_assert_not_equals(dtype, not_expected, actual)
|
||||
```
|
||||
|
||||
__Parameters__
|
||||
|
||||
* `dtype` - One of the possible data types listed [here](index.html#dtype)
|
||||
* `not_expected` - The value that is not expected for this assertion
|
||||
* `actual` - A variant that is checked against the not expected value
|
||||
|
||||
## cmc_assert_greater
|
||||
|
||||
Asserts that a value is above a certain lower boundary.
|
||||
|
||||
```c
|
||||
#define cmc_assert_greater(dtype, boundary, actual)
|
||||
```
|
||||
|
||||
* `dtype` - One of the possible data types listed [here](index.html#dtype)
|
||||
* `boundary` - The expected lowest possible value (excluded)
|
||||
* `actual` - A variant that is checked against the boundary value
|
||||
|
||||
## cmc_assert_greater_equals
|
||||
|
||||
Asserts that a value is above a certain lower boundary or equal to it.
|
||||
|
||||
```c
|
||||
#define cmc_assert_greater_equals(dtype, boundary, actual)
|
||||
```
|
||||
|
||||
* `dtype` - One of the possible data types listed [here](index.html#dtype)
|
||||
* `boundary` - The expected lowest possible value (included)
|
||||
* `actual` - A variant that is checked against the boundary value
|
||||
|
||||
## cmc_assert_lesser
|
||||
|
||||
Asserts that a value is below a certain upper boundary.
|
||||
|
||||
```c
|
||||
#define cmc_assert_lesser(dtype, boundary, actual)
|
||||
```
|
||||
|
||||
* `dtype` - One of the possible data types listed [here](index.html#dtype)
|
||||
* `boundary` - The expected highest possible value (excluded)
|
||||
* `actual` - A variant that is checked against the boundary value
|
||||
|
||||
## cmc_assert_lesser_equals
|
||||
|
||||
Asserts that a value is below a certain upper boundary or equal to it.
|
||||
|
||||
```c
|
||||
#define cmc_assert_lesser_equals(dtype, boundary, actual)
|
||||
```
|
||||
|
||||
* `dtype` - One of the possible data types listed [here](index.html#dtype)
|
||||
* `boundary` - The expected highest possible value (excluded)
|
||||
* `actual` - A variant that is checked against the boundary value
|
||||
|
||||
## cmc_assert_in_range
|
||||
|
||||
Asserts that a value is within a certain range. Range is inclusive on both ends.
|
||||
|
||||
```c
|
||||
#define cmc_assert_in_range(dtype, lower_bound, upper_bound, actual)
|
||||
```
|
||||
|
||||
* `dtype` - One of the possible data types listed [here](index.html#dtype)
|
||||
* `lower_bound` - Smallest value of the expected range
|
||||
* `upper_bound` - Highest value of the expected range
|
||||
* `actual` - A variant that is checked against lower and upper boundaries value
|
||||
|
||||
## cmc_assert_not_in_range
|
||||
|
||||
Asserts that a value is not within a certain range. Range is inclusive on both ends.
|
||||
|
||||
```c
|
||||
#define cmc_assert_not_in_range(dtype, lower_bound, upper_bound, actual)
|
||||
```
|
||||
|
||||
* `dtype` - One of the possible data types listed [here](index.html#dtype)
|
||||
* `lower_bound` - Smallest value of the not expected range
|
||||
* `upper_bound` - Highest value of the not expected range
|
||||
* `actual` - A variant that is checked against lower and upper boundaries value
|
||||
@@ -0,0 +1,60 @@
|
||||
# foreach.h
|
||||
|
||||
For-Each macros. Since doing a for loop with iterators can be a lot to write, these macros solve that problem. There are two of them:
|
||||
|
||||
* `CMC_FOREACH` - Goes from the beginning of a Collection to the end
|
||||
* `CMC_FOREAC_REV` - Goes from the end of a Collection to the beginning
|
||||
|
||||
## CMC_FOREACH
|
||||
|
||||
```c
|
||||
#define CMC_FOREACH(PFX, SNAME, ITERNAME, TARGET)
|
||||
```
|
||||
|
||||
* `PFX` - Functions prefix
|
||||
* `SNAME` - Struct name
|
||||
* `ITERNAME` - Iterator variable name
|
||||
* `TARGET` - Target collection variable name
|
||||
|
||||
## CMC_FOREACH_REV
|
||||
|
||||
```c
|
||||
#define CMC_FOREACH_REV(PFX, SNAME, ITERNAME, TARGET)
|
||||
```
|
||||
|
||||
* `PFX` - Functions prefix
|
||||
* `SNAME` - Struct name
|
||||
* `ITERNAME` - Iterator variable name
|
||||
* `TARGET` - Target collection variable name
|
||||
|
||||
## Example
|
||||
|
||||
```c
|
||||
#include "cmc/list.h"
|
||||
#include "utl/foreach.h"
|
||||
|
||||
CMC_GENERATE_LIST(i32l, i32list, int32_t)
|
||||
|
||||
int main(void)
|
||||
{
|
||||
struct i32list *list = i32l_new(100, &(struct i32list_fval) { NULL });
|
||||
|
||||
for (int i = 0; i < 100; i++)
|
||||
i32l_push_back(list, i);
|
||||
|
||||
CMC_FOREACH(i32l, i32list, it, list)
|
||||
{
|
||||
int value = i32l_iter_value(&it);
|
||||
size_t index = i32l_iter_index(&it);
|
||||
|
||||
if (index == 0)
|
||||
printf("[ %d, ", value);
|
||||
else if (index == i32l_count(list) - 1)
|
||||
printf("%d ]\n", value);
|
||||
else
|
||||
printf("%d, ", value);
|
||||
}
|
||||
|
||||
i32l_free(list);
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,87 @@
|
||||
# futils.h
|
||||
|
||||
Contains common functions that can be used by Functions Table, instead of writing another one every time.
|
||||
|
||||
A quick list of functions present in this file:
|
||||
|
||||
| cmp |
|
||||
| ----------- |
|
||||
| `static inline int cmc_i64_cmp(int64_t x1, int64_t x2);` |
|
||||
| `static inline int cmc_i32_cmp(int32_t x1, int32_t x2);` |
|
||||
| `static inline int cmc_i16_cmp(int16_t x1, int16_t x2);` |
|
||||
| `static inline int cmc_i8_cmp(int8_t x1, int8_t x2);` |
|
||||
| `static inline int cmc_u64_cmp(uint64_t x1, uint64_t x2);` |
|
||||
| `static inline int cmc_u32_cmp(uint32_t x1, uint32_t x2);` |
|
||||
| `static inline int cmc_u16_cmp(uint16_t x1, uint16_t x2);` |
|
||||
| `static inline int cmc_u8_cmp(uint8_t x1, uint8_t x2);` |
|
||||
| `static inline int cmc_size_cmp(size_t x1, size_t x2);` |
|
||||
| `static inline int cmc_imax_cmp(intmax_t x1, intmax_t x2);` |
|
||||
| `static inline int cmc_umax_cmp(uintmax_t x1, uintmax_t x2);` |
|
||||
| `static inline int cmc_float_cmp(float x1, float x2);` |
|
||||
| `static inline int cmc_double_cmp(double x1, double x2);` |
|
||||
| `static inline int cmc_str_cmp(char *ch1, char *ch2);` |
|
||||
|
||||
<br>
|
||||
<br>
|
||||
|
||||
| cpy |
|
||||
| --- |
|
||||
| `static inline char *cmc_str_cpy(char *str);` |
|
||||
|
||||
<br>
|
||||
<br>
|
||||
|
||||
| str |
|
||||
| --- |
|
||||
| `static inline bool cmc_i8_str(FILE *file, int8_t element);` |
|
||||
| `static inline bool cmc_i16_str(FILE *file, int16_t element);` |
|
||||
| `static inline bool cmc_i32_str(FILE *file, int32_t element);` |
|
||||
| `static inline bool cmc_i64_str(FILE *file, int64_t element);` |
|
||||
| `static inline bool cmc_u8_str(FILE *file, uint8_t element);` |
|
||||
| `static inline bool cmc_u16_str(FILE *file, uint16_t element);` |
|
||||
| `static inline bool cmc_u32_str(FILE *file, uint32_t element);` |
|
||||
| `static inline bool cmc_u64_str(FILE *file, uint64_t element);` |
|
||||
| `static inline bool cmc_size_str(FILE *file, size_t element);` |
|
||||
| `static inline bool cmc_imax_str(FILE *file, intmax_t element);` |
|
||||
| `static inline bool cmc_umax_str(FILE *file, uintmax_t element);` |
|
||||
| `static inline bool cmc_float_str(FILE *file, float element);` |
|
||||
| `static inline bool cmc_double_str(FILE *file, double element);` |
|
||||
| `static inline bool cmc_str_str(FILE *file, char *element);` |
|
||||
|
||||
<br>
|
||||
<br>
|
||||
|
||||
| free |
|
||||
| --- |
|
||||
|
||||
<br>
|
||||
<br>
|
||||
|
||||
| hash |
|
||||
| ---- |
|
||||
| `static inline size_t cmc_i64_hash(int64_t e);` |
|
||||
| `static inline size_t cmc_i32_hash(int32_t e);` |
|
||||
| `static inline size_t cmc_i16_hash(int16_t e);` |
|
||||
| `static inline size_t cmc_i8_hash(int8_t e);` |
|
||||
| `static inline size_t cmc_u64_hash(uint64_t e);` |
|
||||
| `static inline size_t cmc_u32_hash(uint32_t e);` |
|
||||
| `static inline size_t cmc_u16_hash(uint16_t e);` |
|
||||
| `static inline size_t cmc_u8_hash(uint8_t e);` |
|
||||
| `static inline size_t cmc_size_hash(size_t e);` |
|
||||
| `static inline size_t cmc_imax_hash(intmax_t e);` |
|
||||
| `static inline size_t cmc_umax_hash(uintmax_t e);` |
|
||||
| `static inline size_t cmc_float_hash(float e);` |
|
||||
| `static inline size_t cmc_double_hash(double e);` |
|
||||
| `static inline size_t cmc_str_hash_djb2(char *str);` |
|
||||
| `static inline size_t cmc_str_hash_sdbm(char *str);` |
|
||||
| `static inline size_t cmc_str_hash_java(char *str);` |
|
||||
| `static inline size_t cmc_str_hash_murmur3(uint64_t e);` |
|
||||
| `static inline size_t cmc_str_hash_murmur3_variant(uint64_t e);` |
|
||||
| `static inline size_t cmc_i64_hash_mix(int64_t element);` |
|
||||
| `static inline size_t cmc_u64_hash_mix(uint64_t element);` |
|
||||
|
||||
<br>
|
||||
<br>
|
||||
|
||||
| pri |
|
||||
| --- |
|
||||
@@ -0,0 +1,12 @@
|
||||
# UTL
|
||||
|
||||
Inside the `./utl/` folder there are many utility functions and macros that can be extensively used with the C Macro Collections library. Most of these utilities can be used without the main library.
|
||||
|
||||
## Contents
|
||||
|
||||
* [assert.h](assert.h/index.html) - Non-abortive assert macros
|
||||
* [foreach.h](foreach.h/index.html) - For Each macros
|
||||
* [futils.h](futils.h/index.html) - Common functions used by Functions Table
|
||||
* [log.h](log.h/index.html) - Logging utility with levels of severity
|
||||
* [test.h](test.h/index.html) - Simple Unit Test building with macros
|
||||
* [timer.h](timer.h/index.html) - Timing code execution utility
|
||||
@@ -0,0 +1,62 @@
|
||||
# Configuration
|
||||
|
||||
## cmc_log_config
|
||||
|
||||
This is an anonymous static `struct` that can be directly modified to change logging levels, enable or disable output or change file output.
|
||||
|
||||
```c
|
||||
static struct
|
||||
{
|
||||
enum cmc_log_type tlevel;
|
||||
enum cmc_log_type flevel;
|
||||
bool tenabled;
|
||||
bool fenabled;
|
||||
bool enabled;
|
||||
FILE * file;
|
||||
} cmc_log_config = { 0, 0, true, true, true, NULL };
|
||||
```
|
||||
|
||||
__Members__
|
||||
|
||||
* `tlevel` - Terminal log level (`stderr`)
|
||||
* `flevel` - File log level
|
||||
* `tenabled` - Terminal logging enabled if `true`
|
||||
* `fenabled` - File logging enabled if `true`
|
||||
* `enabled` - Enables or disables all logs
|
||||
* `FILE *file` - File for output
|
||||
|
||||
This configuration is global to every log. The `FILE` pointer is not handled by the API and needs to be able to write to the file.
|
||||
|
||||
## Log Level
|
||||
|
||||
By tuning `tlevel` and `flevel` (both accessed from `cmc_log_config`) you can filter which log messages are printed or not.
|
||||
|
||||
* If the log level is `0`, all logs are enabled
|
||||
* If the log level `X` is positive:
|
||||
* All levels less than `X` will be disabled
|
||||
* If `X` is greater than the level of `FATAL`, all logs are disabled
|
||||
* If the log level `X` is negative:
|
||||
* All levels greater than `abs(X)` are enabled
|
||||
* If `abs(X)` is greater than the level of `FATAL`, all logs are enabled
|
||||
|
||||
__Examples__
|
||||
|
||||
If you want to output only `TRACE` logs to a file and the rest to `stderr`:
|
||||
|
||||
* `cmc_log_config.tlevel = CMC_LOG_DEBUG`
|
||||
* `cmc_log_config.flevel = -CMC_LOG_TRACE`
|
||||
|
||||
If you want to output only `FATAL` to `stderr` and the rest to a file:
|
||||
|
||||
* `cmc_log_config.tlevel = CMC_LOG_FATAL`
|
||||
* `cmc_log_config.flevel = -CMC_LOG_ERROR`
|
||||
|
||||
If you want to output only `FATAL` and `ERROR` to a file an nothing to `stderr`:
|
||||
|
||||
* `cmc_log_config.tlevel = CMC_LOG_FATAL + 1`
|
||||
* `cmc_log_config.flevel = CMC_LOG_ERROR`
|
||||
|
||||
__Summary__
|
||||
|
||||
* Log Level > 0: "Allow all log levels that are greater than this, including it"
|
||||
* Log Level < 0: "Allow all log levels that are smaller than this, including it"
|
||||
@@ -0,0 +1,23 @@
|
||||
# log.h
|
||||
|
||||
Simple and customizable logging macros. Messages can have a custom logging level and a custom file output. This customization can be done via the global variable [`cmc_log_config`](configuration.html#cmc_log_config). Logs are outputted to `stderr`.
|
||||
|
||||
## cmc_log_type
|
||||
|
||||
Defines the possible Log Levels. These are used to categorize log messages. The higher the number, the more severe or important that message is. Check out [here](meanings.html)
|
||||
|
||||
```c
|
||||
enum cmc_log_type
|
||||
{
|
||||
CMC_LOG_TRACE = 1,
|
||||
CMC_LOG_DEBUG = 2,
|
||||
CMC_LOG_INFO = 3,
|
||||
CMC_LOG_WARN = 4,
|
||||
CMC_LOG_ERROR = 5,
|
||||
CMC_LOG_FATAL = 6
|
||||
};
|
||||
```
|
||||
|
||||
## CMC_LOG_COLOR
|
||||
|
||||
If this macro is defined, logging will be outputted with [ANSI color escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code#Colors). If you are logging into a file, these escape codes are not printed.
|
||||
@@ -0,0 +1,67 @@
|
||||
# Log Functions
|
||||
|
||||
## cmc_log_trace
|
||||
|
||||
Defines a message with a Log Level of `CMC_LOG_TRACE`.
|
||||
|
||||
```c
|
||||
#define cmc_log_trace(fmt, ...)
|
||||
```
|
||||
|
||||
* `fmt` - A string that contains a text to be written that may contain format specifiers
|
||||
* `...` - Optional. Values to be formatted into the string that will be printed
|
||||
|
||||
## cmc_log_debug
|
||||
|
||||
Defines a message with a Log Level of `CMC_LOG_DEBUG`.
|
||||
|
||||
```c
|
||||
#define cmc_log_debug(fmt, ...)
|
||||
```
|
||||
|
||||
* `fmt` - A string that contains a text to be written that may contain format specifiers
|
||||
* `...` - Optional. Values to be formatted into the string that will be printed
|
||||
|
||||
## cmc_log_info
|
||||
|
||||
Defines a message with a Log Level of `CMC_LOG_INFO`.
|
||||
|
||||
```c
|
||||
#define cmc_log_info(fmt, ...)
|
||||
```
|
||||
|
||||
* `fmt` - A string that contains a text to be written that may contain format specifiers
|
||||
* `...` - Optional. Values to be formatted into the string that will be printed
|
||||
|
||||
## cmc_log_warn
|
||||
|
||||
Defines a message with a Log Level of `CMC_LOG_WARN`.
|
||||
|
||||
```c
|
||||
#define cmc_log_warn(fmt, ...)
|
||||
```
|
||||
|
||||
* `fmt` - A string that contains a text to be written that may contain format specifiers
|
||||
* `...` - Optional. Values to be formatted into the string that will be printed
|
||||
|
||||
## cmc_log_error
|
||||
|
||||
Defines a message with a Log Level of `CMC_LOG_ERROR`.
|
||||
|
||||
```c
|
||||
#define cmc_log_error(fmt, ...)
|
||||
```
|
||||
|
||||
* `fmt` - A string that contains a text to be written that may contain format specifiers
|
||||
* `...` - Optional. Values to be formatted into the string that will be printed
|
||||
|
||||
## cmc_log_fatal
|
||||
|
||||
Defines a message with a Log Level of `CMC_LOG_FATAL`.
|
||||
|
||||
```c
|
||||
#define cmc_log_fatal(fmt, ...)
|
||||
```
|
||||
|
||||
* `fmt` - A string that contains a text to be written that may contain format specifiers
|
||||
* `...` - Optional. Values to be formatted into the string that will be printed
|
||||
@@ -0,0 +1,10 @@
|
||||
# Meanings
|
||||
|
||||
What does each Log Level means? It depends on the application. In here you will read more about how the C Macro Collections library uses each of these Log Levels, but this does not mean you should use them like described.
|
||||
|
||||
* __TRACE__ - Trace is used to describe the inner workings of a function or an algorithm. These messages can be used extensively, meaning that their content is probably not very useful, even for debugging.
|
||||
* __DEBUG__ - Mainly used for debugging, tracking certain variables, checking if pointers were deallocated or anything useful to help visualizing what your program is doing in general.
|
||||
* __INFO__ - Info can be used as a heads up. Something that is important to note, but is not quite a warning or an error.
|
||||
* __WARN__ - Warnings are attributed to all cases where the function can recover from and usually treated by it. In most cases the user can treat these warnings himself.
|
||||
* __ERROR__ - Something really bad happened. Your program will not crash yet but depending on what comes next, it will. This might be attributed to pointers that shouldn't be null because later functions might use it.
|
||||
* __FATAL__ - This will probably be the last logging message before your program crashes or goes into madness. Fatal errors are commonly attributed to dereferencing NULL pointers.
|
||||
@@ -0,0 +1,118 @@
|
||||
# test.h
|
||||
|
||||
A simple [Unit Test](https://en.wikipedia.org/wiki/Unit_testing) utility used by the C Macro Collections Library. It has two main components: a Unit and a Test.
|
||||
|
||||
This utility uses two other libraries defined in the C Macro Collections library:
|
||||
|
||||
* [`assert.h`](../assert.h/index.html)
|
||||
* [`timer.h`](../timer.h/index.html)
|
||||
|
||||
When a test is created inside a unit, it will first set the global variable [`cmc_assert_state`](../assert.h/index.html#cmc_assert_state) to `true`. Then, inside the test you can use the assertions provided by the `assert.h` library to pass or fail a unit test automatically. By the end of a test the variable will be checked. If `false` it means that a certain assertion failed.
|
||||
|
||||
The runtime of each Unit Test is calculated using the `timer.h` library.
|
||||
|
||||
## CMC_CREATE_UNIT
|
||||
|
||||
Creates a Unit that can contain tests.
|
||||
|
||||
```c
|
||||
#define CMC_CREATE_UNIT(UNAME, VERBOSE, BODY)
|
||||
```
|
||||
|
||||
* `UNAME` - Unit name. This needs to be a valid function name!
|
||||
* `VERBOSE` - Either `true` or `false`. If `true`, prints every message of `PASSED` or `FAILED` from each test
|
||||
* `BODY` - A block of code containing tests
|
||||
|
||||
## CMC_CREATE_TEST
|
||||
|
||||
Creates a test within a unit. This macro can only be used inside the `BODY` of a `CMC_CREATE_UNIT` since it uses local variables allocated by the latter.
|
||||
|
||||
```c
|
||||
#define CMC_CREATE_TEST(TNAME, BODY)
|
||||
```
|
||||
|
||||
* `TNAME` - Test name
|
||||
* `BODY` - A block of code containing the test itself
|
||||
|
||||
Inside a test you can use the assertions from `assert.h` to automatically pass or fail a test.
|
||||
|
||||
## CMC_TEST_ABORT
|
||||
|
||||
Aborts a unit test. This will jump to a `goto` located at the end of the Unit Test. Must be called inside a `CMC_CREATE_TEST`.
|
||||
|
||||
```c
|
||||
#define CMC_TEST_ABORT()
|
||||
```
|
||||
|
||||
## cmc_test_info
|
||||
|
||||
Contains information about a Unit Test. Used internally.
|
||||
|
||||
```c
|
||||
struct cmc_test_info
|
||||
{
|
||||
uintmax_t total;
|
||||
uintmax_t passed;
|
||||
uintmax_t failed;
|
||||
uintmax_t assert_total;
|
||||
uintmax_t assert_failed;
|
||||
bool aborted;
|
||||
bool verbose;
|
||||
};
|
||||
```
|
||||
|
||||
* `uintmax_t total` - Total tests in the unit test
|
||||
* `uintmax_t passed` - Total tests passed in the unit test
|
||||
* `uintmax_t failed` - Total tests failed in the unit test
|
||||
* `uintmax_t assert_total` - Total assertions in the unit test
|
||||
* `uintmax_t assert_failed` - Total assertions failed in the unit test
|
||||
* `bool aborted` - If the unit test was aborted
|
||||
* `bool verbose` - Information about each test is displayed
|
||||
|
||||
## CMC_TEST_COLOR
|
||||
|
||||
Define this macro if you wish to have color output from the messages printed by each test. Note that a Unit Test need to have `VERBOSE` equal to `true`.
|
||||
|
||||
## Example
|
||||
|
||||
```c
|
||||
#include "utl/test.h"
|
||||
|
||||
CMC_CREATE_UNIT(check_math, true, {
|
||||
CMC_CREATE_TEST(summation, {
|
||||
// assert.h already comes with test.h
|
||||
cmc_assert_equals(int32_t, 4, 2 + 2);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(subtraction, {
|
||||
cmc_assert_equals(int32_t, -1, 2 - 3);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(this will fail, {
|
||||
// test names can be text as long as they don't have a comma
|
||||
cmc_assert_greater(double, 1.0, 0.0);
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(fish, {
|
||||
// You can also manually fail a test
|
||||
if (strcmp("2 + 2", "fish") != 0)
|
||||
cmc_assert_state = false;
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(abort!, {
|
||||
if (4 % 2 == 0)
|
||||
CMC_TEST_ABORT();
|
||||
});
|
||||
|
||||
CMC_CREATE_TEST(sad..., {
|
||||
// This test won't be called because unit test was aborted above
|
||||
cmc_assert(true);
|
||||
});
|
||||
});
|
||||
|
||||
int main(void)
|
||||
{
|
||||
// returns how many tests failed
|
||||
return check_math();
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,63 @@
|
||||
# timer.h
|
||||
|
||||
A very simple library that encapsulates `clock()` from `<time.h>`, making life easier.
|
||||
|
||||
## cmc_timer
|
||||
|
||||
```c
|
||||
struct cmc_timer
|
||||
{
|
||||
clock_t start;
|
||||
clock_t stop;
|
||||
double result;
|
||||
};
|
||||
```
|
||||
|
||||
* `clock_t start` - Start of timing
|
||||
* `clock_t stop` - End of timing
|
||||
* `double result` - Result in milliseconds
|
||||
|
||||
## cmc_timer_start
|
||||
|
||||
Starts the timer by setting `start` with `clock()`.
|
||||
|
||||
```c
|
||||
#define cmc_timer_start(timer)
|
||||
```
|
||||
|
||||
* `timer` - A `struct cmc_timer` variable
|
||||
|
||||
## cmc_timer_stop
|
||||
|
||||
Sets the `stop` variable with `clock()` and calculates the `result` in milliseconds.
|
||||
|
||||
```c
|
||||
#define cmc_timer_start(timer)
|
||||
```
|
||||
|
||||
* `timer` - A `struct cmc_timer` variable
|
||||
|
||||
## Example
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
|
||||
#include "utl/timer.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
struct cmc_timer t;
|
||||
|
||||
cmc_timer_start(t);
|
||||
|
||||
size_t total = 0;
|
||||
|
||||
for (size_t i = 0; i < 1000000000; i++)
|
||||
total += i;
|
||||
|
||||
cmc_timer_stop(t);
|
||||
|
||||
printf("Sum took %.0lf milliseconds to calculate\n", t.result);
|
||||
}
|
||||
```
|
||||
|
||||
Reference in new issue
Block a user