First commit

This commit is contained in:
2026-08-09 16:29:53 +03:00
commit faa660125e
11 changed files with 599 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
build

111
include/DynArray.h Normal file
View File

@@ -0,0 +1,111 @@
#ifndef DYN_ARRAY_H_
#define DYN_ARRAY_H_
#include <stddef.h>
#include <string.h>
// #define SCUFCONT_BOUNDS_CHECK
#ifdef SCUFCONT_BOUNDS_CHECK
#include <stdio.h>
#include <stdlib.h>
#endif // SCUFCONT_BOUNDS_CHECK
#define DYN_ARRAY_EXPANDER(macro, type, type_name, func_name) macro(type, DynArray_##type_name, DynArray_##type_name##_##func_name);
#define DYN_ARRAY_IMPL(type, type_name) \
DYN_ARRAY_EXPANDER(DYN_ARRAY_ALLOC, type, type_name, alloc) \
DYN_ARRAY_EXPANDER(DYN_ARRAY_IS_NULL, type, type_name, is_null) \
DYN_ARRAY_EXPANDER(DYN_ARRAY_PUSH_BACK, type, type_name, push_back) \
DYN_ARRAY_EXPANDER(DYN_ARRAY_AT, type, type_name, at)
#define DYN_ARRAY_HEADER(type, type_name) \
DYN_ARRAY_STRUCT(type, type_name) \
DYN_ARRAY_SIGS(type, type_name)
#define DYN_ARRAY_SIGS(type, type_name) \
DYN_ARRAY_EXPANDER(DYN_ARRAY_ALLOC_SIG, type, type_name, alloc) \
DYN_ARRAY_EXPANDER(DYN_ARRAY_IS_NULL_SIG, type, type_name, is_null) \
DYN_ARRAY_EXPANDER(DYN_ARRAY_PUSH_BACK_SIG, type, type_name, push_back) \
DYN_ARRAY_EXPANDER(DYN_ARRAY_AT_SIG, type, type_name, at)
// DYN_ARRAY_ALLOC_SIG(type, DynArray_##type_name, DynArray_##type_name##_alloc); \
// DYN_ARRAY_PUSH_BACK_SIG(type, DynArray_##type_name, DynArray_##type_name##_push_back);
#define DYN_ARRAY_STRUCT(type, type_name) \
typedef struct { \
type* data; \
size_t len; \
size_t capacity; \
} DynArray_##type_name;
// DYN_ARRAY_STRUCT(int);
#define DYN_ARRAY_ALLOC_SIG(type, type_name, func_name) bool func_name(type_name* darr)
#define DYN_ARRAY_ALLOC(type, type_name, func_name) \
DYN_ARRAY_ALLOC_SIG(type, type_name, func_name) { \
darr->data = (type*) calloc(1, sizeof(type)); \
if (darr->data == NULL) { \
return false; \
} \
darr->len = 0; \
darr->capacity = 1; \
return true; \
}
#define DYN_ARRAY_IS_NULL_SIG(type, type_name, func_name) bool func_name(const type_name* darr)
#define DYN_ARRAY_IS_NULL(type, type_name, func_name) \
DYN_ARRAY_IS_NULL_SIG(type, type_name, func_name) { \
return darr->data == NULL; \
}
// DYN_ARRAY_ALLOC(int, DynArray_int, DynArray_int_alloc);
#define DYN_ARRAY_PUSH_BACK_SIG(type, type_name, func_name) int func_name(type_name* darr, const type* new_elem)
#define DYN_ARRAY_PUSH_BACK(type, type_name, func_name) \
DYN_ARRAY_PUSH_BACK_SIG(type, type_name, func_name) { \
if (darr->len + 1 > darr->capacity) { \
darr->capacity *= 2; \
darr->data = (type*) realloc(darr->data, darr->capacity * sizeof(type)); \
if (darr->data == NULL) { \
return -1; \
} \
} \
memcpy(darr->data + darr->len, new_elem, sizeof(type)); \
darr->len++; \
return 1; \
}
#define DYN_ARRAY_AT_SIG(type, type_name, func_name) type* func_name(type_name* darr, const size_t i)
// DYN_ARRAY_PUSH_BACK(int, DynArray_int, DynArray_int_push_back);
#ifdef SCUFCONT_BOUNDS_CHECK
#define DYN_ARRAY_AT(type, type_name, func_name) \
DYN_ARRAY_AT_SIG(type, type_name, func_name) { \
if (i < 0 || i > darr->len) { \
fprintf(stderr, #func_name " out-of-bounds. i: %zu, len: %zu", i, darr->len); \
exit(1); \
} \
\
return darr->data + i; \
}
#else
#define DYN_ARRAY_AT(type, type_name, func_name) \
DYN_ARRAY_AT_SIG(type, type_name, func_name) { \
return darr->data + i; \
}
#endif // SCUFCONT_BOUNDS_CHECK
// DYN_ARRAY_AT(char*, DynArray_charp, DynArray_charp_at)
#endif // DYN_ARRAY_H_

51
include/DynString.h Normal file
View File

@@ -0,0 +1,51 @@
#ifndef DYN_STRING_H_
#define DYN_STRING_H_
#include <stdbool.h>
#include <stddef.h>
#include "scufcont.h"
#define DYN_STRING_FMT(dstr) (int) (dstr).len, (dstr).data
#define DynString_is_null(dstr) ((dstr)->data == NULL)
#define DynString_c_str(dstr) ((dstr)->data)
typedef struct {
char* data;
size_t len;
size_t capacity;
} DynString;
ScufContResult DynString_alloc(DynString* dstr, const char* str, const size_t len);
ScufContResult DynString_reserve(DynString* dstr, const size_t new_capacity);
void DynString_free(DynString* dstr);
bool DynString_equal(const DynString* s1, const DynString* s2);
bool DynString_equal_c_str(const DynString* s1, const char* s2);
bool DynString_equal_next_chars_c_str(
const DynString* dstr,
const char* str,
const size_t str_len,
const size_t s
);
ScufContResult DynString_concat_c_str(
DynString* dstr,
const char* str,
const size_t str_len
);
ScufContResult DynString_alloc_concat_c_strs(
DynString* dstr,
const char* s1,
const size_t s1_len,
const char* s2,
const size_t s2_len
);
ScufContResult DynString_split(
const DynString* dstr,
const char* delim,
DynString** split_arr,
size_t* arr_len
);
void DynString_split_arr_free(DynString** arr, const size_t arr_len);
#endif // DYN_STRING_H_

11
include/scufcont.h Normal file
View File

@@ -0,0 +1,11 @@
#ifndef SCUFCONT_H_
#define SCUFCONT_H_
typedef enum {
SCUFCONT_SUCCESS,
SCUFCONT_OUT_OF_MEMORY
} ScufContResult;
extern const char* SCUFCONT_VERSION;
#endif // SCUFCONT_H_

37
meson.build Normal file
View File

@@ -0,0 +1,37 @@
project(
'scufcont',
'c',
version: '0.0.1',
default_options: {
'buildtype': 'release'
}
)
c_args = [
'-std=c99',
'-Wall',
'-Wextra',
'-pedantic'
]
cmocka_dep = dependency('cmocka')
scufcont_include = include_directories('include')
scufcont_lib = library(
'scufcont',
'src/DynString.c',
'src/scufcont.c',
include_directories: [
'include'
],
c_args: [
c_args,
'-D_SCUFCONT_VERSION="' + meson.project_version() + '"',
]
)
if get_option('SCUFCONT_BUILD_TESTS') == true
subdir('tests')
endif

1
meson_options.txt Normal file
View File

@@ -0,0 +1 @@
option('SCUFCONT_BUILD_TESTS', type: 'boolean', value: false)

198
src/DynString.c Normal file
View File

@@ -0,0 +1,198 @@
#include <errno.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include "scufcont.h"
#include "DynString.h"
ScufContResult DynString_alloc(DynString* dstr, const char* str, const size_t len) {
dstr->data = (char*) malloc(sizeof(char) * (len + 1));
dstr->len = len;
dstr->capacity = len + 1;
if (dstr->data == NULL) {
return SCUFCONT_OUT_OF_MEMORY;
}
dstr->data = memcpy(dstr->data, str, len);
dstr->data[len] = '\0';
return SCUFCONT_SUCCESS;
}
ScufContResult DynString_reserve(DynString* dstr, const size_t new_capacity) {
if (dstr->capacity < new_capacity) {
errno = 0;
dstr->data = (char*) realloc(dstr->data, sizeof(char) * new_capacity);
if (errno != 0) {
return SCUFCONT_OUT_OF_MEMORY;
}
dstr->capacity = new_capacity;
}
return SCUFCONT_SUCCESS;
}
void DynString_free(DynString* dstr) {
if (dstr->data != NULL) {
free(dstr->data);
dstr->data = NULL;
}
dstr->len = 0;
dstr->capacity = 0;
}
bool DynString_equal(const DynString* s1, const DynString* s2) {
if (s1->data == NULL || s2->data == NULL) {
return false;
}
if (s1->len != s2->len) {
return false;
}
for (size_t i = 0; i < s1->len; i++) {
if (s1->data[i] != s2->data[i]) {
return false;
}
}
return true;
}
bool DynString_equal_c_str(const DynString* s1, const char* s2) {
if (s1->data == NULL || s2 == NULL) {
return false;
}
if (s1->len != strlen(s2)) {
return false;
}
for (size_t i = 0; i < s1->len; i++) {
if (s1->data[i] != s2[i]) {
return false;
}
}
return true;
}
bool DynString_equal_next_chars_c_str(
const DynString* dstr,
const char* str,
const size_t str_len,
size_t s
) {
if (dstr->len < s || dstr->len < s + str_len) {
return false;
}
const size_t e = s + str_len;
for (size_t i = 0; s < e; s++, i++) {
if (dstr->data[s] != str[i]) {
return false;
}
}
return true;
}
ScufContResult DynString_concat_c_str(
DynString* dstr,
const char* str,
const size_t str_len
) {
const size_t new_len = dstr->len + str_len;
if (new_len > dstr->capacity) {
ScufContResult res = DynString_reserve(dstr, new_len + 1);
if (res != SCUFCONT_SUCCESS) {
return res;
}
}
strncpy(dstr->data + dstr->len, str, str_len);
dstr->data[new_len] = '\0';
dstr->len = new_len;
return SCUFCONT_SUCCESS;
}
ScufContResult DynString_alloc_concat_c_strs(
DynString* dstr,
const char* s1,
const size_t s1_len,
const char* s2,
const size_t s2_len
) {
ScufContResult res = DynString_alloc(dstr, s1, s1_len);
if (res != SCUFCONT_SUCCESS) {
return res;
}
res = DynString_concat_c_str(dstr, s2, s2_len);
if (res != SCUFCONT_SUCCESS) {
return res;
}
return SCUFCONT_SUCCESS;
}
ScufContResult DynString_split(
const DynString* dstr,
const char* delim,
DynString** split_arr,
size_t* arr_len
) {
ScufContResult ret = SCUFCONT_SUCCESS;
*arr_len = 0;
DynString elem;
ScufContResult res = DynString_alloc(&elem, "", 0);
if (res != SCUFCONT_SUCCESS) {
return res;
}
const size_t delim_len = strlen(delim);
for (size_t i = 0; i < dstr->len; i++) {
if (DynString_equal_next_chars_c_str(dstr, delim, delim_len, i)) {
*arr_len += 1;
*split_arr = realloc(*split_arr, sizeof(DynString) * (*arr_len));
if (split_arr == NULL) {
ret = SCUFCONT_OUT_OF_MEMORY;
break;
}
(*split_arr)[(*arr_len) - 1] = elem;
res = DynString_alloc(&elem, "", 0);
if (res != SCUFCONT_SUCCESS) {
ret = res;
break;
}
i += delim_len - 1;
} else {
DynString_concat_c_str(&elem, dstr->data + i, 1);
}
}
if (ret == SCUFCONT_SUCCESS) {
*split_arr = realloc(*split_arr, sizeof(DynString) * (*arr_len + 1));
if (split_arr != NULL) {
(*split_arr)[*arr_len] = elem;
*arr_len += 1;
return ret;
}
ret = SCUFCONT_OUT_OF_MEMORY;
}
for (size_t i = 0; i < *arr_len; i++) {
DynString_free((*split_arr) + i);
}
free(*split_arr);
*split_arr = NULL;
DynString_free(&elem);
return ret;
}
void DynString_split_arr_free(DynString** arr, const size_t arr_len) {
for (size_t i = 0; i < arr_len; i++) {
DynString_free((*arr) + i);
}
free(*arr);
*arr = NULL;
}

3
src/scufcont.c Normal file
View File

@@ -0,0 +1,3 @@
#include "scufcont.h"
const char* SCUFCONT_VERSION = _SCUFCONT_VERSION;

167
tests/cmocka/main.c Normal file
View File

@@ -0,0 +1,167 @@
#include <string.h>
#include <stdlib.h>
#include <cmocka.h>
#include "scufcont.h"
#include "DynString.h"
void test_DynString_alloc(void** state) {
(void) state;
const char* c_str = "Hello";
const size_t c_str_len = strlen(c_str);
DynString dstr;
assert_true(DynString_alloc(&dstr, c_str, c_str_len) == SCUFCONT_SUCCESS);
assert_memory_equal(dstr.data, c_str, c_str_len + 1);
assert_true(dstr.len == c_str_len);
assert_true(dstr.capacity == c_str_len + 1);
free(dstr.data);
}
void test_DynString_free(void** state) {
(void) state;
DynString dstr;
assert_true(DynString_alloc(&dstr, "Hello", strlen("Hello")) == SCUFCONT_SUCCESS);
DynString_free(&dstr);
assert_null(dstr.data);
assert_true(dstr.len == 0);
assert_true(dstr.capacity == 0);
}
void test_DynString_equal(void** state) {
(void) state;
DynString s1;
assert_true(DynString_alloc(&s1, "Hello", strlen("Hello")) == SCUFCONT_SUCCESS);
DynString s2;
assert_true(DynString_alloc(&s2, "Hello", strlen("Hello")) == SCUFCONT_SUCCESS);
assert_true(DynString_equal(&s1, &s2));
DynString_free(&s1);
DynString_free(&s2);
assert_true(DynString_alloc(&s1, "Hello", strlen("Hello")) == SCUFCONT_SUCCESS);
assert_true(DynString_alloc(&s2, "Hell0", strlen("Hell0")) == SCUFCONT_SUCCESS);
assert_false(DynString_equal(&s1, &s2));
DynString_free(&s1);
DynString_free(&s2);
assert_true(DynString_alloc(&s1, "Hello World", strlen("Hello World")) == SCUFCONT_SUCCESS);
assert_true(DynString_alloc(&s2, "Hello", strlen("Hello")) == SCUFCONT_SUCCESS);
assert_false(DynString_equal(&s1, &s2));
DynString_free(&s1);
DynString_free(&s2);
}
void test_DynString_equal_c_str(void** state) {
(void) state;
DynString s1;
assert_true(DynString_alloc(&s1,"Hello", strlen("Hello")) == SCUFCONT_SUCCESS);
char* s2 = "Hello";
assert_true(DynString_equal_c_str(&s1, s2));
DynString_free(&s1);
assert_true(DynString_alloc(&s1, "Hello", strlen("Hello")) == SCUFCONT_SUCCESS);
s2 = "Hell0";
assert_false(DynString_equal_c_str(&s1, s2));
DynString_free(&s1);
assert_true(DynString_alloc(&s1, "Hello World", strlen("Hello World")) == SCUFCONT_SUCCESS);
s2 = "Hello";
assert_false(DynString_equal_c_str(&s1, s2));
DynString_free(&s1);
}
void test_DynString_equal_next_chars_c_str(void** state) {
(void) state;
DynString dstr;
assert_true(DynString_alloc(&dstr, "Hello", strlen("Hello")) == SCUFCONT_SUCCESS);
char* str = "ll";
size_t str_len = strlen(str);
assert_true(DynString_equal_next_chars_c_str(&dstr, str, str_len, 2));
assert_false(DynString_equal_next_chars_c_str(&dstr, str, str_len, 1));
DynString_free(&dstr);
}
void test_DynString_concat_c_str(void** state) {
(void) state;
const char* dstr_c_str = "Hello";
const char* c_str = " World";
const size_t combined_length = strlen(dstr_c_str) + strlen(c_str);
DynString dstr;
assert_true(DynString_alloc(&dstr, dstr_c_str, strlen("Hello")) == SCUFCONT_SUCCESS);
DynString_concat_c_str(&dstr, c_str, strlen(c_str));
assert_memory_equal(dstr.data, (void*) "Hello World\0", strlen("Hello World") + 1);
assert_true(dstr.len == combined_length);
assert_true(dstr.capacity == combined_length + 1);
DynString_free(&dstr);
}
void test_DynString_c_str(void** state) {
(void) state;
const char* c_str = "Hello";
DynString dstr;
assert_true(DynString_alloc(&dstr, c_str, strlen(c_str)) == SCUFCONT_SUCCESS);
assert_string_equal(c_str, DynString_c_str(&dstr));
DynString_concat_c_str(&dstr, " World", strlen(" World"));
assert_string_equal("Hello World", DynString_c_str(&dstr));
}
void test_DynString_split(void** state) {
(void) state;
DynString dstr;
assert_true(DynString_alloc(&dstr, "Hello World", strlen("Hello World")) == SCUFCONT_SUCCESS);
size_t arr_len = 0;
DynString* arr = NULL;
ScufContResult res = DynString_split(&dstr, " ", &arr, &arr_len);
assert_true(res == SCUFCONT_SUCCESS);
assert_true(arr_len == 2);
assert_string_equal(DynString_c_str(arr), "Hello");
assert_string_equal(DynString_c_str(arr + 1), "World");
DynString_free(&dstr);
DynString_split_arr_free(&arr, arr_len);
assert_true(arr == NULL);
assert_true(DynString_alloc(&dstr, " Hello World", strlen(" Hello World")) == SCUFCONT_SUCCESS);
arr_len = 0;
res = DynString_split(&dstr, " ", &arr, &arr_len);
assert_true(arr_len == 3);
assert_string_equal(DynString_c_str(arr), "");
assert_string_equal(DynString_c_str(arr + 1), "Hello");
assert_string_equal(DynString_c_str(arr + 2), "World");
DynString_free(&dstr);
DynString_split_arr_free(&arr, arr_len);
assert_true(arr == NULL);
}
int main(void) {
const struct CMUnitTest tests[] = {
cmocka_unit_test(test_DynString_alloc),
cmocka_unit_test(test_DynString_free),
cmocka_unit_test(test_DynString_equal),
cmocka_unit_test(test_DynString_equal_c_str),
cmocka_unit_test(test_DynString_equal_next_chars_c_str),
cmocka_unit_test(test_DynString_concat_c_str),
cmocka_unit_test(test_DynString_c_str),
cmocka_unit_test(test_DynString_split),
};
return cmocka_run_group_tests(tests, NULL, NULL);
}

18
tests/cmocka/meson.build Normal file
View File

@@ -0,0 +1,18 @@
cmocka_tests = executable(
'cmocka_tests',
'main.c',
include_directories: [
scufcont_include
],
c_args: [
c_args,
'-ggdb',
'-O0'
],
dependencies: [
cmocka_dep
],
link_with: [ scufcont_lib ]
)
test('cmocka', cmocka_tests)

1
tests/meson.build Normal file
View File

@@ -0,0 +1 @@
subdir('cmocka')