Aaro Saila hace 1 mes
commit
faa660125e
Se han modificado 11 ficheros con 599 adiciones y 0 borrados
  1. 1 0
      .gitignore
  2. 111 0
      include/DynArray.h
  3. 51 0
      include/DynString.h
  4. 11 0
      include/scufcont.h
  5. 37 0
      meson.build
  6. 1 0
      meson_options.txt
  7. 198 0
      src/DynString.c
  8. 3 0
      src/scufcont.c
  9. 167 0
      tests/cmocka/main.c
  10. 18 0
      tests/cmocka/meson.build
  11. 1 0
      tests/meson.build

+ 1 - 0
.gitignore

@@ -0,0 +1 @@
+build

+ 111 - 0
include/DynArray.h

@@ -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 - 0
include/DynString.h

@@ -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 - 0
include/scufcont.h

@@ -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 - 0
meson.build

@@ -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 - 0
meson_options.txt

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

+ 198 - 0
src/DynString.c

@@ -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 - 0
src/scufcont.c

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

+ 167 - 0
tests/cmocka/main.c

@@ -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 - 0
tests/cmocka/meson.build

@@ -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 - 0
tests/meson.build

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