Added String type documentation
All checks were successful
clang-build / clang-build (push) Successful in 30s
gcc-build / gcc-build (push) Successful in 19s

This commit is contained in:
2026-03-16 09:38:38 +01:00
parent 6110fc963c
commit f8a77a6056
17 changed files with 892 additions and 555 deletions

View File

@@ -13,7 +13,6 @@ BENCH_OBJ_DIR = bench_obj
TESTS_SRC = tests
TARGET = usage
TEST_V_TARGET = test_vector
TEST_M_TARGET = test_map
TEST_B_TARGET = test_bigint
@@ -21,16 +20,12 @@ TEST_S_TARGET = test_string
BENCH_TARGET = benchmark_datum
LIB_OBJS = $(OBJ_DIR)/vector.o $(OBJ_DIR)/map.o $(OBJ_DIR)/bigint.o $(OBJ_DIR)/string.o
PROG_OBJS = $(OBJ_DIR)/usage.o
.PHONY: all clean
.PHONY: all clean examples
all: $(TARGET) $(TEST_V_TARGET) $(TEST_M_TARGET) $(TEST_B_TARGET) $(TEST_S_TARGET) $(BENCH_TARGET)
all: $(TEST_V_TARGET) $(TEST_M_TARGET) $(TEST_B_TARGET) $(TEST_S_TARGET) $(BENCH_TARGET) examples
bench: $(BENCH_TARGET)
$(TARGET): $(PROG_OBJS) $(LIB_OBJS)
$(CC) $(CFLAGS) -o $@ $^
$(TEST_V_TARGET): $(OBJ_DIR)/test_vector.o $(OBJ_DIR)/vector.o
$(CC) $(CFLAGS) -o $@ $^
@@ -43,10 +38,10 @@ $(TEST_B_TARGET): $(OBJ_DIR)/test_bigint.o $(OBJ_DIR)/bigint.o $(OBJ_DIR)/vector
$(TEST_S_TARGET): $(OBJ_DIR)/test_string.o $(OBJ_DIR)/string.o
$(CC) $(CFLAGS) -o $@ $^
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR)
$(CC) $(CFLAGS) -c -o $@ $<
examples: $(LIB_OBJS)
$(MAKE) -C examples
$(OBJ_DIR)/usage.o: usage.c | $(OBJ_DIR)
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR)
$(CC) $(CFLAGS) -c -o $@ $<
$(OBJ_DIR)/%.o: $(TESTS_SRC)/%.c | $(OBJ_DIR)
@@ -69,4 +64,5 @@ $(BENCH_OBJ_DIR):
mkdir -p $(BENCH_OBJ_DIR)
clean:
rm -rf $(OBJ_DIR) $(BENCH_OBJ_DIR) $(TARGET) $(TEST_V_TARGET) $(TEST_M_TARGET) $(TEST_B_TARGET) $(TEST_S_TARGET) $(BENCH_TARGET)
rm -rf $(OBJ_DIR) $(BENCH_OBJ_DIR) $(TEST_V_TARGET) $(TEST_M_TARGET) $(TEST_B_TARGET) $(TEST_S_TARGET) $(BENCH_TARGET)
$(MAKE) -C examples clean

View File

@@ -11,7 +11,8 @@ the standard library. It currently features:
- [**Vector**](/docs/vector.md): a growable, contiguous array of homogenous generic data types;
- [**Map**](/docs/map.md): an associative array of generic heterogenous data types;
- [**BigInt**](/docs/bigint.md): a data type for arbitrary large integers.
- [**BigInt**](/docs/bigint.md): a data type for arbitrary large integers;
- [**String**](/docs/string.md): an immutable, null-terminated string type with partial UTF-8 support.
## Usage
At its simplest, you can use this library as follows:
@@ -134,20 +135,44 @@ int main(void) {
}
```
For a more exhaustive example, refer to the `usage.c` file. There, you will find a program with proper error management
and a sample usage for every available method. To run it, first issue the following command:
### `String` usage
```c
#include <stdio.h>
#include "src/string.h"
/*
* Compile with: gcc main.c src/string.c
* Output: Final string: "Hello,World,😀" Splitted: ["Hello" "World" "😀" ]
*/
int main(void) {
string_t *x = string_new(" Hello, ").value.string;
string_t *x_trm = string_trim(x).value.string;
string_t *y = string_new("😀,dlroW").value.string;
string_t *y_rev = string_reverse(y).value.string;
string_t *str = string_concat(x_trm, y_rev).value.string;
string_t **strings = string_split(str, ",").value.split.strings;
printf("Final string: \"%s\" Splitted: [", str->data);
for (int idx = 0; idx < 3; idx++) { printf("\"%s\" ", strings[idx]->data); }
printf("]\n");
string_split_destroy(strings, 3); string_destroy(str);
string_destroy(x); string_destroy(y);
string_destroy(x_trm); string_destroy(y_rev);
return 0;
}
```
For additional usage samples, refer to the [`examples/`](/examples) directory. You can compile these example programs with the following command:
```sh
$ make clean all
```
This will compile the library as well as the `usage.c` file, the unit tests and the benchmark. After that, you can run it by typing `./usage`.
> [!NOTE]
> This project is primarily developed for learning purposes and was not created with industrial
> or production use in mind. As such, it is not intended to compete with any existing C library such as the
> GNU Multiple Precision Arithmetic Library (GMP).
## Documentation
For additional details about this library (internal design, memory management, data ownership, etc.) go to the [docs folder](/docs).

View File

@@ -7,4 +7,5 @@ At the time being, this documentation includes the following pages:
- [vector.md](vector.md): vector documentation;
- [map.md](map.md): map documentation;
- [bigint.md](bigint.md): bigint documentation.
- [bigint.md](bigint.md): bigint documentation;
- [string.md](string.md): string documentation.

View File

@@ -80,8 +80,6 @@ Each method that returns such type indicates whether the operation was successfu
by setting the `status` field and by providing a descriptive message on the `message`
field. If the operation was successful (that is, `status == BIGINT_OK`), you can either
move on with the rest of the program or read the returned value from the sum data type.
Of course, you can choose to ignore the return value (if you're brave enough :D) as
illustrated on the first part of the README.
The sum data type (i.e., the `value` union) defines four different variables. Each
of them has an unique scope as described below:

View File

@@ -72,5 +72,4 @@ typedef struct {
Each method that returns such type indicates whether the operation was successful or not by setting
the `status` field and by providing a descriptive message on the `message` field. If the operation was
successful (that is, `status == MAP_OK`), you can either move on with the rest of the program or read
the returned value from the sum data type. Of course, you can choose to ignore the return value (if you're brave enough :D) as illustrated
on the first part of the README.
the returned value from the sum data type.

85
docs/string.md Normal file
View File

@@ -0,0 +1,85 @@
# String Technical Details
In this document you can find a quick overview of the technical aspects (internal design, memory layout, etc.) of the `String` data structure.
`String` is an immutable, null-terminated string data type with partial UTF-8 support.
This means that methods return a new string instance rather than modifying the string in-place.
Internally, this data structure is represented by the following layout:
```c
typedef struct {
char *data;
size_t byte_size;
size_t byte_capacity;
size_t char_count;
} string_t;
```
where the `data` field represent the actual string, `byte_size`
indicates the actual size (in bytes), `byte_capacity` represents the total number
of allocated memory (in bytes) and `char_count` represents the number of symbols.
As mentioned earlier, this data type provides partial UTF-8 support. It is able
to recognize UTF-8 byte sequences as individual Unicode code points and has
full support for Unicode symbols such as emojis. However, it does not support
localization. In particular, it does not perform local-aware conversions. For instance, uppercase/lowercase transformations are limited to ASCII characters only. As a result, the German scharfes S (`ß`) is not convert to `SS`, the Spanish `Ñ` is not converted to `ñ` and the Italian `é` (and its variants) is not treated as a single symbol but rather as a base letter combined with an accent.
At the time being, `String` supports the following methods:
- `string_result_t string_new(c_str)`: create a new string;
- `string_result_t string_clone(str)`: clone an existing string;
- `string_result_t string_concat(x, y)`: concatenate two strings together;
- `string_result_t string_contains(haystack, needle)`: search whether the `haystack` string contains `needle`;
- `string_result_t string_slice(str, start, end)`: return a slice (a new string) from `str` between `start` and `end` indices (inclusive);
- `string_result_t string_eq(x, y, case_sensitive)`: check whether `x` and `y` are equal;
- `string_result_t string_get_at(str, position)`: get the UTF-8 symbol indexed by `position` from `str`;
- `string_result_t string_set_at(str, position, utf8_char)`: write a UTF-8 symbol into `str` at index `position`;
- `string_result_t string_to_lower(str)`: convert a string to lowercase;
- `string_result_t string_to_upper(str)`: convert a string to uppercase;
- `string_result_t string_reverse(str)`: reverse a string;
- `string_result_t string_trim(str)`: remove leading and trailing white space from a string;
- `string_result_t string_split(str, delim)`: split a string into an array of `string_t` by specifying a separator;
- `string_result_t string_destroy(str)`: remove a string from memory;
- `string_result_t string_split_destroy(split, count)`: remove an array of strings from memory;
- `size_t string_size(str)`: return string character count.
As you can see from the previous function signatures, most methods that operate on the `String`
data type return a custom type called `string_result_t` which is defined as follows:
```c
typedef enum {
STRING_OK = 0x0,
STRING_ERR_ALLOCATE,
STRING_ERR_INVALID,
STRING_ERR_INVALID_UTF8,
STRING_ERR_OVERFLOW
} string_status_t;
typedef struct {
string_status_t status;
uint8_t message[RESULT_MSG_SIZE];
union {
string_t *string;
char *symbol;
int64_t idx;
bool is_equ;
struct {
string_t **strings;
size_t count;
} split;
} value;
} string_result_t;
```
Each method that returns such type indicates whether the operation was successful or not
by setting the `status` field and by providing a descriptive message on the `message`
field. If the operation was successful (that is, `status == STRING_OK`) you can either
move on with the rest of your program or read the returned value from the sum data type.
The sum data type (i.e., the `value` union) defines five different variables.
Each of them has an unique scope as described below:
- `string`: result of `new`, `clone`, `slice`, `reverse` and `trim` functions;
- `symbol`: result of `get_at` function;
- `idx`: result of `contains` function;
- `is_eq`: result of `equ` function. It's true when two strings are equal, false otherwise;
- `split`: result of `split` function. It contains an array of `string_t` and its number of elements.

View File

@@ -65,8 +65,7 @@ typedef struct {
Each method that returns such type indicates whether the operation was successful or not
by setting the `status` field and by providing a descriptive message on the `message`
field. If the operation was successful (that is, `status == VECTOR_OK`), you can either
move on with the rest of the program or read the returned value from the sum data type. Of course, you can choose to
ignore the return value (if you're brave enough :D) as illustrated on the first part of the README.
move on with the rest of the program or read the returned value from the sum data type.
## Functional methods
`Vector` provides three functional methods called `map`, `filter` and `reduce` which allow the caller to apply a computation to the vector,

37
examples/Makefile Normal file
View File

@@ -0,0 +1,37 @@
CC = gcc
CFLAGS = -Wall -Wextra -Werror -pedantic-errors -fstack-protector-strong \
-fsanitize=address -fsanitize=undefined -fstack-clash-protection \
-Wwrite-strings -g -std=c99
SRC_DIR = ../src
OBJ_DIR = ../obj
TARGETS = vector_basic vector_sorting vector_functional map_basic bigint_operations string_basic string_advanced
.PHONY: all clean
all: $(TARGETS)
vector_basic: vector_basic.c $(OBJ_DIR)/vector.o
$(CC) $(CFLAGS) -o $@ $^
vector_sorting: vector_sorting.c $(OBJ_DIR)/vector.o
$(CC) $(CFLAGS) -o $@ $^
vector_functional: vector_functional.c $(OBJ_DIR)/vector.o
$(CC) $(CFLAGS) -o $@ $^
map_basic: map_basic.c $(OBJ_DIR)/map.o
$(CC) $(CFLAGS) -o $@ $^
bigint_operations: bigint_operations.c $(OBJ_DIR)/bigint.o $(OBJ_DIR)/vector.o
$(CC) $(CFLAGS) -o $@ $^
string_basic: string_basic.c $(OBJ_DIR)/string.o
$(CC) $(CFLAGS) -o $@ $^
string_advanced: string_advanced.c $(OBJ_DIR)/string.o
$(CC) $(CFLAGS) -o $@ $^
clean:
rm -f $(TARGETS)

View File

@@ -0,0 +1,118 @@
/*
* Bigint operations example.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../src/bigint.h"
int main(void) {
const char *x_origin = "8036732204560262312865077650774313136023641621894661847778962273940232785242208265819059749867858355";
const char *y_origin = "7078840479830524979114102683681365071561983635405714511439038016617918064981439736383067887133445937";
const size_t x_len = strlen(x_origin);
const size_t y_len = strlen(y_origin);
const size_t large_x_size = x_len * 100 + 1;
const size_t large_y_size = y_len * 100 + 1;
char *large_x = malloc(large_x_size);
char *large_y = malloc(large_y_size);
if (large_x == NULL || large_y == NULL) {
printf("Error while allocating memory for strings\n");
free(large_x);
free(large_y);
return 1;
}
large_x[0] = '\0';
large_y[0] = '\0';
// Concatenate 100 times
for (size_t idx = 0; idx < 100; idx++) {
strcat(large_x, x_origin);
strcat(large_y, y_origin);
}
// Create two big integers from previous strings
bigint_result_t x_res = bigint_from_string(large_x);
if (x_res.status != BIGINT_OK) {
printf("Error while creating big number: %s\n", x_res.message);
return 1;
}
bigint_result_t y_res = bigint_from_string(large_y);
if (x_res.status != BIGINT_OK) {
printf("Error while creating big number: %s\n", x_res.message);
return 1;
}
bigint_t *x = x_res.value.number;
bigint_t *y = y_res.value.number;
// Sum two big integers
bigint_result_t sum_res = bigint_add(x, y);
if (sum_res.status != BIGINT_OK) {
printf("Error while summing two big numbers: %s\n", sum_res.message);
return 1;
}
bigint_t *sum = sum_res.value.number;
// Print result
bigint_printf("Sum result = %B\n", sum);
// Subtract two big integers
bigint_result_t diff_res = bigint_sub(x, y);
if (diff_res.status != BIGINT_OK) {
printf("Error while subtracting two big numbers: %s\n", diff_res.message);
return 1;
}
bigint_t *diff = diff_res.value.number;
// Print result
bigint_printf("difference result = %B\n", diff);
// Multiply two big integers
bigint_result_t prod_res = bigint_prod(x, y);
if (prod_res.status != BIGINT_OK) {
printf("Error while multiplying two big numbers: %s\n", prod_res.message);
return 1;
}
bigint_t *prod = prod_res.value.number;
// Print result
bigint_printf("multiplication result = %B\n", prod);
bigint_t *a = bigint_from_string(large_x).value.number;
bigint_t *b = bigint_from_string(y_origin).value.number;
// Divide two big integers
bigint_result_t div_res = bigint_divmod(a, b);
if (div_res.status != BIGINT_OK) {
printf("Error while dividing two big numbers: %s\n", div_res.message);
return 1;
}
bigint_t *quotient = div_res.value.division.quotient;
bigint_t *remainder = div_res.value.division.remainder;
// Print result
bigint_printf(
"division result = %B\
\nmod result = %B\n",
quotient, remainder);
// Destroy big numbers and strings
bigint_destroy(x); bigint_destroy(y);
bigint_destroy(a); bigint_destroy(b);
bigint_destroy(sum); bigint_destroy(diff);
bigint_destroy(prod); bigint_destroy(quotient); bigint_destroy(remainder);
free(large_x); free(large_y);
return 0;
}

101
examples/map_basic.c Normal file
View File

@@ -0,0 +1,101 @@
/*
* Basic map operations example.
*/
#include <stdio.h>
#include <stdlib.h>
#include "../src/map.h"
int main(void) {
// Create a new map
map_result_t res = map_new();
if (res.status != MAP_OK) {
printf("Error while creating map: %s\n", res.message);
return 1;
}
map_t *map = res.value.map;
// Add some values
const int x = 0xB00B5;
const char *y = "Hello";
map_result_t add_res = map_add(map, "x", (void*)&x);
if (add_res.status != MAP_OK) {
printf("Error while adding elements: %s\n", add_res.message);
return 1;
}
add_res = map_add(map, "y", (void*)y);
if (add_res.status != MAP_OK) {
printf("Error while adding elements: %s\n", add_res.message);
return 1;
}
// Print size and capacity
printf("Map size (should be 2): %zu\n", map_size(map));
printf("Map capacity (should be > 2): %zu\n\n", map_capacity(map));
// Retrieve keys
map_result_t get_res = map_get(map, "x");
if (get_res.status != MAP_OK) {
printf("Cannot retrieve map element 'x': %s\n", get_res.message);
return 1;
} else {
const int *val = (const int*)get_res.value.element;
printf("Key 'x' contains (should be 'B00B5'): %X\n", *val);
}
get_res = map_get(map, "y");
if (get_res.status != MAP_OK) {
printf("Cannot retrieve map element 'y': %s\n", get_res.message);
return 1;
} else {
const char *val = (const char*)get_res.value.element;
printf("Key 'y' contains (should be 'Hello') : %s\n\n", val);
}
// Update key
const int new_x = 0xC0FFEE;
map_result_t up_res = map_add(map, "x", (void*)&new_x);
up_res = map_get(map, "x");
if (get_res.status != MAP_OK) {
printf("Cannot retrieve map element 'x': %s\n", get_res.message);
return 1;
} else {
const int *val = (const int*)up_res.value.element;
printf("Key 'x' (should be updated to 'C0FFEE'): %X\n\n", *val);
}
// Remove an element
map_result_t rm_res = map_remove(map, "y");
if (rm_res.status != MAP_OK) {
printf("Cannot remove map element 'y': %s\n", rm_res.message);
return 1;
} else {
printf("Map element 'y' removed (size should be 1): %zu\n\n", map_size(map));
}
// Clear the map
map_result_t clear_res = map_clear(map);
if (clear_res.status != MAP_OK) {
printf("Cannot clear map: %s\n", clear_res.message);
return 1;
} else {
printf("Map cleared (size should be 0): %zu\n", map_size(map));
}
printf("\n");
// Delete the map
map_result_t del_res = map_destroy(map);
if (del_res.status != MAP_OK) {
printf("Error while destroying the map: %s\n", del_res.message);
return 1;
}
return 0;
}

103
examples/string_advanced.c Normal file
View File

@@ -0,0 +1,103 @@
/*
* Advanced string manipulation example.
*/
#include <stdio.h>
#include <stdlib.h>
#include "../src/string.h"
int main(void) {
// Create a string for manipulation
string_result_t res = string_new("Hello, World! 😜");
if (res.status != STRING_OK) {
printf("Error: %s\n", res.message);
return 1;
}
string_t *str = res.value.string;
printf("Original string: \"%s\"\n\n", str->data);
// Uppercase string
string_result_t res_upper = string_to_upper(str);
if (res_upper.status != STRING_OK) {
printf("Error: %s\n", res_upper.message);
return 1;
}
printf("Uppercase: \"%s\"\n", res_upper.value.string->data);
string_destroy(res_upper.value.string);
// Lowercase string
string_result_t res_lower = string_to_lower(str);
if (res_lower.status != STRING_OK) {
printf("Error: %s\n", res_lower.message);
return 1;
}
printf("Lowercase: \"%s\"\n\n", res_lower.value.string->data);
string_destroy(res_lower.value.string);
// Reverse string
string_result_t res_rev = string_reverse(str);
if (res_rev.status != STRING_OK) {
printf("Error: %s\n", res_rev.message);
return 1;
}
printf("Reversed: \"%s\"\n\n", res_rev.value.string->data);
string_destroy(res_rev.value.string);
// Change first character of the string
string_result_t res_set = string_set_at(str, 0, "J");
if (res_set.status != STRING_OK) {
printf("Error: %s\n", res_set.message);
return 1;
}
printf("Updated string: \"%s\"\n\n", res_set.value.string->data);
string_destroy(res_set.value.string);
// Get character from string (the emoji)
string_result_t res_get = string_get_at(str, 14);
if (res_get.status != STRING_OK) {
printf("Error: %s\n", res_get.message);
return 1;
}
printf("Extracted symbol: \"%s\"\n", res_get.value.symbol);
free(res_get.value.symbol);
// Trim string
string_t *to_trim = string_new(" foo ").value.string;
string_result_t res_trim = string_trim(to_trim);
if (res_trim.status != STRING_OK) {
printf("Error: %s\n", res_trim.message);
return 1;
}
printf("Trimmed string: \"%s\"\n\n", res_trim.value.string->data);
string_destroy(to_trim);
string_destroy(res_trim.value.string);
// Split string
string_t *to_split = string_new("foo/bar/biz").value.string;
string_result_t res_split = string_split(to_split, "/");
if (res_split.status != STRING_OK) {
printf("Error: %s\n", res_split.message);
return 1;
}
const size_t count = res_split.value.split.count;
string_t **strings = res_split.value.split.strings;
printf("Original string: \"%s\"\nSplitted string: ", to_split->data);
for (size_t idx = 0; idx < count; idx++) {
printf("\"%s\" ", strings[idx]->data);
}
printf("\n");
string_split_destroy(strings, count);
string_destroy(to_split);
string_destroy(str);
return 0;
}

95
examples/string_basic.c Normal file
View File

@@ -0,0 +1,95 @@
/*
* Basic string operations example.
*/
#include <stdio.h>
#include <stdlib.h>
#include "../src/string.h"
int main(void) {
// Create a new string
string_result_t res = string_new("Hello, ");
if (res.status != STRING_OK) {
printf("Error: %s\n", res.message);
return 1;
}
string_t *str1 = res.value.string;
printf("Created string: \"%s\"\n", str1->data);
printf("Character count: %zu (%zu actual bytes)\n", string_size(str1), str1->byte_size);
string_result_t res_clone = string_clone(str1);
if (res_clone.status != STRING_OK) {
printf("Error: %s\n", res.message);
return 1;
}
string_t *cloned = res_clone.value.string;
printf("Cloned string: \"%s\"\n\n", cloned->data);
string_destroy(cloned);
// Concatenation of strings
string_result_t res_suffix = string_new("World! 😜");
if (res_suffix.status != STRING_OK) {
printf("Error: %s\n", res.message);
return 1;
}
string_t *suffix = res_suffix.value.string;
printf("Created another string: \"%s\"\n", suffix->data);
printf("Character count: %zu (%zu actual bytes)\n\n", string_size(suffix), suffix->byte_size);
string_result_t res_cat = string_concat(str1, suffix);
if (res_cat.status != STRING_OK) {
printf("Error: %s\n", res_cat.message);
return 1;
}
string_destroy(suffix);
string_t *concat_str = res_cat.value.string;
printf("Concatenation result: \"%s\"\n\n", concat_str->data);
// String contains
string_t *haystack = string_new("The quick brown fox jumps over the lazy dog.").value.string;
string_t *needle = string_new("brown fox").value.string;
string_result_t res_contains = string_contains(haystack, needle);
if (res_contains.status != STRING_OK) {
printf("Error: %s\n", res_contains.message);
return 1;
}
if (res_contains.value.idx != -1) {
printf("Substring found. Starting at index %ld\n\n", res_contains.value.idx);
}
string_destroy(haystack);
string_destroy(needle);
// String slicing
string_result_t res_slice = string_slice(concat_str, 7, 14);
if (res_slice.status != STRING_OK) {
printf("Error: %s\n", res_slice.message);
return 1;
}
printf("Slice of string: \"%s\"\n\n", res_slice.value.string->data);
string_destroy(res_slice.value.string);
// String equality
string_t *compare = string_new("hello, World! 😜").value.string;
string_result_t res_eq = string_eq(concat_str, compare, true);
if (res_eq.value.is_equ) {
printf("The two strings are equal\n\n");
} else {
printf("The two strings are not equal\n\n");
}
string_destroy(compare);
string_destroy(concat_str);
string_destroy(str1);
return 0;
}

78
examples/vector_basic.c Normal file
View File

@@ -0,0 +1,78 @@
/*
* Basic vector operations example.
*/
#include <stdio.h>
#include <stdlib.h>
#include "../src/vector.h"
int main(void) {
// Create a vector of 3 integers
vector_result_t res = vector_new(3, sizeof(int));
if (res.status != VECTOR_OK) {
printf("Error while creating vector: %s\n", res.message);
return 1;
}
vector_t *vector = res.value.vector;
// Push some values to trigger reallocation
for (int idx = 0; idx < 5; idx++) {
vector_result_t add_res = vector_push(vector, &idx);
if (add_res.status != VECTOR_OK) {
printf("Error while adding elements: %s\n", add_res.message);
return 1;
}
}
// Print vector size and capacity
printf("Vector size (should be 5): %zu\n", vector_size(vector));
printf("Vector capacity (should be > 5): %zu\n\n", vector_capacity(vector));
// Print the whole vector
size_t sz = vector_size(vector);
for (size_t idx = 0; idx < sz; idx++) {
vector_result_t get_res = vector_get(vector, idx);
if (get_res.status != VECTOR_OK) {
printf("Cannot retrieve vec[%zu]: %s\n", idx, get_res.message);
return 1;
} else {
int val = *(int *)get_res.value.element;
printf("vec[%zu] (should be '%zu') = %d\n", idx, idx, val);
}
}
// Set an element at index 2
int new_val = 0xBABE;
vector_result_t set_res = vector_set(vector, 2, &new_val);
if (set_res.status == VECTOR_OK) {
printf("vec[2] (should be updated to 'BABE'): %X\n\n", new_val);
}
// Pop last element
vector_result_t pop_res = vector_pop(vector);
if (pop_res.status == VECTOR_OK) {
int val = *(int *)pop_res.value.element;
printf("Popped value (should be 5) : %d\n\n", val);
}
// Clear vector
vector_result_t clear_res = vector_clear(vector);
if (clear_res.status != VECTOR_OK) {
printf("Cannot clear vector: %s\n", clear_res.message);
return 1;
} else {
printf("Vector cleared (size should be 0): %zu\n\n", vector_size(vector));
}
// Free vector
vector_result_t del_res = vector_destroy(vector);
if (del_res.status != VECTOR_OK) {
printf("Error while destroying the vector: %s\n", del_res.message);
return 1;
}
return 0;
}

View File

@@ -0,0 +1,118 @@
/*
* Vector functional operations example.
*/
#include <stdio.h>
#include <stdlib.h>
#include "../src/vector.h"
#define UNUSED(X) (void)(X)
static void square(void *element, void *env);
static int is_even(const void *element, void *env);
static void adder(void *accumulator, const void *element, void *env);
int main(void) {
// Create a vector
vector_result_t res = vector_new(1, sizeof(int));
if (res.status != VECTOR_OK) {
printf("Error while creating vector: %s\n", res.message);
return 1;
}
vector_t *vector = res.value.vector;
// Map vector elements
for (size_t idx = 1; idx <= 5; idx++) {
vector_result_t map_push_res = vector_push(vector, &idx);
if (map_push_res.status != VECTOR_OK) {
printf("Error while adding elements: %s\n", map_push_res.message);
return 1;
}
}
size_t sz = vector_size(vector);
// Square vector elements: [1, 2, 3, 4, 5] -> [1, 4, 9, 16, 25]
vector_result_t map_res = vector_map(vector, square, NULL);
if (map_res.status != VECTOR_OK) {
printf("Error while mapping vector: %s\n", map_res.message);
return 1;
}
printf("Squared vector: ");
for (size_t idx = 0; idx < sz; idx++) {
vector_result_t map_get_res = vector_get(vector, idx);
if (map_get_res.status != VECTOR_OK) {
printf("Cannot retrieve vec[%zu]: %s\n", idx, map_get_res.message);
return 1;
} else {
int *val = (int*)map_get_res.value.element;
printf("%d ", *val);
}
}
printf("\n");
// Filter vector elements: [1, 4, 9, 16, 25] -> [4, 16]
vector_result_t filter_res = vector_filter(vector, is_even, NULL);
if (filter_res.status != VECTOR_OK) {
printf("Error while filtering vector: %s\n", filter_res.message);
return 1;
}
sz = vector_size(vector);
printf("Filtered vector: ");
for (size_t idx = 0; idx < sz; idx++) {
vector_result_t map_get_res = vector_get(vector, idx);
if (map_get_res.status != VECTOR_OK) {
printf("Cannot retrieve vec[%zu]: %s\n", idx, map_get_res.message);
return 1;
} else {
int *val = (int*)map_get_res.value.element;
printf("%d ", *val);
}
}
printf("\n");
// Reduce vector elements: [4, 16] -> 20
int sum = 0;
vector_result_t reduce_res = vector_reduce(vector, &sum, adder, NULL);
if (reduce_res.status != VECTOR_OK) {
printf("Error while reducing vector: %s\n", reduce_res.message);
return 1;
}
printf("Sum of vector: %d\n", sum);
// Free vector
vector_result_t del_res = vector_destroy(vector);
if (del_res.status != VECTOR_OK) {
printf("Error while destroying the vector: %s\n", del_res.message);
return 1;
}
return 0;
}
void square(void *element, void *env) {
UNUSED(env);
int *value = (int*)element;
*value = (*value) * (*value);
}
int is_even(const void *element, void *env) {
UNUSED(env);
int value = *(int*)element;
return (value % 2) == 0;
}
void adder(void *accumulator, const void *element, void *env) {
UNUSED(env);
*(int*)accumulator += *(int*)element;
}

110
examples/vector_sorting.c Normal file
View File

@@ -0,0 +1,110 @@
/*
* Vector sorting example.
*/
#include <stdio.h>
#include <stdlib.h>
#include "../src/vector.h"
static vector_order_t cmp_int_asc(const void *x, const void *y);
static vector_order_t cmp_int_desc(const void *x, const void *y);
int main(void) {
// Create a vector
vector_result_t res = vector_new(1, sizeof(int));
if (res.status != VECTOR_OK) {
printf("Error while creating vector: %s\n", res.message);
return 1;
}
vector_t *vector = res.value.vector;
// Sort vector in ascending order
int values[] = {5, 10, -9, 3, 1, 0, 4};
for (size_t idx = 0; idx < 7; idx++) {
vector_result_t sort_push_res = vector_push(vector, &values[idx]);
if (sort_push_res.status != VECTOR_OK) {
printf("Error while adding elements: %s\n", sort_push_res.message);
return 1;
}
}
printf("Added new elements. Before sort: ");
size_t sz = vector_size(vector);
for (size_t idx = 0; idx < sz; idx++) {
vector_result_t sort_get_res = vector_get(vector, idx);
if (sort_get_res.status != VECTOR_OK) {
printf("Cannot retrieve vec[%zu]: %s\n", idx, sort_get_res.message);
return 1;
} else {
const int *val = (int*)sort_get_res.value.element;
printf("%d ", *val);
}
}
printf("\n");
vector_result_t sort_asc_res = vector_sort(vector, cmp_int_asc);
if (sort_asc_res.status != VECTOR_OK) {
printf("Cannot sort array: %s\n", sort_asc_res.message);
return 1;
}
printf("After sort in ascending order: ");
for (size_t idx = 0; idx < sz; idx++) {
vector_result_t sort_get_res = vector_get(vector, idx);
if (sort_get_res.status != VECTOR_OK) {
printf("Cannot retrieve vec[%zu]: %s\n", idx, sort_get_res.message);
return 1;
} else {
int *val = (int*)sort_get_res.value.element;
printf("%d ", *val);
}
}
printf("\n");
// Sort vector in descending order
vector_result_t sort_desc_res = vector_sort(vector, cmp_int_desc);
if (sort_desc_res.status != VECTOR_OK) {
printf("Cannot sort array: %s\n", sort_desc_res.message);
return 1;
}
printf("After sort in descending order: ");
for (size_t idx = 0; idx < sz; idx++) {
vector_result_t sort_get_res = vector_get(vector, idx);
if (sort_get_res.status != VECTOR_OK) {
printf("Cannot retrieve vec[%zu]: %s\n", idx, sort_get_res.message);
return 1;
} else {
int *val = (int*)sort_get_res.value.element;
printf("%d ", *val);
}
}
printf("\n\n");
// Free vector
vector_result_t del_res = vector_destroy(vector);
if (del_res.status != VECTOR_OK) {
printf("Error while destroying the vector: %s\n", del_res.message);
return 1;
}
return 0;
}
vector_order_t cmp_int_asc(const void *x, const void *y) {
int x_int = *(const int*)x;
int y_int = *(const int*)y;
if (x_int < y_int) return VECTOR_ORDER_LT;
if (x_int > y_int) return VECTOR_ORDER_GT;
return VECTOR_ORDER_EQ;
}
vector_order_t cmp_int_desc(const void *x, const void *y) {
return cmp_int_asc(y, x);
}

View File

@@ -910,7 +910,7 @@ string_result_t string_destroy(string_t *str) {
}
/**
* string_split_destory
* string_split_destroy
* @split: an array of pointers of String
* @count: the number of elements
*

526
usage.c
View File

@@ -1,526 +0,0 @@
/*
* Sample usage of the Datum library.
*
* This program is a complete example on how to use Datum
* with *verbose* error checking. For a more minimal usage, you may want to ignore
* return messages/codes and get straight to the actual result. See the early
* part of the README.md file for such example (use it at your own risk).
*
* Developed by Marco Cetica (c) 2025, <email@marcocetica.com>
*
*/
#define SEP(SIZE) do { \
for (size_t i = 0; i < SIZE; i++) { \
printf("="); \
}; \
puts("\n"); \
} while(0)
#define UNUSED(X) (void)(X)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "src/vector.h"
#include "src/map.h"
#include "src/bigint.h"
static int vector_usage(void);
static int map_usage(void);
static int bigint_usage(void);
static vector_order_t cmp_int_asc(const void *x, const void *y);
static vector_order_t cmp_int_desc(const void *x, const void *y);
static void square(void *element, void *env);
static int is_even(const void *element, void *env);
static void adder(void *accumulator, const void *element, void *env);
int main(void) {
int st;
st = vector_usage();
if (st) { return st; }
SEP(50);
st = map_usage();
if (st) { return st; }
SEP(50);
st = bigint_usage();
if (st) { return st; }
return 0;
}
vector_order_t cmp_int_asc(const void *x, const void *y) {
int x_int = *(const int*)x;
int y_int = *(const int*)y;
if (x_int < y_int) return VECTOR_ORDER_LT;
if (x_int > y_int) return VECTOR_ORDER_GT;
return VECTOR_ORDER_EQ;
}
vector_order_t cmp_int_desc(const void *x, const void *y) {
return cmp_int_asc(y, x);
}
void square(void *element, void *env) {
UNUSED(env);
int *value = (int*)element;
*value = (*value) * (*value);
}
int is_even(const void *element, void *env) {
UNUSED(env);
int value = *(int*)element;
return (value % 2) == 0;
}
void adder(void *accumulator, const void *element, void *env) {
UNUSED(env);
*(int*)accumulator += *(int*)element;
}
int vector_usage(void) {
// Create a vector of 3 integers
vector_result_t res = vector_new(3, sizeof(int));
if (res.status != VECTOR_OK) {
printf("Error while creating vector: %s\n", res.message);
return 1;
}
vector_t *vector = res.value.vector;
// Push some values to trigger reallocation
for (int idx = 0; idx < 5; idx++) {
vector_result_t add_res = vector_push(vector, &idx);
if (add_res.status != VECTOR_OK) {
printf("Error while adding elements: %s\n", add_res.message);
return 1;
}
}
// Print vector size and capacity
printf("Vector size (should be 5): %zu\n", vector_size(vector));
printf("Vector capacity (should be > 5): %zu\n\n", vector_capacity(vector));
// Print the whole vector
size_t sz = vector_size(vector);
for (size_t idx = 0; idx < sz; idx++) {
vector_result_t get_res = vector_get(vector, idx);
if (get_res.status != VECTOR_OK) {
printf("Cannot retrieve vec[%zu]: %s\n", idx, get_res.message);
return 1;
} else {
int val = *(int *)get_res.value.element;
printf("vec[%zu] (should be '%zu') = %d\n", idx, idx, val);
}
}
// Set an element at index 2
int new_val = 0xBABE;
vector_result_t set_res = vector_set(vector, 2, &new_val);
if (set_res.status == VECTOR_OK) {
printf("vec[2] (should be updated to 'BABE'): %X\n\n", new_val);
}
// Pop last element
vector_result_t pop_res = vector_pop(vector);
if (pop_res.status == VECTOR_OK) {
int val = *(int *)pop_res.value.element;
printf("Popped value (should be 5) : %d\n\n", val);
}
// Clear vector
vector_result_t clear_res = vector_clear(vector);
if (clear_res.status != VECTOR_OK) {
printf("Cannot clear vector: %s\n", clear_res.message);
return 1;
} else {
printf("Vector cleared (size should be 0): %zu\n\n", vector_size(vector));
}
// Sort vector in ascending order
int values[] = {5, 10, -9, 3, 1, 0, 4};
for (size_t idx = 0; idx < 7; idx++) {
vector_result_t sort_push_res = vector_push(vector, &values[idx]);
if (sort_push_res.status != VECTOR_OK) {
printf("Error while adding elements: %s\n", sort_push_res.message);
return 1;
}
}
printf("Added new elements. Before sort: ");
sz = vector_size(vector);
for (size_t idx = 0; idx < sz; idx++) {
vector_result_t sort_get_res = vector_get(vector, idx);
if (sort_get_res.status != VECTOR_OK) {
printf("Cannot retrieve vec[%zu]: %s\n", idx, sort_get_res.message);
return 1;
} else {
const int *val = (int*)sort_get_res.value.element;
printf("%d ", *val);
}
}
printf("\n");
vector_result_t sort_asc_res = vector_sort(vector, cmp_int_asc);
if (sort_asc_res.status != VECTOR_OK) {
printf("Cannot sort array: %s\n", sort_asc_res.message);
return 1;
}
printf("After sort in ascending order: ");
for (size_t idx = 0; idx < sz; idx++) {
vector_result_t sort_get_res = vector_get(vector, idx);
if (sort_get_res.status != VECTOR_OK) {
printf("Cannot retrieve vec[%zu]: %s\n", idx, sort_get_res.message);
return 1;
} else {
int *val = (int*)sort_get_res.value.element;
printf("%d ", *val);
}
}
printf("\n");
// Sort vector in descending order
vector_result_t sort_desc_res = vector_sort(vector, cmp_int_desc);
if (sort_desc_res.status != VECTOR_OK) {
printf("Cannot sort array: %s\n", sort_desc_res.message);
return 1;
}
printf("After sort in descending order: ");
for (size_t idx = 0; idx < sz; idx++) {
vector_result_t sort_get_res = vector_get(vector, idx);
if (sort_get_res.status != VECTOR_OK) {
printf("Cannot retrieve vec[%zu]: %s\n", idx, sort_get_res.message);
return 1;
} else {
int *val = (int*)sort_get_res.value.element;
printf("%d ", *val);
}
}
printf("\n\n");
vector_result_t map_clear_res = vector_clear(vector);
if (map_clear_res.status != VECTOR_OK) {
printf("Cannot clear vector: %s\n", map_clear_res.message);
return 1;
}
// Map vector elements
for (size_t idx = 1; idx <= 5; idx++) {
vector_result_t map_push_res = vector_push(vector, &idx);
if (map_push_res.status != VECTOR_OK) {
printf("Error while adding elements: %s\n", map_push_res.message);
return 1;
}
}
sz = vector_size(vector);
// Square vector elements: [1, 2, 3, 4, 5] -> [1, 4, 9, 16, 25]
vector_result_t map_res = vector_map(vector, square, NULL);
if (map_res.status != VECTOR_OK) {
printf("Error while mapping vector: %s\n", map_res.message);
return 1;
}
printf("Squared vector: ");
for (size_t idx = 0; idx < sz; idx++) {
vector_result_t map_get_res = vector_get(vector, idx);
if (map_get_res.status != VECTOR_OK) {
printf("Cannot retrieve vec[%zu]: %s\n", idx, map_get_res.message);
return 1;
} else {
int *val = (int*)map_get_res.value.element;
printf("%d ", *val);
}
}
printf("\n");
// Filter vector elements: [1, 4, 9, 16, 25] -> [4, 16]
vector_result_t filter_res = vector_filter(vector, is_even, NULL);
if (filter_res.status != VECTOR_OK) {
printf("Error while filtering vector: %s\n", filter_res.message);
return 1;
}
sz = vector_size(vector);
printf("Filtered vector: ");
for (size_t idx = 0; idx < sz; idx++) {
vector_result_t map_get_res = vector_get(vector, idx);
if (map_get_res.status != VECTOR_OK) {
printf("Cannot retrieve vec[%zu]: %s\n", idx, map_get_res.message);
return 1;
} else {
int *val = (int*)map_get_res.value.element;
printf("%d ", *val);
}
}
printf("\n");
// Reduce vector elements: [4, 16] -> 20
int sum = 0;
vector_result_t reduce_res = vector_reduce(vector, &sum, adder, NULL);
if (reduce_res.status != VECTOR_OK) {
printf("Error while reducing vector: %s\n", reduce_res.message);
return 1;
}
printf("Sum of vector: %d\n\n", sum);
// Free vector
vector_result_t del_res = vector_destroy(vector);
if (del_res.status != VECTOR_OK) {
printf("Error while destroying the vector: %s\n", del_res.message);
return 1;
}
return 0;
}
int map_usage(void) {
// Create a new map
map_result_t res = map_new();
if (res.status != MAP_OK) {
printf("Error while creating map: %s\n", res.message);
return 1;
}
map_t *map = res.value.map;
// Add some values
const int x = 0xB00B5;
const char *y = "Hello";
map_result_t add_res = map_add(map, "x", (void*)&x);
if (add_res.status != MAP_OK) {
printf("Error while adding elements: %s\n", add_res.message);
return 1;
}
add_res = map_add(map, "y", (void*)y);
if (add_res.status != MAP_OK) {
printf("Error while adding elements: %s\n", add_res.message);
return 1;
}
// Print size and capacity
printf("Map size (should be 2): %zu\n", map_size(map));
printf("Map capacity (should be > 2): %zu\n\n", map_capacity(map));
// Retrieve keys
map_result_t get_res = map_get(map, "x");
if (get_res.status != MAP_OK) {
printf("Cannot retrieve map element 'x': %s\n", get_res.message);
return 1;
} else {
const int *val = (const int*)get_res.value.element;
printf("Key 'x' contains (should be 'B00B5'): %X\n", *val);
}
get_res = map_get(map, "y");
if (get_res.status != MAP_OK) {
printf("Cannot retrieve map element 'y': %s\n", get_res.message);
return 1;
} else {
const char *val = (const char*)get_res.value.element;
printf("Key 'y' contains (should be 'Hello') : %s\n\n", val);
}
// Update key
const int new_x = 0xC0FFEE;
map_result_t up_res = map_add(map, "x", (void*)&new_x);
up_res = map_get(map, "x");
if (get_res.status != MAP_OK) {
printf("Cannot retrieve map element 'x': %s\n", get_res.message);
return 1;
} else {
const int *val = (const int*)up_res.value.element;
printf("Key 'x' (should be updated to 'C0FFEE'): %X\n\n", *val);
}
// Remove an element
map_result_t rm_res = map_remove(map, "y");
if (rm_res.status != MAP_OK) {
printf("Cannot remove map element 'y': %s\n", rm_res.message);
return 1;
} else {
printf("Map element 'y' removed (size should be 1): %zu\n\n", map_size(map));
}
// Clear the map
map_result_t clear_res = map_clear(map);
if (clear_res.status != MAP_OK) {
printf("Cannot clear map: %s\n", clear_res.message);
return 1;
} else {
printf("Map cleared (size should be 0): %zu\n", map_size(map));
}
printf("\n");
// Delete the map
map_result_t del_res = map_destroy(map);
if (del_res.status != MAP_OK) {
printf("Error while destroying the map: %s\n", del_res.message);
return 1;
}
return 0;
}
int bigint_usage(void) {
const char *x_origin = "8036732204560262312865077650774313136023641621894661847778962273940232785242208265819059749867858355";
const char *y_origin = "7078840479830524979114102683681365071561983635405714511439038016617918064981439736383067887133445937";
const size_t x_len = strlen(x_origin);
const size_t y_len = strlen(y_origin);
const size_t large_x_size = x_len * 100 + 1;
const size_t large_y_size = y_len * 100 + 1;
char *large_x = malloc(large_x_size);
char *large_y = malloc(large_y_size);
if (large_x == NULL || large_y == NULL) {
printf("Error while allocating memory for strings\n");
free(large_x);
free(large_y);
return 1;
}
large_x[0] = '\0';
large_y[0] = '\0';
// Concatenate 100 times
for (size_t idx = 0; idx < 100; idx++) {
strcat(large_x, x_origin);
strcat(large_y, y_origin);
}
// Create two big integers from previous strings
bigint_result_t x_res = bigint_from_string(large_x);
if (x_res.status != BIGINT_OK) {
printf("Error while creating big number: %s\n", x_res.message);
return 1;
}
bigint_result_t y_res = bigint_from_string(large_y);
if (x_res.status != BIGINT_OK) {
printf("Error while creating big number: %s\n", x_res.message);
return 1;
}
bigint_t *x = x_res.value.number;
bigint_t *y = y_res.value.number;
// Sum two big integers
bigint_result_t sum_res = bigint_add(x, y);
if (sum_res.status != BIGINT_OK) {
printf("Error while summing two big numbers: %s\n", sum_res.message);
return 1;
}
bigint_t *sum = sum_res.value.number;
// Print result
bigint_printf("Sum result = %B\n", sum);
// Subtract two big integers
bigint_result_t diff_res = bigint_sub(x, y);
if (diff_res.status != BIGINT_OK) {
printf("Error while subtracting two big numbers: %s\n", diff_res.message);
return 1;
}
bigint_t *diff = diff_res.value.number;
// Print result
bigint_printf("difference result = %B\n", diff);
// Multiply two big integers
bigint_result_t prod_res = bigint_prod(x, y);
if (prod_res.status != BIGINT_OK) {
printf("Error while multiplying two big numbers: %s\n", prod_res.message);
return 1;
}
bigint_t *prod = prod_res.value.number;
// Print result
bigint_printf("multiplication result = %B\n", prod);
bigint_t *a = bigint_from_string(large_x).value.number;
bigint_t *b = bigint_from_string(y_origin).value.number;
// Divide two big integers
bigint_result_t div_res = bigint_divmod(a, b);
if (div_res.status != BIGINT_OK) {
printf("Error while dividing two big numbers: %s\n", div_res.message);
return 1;
}
bigint_t *quotient = div_res.value.division.quotient;
bigint_t *remainder = div_res.value.division.remainder;
// Print result
bigint_printf(
"division result = %B\
\nmod result = %B\n",
quotient, remainder);
// Destroy big numbers and strings
bigint_destroy(x); bigint_destroy(y);
bigint_destroy(a); bigint_destroy(b);
bigint_destroy(sum); bigint_destroy(diff);
bigint_destroy(prod); bigint_destroy(quotient); bigint_destroy(remainder);
free(large_x); free(large_y);
return 0;
}