summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2010-10-20 20:33:38 +0100
committerLars Wirzenius <liw@liw.fi>2010-10-20 20:33:38 +0100
commit606314cb4919e3931036b09ed18f7fe936c26e42 (patch)
tree42bd485f7eb0e0e91ce8d95ed22e1f1f1b63f33b
downloaddynstr-606314cb4919e3931036b09ed18f7fe936c26e42.tar.gz
Initial import.
-rw-r--r--Makefile14
-rw-r--r--dynstr.c25
-rw-r--r--dynstr.h146
-rw-r--r--unittests.c49
4 files changed, 234 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..19e6dbb
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,14 @@
+CFLAGS = --std=c99 -Wall -Werror
+
+all: unittests
+
+dynstr.o unittests.o: dynstr.h
+
+unittests: unittests.o dynstr.o
+
+check: all
+ ./unittests
+
+clean:
+ rm -f *.o *.a *.so core unittests
+
diff --git a/dynstr.c b/dynstr.c
new file mode 100644
index 0000000..51955c2
--- /dev/null
+++ b/dynstr.c
@@ -0,0 +1,25 @@
+#define _GNU_SOURCE
+#include <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "dynstr.h"
+
+
+struct Dynstr {
+ char *mem;
+ size_t size;
+ bool dynamic;
+};
+
+
+dynstr_error_handler *dynstr_get_malloc_error_handler(void)
+{
+ return NULL;
+}
+
+void dynstr_malloc_error_indicate(int error, size_t size, void *oldptr)
+{
+}
diff --git a/dynstr.h b/dynstr.h
new file mode 100644
index 0000000..a94d330
--- /dev/null
+++ b/dynstr.h
@@ -0,0 +1,146 @@
+/*
+ * dynstr.h - dynamic C string library
+ *
+ * This is a library for managing dynamic C strings of bytes (not
+ * multibyte characters).
+ *
+ * Each dynamic string is immutable: once created, it cannot be modified.
+ * To achieve changes, create new strings. This allows an implementation
+ * of the API to make use of copy-on-write techniques to save memory,
+ * and avoid unnecessary copying.
+ *
+ * Dynamic allocation can fail. By default, the routines in this library
+ * return NULL for failures. The caller may use the
+ * dynstr_set_malloc_error_handler function to set a function that gets
+ * called before NULL is returned. This function can, for example, abort
+ * the program (see dynstr_malloc_error_abort), or use longjmp to jump
+ * some error handling routine.
+ */
+
+#ifndef DYNSTR_H
+#define DYNSTR_H
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stddef.h>
+
+
+/* Version number of the shared library. */
+#define DYNSTR_VERSION_MAJOR 0
+#define DYNSTR_VERSION_MINOR 0
+#define DYNSTR_VERSION_PATCH 0
+
+
+/* Magic value returned by functions that search for things within
+ * dynamic strings, to indicate 'not found'. */
+#define DYNSTR_NOT_FOUND (~(size_t)0)
+
+
+/* The abstract type for dynamic strings. The type is opaque: the caller
+ * may only operate on it using the functions declared in this header. */
+typedef struct Dynstr Dynstr;
+
+
+/* Type of callback functions for handling malloc failures. */
+typedef void dynstr_error_handler(int error, size_t size, void *oldptr);
+
+/* Get and set malloc error handler. */
+dynstr_error_handler *dynstr_get_malloc_error_handler(void);
+void dynstr_set_malloc_error_handler(dynstr_error_handler *handler);
+
+/* Malloc error handler that aborts the program (using abort, not exit). */
+void dynstr_malloc_error_abort(int error, size_t size, void *oldptr);
+
+/* Malloc error handler that does nothing. This is the default handler.
+ * It causes NULL to be returned. */
+void dynstr_malloc_error_indicate(int error, size_t size, void *oldptr);
+
+
+/* Create a new, empty string. */
+Dynstr *dynstr_new_empty(void);
+
+/* Create a new string, copy contents from a NUL-terminated C string. */
+Dynstr *dynstr_new_from_cstring(const char *cstring);
+
+/* Create a new string, copy contents from an arbitrary memory area. */
+Dynstr *dynstr_new_from_memory(const void *mem, size_t size);
+
+/* Create a new string, use contents from a constant C string. The caller
+ * MUST make sure the contents of the C string do not change while the
+ * dynamic string exists, since the dynamic string will not copy the
+ * contents, but use it directly. */
+Dynstr *dynstr_new_from_constant_cstring(const char *cstring);
+
+/* Create a new string, use contents from an arbitrary memory area. The caller
+ * MUST make sure the contents of the memory area do not change while the
+ * dynamic string exists, since the dynamic string will not copy the
+ * contents, but use it directly. */
+Dynstr *dynstr_new_from_constant_memory(const void *mem, size_t size);
+
+/* Free a dynamic string. */
+void dynstr_free(Dynstr *dynstr);
+
+/* Return length of dynamic string, in bytes. */
+size_t dynstr_len(Dynstr *dynstr);
+
+/* Is the dynamic string empty? In other words, is its length 0? */
+bool dynstr_is_empty(Dynstr *dynstr);
+
+/* Create a new string, copying its contents from an existing one. */
+Dynstr *dynstr_substr(Dynstr *dynstr, size_t offset, size_t size);
+
+/* Create a new string by catenating zero or more existing ones. The
+ * argument list has zero or more pointers to dynamic strings, and
+ * then a NULL pointer. */
+Dynstr *dynstr_cat(Dynstr *dynstr, ...);
+Dynstr *dynstr_vcat(Dynstr *, va_list);
+
+/* Return value of byte at a given offset. The value is non-negative. If
+ * the offset is past the end of the string, -1 is returned. */
+int dynstr_byte_at(Dynstr *dynstr, size_t offset);
+
+/* Copy contents of a dynamic string into a memory area. If the dynamic
+ * string is not long enough, or the offset is past the end of the string,
+ * the copy is silently truncated. */
+size_t dynstr_memcpy(void *mem, Dynstr *dynstr, size_t offset, size_t size);
+
+/* Search for first or last byte with a given value in a string,
+ * within a given range. Return offset of matching byte, or
+ * DYNSTR_NOT_FOUND if not found. Offset is from beginning of string,
+ * not beginning of range. */
+size_t dynstr_first_byte(Dynstr *dynstr, size_t offset, size_t size, int byte);
+size_t dynstr_last_byte(Dynstr *dynstr, size_t offset, size_t size, int byte);
+
+/* Search for first or last occurrence of a substring in a string,
+ * within a given range. Return offset of match, or
+ * DYNSTR_NOT_FOUND if not found. Offset is from beginning of string,
+ * not beginning of range. */
+size_t dynstr_first_string(Dynstr *dynstr, size_t offset, size_t size,
+ Dynstr *pattern);
+size_t dynstr_last_string(Dynstr *dynstr, size_t offset, size_t size,
+ Dynstr *pattern);
+
+/* Write a dynamic string into an open file, either FILE or a Unix file
+ * handle. Return value is number of bytes written, just like for fwrite(3).
+ * If an error occurs, the size is less than the length of the string,
+ * and errno has been set by fwrite(3). */
+size_t dynstr_fwrite(FILE *file, Dynstr *dynstr);
+size_t dynstr_write(int file, Dynstr *dynstr);
+
+/* Read a number of bytes from an open file, either FILE or a Unix file
+ * handle. Return value is number of bytes read, just like for fread(3).
+ * A value less than the requested may indicate an error or end-of-file.
+ * Use ferror(3) and feof(3) to determine which. If it was an error, errno
+ * has been set by fread(3). */
+Dynstr *dynstr_read(int file, size_t size);
+Dynstr *dynstr_fread(FILE *file, size_t size);
+
+/* Read a line from an open file, either a FILE or a Unix file handle.
+ * The line ends with the first newline byte, or at the end of the file.
+ * If there was an error, NULL is returned, and errno is set, and the
+ * partially read line is discarded. */
+Dynstr *dynstr_freadline(FILE *file);
+Dynstr *dynstr_readline(int file);
+
+#endif
diff --git a/unittests.c b/unittests.c
new file mode 100644
index 0000000..0f43982
--- /dev/null
+++ b/unittests.c
@@ -0,0 +1,49 @@
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "dynstr.h"
+
+
+#define FAIL_UNLESS_EQUAL(a,b) \
+ do { \
+ if ((a) != (b)) { \
+ printf("FAIL: not equal: '%s' and '%s'\n", #a, #b); \
+ return false;\
+ } \
+ } while (0)
+
+
+static int test_default_error_handler_is_indicate(void)
+{
+ FAIL_UNLESS_EQUAL(dynstr_get_malloc_error_handler(),
+ dynstr_malloc_error_indicate);
+ return true;
+}
+
+
+struct test {
+ const char *name;
+ int (*test)(void);
+};
+
+
+#define TEST(func) { #func, func }
+
+static const struct test tests[] = {
+ TEST(test_default_error_handler_is_indicate),
+};
+static const int num_tests = sizeof(tests) / sizeof(tests[0]);
+
+int main(void)
+{
+ int exit;
+
+ exit = EXIT_SUCCESS;
+ for (int i = 0; i < num_tests; ++i) {
+ if (!tests[i].test()) {
+ exit = EXIT_FAILURE;
+ }
+ }
+ return exit;
+}