diff options
author | Lars Wirzenius <liw@liw.fi> | 2010-10-20 20:33:38 +0100 |
---|---|---|
committer | Lars Wirzenius <liw@liw.fi> | 2010-10-20 20:33:38 +0100 |
commit | 606314cb4919e3931036b09ed18f7fe936c26e42 (patch) | |
tree | 42bd485f7eb0e0e91ce8d95ed22e1f1f1b63f33b | |
download | dynstr-606314cb4919e3931036b09ed18f7fe936c26e42.tar.gz |
Initial import.
-rw-r--r-- | Makefile | 14 | ||||
-rw-r--r-- | dynstr.c | 25 | ||||
-rw-r--r-- | dynstr.h | 146 | ||||
-rw-r--r-- | unittests.c | 49 |
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; +} |