diff options
-rw-r--r-- | deps.mk | 6 | ||||
-rw-r--r-- | src/string.c | 149 | ||||
-rw-r--r-- | src/string.h | 22 | ||||
-rw-r--r-- | tests/string.c | 351 |
4 files changed, 522 insertions, 6 deletions
@@ -77,7 +77,7 @@ src/lib.o: src/logerr.h src/logerr.o: src/math.o: src/random.o: src/logerr.h -src/string.o: +src/string.o: src/logerr.h src/math.h src/util.h src/testing.o: src/util.o: src/logerr.h src/vector.o: src/logerr.h src/catalog.h src/i18n.h src/math.h @@ -88,7 +88,7 @@ tests/lib.o: src/logerr.h tests/logerr.o: src/testing.h tests/slurp.h tests/math.o: src/testing.h tests/random.o: src/logerr.h src/testing.h -tests/string.o: +tests/string.o: src/logerr.h src/math.h src/util.h src/testing.h tests/testing.o: tests/util.o: src/logerr.h src/testing.h tests/slurp.h tests/vector.o: src/logerr.h src/catalog.h src/i18n.h src/math.h src/testing.h @@ -99,7 +99,7 @@ tests/lib.a: src/logerr.o tests/logerr.a: src/testing.o tests/slurp.o tests/math.a: src/testing.o tests/random.a: src/logerr.o src/testing.o -tests/string.a: +tests/string.a: src/logerr.o src/math.o src/util.o src/testing.o tests/testing.a: tests/util.a: src/logerr.o src/testing.o tests/slurp.o tests/vector.a: src/logerr.o src/catalog.o src/i18n.o src/math.o src/testing.o diff --git a/src/string.c b/src/string.c index 6e1ba40..dce00f3 100644 --- a/src/string.c +++ b/src/string.c @@ -1,11 +1,156 @@ #include "config.h" +#include <assert.h> +#include <errno.h> #include <stddef.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "logerr.h" +#include "math.h" +#include "util.h" #include "string.h" + struct String { - char *bytes; - size_t length; + const size_t length; + const uint8_t *const bytes; }; + + + +int +string_new_with( + const char *const string, + const size_t length, + const struct String **const out +) { + int rc = -1; + + const struct String *ret = NULL; + const uint8_t *bytes = NULL; + + ret = malloc(sizeof(*ret)); + if (ret == NULL) { + logerr("malloc(): %s", strerror(errno)); + goto out; + } + + bytes = malloc(length + NULL_TERMINATOR); + if (bytes == NULL) { + logerr("malloc(): %s", strerror(errno)); + goto out; + } + + memcpy((void *)bytes, string, length); + memcpy((void *)(bytes + length), "", NULL_TERMINATOR); + memcpy((void *)ret, &(struct String) { + .length = length, + .bytes = bytes, + }, sizeof(*ret)); + *out = ret; + rc = 0; +out: + if (rc) { + if (bytes != NULL) { + free((void *)bytes); + ret = NULL; + } + if (ret != NULL) { + free((void *)ret); + ret = NULL; + } + } + return rc; +} + +int +string_new(const char *const string, const struct String **const out) { + return string_new_with(string, strlen(string), out); +} + +void +string_free(const struct String **const s) { + free((void *)(*s)->bytes); + free((void *)(*s)); + *s = NULL; +} + +const char * +cstr(const struct String *s) { + return (const char *)s->bytes; +} + +enum Comparison +string_equal(const struct String *const s1, const struct String *const s2) { + if (s1->length < s2->length) { + return Comparison_LT; + } else if (s1->length > s2->length) { + return Comparison_GT; + } else { + const int ret = strcmp((const char *)s1->bytes, (const char *)s2->bytes); + if (ret < 0) { + return Comparison_LT; + } else if (ret > 0) { + return Comparison_GT; + } else { + return Comparison_EQ; + } + } +} + +int +string_append( + const struct String *const s1, + const struct String *const s2, + const struct String **const out +) { + int rc = -1; + + const struct String *ret = NULL; + const uint8_t *bytes = NULL; + + size_t length; + if (add_size(s1->length, s2->length, &length)) { + logerr("add_size()"); + goto out; + } + + ret = malloc(sizeof(*ret)); + if (ret == NULL) { + logerr("malloc(): %s", strerror(errno)); + goto out; + } + + bytes = malloc(length + NULL_TERMINATOR); + if (bytes == NULL) { + logerr("malloc(): %s", strerror(errno)); + goto out; + } + + memcpy((void *)bytes, s1->bytes, s1->length); + memcpy((void *)(bytes + s1->length), s2->bytes, s2->length); + memcpy((void *)(bytes + length), "", NULL_TERMINATOR); + memcpy((void *)ret, &(struct String) { + .length = length, + .bytes = bytes, + }, sizeof(*ret)); + *out = ret; + rc = 0; +out: + if (rc) { + if (bytes != NULL) { + free((void *)bytes); + bytes = NULL; + } + if (ret != NULL) { + free((void *)ret); + ret = NULL; + } + } + return rc; +} diff --git a/src/string.h b/src/string.h index 2a60e26..e945b40 100644 --- a/src/string.h +++ b/src/string.h @@ -1 +1,23 @@ struct String; + +enum Comparison { + Comparison_LT, + Comparison_EQ, + Comparison_GT, +}; + +int +string_new(const char *const string, const struct String **out); + +void +string_free(const struct String **const s); + +enum Comparison +string_equal(const struct String *const s1, const struct String *const s2); + +int +string_append( + const struct String *const s1, + const struct String *const s2, + const struct String **const out +); diff --git a/tests/string.c b/tests/string.c index 4d1ee74..7748b6e 100644 --- a/tests/string.c +++ b/tests/string.c @@ -1,6 +1,355 @@ #include "../src/string.c" +#include "../src/testing.h" + + + +static int +test_string_new_with(void) { + int rc = -1; + + const struct String *s = NULL; + + test_start("string_new_with()"); + { + testing("length value isn't checked"); + + const char *const string = "abc"; + if (string_new_with(string, 123U, &s)) { + logerr("string_new_with()"); + goto out; + } + + assert(s->length == 123U); + + test_ok(); + } + rc = 0; +out: + if (s != NULL) { + string_free(&s); + } + return rc; +} + +static int +test_string_new(void) { + int rc = -1; + + test_start("string_new()"); + { + testing("simple allocation of a string"); + + const struct String *s; + if (string_new("a string", &s)) { + logerr("string_new()"); + goto out; + } + + assert(s->length == 8U); + assert(strcmp(cstr(s), "a string") == 0); + + string_free(&s); + + test_ok(); + } + + rc = 0; +out: + return rc; +} + +static int +test_string_free(void) { + int rc = -1; + + const struct String *s = NULL; + + test_start("string_free()"); + { + testing("*v becomes NULL again after string_free(&s)"); + assert(s == NULL); + if (string_new("", &s)) { + logerr("string_new()"); + goto out; + } + + assert(s != NULL); + string_free(&s); + assert(s == NULL); + + test_ok(); + } + + rc = 0; +out: + return rc; +} + +static int +test_cstr(void) { + int rc = -1; + + const struct String *s = NULL; + + test_start("cstr()"); + + const char *const string = "some string"; + const size_t length = strlen(string); + if (string_new(string, &s)) { + logerr("string_new()"); + goto out; + } + + { + testing("we can dump a NULL-terminated string"); + + assert(strcmp(cstr(s), string) == 0); + assert(cstr(s)[length] == '\0'); + + test_ok(); + } + + rc = 0; +out: + if (s != NULL) { + string_free(&s); + } + return rc; +} + +static int +test_string_equal(void) { + int rc = -1; + + const struct String *s1 = NULL; + const struct String *s2 = NULL; + const struct String *s3 = NULL; + const struct String *s4 = NULL; + + test_start("string_equal()"); + + if (string_new("aac", &s1)) { + logerr("string_new()"); + goto out; + } + + if (string_new("abc", &s2)) { + logerr("string_new()"); + goto out; + } + + if (string_new("acc", &s3)) { + logerr("string_new()"); + goto out; + } + + if (string_new("abc", &s4)) { + logerr("string_new()"); + goto out; + } + + { + testing("different identities"); + + assert(s1 != s2); + assert(s1 != s3); + assert(s1 != s4); + + assert(s2 != s3); + assert(s2 != s4); + + assert(s3 != s4); + + test_ok(); + } + { + testing("self equality"); + + assert(string_equal(s1, s1) == Comparison_EQ); + assert(string_equal(s2, s2) == Comparison_EQ); + assert(string_equal(s3, s3) == Comparison_EQ); + assert(string_equal(s4, s4) == Comparison_EQ); + + test_ok(); + } + { + testing("lesser/greater values"); + + assert(string_equal(s1, s2) == Comparison_LT); + assert(string_equal(s1, s3) == Comparison_LT); + assert(string_equal(s1, s4) == Comparison_LT); + + assert(string_equal(s2, s3) == Comparison_LT); + + assert(string_equal(s4, s3) == Comparison_LT); + + assert(string_equal(s2, s1) == Comparison_GT); + assert(string_equal(s3, s1) == Comparison_GT); + assert(string_equal(s4, s1) == Comparison_GT); + + assert(string_equal(s3, s2) == Comparison_GT); + + assert(string_equal(s3, s4) == Comparison_GT); + + test_ok(); + } + { + testing("equal values"); + + assert(string_equal(s2, s4) == Comparison_EQ); + assert(string_equal(s4, s2) == Comparison_EQ); + + test_ok(); + } + + rc = 0; +out: + if (s4 != NULL) { + string_free(&s4); + } + if (s3 != NULL) { + string_free(&s3); + } + if (s2 != NULL) { + string_free(&s2); + } + if (s1 != NULL) { + string_free(&s1); + } + return rc; +} + +static int +test_string_append(void) { + int rc = -1; + + const struct String *s = NULL; + const struct String *s1 = NULL; + const struct String *s2 = NULL; + const struct String *s3 = NULL; + + test_start("string_append()"); + + if (string_new("", &s1)) { + logerr("string_new()"); + goto out; + } + + if (string_new("", &s2)) { + logerr("string_new()"); + goto out; + } + + if (string_new("abc", &s3)) { + logerr("string_new()"); + goto out; + } + + { + testing("empty strings"); + + if (string_append(s1, s2, &s)) { + logerr("string_append()"); + goto out; + } + assert(string_equal(s, s1) == Comparison_EQ); + string_free(&s); + + if (string_append(s2, s2, &s)) { + logerr("string_append()"); + goto out; + } + assert(string_equal(s, s1) == Comparison_EQ); + string_free(&s); + + if (string_append(s1, s3, &s)) { + logerr("string_append()"); + goto out; + } + assert(string_equal(s, s3) == Comparison_EQ); + string_free(&s); + + if (string_append(s2, s3, &s)) { + logerr("string_append()"); + goto out; + } + assert(string_equal(s, s3) == Comparison_EQ); + string_free(&s); + + test_ok(); + } + { + testing("same string in both arguments"); + + if (string_append(s3, s3, &s)) { + logerr("string_append()"); + goto out; + } + + assert(string_equal(s, &(struct String) { + .length = 6U, + .bytes = (const uint8_t *)"abcabc", + }) == Comparison_EQ); + + string_free(&s); + + test_ok(); + } + + rc = 0; +out: + if (s3 != NULL) { + string_free(&s3); + } + if (s2 != NULL) { + string_free(&s2); + } + if (s1 != NULL) { + string_free(&s1); + } + if (s != NULL) { + string_free(&s); + } + return rc; +} + + int main(void) { - return 0; + int rc = EXIT_FAILURE; + + if (test_string_new_with()) { + logerr("test_string_new_with()"); + goto out; + } + + if (test_string_new()) { + logerr("test_string_new()"); + goto out; + } + + if (test_string_free()) { + logerr("test_string_free()"); + goto out; + } + + if (test_cstr()) { + logerr("test_cstr()"); + goto out; + } + + if (test_string_equal()) { + logerr("test_string_equal()"); + goto out; + } + + if (test_string_append()) { + logerr("test_string_append()"); + goto out; + } + + rc = EXIT_SUCCESS; +out: + return rc; } |