Compare commits

...

9 Commits

Author SHA1 Message Date
78b5ff5955 Merge pull request 'string_impl' (#2) from string_impl into master
All checks were successful
clang-build / clang-build (push) Successful in 30s
gcc-build / gcc-build (push) Successful in 21s
Reviewed-on: #2
2026-03-16 09:55:28 +01:00
de22c706af Added String benchmark function
All checks were successful
clang-build / clang-build (push) Successful in 30s
gcc-build / gcc-build (push) Successful in 21s
clang-build / clang-build (pull_request) Successful in 31s
gcc-build / gcc-build (pull_request) Successful in 20s
2026-03-16 09:53:07 +01:00
f8a77a6056 Added String type documentation
All checks were successful
clang-build / clang-build (push) Successful in 30s
gcc-build / gcc-build (push) Successful in 19s
2026-03-16 09:42:15 +01:00
6110fc963c Fixed a bug and added unit tests for String type
All checks were successful
clang-build / clang-build (push) Successful in 30s
gcc-build / gcc-build (push) Successful in 18s
2026-03-13 18:27:00 +01:00
1871035cd6 Added string_{trim,split,destroy,split_destroy}
All checks were successful
clang-build / clang-build (push) Successful in 28s
gcc-build / gcc-build (push) Successful in 19s
2026-03-13 17:36:37 +01:00
b4a704f1b4 Added `string_{get_at,set_at,to_lower,to_upper,reverse}
Some checks failed
clang-build / clang-build (push) Failing after 18s
gcc-build / gcc-build (push) Successful in 19s
2026-03-13 17:16:02 +01:00
086446039b Fixed a bug about string comparison
Some checks failed
clang-build / clang-build (push) Failing after 20s
gcc-build / gcc-build (push) Failing after 5s
2026-03-13 11:48:42 +01:00
55b14ee949 Added string_{new,clone,concat,contains,slice,eq}
Some checks failed
clang-build / clang-build (push) Failing after 24s
gcc-build / gcc-build (push) Failing after 4s
2026-03-13 11:39:50 +01:00
110ccae8b9 Added UTF-8 helper functions
All checks were successful
clang-build / clang-build (push) Successful in 1m25s
gcc-build / gcc-build (push) Successful in 18s
2026-03-13 10:43:36 +01:00
22 changed files with 2277 additions and 564 deletions

View File

@@ -16,7 +16,7 @@ jobs:
- name: Run unit tests
run: |
./test_vector && ./test_map && ./test_bigint
./test_vector && ./test_map && ./test_bigint && ./test_string
- name: Run benchmarks
run: |

View File

@@ -13,7 +13,7 @@ jobs:
- name: Run unit tests
run: |
./test_vector && ./test_map && ./test_bigint
./test_vector && ./test_map && ./test_bigint && ./test_string
- name: Run benchmarks
run: |

View File

@@ -13,23 +13,19 @@ 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
TEST_S_TARGET = test_string
BENCH_TARGET = benchmark_datum
LIB_OBJS = $(OBJ_DIR)/vector.o $(OBJ_DIR)/map.o $(OBJ_DIR)/bigint.o
PROG_OBJS = $(OBJ_DIR)/usage.o
LIB_OBJS = $(OBJ_DIR)/vector.o $(OBJ_DIR)/map.o $(OBJ_DIR)/bigint.o $(OBJ_DIR)/string.o
.PHONY: all clean
.PHONY: all clean examples
all: $(TARGET) $(TEST_V_TARGET) $(TEST_M_TARGET) $(TEST_B_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 $@ $^
@@ -39,10 +35,13 @@ $(TEST_M_TARGET): $(OBJ_DIR)/test_map.o $(OBJ_DIR)/map.o
$(TEST_B_TARGET): $(OBJ_DIR)/test_bigint.o $(OBJ_DIR)/bigint.o $(OBJ_DIR)/vector.o
$(CC) $(CFLAGS) -o $@ $^
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR)
$(CC) $(CFLAGS) -c -o $@ $<
$(TEST_S_TARGET): $(OBJ_DIR)/test_string.o $(OBJ_DIR)/string.o
$(CC) $(CFLAGS) -o $@ $^
$(OBJ_DIR)/usage.o: usage.c | $(OBJ_DIR)
examples: $(LIB_OBJS)
$(MAKE) -C examples
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR)
$(CC) $(CFLAGS) -c -o $@ $<
$(OBJ_DIR)/%.o: $(TESTS_SRC)/%.c | $(OBJ_DIR)
@@ -52,7 +51,7 @@ $(OBJ_DIR):
mkdir -p $(OBJ_DIR)
# Benchmark rules
$(BENCH_TARGET): $(BENCH_OBJ_DIR)/bench.o $(BENCH_OBJ_DIR)/vector.o $(BENCH_OBJ_DIR)/map.o $(BENCH_OBJ_DIR)/bigint.o
$(BENCH_TARGET): $(BENCH_OBJ_DIR)/bench.o $(BENCH_OBJ_DIR)/vector.o $(BENCH_OBJ_DIR)/map.o $(BENCH_OBJ_DIR)/bigint.o $(BENCH_OBJ_DIR)/string.o
$(CC) $(BENCH_FLAGS) -o $@ $^
$(BENCH_OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(BENCH_OBJ_DIR)
@@ -65,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) $(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).
@@ -162,14 +187,16 @@ $ ./test_bigint
```
## Benchmark
Under the [`benchmark/`](/benchmark/) folder, you can find a simple benchmark program that stress the `Vector` and the `Map` data structures. You can run it by issuing the following command:
Under the [`benchmark/`](/benchmark/) folder, you can find a very simple benchmark program that stress the data structures.
You can run it by issuing the following command:
```sh
$ make clean all CC=clang
$ ./benchmark_datum
omputing Vector average time...average time: 8 ms
Computing Map average time...average time: 53 ms
Computing BigInt average time...average time: 76 ms
Computing Vector average time...average time: 6 ms
Computing Map average time...average time: 49 ms
Computing BigInt average time...average time: 67 ms
Computing String average time...average time: 13 ms
```

View File

@@ -7,6 +7,7 @@
#include "../src/vector.h"
#include "../src/map.h"
#include "../src/bigint.h"
#include "../src/string.h"
typedef void (*test_fn_t)(size_t iterations);
@@ -116,6 +117,31 @@ void test_bigint(size_t iterations) {
}
}
void test_string(size_t iterations) {
volatile size_t total_len = 0;
for (size_t idx = 0; idx < iterations; idx++) {
string_t *str1 = string_new("hello").value.string;
string_t *str2 = string_new(" World").value.string;
string_result_t concat = string_concat(str1, str2);
string_result_t upper = string_to_upper(concat.value.string);
total_len += string_size(upper.value.string);
string_result_t needle = string_new("WORLD");
string_result_t contains = string_contains(upper.value.string, needle.value.string);
if (contains.value.idx >= 0) {
total_len += contains.value.idx;
}
string_destroy(str1);
string_destroy(str2);
string_destroy(concat.value.string);
string_destroy(upper.value.string);
string_destroy(needle.value.string);
}
}
static inline uint64_t now_ns(void) {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
@@ -155,5 +181,9 @@ int main(void) {
fflush(stdout);
printf("average time: %lld ms\n", benchmark(test_bigint, 1e5, 30));
printf("Computing String average time...");
fflush(stdout);
printf("average time: %lld ms\n", benchmark(test_string, 1e5, 30));
return 0;
}

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);
}

941
src/string.c Normal file
View File

@@ -0,0 +1,941 @@
#define SET_MSG(result, msg) \
do { \
snprintf((char *)(result).message, RESULT_MSG_SIZE, "%s", (const char *)msg); \
} while (0)
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "string.h"
// Check if a character is a space
static inline bool is_space(unsigned char c) {
return (c == ' ' || c == '\t' ||
c == '\n' || c == '\r' ||
c == '\f' || c == '\v');
}
// Get byte length of an UTF-8 sequence
static inline int utf8_char_len(unsigned char byte) {
if ((byte & 0x80) == 0x00) return 1;
if ((byte & 0xE0) == 0xC0) return 2;
if ((byte & 0xF0) == 0xE0) return 3;
if ((byte & 0xF8) == 0xF0) return 4;
return -1;
}
// Validate an UTF-8 symbol
static bool utf8_is_char_valid(const char *utf8_char, int *out_len) {
if (utf8_char == NULL) {
return false;
}
const size_t len = utf8_char_len((unsigned char)utf8_char[0]);
if (len <= 0) {
return false;
}
for (size_t idx = 1; idx < len; idx++) {
if ((utf8_char[idx] & 0xC0) != 0x80) {
return false;
}
}
if (utf8_char[len] != '\0') {
return false;
}
if (out_len) {
*out_len = len;
}
return true;
}
// Validate an UTF-8 symbol and measure byte length and character count
static bool utf8_scan(const char *str, size_t *out_byte_size, size_t *out_char_count) {
size_t b_size = 0;
size_t c_count = 0;
const unsigned char *p = (const unsigned char *)str;
while (p[b_size] != '\0') {
size_t len = utf8_char_len(p[b_size]);
if (len <= 0) {
return false;
}
for (size_t idx = 1; idx < len; idx++) {
if (p[b_size + idx] == '\0' || (p[b_size + idx] & 0xC0) != 0x80) {
return false;
}
}
b_size += len;
c_count++;
}
*out_byte_size = b_size;
*out_char_count = c_count;
return true;
}
// Decode an UTF-8 symbol to a codepoint
static uint32_t utf8_decode(const char *str, int *char_len) {
unsigned char byte = (unsigned char)*str;
*char_len = utf8_char_len(byte);
uint32_t result = 0;
switch (*char_len) {
case 1:
result = byte;
break;
case 2:
result = ((byte & 0x1F) << 6) |
(str[1] & 0x3F);
break;
case 3:
result = ((byte & 0x0F) << 12) |
((str[1] & 0x3F) << 6) |
(str[2] & 0x3F);
break;
case 4:
result = ((byte & 0x07) << 18) |
((str[1] & 0x3F) << 12) |
((str[2] & 0x3F) << 6) |
(str[3] & 0x3F);
break;
default:
result = 0;
break;
}
return result;
}
// Encode a codepoint to an UTF-8 symbol
static int utf8_encode(uint32_t codepoint, char *out) {
if (codepoint <= 0x7F) {
out[0] = (char)codepoint;
return 1;
}
if (codepoint <= 0x7FF) {
out[0] = (char)(0xC0 | (codepoint >> 6));
out[1] = (char)(0x80 | (codepoint & 0x3F));
return 2;
}
if (codepoint <= 0xFFFF) {
out[0] = (char)(0xE0 | (codepoint >> 12));
out[1] = (char)(0x80 | ((codepoint >> 6) & 0x3F));
out[2] = (char)(0x80 | (codepoint & 0x3F));
return 3;
}
if (codepoint <= 0x10FFFF) {
out[0] = (char)(0xF0 | (codepoint >> 18));
out[1] = (char)(0x80 | ((codepoint >> 12) & 0x3F));
out[2] = (char)(0x80 | ((codepoint >> 6) & 0x3F));
out[3] = (char)(0x80 | (codepoint & 0x3F));
return 4;
}
return 0;
}
/**
* string_new
* @c_str: a C-string
*
* Returns a string_result_t containing a new String data type
*/
string_result_t string_new(const char *c_str) {
string_result_t result = {0};
if (c_str == NULL) {
result.status = STRING_ERR_INVALID;
SET_MSG(result, "Invalid input string");
return result;
}
size_t b_size, c_count;
if (utf8_scan(c_str, &b_size, &c_count) == 0) {
result.status = STRING_ERR_INVALID_UTF8;
SET_MSG(result, "Malformed UTF-8 sequence");
return result;
}
string_t *str = malloc(sizeof(string_t));
if (str == NULL) {
result.status = STRING_ERR_ALLOCATE;
SET_MSG(result, "Cannot allocate memory");
return result;
}
str->data = malloc(b_size + 1);
if (str->data == NULL) {
free(str);
result.status = STRING_ERR_ALLOCATE;
SET_MSG(result, "Cannot allocate memory");
return result;
}
memcpy(str->data, c_str, b_size);
str->data[b_size] = '\0';
str->byte_size = b_size;
str->byte_capacity = b_size + 1;
str->char_count = c_count;
result.status = STRING_OK;
result.value.string = str;
SET_MSG(result, "String successfully created");
return result;
}
/**
* string_clone
* @str: a non-null string
*
* Deep copies @str
*
* Returns a string_result_t containing the copied string
*/
string_result_t string_clone(const string_t *str) {
string_result_t result = {0};
if (str == NULL) {
result.status = STRING_ERR_INVALID;
SET_MSG(result, "Invalid string");
return result;
}
string_t *str_copy = malloc(sizeof(string_t));
if (str_copy == NULL) {
result.status = STRING_ERR_ALLOCATE;
SET_MSG(result, "Cannot allocate memory");
return result;
}
str_copy->data = malloc(str->byte_size + 1);
if (str_copy->data == NULL) {
free(str_copy);
result.status = STRING_ERR_ALLOCATE;
SET_MSG(result, "Cannot allocate memory");
return result;
}
memcpy(str_copy->data, str->data, str->byte_size);
str_copy->data[str->byte_size] = '\0';
str_copy->byte_size = str->byte_size;
str_copy->byte_capacity = str->byte_size + 1;
str_copy->char_count = str->char_count;
result.status = STRING_OK;
result.value.string = str_copy;
SET_MSG(result, "String successfully copied");
return result;
}
/**
* string_concat
* @x: a non-null string
* @y: a non-null string
*
* Concats @x and @y in a new String
*
* Returns a string_result_t containing the new string
*/
string_result_t string_concat(const string_t *x, const string_t *y) {
string_result_t result = {0};
if (x == NULL || y == NULL) {
result.status = STRING_ERR_INVALID;
SET_MSG(result, "Invalid strings");
return result;
}
if (x->byte_size > SIZE_MAX - y->byte_size - 1) {
result.status = STRING_ERR_OVERFLOW;
SET_MSG(result, "Concatenation exceeds size limits");
return result;
}
size_t new_size = x->byte_size + y->byte_size;
char *buf = malloc(new_size + 1);
if (buf == NULL) {
result.status = STRING_ERR_ALLOCATE;
SET_MSG(result, "Cannot allocate memory");
return result;
}
memcpy(buf, x->data, x->byte_size);
memcpy(buf + x->byte_size, y->data, y->byte_size);
buf[new_size] = '\0';
result = string_new(buf);
free(buf);
SET_MSG(result, "String successfully concatenated");
return result;
}
/**
* string_contains
* @haystack: a non-null string
* @needle: a non-null string
*
* Finds @needle on @haystack
*
* Returns a string_result_t containing the index to the beginning of the located string
* (if the substring has been found)
*/
string_result_t string_contains(const string_t *haystack, const string_t *needle) {
string_result_t result = {
.status = STRING_OK,
.value.idx = -1
};
if (haystack == NULL || needle == NULL || needle->byte_size == 0) {
result.status = STRING_ERR_INVALID;
SET_MSG(result, "Invalid substrings");
return result;
}
const char *found = strstr(haystack->data, needle->data);
if (found) {
size_t char_idx = 0;
const char *ptr = haystack->data;
while (ptr < found) {
ptr += utf8_char_len((unsigned char)*ptr);
char_idx++;
}
result.value.idx = (int64_t)char_idx;
SET_MSG(result, "Substring found");
} else {
SET_MSG(result, "Substring not found");
}
return result;
}
/**
* string_slice
* @str: a non-null string
* @start: the lower bound (inclusive)
* @end: the upper bound (inclusive)
*
* Extracts a slice from @str between @start and @end (inclusive)
*
* Returns a string_result_t data type containing the slice
*/
string_result_t string_slice(const string_t *str, size_t start, size_t end) {
string_result_t result = {0};
if (str == NULL) {
result.status = STRING_ERR_INVALID;
SET_MSG(result, "Invalid string");
return result;
}
if (start > end || end >= str->char_count) {
result.status = STRING_ERR_OVERFLOW;
SET_MSG(result, "Index out of bounds");
return result;
}
size_t start_byte_offset = 0;
for (size_t idx = 0; idx < start; idx++) {
start_byte_offset += utf8_char_len((unsigned char)str->data[start_byte_offset]);
}
size_t end_byte_offset = start_byte_offset;
for (size_t idx = start; idx <= end; idx++) {
end_byte_offset += utf8_char_len((unsigned char)str->data[end_byte_offset]);
}
const size_t slice_byte_size = (end_byte_offset - start_byte_offset);
string_t *slice = malloc(sizeof(string_t));
if (slice == NULL) {
result.status = STRING_ERR_ALLOCATE;
SET_MSG(result, "Cannot allocate memory");
return result;
}
slice->data = malloc(slice_byte_size + 1);
if (slice->data == NULL) {
free(slice);
result.status = STRING_ERR_ALLOCATE;
SET_MSG(result, "Cannot allocate memory");
return result;
}
memcpy(slice->data, str->data + start_byte_offset, slice_byte_size);
slice->data[slice_byte_size] = '\0';
slice->byte_size = slice_byte_size;
slice->byte_capacity = slice_byte_size + 1;
slice->char_count = end - start + 1;
result.status = STRING_OK;
result.value.string = slice;
SET_MSG(result, "String sliced successfully");
return result;
}
/**
* string_eq
* @x: a non-null string
* @y: a non-null string
* @case_sensitive: boolean value for case sensitive comparison
*
* Compares two Strings
*
* Returns a string_result_t containing the comparison result
*/
string_result_t string_eq(const string_t *x, const string_t *y, bool case_sensitive) {
string_result_t result = {
.status = STRING_OK,
.value.is_equ = false
};
if (x == NULL || y == NULL) {
result.status = STRING_ERR_INVALID;
SET_MSG(result, "Invalid strings");
return result;
}
if (x->char_count != y->char_count) {
result.value.is_equ = false;
result.status = STRING_OK;
SET_MSG(result, "Comparison completed successfully");
return result;
}
if (case_sensitive) {
result.value.is_equ = (strcmp(x->data, y->data) == 0);
} else {
const char *p1 = x->data, *p2 = y->data;
while (*p1 && *p2) {
int l1, l2;
const uint32_t codepoint1 = utf8_decode(p1, &l1);
const uint32_t codepoint2 = utf8_decode(p2, &l2);
const uint32_t c1 = (codepoint1 >= 'A' && codepoint1 <= 'Z') ? codepoint1 + 32 : codepoint1;
const uint32_t c2 = (codepoint2 >= 'A' && codepoint2 <= 'Z') ? codepoint2 + 32 : codepoint2;
if (c1 != c2) {
result.value.is_equ = false;
return result;
}
p1 += l1;
p2 += l2;
}
result.value.is_equ = (*p1 == *p2);
}
SET_MSG(result, "Comparison completed successfully");
return result;
}
/**
* string_get_at
* @str: a non-null string
* @position: the position of the symbol to read
*
* Gets symbol indexed by @position from @str
*
* Returns a string_result_t containing the symbol as a C string
*/
string_result_t string_get_at(const string_t *str, size_t position) {
string_result_t result = {0};
if (str == NULL) {
result.status = STRING_ERR_INVALID;
SET_MSG(result, "Invalid string");
return result;
}
if (position >= str->char_count) {
result.status = STRING_ERR_OVERFLOW;
SET_MSG(result, "Index out of bounds");
return result;
}
const char *ptr = str->data;
for (size_t idx = 0; idx < position; idx++) {
ptr += utf8_char_len((unsigned char)*ptr);
}
int char_len = utf8_char_len((unsigned char)*ptr);
char *utf8_char = malloc(char_len + 1);
if (utf8_char == NULL) {
result.status = STRING_ERR_ALLOCATE;
SET_MSG(result, "Cannot allocate memory");
return result;
}
memcpy(utf8_char, ptr, char_len);
utf8_char[char_len] = '\0';
result.value.symbol = utf8_char;
result.status = STRING_OK;
SET_MSG(result, "Symbol successfully retrieved");
return result;
}
/**
* string_set_at
* @str: a non-null string
* @position: the position to write into
* @utf8_char: an UTF8 symbol
*
* Writes @utf8_char into @str at index @position
*
* Returns a string_result_t data type
*/
string_result_t string_set_at(const string_t *str, size_t position, const char *utf8_char) {
string_result_t result = {0};
if (str == NULL) {
result.status = STRING_ERR_INVALID;
SET_MSG(result, "Invalid string");
return result;
}
int new_char_bytes;
if (utf8_is_char_valid(utf8_char, &new_char_bytes) == 0) {
result.status = STRING_ERR_INVALID_UTF8;
SET_MSG(result, "Invalid UTF-8 character");
return result;
}
if (position >= str->char_count) {
result.status = STRING_ERR_OVERFLOW;
SET_MSG(result, "Index out of bounds");
return result;
}
// Locate the byte offset of the character to replace
const char *pos = str->data;
for (size_t idx = 0; idx < position; idx++) {
pos += utf8_char_len((unsigned char)*pos);
}
const size_t prefix_len = pos - str->data;
const int old_char_bytes = utf8_char_len((unsigned char)*pos);
const size_t suffix_len = str->byte_size - prefix_len - old_char_bytes;
const size_t new_total_bytes = prefix_len + new_char_bytes + suffix_len;
string_t *new_str = malloc(sizeof(string_t));
if (new_str == NULL) {
result.status = STRING_ERR_ALLOCATE;
SET_MSG(result, "Cannot allocate memory");
return result;
}
new_str->data = malloc(new_total_bytes + 1);
if (new_str->data == NULL) {
free(new_str);
result.status = STRING_ERR_ALLOCATE;
SET_MSG(result, "Cannot allocate memory");
return result;
}
// Copy prefix data from original string
memcpy(new_str->data, str->data, prefix_len);
// Copy the new character at requested index
memcpy(new_str->data + prefix_len, utf8_char, new_char_bytes);
// Copy suffix data from the original string by skipping the overwritten character
memcpy(new_str->data + prefix_len + new_char_bytes, pos + old_char_bytes, suffix_len);
new_str->data[new_total_bytes] = '\0';
new_str->byte_size = new_total_bytes;
new_str->byte_capacity = new_total_bytes + 1;
new_str->char_count = str->char_count;
result.status = STRING_OK;
result.value.string = new_str;
SET_MSG(result, "Symbol successfully set");
return result;
}
/**
* string_to_lower
* @str: a non-null string
*
* Converts a String to lowercase
*
* Returns a string_result_t containing a new string
*/
string_result_t string_to_lower(const string_t *str) {
string_result_t result = {0};
if (str == NULL) {
result.status = STRING_ERR_INVALID;
SET_MSG(result, "Invalid string");
return result;
}
char *buf = malloc(str->byte_capacity);
if (buf == NULL) {
result.status = STRING_ERR_ALLOCATE;
SET_MSG(result, "Cannot allocate memory");
return result;
}
const char *src = str->data;
char *dst = buf;
while (*src) {
int len;
uint32_t codepoint = utf8_decode(src, &len);
uint32_t lower = (codepoint >= 'A' && codepoint <= 'Z') ? codepoint + 32 : codepoint;
dst += utf8_encode(lower, dst);
src += len;
}
*dst = '\0';
result = string_new(buf);
free(buf);
SET_MSG(result, "String successfully converted to lowercase");
return result;
}
/**
* string_to_upper
* @str: a non-null string
*
* Converts a String to uppercase
*
* Returns a string_result_t containing a new string
*/
string_result_t string_to_upper(const string_t *str) {
string_result_t result = {0};
if (str == NULL) {
result.status = STRING_ERR_INVALID;
SET_MSG(result, "Invalid string");
return result;
}
char *buf = malloc(str->byte_capacity);
if (buf == NULL) {
result.status = STRING_ERR_ALLOCATE;
SET_MSG(result, "Cannot allocate memory");
return result;
}
const char *src = str->data;
char *dst = buf;
while (*src) {
int len;
uint32_t codepoint = utf8_decode(src, &len);
uint32_t upper = (codepoint >= 'a' && codepoint <= 'z') ? codepoint - 32 : codepoint;
dst += utf8_encode(upper, dst);
src += len;
}
*dst = '\0';
result = string_new(buf);
free(buf);
SET_MSG(result, "String successfully converted to uppercase");
return result;
}
/**
* string_reverse
* @str: a non-null string
*
* Reverses @str
*
* Returns a new string_result_t containing the reversed string
*/
string_result_t string_reverse(const string_t *str) {
string_result_t result = {0};
if (str == NULL) {
result.status = STRING_ERR_INVALID;
SET_MSG(result, "Invalid string");
return result;
}
char *buf = malloc(str->byte_capacity);
if (buf == NULL) {
result.status = STRING_ERR_ALLOCATE;
SET_MSG(result, "Cannot allocate memory");
return result;
}
const char **pos = malloc(str->char_count * sizeof(char *));
if (pos == NULL) {
free(buf);
result.status = STRING_ERR_ALLOCATE;
SET_MSG(result, "Cannot allocate memory");
return result;
}
const char *ptr = str->data;
for (size_t idx = 0; idx < str->char_count; idx++) {
pos[idx] = ptr;
ptr += utf8_char_len((unsigned char)*ptr);
}
char *dst = buf;
for (int64_t idx = (int64_t)str->char_count - 1; idx >= 0; idx--) {
int len = utf8_char_len((unsigned char)*pos[idx]);
memcpy(dst, pos[idx], len);
dst += len;
}
*dst = '\0';
free(pos);
result = string_new(buf);
free(buf);
SET_MSG(result, "String successfully reversed");
return result;
}
/**
* string_trim
* @str: a non-null string
*
* Trims whitespace from @str
*
* Returns a string_result_t containing the trimmed string
*/
string_result_t string_trim(const string_t *str) {
string_result_t result = {0};
if (str == NULL) {
result.status = STRING_ERR_INVALID;
SET_MSG(result, "Invalid string");
return result;
}
const char *start = str->data;
while (*start && is_space((unsigned char)*start)) {
start++;
}
if (*start == '\0') {
return string_new("");
}
const char *end = str->data + str->byte_size - 1;
while (end > start && is_space((unsigned char)*end)) {
end--;
}
const size_t len = (end - start) + 1;
char *trimmed = malloc(len + 1);
if (trimmed == NULL) {
result.status = STRING_ERR_ALLOCATE;
SET_MSG(result, "Cannot allocate memory");
return result;
}
memcpy(trimmed, start, len);
trimmed[len] = '\0';
result = string_new(trimmed);
free(trimmed);
result.status = STRING_OK;
SET_MSG(result, "String successfully trimmed");
return result;
}
/**
* string_split
* @str: a non-null string
* @delim: delimiter string
*
* Splits @str by @delim
*
* Returns a string_result_t containing an array of String pointers
*/
string_result_t string_split(const string_t *str, const char *delim) {
string_result_t result = {0};
string_result_t tmp_res = {0};
if (str == NULL || delim == NULL || delim[0] == '\0') {
result.status = STRING_ERR_INVALID;
SET_MSG(result, "Invalid strings");
return result;
}
const char *ptr = str->data;
const size_t delim_len = strlen(delim);
size_t count = 1;
while ((ptr = strstr(ptr, delim))) {
count++;
ptr += delim_len;
}
string_t **string_array = malloc(count * sizeof(string_t *));
if (string_array == NULL) {
result.status = STRING_ERR_ALLOCATE;
SET_MSG(result, "Cannot allocate memory");
return result;
}
const char *start = str->data;
size_t idx = 0;
while ((ptr = strstr(start, delim))) {
const size_t part_len = ptr - start;
char *tmp = malloc(part_len + 1);
if (tmp == NULL) {
result.status = STRING_ERR_ALLOCATE;
SET_MSG(result, "Cannot allocate memory");
goto cleanup;
}
memcpy(tmp, start, part_len);
tmp[part_len] = '\0';
tmp_res = string_new(tmp);
free(tmp);
if (tmp_res.status != STRING_OK) { result = tmp_res; goto cleanup; }
string_array[idx++] = tmp_res.value.string;
start = ptr + delim_len;
}
tmp_res = string_new(start);
if (tmp_res.status != STRING_OK) { result = tmp_res; goto cleanup; }
string_array[idx] = tmp_res.value.string;
result.status = STRING_OK;
result.value.split.strings = string_array;
result.value.split.count = count;
SET_MSG(result, "String successfully split");
return result;
cleanup:
for (size_t j = 0; j < idx; j++) { string_destroy(string_array[j]); }
free(string_array);
return result;
}
/**
* string_destroy
* @str: a non-null string
*
* Destroys @str
*
* Returns a string_result_t data type
*/
string_result_t string_destroy(string_t *str) {
string_result_t result = {0};
if (str == NULL) {
result.status = STRING_ERR_INVALID;
SET_MSG(result, "Invalid string");
return result;
}
free(str->data);
free(str);
result.status = STRING_OK;
SET_MSG(result, "String successfully deleted");
return result;
}
/**
* string_split_destroy
* @split: an array of pointers of String
* @count: the number of elements
*
* Destroys the @split array of Strings
*
* Returns a string_result_t data type
*/
string_result_t string_split_destroy(string_t **split, size_t count) {
string_result_t result = {0};
if (split == NULL) {
result.status = STRING_ERR_INVALID;
SET_MSG(result, "Invalid string");
return result;
}
for (size_t idx = 0; idx < count; idx++) {
string_destroy(split[idx]);
}
free(split);
result.status = STRING_OK;
SET_MSG(result, "Array of strings successfully deleted");
return result;
}

70
src/string.h Normal file
View File

@@ -0,0 +1,70 @@
#ifndef STRING_H
#define STRING_H
#define RESULT_MSG_SIZE 64
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
typedef enum {
STRING_OK = 0x0,
STRING_ERR_ALLOCATE,
STRING_ERR_INVALID,
STRING_ERR_INVALID_UTF8,
STRING_ERR_OVERFLOW
} string_status_t;
typedef struct {
char *data;
size_t byte_size; // Size in bytes minus the NULL terminator
size_t byte_capacity; // Total allocated memory
size_t char_count; // Number of symbols
} string_t;
typedef struct {
string_status_t status;
uint8_t message[RESULT_MSG_SIZE];
union {
string_t *string; // For new, clone, slice, reverse, trim
char *symbol; // For get_at
int64_t idx; // For contains
bool is_equ; // For comparison
struct { // For split
string_t **strings;
size_t count;
} split;
} value;
} string_result_t;
#ifdef __cplusplus
#extern "C" {
#endif
// Public APIs
string_result_t string_new(const char *c_str);
string_result_t string_clone(const string_t *str);
string_result_t string_concat(const string_t *x, const string_t *y);
string_result_t string_contains(const string_t *haystack, const string_t *needle);
string_result_t string_slice(const string_t *str, size_t start, size_t end);
string_result_t string_eq(const string_t *x, const string_t *y, bool case_sensitive);
string_result_t string_get_at(const string_t *str, size_t position);
string_result_t string_set_at(const string_t *str, size_t position, const char *utf8_char);
string_result_t string_to_lower(const string_t *str);
string_result_t string_to_upper(const string_t *str);
string_result_t string_reverse(const string_t *str);
string_result_t string_trim(const string_t *str);
string_result_t string_split(const string_t *str, const char *delim);
string_result_t string_destroy(string_t *str);
string_result_t string_split_destroy(string_t **split, size_t count);
// Inline methods
static inline size_t string_size(const string_t *str) {
return str ? str->char_count : 0;
}
#ifdef __cplusplus
}
#endif
#endif

329
tests/test_string.c Normal file
View File

@@ -0,0 +1,329 @@
/*
* Unit tests for String data type
*/
#define TEST(NAME) do { \
printf("Running test_%s...", #NAME); \
test_##NAME(); \
printf(" PASSED\n"); \
} while(0)
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include "../src/string.h"
// Test string creation
void test_string_new(void) {
string_result_t res = string_new("hello");
assert(res.status == STRING_OK);
assert(res.value.string != NULL);
assert(strcmp(res.value.string->data, "hello") == 0);
assert(string_size(res.value.string) == 5);
assert(res.value.string->byte_size == 5);
string_destroy(res.value.string);
}
// Test empty string
void test_string_new_empty(void) {
string_result_t res = string_new("");
assert(res.status == STRING_OK);
assert(string_size(res.value.string) == 0);
assert(res.value.string->byte_size == 0);
assert(res.value.string->data[0] == '\0');
string_destroy(res.value.string);
}
// Test cloning an existing string
void test_string_clone(void) {
string_t *original = string_new("Original").value.string;
string_result_t res = string_clone(original);
assert(res.status == STRING_OK);
assert(res.value.string != original); // Different memory address
assert(strcmp(res.value.string->data, original->data) == 0);
assert(res.value.string->byte_size == original->byte_size);
string_destroy(original);
string_destroy(res.value.string);
}
// Test string concatenation
void test_string_concat(void) {
string_t *str1 = string_new("Foo").value.string;
string_t *str2 = string_new(" Bar").value.string;
string_result_t res = string_concat(str1, str2);
assert(res.status == STRING_OK);
assert(strcmp(res.value.string->data, "Foo Bar") == 0);
assert(string_size(res.value.string) == 7);
string_destroy(str1);
string_destroy(str2);
string_destroy(res.value.string);
}
// Test if string contains substring
void test_string_contains(void) {
string_t *haystack = string_new("Hello 🌍 World").value.string;
string_t *needle_ascii = string_new("World").value.string;
string_t *needle_utf8 = string_new("🌍").value.string;
string_t *needle_none = string_new("not found").value.string;
// World starts at symbol 8
string_result_t res1 = string_contains(haystack, needle_ascii);
assert(res1.status == STRING_OK);
assert(res1.value.idx == 8);
// 🌍 is at position 6
string_result_t res2 = string_contains(haystack, needle_utf8);
assert(res2.status == STRING_OK);
assert(res2.value.idx == 6);
// Not found should return -1
string_result_t res3 = string_contains(haystack, needle_none);
assert(res3.status == STRING_OK);
assert(res3.value.idx == -1);
string_destroy(haystack);
string_destroy(needle_ascii);
string_destroy(needle_utf8);
string_destroy(needle_none);
}
// Test string slicing
void test_string_slice(void) {
// ASCII slice
string_t *str1 = string_new("foobar").value.string;
string_result_t res1 = string_slice(str1, 2, 4);
assert(res1.status == STRING_OK);
assert(strcmp(res1.value.string->data, "oba") == 0);
assert(res1.value.string->char_count == 3);
// UTF-8 slice
string_t *str2 = string_new("AB😀🌍").value.string;
string_result_t res2 = string_slice(str2, 2, 2);
assert(res2.status == STRING_OK);
assert(strcmp(res2.value.string->data, "😀") == 0);
assert(res2.value.string->byte_size == 4); // emoji = 4 bytes
// UTF-8 + ASCII slice
string_result_t res3 = string_slice(str2, 0, 2);
assert(res3.status == STRING_OK);
assert(strcmp(res3.value.string->data, "AB😀") == 0);
// Invalid bounds
string_result_t res4 = string_slice(str1, 5, 2);
assert(res4.status == STRING_ERR_OVERFLOW);
res4 = string_slice(str1, 1, 50);
assert(res4.status == STRING_ERR_OVERFLOW);
string_destroy(str1);
string_destroy(str2);
string_destroy(res1.value.string);
string_destroy(res2.value.string);
string_destroy(res3.value.string);
}
// Test case-insensitive and sensitive comparison
void test_string_eq(void) {
string_t *str1 = string_new("Foo").value.string;
string_t *str2 = string_new("foo").value.string;
// Case sensitive comparison should be false
assert(string_eq(str1, str2, true).value.is_equ == false);
// Case insensitive comparison should be true
assert(string_eq(str1, str2, false).value.is_equ == true);
string_destroy(str1);
string_destroy(str2);
}
// Test string reverse using UTF-8 symbols
void test_string_reverse_utf8(void) {
string_t *str = string_new("A🌍Z").value.string;
string_result_t res = string_reverse(str);
assert(res.status == STRING_OK);
assert(string_size(res.value.string) == 3);
assert(strcmp(res.value.string->data, "Z🌍A") == 0);
assert(string_size(res.value.string) == 3);
string_destroy(str);
string_destroy(res.value.string);
}
// Test string get_at
void test_string_get_at(void) {
string_t *str = string_new("AB😀🌍").value.string;
// 😀 is at index 2
string_result_t res1 = string_get_at(str, 2);
assert(res1.status == STRING_OK);
assert(strcmp((char*)res1.value.symbol, "😀") == 0);
free(res1.value.symbol);
// 🌍 is at index 3
string_result_t res2 = string_get_at(str, 3);
assert(res2.status == STRING_OK);
assert(strcmp((char*)res2.value.symbol, "🌍") == 0);
free(res2.value.symbol);
string_destroy(str);
}
// Test string get_at with invalid index
void test_string_get_at_overflow(void) {
string_t *str = string_new("ABC").value.string;
string_result_t res = string_get_at(str, 50);
assert(res.status == STRING_ERR_OVERFLOW);
string_destroy(str);
}
// Test mutation of UTF-8 symbol
void test_string_set_at(void) {
string_t *str = string_new("ABC").value.string;
// Replace 'B' with an emoji
string_result_t res = string_set_at(str, 1, "😀");
string_t *altered = res.value.string;
assert(res.status == STRING_OK);
assert(strcmp(altered->data, "A😀C") == 0);
assert(string_size(altered) == 3);
assert(altered->byte_size == 6); // that is: A (1B) + emoji (4B) + C (1B)
string_destroy(str);
string_destroy(altered);
}
// Test mutation of invalid UTF-8 symbol
void test_string_set_at_invalid_utf8(void) {
string_t *str = string_new("ABC").value.string;
const char * const invalid_sym1 = "\xFF";
const char * const invalid_sym2 = "\x80";
string_result_t res1 = string_set_at(str, 1, invalid_sym1);
assert(res1.status == STRING_ERR_INVALID_UTF8);
string_result_t res2 = string_set_at(str, 1, invalid_sym2);
assert(res2.status == STRING_ERR_INVALID_UTF8);
string_destroy(str);
}
// Test mutation with overflow
void test_string_set_at_overflow(void) {
string_t *str = string_new("ABC").value.string;
string_result_t res = string_set_at(str, 10, "a");
assert(res.status == STRING_ERR_OVERFLOW);
string_destroy(str);
}
// Test string to lowercase
void test_string_to_lower(void) {
string_t *str = string_new("AbC").value.string;
string_result_t res = string_to_lower(str);
assert(res.status == STRING_OK);
assert(strcmp(res.value.string->data, "abc") == 0);
string_destroy(str);
string_destroy(res.value.string);
}
// Test string to uppercase
void test_string_to_upper(void) {
string_t *str = string_new("aBc").value.string;
string_result_t res = string_to_upper(str);
assert(res.status == STRING_OK);
assert(strcmp(res.value.string->data, "ABC") == 0);
string_destroy(str);
string_destroy(res.value.string);
}
// Test whitespace trimming
void test_string_trim(void) {
string_t *str = string_new(" \t Foo Bar \n ").value.string;
string_result_t res = string_trim(str);
assert(res.status == STRING_OK);
assert(strcmp(res.value.string->data, "Foo Bar") == 0);
string_destroy(str);
string_destroy(res.value.string);
}
// Test string splitting into an array
void test_string_split(void) {
string_t *str = string_new("Red,Green,Blue").value.string;
string_result_t res = string_split(str, ",");
assert(res.status == STRING_OK);
assert(res.value.split.count == 3);
const size_t count = res.value.split.count;
string_t **strings = res.value.split.strings;
const char *expected[] = { "Red", "Green", "Blue" };
for (size_t idx = 0; idx < count; idx++) {
assert(strcmp(strings[idx]->data, expected[idx]) == 0);
}
string_split_destroy(strings, count);
string_destroy(str);
}
// Test string destroy
void test_string_destroy(void) {
string_t *str = string_new("delete me").value.string;
string_result_t res = string_destroy(str);
assert(res.status == STRING_OK);
string_result_t res_null = string_destroy(NULL);
assert(res_null.status == STRING_ERR_INVALID);
}
int main(void) {
printf("=== Running String unit tests ===\n\n");
TEST(string_new);
TEST(string_new_empty);
TEST(string_clone);
TEST(string_concat);
TEST(string_contains);
TEST(string_slice);
TEST(string_eq);
TEST(string_reverse_utf8);
TEST(string_get_at);
TEST(string_get_at_overflow);
TEST(string_set_at);
TEST(string_set_at_overflow);
TEST(string_set_at_invalid_utf8);
TEST(string_to_lower);
TEST(string_to_upper);
TEST(string_trim);
TEST(string_split);
TEST(string_destroy);
printf("\n=== All tests passed! ===\n");
return 0;
}

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;
}