diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f4ab3d5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +test +*.o diff --git a/History.md b/History.md new file mode 100644 index 0000000..363e1bd --- /dev/null +++ b/History.md @@ -0,0 +1,5 @@ + +0.0.2 / 2012-09-17 +================== + + * add `string_to_seconds(str)` diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e2f516c --- /dev/null +++ b/Makefile @@ -0,0 +1,6 @@ + +test: buffer.c test.c + @$(CC) $^ -std=c99 -o $@ + @./test + +.PHONY: test diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..514cb9e --- /dev/null +++ b/Readme.md @@ -0,0 +1,3 @@ + +# buffer.c + diff --git a/buffer.c b/buffer.c new file mode 100644 index 0000000..3c9bf35 --- /dev/null +++ b/buffer.c @@ -0,0 +1,175 @@ + +// +// buffer.c +// +// Copyright (c) 2012 TJ Holowaychuk +// + +#include +#include +#include "buffer.h" + +/* + * Compute the nearest multiple of `a` from `b`. + */ + +#define nearest_multiple_of(a, b) \ + (((b) + ((a) - 1)) & ~((a) - 1)) + +/* + * Allocate a new buffer with BUFFER_DEFAULT_SIZE. + */ + +buffer_t * +buffer_new() { + return buffer_new_with_size(BUFFER_DEFAULT_SIZE); +} + +/* + * Allocate a new buffer with `n` bytes. + */ + +buffer_t * +buffer_new_with_size(size_t n) { + buffer_t *self = malloc(sizeof(buffer_t)); + if (!self) return NULL; + self->len = n; + self->data = calloc(n, 1); + return self; +} + +/* + * Allocate a new buffer with `str`. + */ + +buffer_t * +buffer_new_with_string(char *str) { + size_t len = strlen(str); + buffer_t *self = buffer_new_with_size(len); + if (!self) return NULL; + memcpy(self->data, str, len); + return self; +} + +/* + * Free the buffer. + */ + +void +buffer_free(buffer_t *self) { + free(self->data); + free(self); +} + +/* + * Return buffer size. + */ + +size_t +buffer_size(buffer_t *self) { + return self->len; +} + +/* + * Return string length. + */ + +size_t +buffer_length(buffer_t *self) { + return strlen(self->data); +} + +/* + * Resize to hold `n` bytes. + */ + +int +buffer_resize(buffer_t *self, size_t n) { + n = nearest_multiple_of(1024, n); + self->len = n; + self->data = realloc(self->data, n); + return self->data ? 0 : -1; +} + +/* + * Append `str` to `self` and return 0 on success, -1 on failure. + */ + +int +buffer_append(buffer_t *self, char *str) { + size_t len = strlen(str); + size_t prev = strlen(self->data); + size_t needed = len + prev; + + // enough space + if (self->len > needed) { + strcat(self->data, str); + return 0; + } + + // resize + int ret = buffer_resize(self, needed); + if (-1 == ret) return -1; + strcat(self->data, str); + + return 0; +} + +/* + * Prepend `str` to `self` and return 0 on success, -1 on failure. + */ + +int +buffer_prepend(buffer_t *self, char *str) { + size_t len = strlen(str); + size_t prev = strlen(self->data); + size_t needed = len + prev; + + // enough space + if (self->len > needed) goto move; + + // resize + int ret = buffer_resize(self, needed); + if (-1 == ret) return -1; + + // move + move: + memmove(self->data + len, self->data, len + 1); + memcpy(self->data, str, len); + + return 0; +} + +/* + * Return a new buffer based on the `from..to` slice of `buf`, + * or NULL on error. + */ + +buffer_t * +buffer_slice(buffer_t *buf, size_t from, ssize_t to) { + size_t len = strlen(buf->data); + + // bad range + if (to < from) return NULL; + + // relative to end + if (to < 0) to = len - ~to; + + // cap end + if (to > len) to = len; + + size_t n = to - from; + buffer_t *self = buffer_new_with_size(n + 1); + memcpy(self->data, buf->data + from, n); + return self; +} + +/* + * Return 1 if the buffers contain equivalent data. + */ + +int +buffer_equals(buffer_t *self, buffer_t *other) { + return 0 == strcmp(self->data, other->data); +} + diff --git a/buffer.h b/buffer.h new file mode 100644 index 0000000..9f8498c --- /dev/null +++ b/buffer.h @@ -0,0 +1,62 @@ + +// +// buffer.h +// +// Copyright (c) 2012 TJ Holowaychuk +// + +#ifndef BUFFER +#define BUFFER + +/* + * Default buffer size. + */ + +#ifndef BUFFER_DEFAULT_SIZE +#define BUFFER_DEFAULT_SIZE 64 +#endif + +/* + * Buffer struct. + */ + +typedef struct { + size_t len; + char *data; +} buffer_t; + +// prototypes + +buffer_t * +buffer_new(); + +buffer_t * +buffer_new_with_size(size_t n); + +buffer_t * +buffer_new_with_string(char *str); + +size_t +buffer_size(buffer_t *self); + +size_t +buffer_length(buffer_t *self); + +void +buffer_free(buffer_t *self); + +int +buffer_prepend(buffer_t *self, char *str); + +int +buffer_append(buffer_t *self, char *str); + +int +buffer_equals(buffer_t *self, buffer_t *other); + +buffer_t * +buffer_slice(buffer_t *self, size_t from, ssize_t to); + +#define buffer_string(self) (self->data) + +#endif diff --git a/package.json b/package.json new file mode 100644 index 0000000..cbb43b4 --- /dev/null +++ b/package.json @@ -0,0 +1,9 @@ +{ + "name": "buffer", + "version": "0.0.1", + "repo": "visionmedia/buffer.c", + "description": "Higher level C-string utilities", + "keywords": ["buf", "buffer", "string", "str", "util", "utils"], + "license": "MIT", + "src": ["buffer.c", "buffer.h"] +} diff --git a/test.c b/test.c new file mode 100644 index 0000000..2d2f645 --- /dev/null +++ b/test.c @@ -0,0 +1,136 @@ + +// +// test.c +// +// Copyright (c) 2012 TJ Holowaychuk +// + +#include +#include +#include +#include +#include "buffer.h" + +void +equal(char *a, char *b) { + if (strcmp(a, b)) { + printf("\n"); + printf(" expected: '%s'\n", a); + printf(" actual: '%s'\n", b); + printf("\n"); + exit(1); + } +} + +void +test_buffer_new() { + buffer_t *buf = buffer_new(); + assert(BUFFER_DEFAULT_SIZE == buffer_size(buf)); + assert(0 == buffer_length(buf)); +} + +void +test_buffer_new_with_size() { + buffer_t *buf = buffer_new_with_size(1024); + assert(1024 == buffer_size(buf)); + assert(0 == buffer_length(buf)); +} + +void +test_buffer_append() { + buffer_t *buf = buffer_new(); + assert(0 == buffer_append(buf, "Hello")); + assert(0 == buffer_append(buf, " World")); + assert(strlen("Hello World") == buffer_length(buf)); + equal("Hello World", buffer_string(buf)); +} + +void +test_buffer_append__grow() { + buffer_t *buf = buffer_new_with_size(10); + assert(0 == buffer_append(buf, "Hello")); + assert(0 == buffer_append(buf, " tobi")); + assert(0 == buffer_append(buf, " was")); + assert(0 == buffer_append(buf, " here")); + + char *str = "Hello tobi was here"; + equal(str, buffer_string(buf)); + assert(1024 == buffer_size(buf)); + assert(strlen(str) == buffer_length(buf)); +} + +void +test_buffer_prepend() { + buffer_t *buf = buffer_new(); + assert(0 == buffer_append(buf, " World")); + assert(0 == buffer_prepend(buf, "Hello")); + assert(strlen("Hello World") == buffer_length(buf)); + equal("Hello World", buffer_string(buf)); +} + +void +test_buffer_slice() { + buffer_t *buf = buffer_new(); + buffer_append(buf, "Tobi Ferret"); + + buffer_t *a = buffer_slice(buf, 2, 8); + equal("Tobi Ferret", buffer_string(buf)); + equal("bi Fer", buffer_string(a)); +} + +void +test_buffer_slice__range_error() { + buffer_t *buf = buffer_new_with_string("Tobi Ferret"); + buffer_t *a = buffer_slice(buf, 10, 2); + assert(NULL == a); +} + +void +test_buffer_slice__end() { + buffer_t *buf = buffer_new_with_string("Tobi Ferret"); + + buffer_t *a = buffer_slice(buf, 5, -1); + equal("Tobi Ferret", buffer_string(buf)); + equal("Ferret", buffer_string(a)); + + buffer_t *b = buffer_slice(buf, 5, -3); + equal("Ferr", buffer_string(b)); + + buffer_t *c = buffer_slice(buf, 8, -1); + equal("ret", buffer_string(c)); +} + +void +test_buffer_slice__end_overflow() { + buffer_t *buf = buffer_new_with_string("Tobi Ferret"); + buffer_t *a = buffer_slice(buf, 5, 1000); + equal("Tobi Ferret", buffer_string(buf)); + equal("Ferret", buffer_string(a)); +} + +void +test_buffer_equals() { + buffer_t *a = buffer_new_with_string("Hello"); + buffer_t *b = buffer_new_with_string("Hello"); + + assert(1 == buffer_equals(a, b)); + + buffer_append(b, " World"); + assert(0 == buffer_equals(a, b)); +} + +int +main(){ + test_buffer_new(); + test_buffer_new_with_size(); + test_buffer_append(); + test_buffer_append__grow(); + test_buffer_prepend(); + test_buffer_slice(); + test_buffer_slice__range_error(); + test_buffer_slice__end(); + test_buffer_slice__end_overflow(); + test_buffer_equals(); + printf("\n \e[32m\u2713 \e[90mok\e[0m\n\n"); + return 0; +}