summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--deps.mk6
-rw-r--r--src/string.c149
-rw-r--r--src/string.h22
-rw-r--r--tests/string.c351
4 files changed, 522 insertions, 6 deletions
diff --git a/deps.mk b/deps.mk
index 5fb0961..048dea4 100644
--- a/deps.mk
+++ b/deps.mk
@@ -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;
}