diff options
Diffstat (limited to 'lib/libdvec/src')
| -rw-r--r-- | lib/libdvec/src/dvec.c | 510 | ||||
| -rw-r--r-- | lib/libdvec/src/test.c | 77 |
2 files changed, 587 insertions, 0 deletions
diff --git a/lib/libdvec/src/dvec.c b/lib/libdvec/src/dvec.c new file mode 100644 index 0000000..bc71f3f --- /dev/null +++ b/lib/libdvec/src/dvec.c @@ -0,0 +1,510 @@ +#include "dvec.h" + +#include <stddef.h> +#include <string.h> + +#define ERR(x) ((x) >= 0 ? DVEC_BAD : (x)) + +struct sort_order_user_proxy { + dvec_sort_order_fn order_fn; + const void *user; +}; + +int +dvec_init(struct dvec *dvec, size_t dsize, size_t cap, + const struct allocator *allocator) +{ + int rc = 0; + + LIBDVEC_ABORT_ON_ARGS(!dvec || !dsize || !allocator); + + dvec->allocator = allocator; + dvec->dsize = dsize; + dvec->cap = cap; + dvec->len = 0; + dvec->data = NULL; + dvec->locked = false; + dvec->fused = false; + if (dvec->cap) { + dvec->data = allocator->alloc(allocator, cap * dsize, &rc); + LIBDVEC_ABORT_ON_ALLOC(!dvec->data); + if (!dvec->data) return ERR(rc); + } + + return DVEC_OK; +} + +int +dvec_deinit(struct dvec *dvec) +{ + int rc; + + LIBDVEC_ABORT_ON_ARGS(!dvec); + + rc = dvec->allocator->free(dvec->allocator, dvec->data); + LIBDVEC_ABORT_ON_ALLOC(rc); + + return rc >= 0 ? DVEC_OK : ERR(rc); +} + +struct dvec * +dvec_alloc(size_t dsize, size_t cap, + const struct allocator *allocator, int *rc) +{ + struct dvec *dvec; + int _rc; + + LIBDVEC_ABORT_ON_ARGS(!allocator); + + dvec = allocator->alloc(allocator, sizeof(struct dvec), &_rc); + LIBDVEC_ABORT_ON_ALLOC(!dvec); + if (!dvec) { + if (rc) *rc = ERR(_rc); + return NULL; + } + + _rc = dvec_init(dvec, dsize, cap, allocator); + if (_rc < 0) { + if (rc) *rc = _rc; + allocator->free(allocator, dvec); + return NULL; + } + + return dvec; +} + +static size_t +aligned_size(size_t size, size_t dsize) +{ + return size + (size % dsize ? dsize - size % dsize : 0); +} + +struct dvec * +dvec_alloc_fused(size_t dsize, size_t cap, + const struct allocator *allocator, int *rc) +{ + struct dvec *dvec; + size_t off; + int _rc = 0; + + LIBDVEC_ABORT_ON_ARGS(!allocator); + + off = aligned_size(sizeof(struct dvec), dsize); + dvec = allocator->alloc(allocator, off + cap * dsize, &_rc); + if (!dvec) { + if (_rc) *rc = ERR(_rc); + return NULL; + } + + dvec->allocator = allocator; + dvec->dsize = dsize; + dvec->cap = cap; + dvec->len = 0; + dvec->data = (uint8_t *) dvec + off; + dvec->locked = false; + dvec->fused = true; + + return dvec; +} + +int +dvec_free(struct dvec *dvec) +{ + const struct allocator *allocator; + int rc; + + if (!dvec) return DVEC_OK; + + allocator = dvec->allocator; + + if (dvec->fused) { + rc = allocator->free(allocator, dvec); + if (rc < 0) return rc; + } else { + rc = dvec_deinit(dvec); + if (rc) return rc; + + rc = allocator->free(allocator, dvec); + LIBDVEC_ABORT_ON_ALLOC(rc); + if (rc < 0) return rc; + } + + return DVEC_OK; +} + +int +dvec_copy(struct dvec *dst, struct dvec *src) +{ + int rc; + + LIBDVEC_ABORT_ON_ARGS(!dst || !src || dst->dsize != src->dsize); + + rc = dvec_reserve(dst, src->len); + if (rc) return rc; + + dst->len = src->len; + memcpy(dst->data, src->data, src->len * src->dsize); + + return DVEC_OK; +} + +void +dvec_move(struct dvec *dst, struct dvec *src) +{ + LIBDVEC_ABORT_ON_ARGS(!dst || !src); + + memcpy(dst, src, sizeof(struct dvec)); +} + +void +dvec_swap(struct dvec *dst, struct dvec *src) +{ + struct dvec tmp; + + LIBDVEC_ABORT_ON_ARGS(!dst || !src); + + memcpy(&tmp, dst, sizeof(struct dvec)); + memcpy(dst, src, sizeof(struct dvec)); + memcpy(src, &tmp, sizeof(struct dvec)); +} + +void +dvec_clear(struct dvec *dvec) +{ + LIBDVEC_ABORT_ON_ARGS(!dvec); + + dvec->len = 0; +} + +int +dvec_reserve(struct dvec *dvec, size_t len) +{ + void *data; + size_t cap, off; + int rc = 1; + + LIBDVEC_ABORT_ON_ARGS(!dvec); + + if (len <= dvec->cap) return DVEC_OK; + + if (dvec->locked) return DVEC_LOCKED; + + cap = 2 * dvec->cap; + if (len > cap) cap = len; + + if (dvec->fused) { + off = aligned_size(sizeof(struct dvec), dvec->dsize); + data = dvec->allocator->realloc(dvec->allocator, dvec, + off + dvec->cap * dvec->dsize, &rc); + LIBDVEC_ABORT_ON_ALLOC(!data); + if (!data) return ERR(rc); + dvec = data; + dvec->data = (uint8_t *) dvec + off; + } else { + data = dvec->allocator->realloc(dvec->allocator, + dvec->data, cap * dvec->dsize, &rc); + LIBDVEC_ABORT_ON_ALLOC(!data); + if (!data) return ERR(rc); + dvec->data = data; + } + + dvec->cap = cap; + + return DVEC_OK; +} + +int +dvec_shrink(struct dvec *dvec) +{ + void *data; + size_t cap, off; + int rc = 0; + + LIBDVEC_ABORT_ON_ARGS(!dvec); + + if (dvec->locked) return DVEC_LOCKED; + + cap = dvec->len; + + if (dvec->fused) { + off = aligned_size(sizeof(struct dvec), dvec->dsize); + data = dvec->allocator->realloc(dvec->allocator, dvec, + off + cap * dvec->dsize, &rc); + LIBDVEC_ABORT_ON_ALLOC(!data); + if (!data) return ERR(rc); + dvec = data; + dvec->data = (uint8_t *) dvec + off; + } else if (!dvec->len) { + rc = dvec->allocator->free(dvec->allocator, dvec->data); + LIBDVEC_ABORT_ON_ALLOC(rc); + if (rc < 0) return rc; + } else { + data = dvec->allocator->realloc(dvec->allocator, + &dvec->data, cap * dvec->dsize, &rc); + LIBDVEC_ABORT_ON_ALLOC(!data); + if (!data) return ERR(rc); + dvec->data = data; + } + + dvec->cap = cap; + + return DVEC_OK; +} + +int +dvec_add(struct dvec *dvec, size_t index, size_t len) +{ + int rc; + + LIBDVEC_ABORT_ON_ARGS(!dvec || index > dvec->len); + + rc = dvec_reserve(dvec, dvec->len + len); + if (rc) return rc; + + if (index < dvec->len) { + memmove(dvec->data + (index + len) * dvec->dsize, + dvec->data + index * dvec->dsize, + (dvec->len - index) * dvec->dsize); + } + + dvec->len += len; + + return DVEC_OK; +} + +void +dvec_rm(struct dvec *dvec, size_t index, size_t count) +{ + LIBDVEC_ABORT_ON_ARGS(!dvec || index + count > dvec->len); + + if (index + count < dvec->len) { + memmove(dvec->data + index * dvec->dsize, + dvec->data + (index + count) * dvec->dsize, + (dvec->len - (index + count)) * dvec->dsize); + } + + dvec->len -= count; +} + +void +dvec_replace(struct dvec *dvec, size_t index, const void *data, size_t count) +{ + LIBDVEC_ABORT_ON_ARGS(!dvec || !data || index + count > dvec->len); + + memcpy(dvec->data + index * dvec->dsize, data, count * dvec->dsize); +} + +void * +dvec_iter_fwd(const struct dvec *dvec, const void *p) +{ + LIBDVEC_ABORT_ON_ARGS(!dvec); + LIBDVEC_ABORT_ON_ARGS(p && (p < dvec->data)); + LIBDVEC_ABORT_ON_ARGS(p && (p >= dvec->data + dvec->len * dvec->dsize)); + LIBDVEC_ABORT_ON_ARGS(p && ((size_t) (p - dvec->data) % dvec->dsize)); + + if (p == NULL) { + if (dvec->len > 0) + return dvec->data; + else + return NULL; + } else if ((uint8_t *) p < dvec->data + dvec->dsize * (dvec->len - 1)) { + return (uint8_t *) p + dvec->dsize; + } else { + return NULL; + } +} + +void * +dvec_iter_bwd(const struct dvec *dvec, const void *p) +{ + LIBDVEC_ABORT_ON_ARGS(!dvec); + LIBDVEC_ABORT_ON_ARGS(p && (p < dvec->data)); + LIBDVEC_ABORT_ON_ARGS(p && (p >= dvec->data + dvec->len * dvec->dsize)); + LIBDVEC_ABORT_ON_ARGS(p && ((size_t) (p - dvec->data) % dvec->dsize)); + + if (p == NULL) { + if (dvec->len > 0) + return dvec->data + (dvec->len - 1) * dvec->dsize; + else + return NULL; + } else if ((uint8_t *) p > dvec->data) { + return (uint8_t *) p - dvec->dsize; + } else { + return NULL; + } +} + +static void +dvec_quick_sort_swap(void *a, void *b, void *tmp, size_t dsize) +{ + memcpy(tmp, a, dsize); + memcpy(a, b, dsize); + memcpy(b, tmp, dsize); +} + +int +dvec_quick_sort(struct dvec *dvec, const struct allocator *allocator, + void *tmp, bool reverse, dvec_sort_order_fn in_order, const void *user) +{ + const size_t dsize = dvec->dsize; + struct dvec stack; + uint8_t *start, *end; + uint8_t *lo, *hi, *pivot; + bool lo_order, hi_order; + bool modified = false; + int rc; + + if (!dvec->len) return DVEC_OK; + + rc = dvec_init(&stack, sizeof(uint8_t *), 32, allocator); + if (rc) return rc; + + *(uint8_t **)dvec_push(&stack) = dvec_front(dvec); + *(uint8_t **)dvec_push(&stack) = dvec_back(dvec); + + while (!dvec_empty(&stack)) { + end = *(uint8_t **)dvec_pop(&stack); + start = *(uint8_t **)dvec_pop(&stack); + + if (start == end) continue; + + if (start + dsize >= end) { + if (!in_order(start, end, user)) { + dvec_quick_sort_swap(start, end, tmp, dsize); + modified = true; + } + continue; + } + + lo = start; + hi = end; + pivot = start + (size_t) (end - start) / dsize / 2 * dsize; + + while (lo < hi) { + lo_order = lo != pivot && in_order(lo, pivot, user); + hi_order = hi != pivot && in_order(pivot, hi, user); + if (!lo_order && !hi_order) { + if (lo == pivot) pivot = hi; + else if (hi == pivot) pivot = lo; + dvec_quick_sort_swap(lo, hi, tmp, dsize); + modified = true; + } + if (lo_order) lo += dsize; + if (hi_order) hi -= dsize; + } + + if (start != pivot) { + *(uint8_t **)dvec_push(&stack) = start; + *(uint8_t **)dvec_push(&stack) = pivot - dsize; + } + + if (end != pivot) { + *(uint8_t **)dvec_push(&stack) = pivot + dsize; + *(uint8_t **)dvec_push(&stack) = end; + } + } + + dvec_deinit(&stack); + + return modified ? DVEC_MODIFIED : DVEC_OK; +} + +static bool +dvec_quick_sort_ex_order(const void *p1, const void *p2, const void *user) +{ + const struct sort_order_user_proxy *user_proxy = user; + const void *d1 = *(void **)p1, *d2 = *(void **)p2; + + return user_proxy->order_fn(d1, d2, user_proxy->user); +} + +int +dvec_quick_sort_ex(struct dvec *dvec, const struct allocator *allocator, + void *tmp, bool reverse, dvec_sort_order_fn in_order, const void *user) +{ + const struct sort_order_user_proxy user_proxy = { + .order_fn = in_order, + .user = user + }; + struct dvec ptrmap; + void *p, **pp, **dst, **src; + size_t dsize; + int rc; + + if (!dvec->len) return DVEC_OK; + + dsize = dvec->dsize; + + rc = dvec_init(&ptrmap, sizeof(void *), dvec->len, allocator); + if (rc) return rc; + + for (DVEC_ITER(dvec, p)) + *(void **)dvec_push(&ptrmap) = p; + + rc = dvec_quick_sort(&ptrmap, allocator, tmp, reverse, + dvec_quick_sort_ex_order, &user_proxy); + if (rc) return rc; + + for (DVEC_ITER(&ptrmap, pp)) { + if (!*pp || dvec_idx(&ptrmap, pp) == dvec_idx(dvec, *pp)) + continue; + dst = pp; + memcpy(tmp, *dst, dsize); + while (1) { + src = dvec_at(&ptrmap, dvec_idx(dvec, *dst)); + if (!*src) break; + memcpy(*dst, *src, dsize); + *dst = NULL; + dst = src; + } + memcpy(*dst, tmp, dsize); + } + + rc = dvec_deinit(&ptrmap); + if (rc) return rc; + + return DVEC_OK; +} + +ssize_t +dvec_binary_search(struct dvec *dvec, dvec_search_cmp_fn search_cmp, + void *user, ssize_t *lower, ssize_t *higher) +{ + size_t min, max, pos; + int rc; + + min = 0; + max = dvec->len; + if (lower) *lower = -1; + if (higher) *higher = -1; + + while (min < max) { + pos = min + (max - min) / 2; + rc = search_cmp(dvec_at(dvec, pos), user); + if (!rc) { + if (lower && pos > 0) + *lower = (ssize_t) pos - 1; + if (higher && pos < dvec->len - 1) + *higher = (ssize_t) pos + 1; + return (ssize_t) pos; + } else if (rc > 0) { + if (pos + 1 == max) { + if (lower) *lower = (ssize_t) min; + if (higher && min < dvec->len - 1) + *higher = (ssize_t) min + 1; + break; + } + min = pos + 1; + } else { + if (min == pos) { + if (higher) *higher = (ssize_t) min; + if (lower && min > 0) + *lower = (ssize_t) min - 1; + break; + } + max = pos; + } + } + + return -1; +} diff --git a/lib/libdvec/src/test.c b/lib/libdvec/src/test.c new file mode 100644 index 0000000..af0ee24 --- /dev/null +++ b/lib/libdvec/src/test.c @@ -0,0 +1,77 @@ +#include "allocator.h" +#include "dvec.h" + +#include <err.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +static const char *dvec_err[] = { + DVEC_STRERR_INIT +}; + +bool +str_sort_order(const void *p1, const void *p2, const void *user) +{ + const char *s1 = *(const char **)p1; + const char *s2 = *(const char **)p2; + + return strcmp(s1, s2) <= 0; +} + +int +str_search(const void *p, const void *user) +{ + const char *str = *(const char **)p, *cmp = user; + + return strcmp(cmp, str); +} + +int +main(int argc, const char **argv) +{ + struct dvec dvec, *fused; + ssize_t below, above, on; + const char **val, *tmp; + int i, rc; + + rc = dvec_init(&dvec, sizeof(const char *), 16, &stdlib_heap_allocator); + if (rc) DVEC_ERR(dvec_err, rc); + + for (i = 1; i < argc; i++) { + rc = dvec_add_back(&dvec, 1); + if (rc) DVEC_ERR(dvec_err, rc); + val = dvec_back(&dvec); + *val = argv[i]; + } + + rc = dvec_quick_sort_ex(&dvec, &stdlib_heap_allocator, &tmp, + false, str_sort_order, NULL); + if (rc < 0) DVEC_ERR(dvec_err, rc); + + on = dvec_binary_search(&dvec, str_search, "abc", &below, &above); + + for (DVEC_ITER(&dvec, val)) + printf("%s\n", *val); + + printf("dvec len: %lu\n", dvec.len); + + printf("\n"); + printf("below: %li\n", below); + printf("on: %li\n", on); + printf("above: %li\n", above); + printf("\n"); + + fused = dvec_alloc_fused(sizeof(const char *), 0, + &stdlib_heap_allocator, &rc); + if (!fused) DVEC_ERR(dvec_err, rc); + + printf("fused: %p %p (+%li)\n", (void *) fused, + fused->data, fused->data - (uint8_t *) fused); + + rc = dvec_free(fused); + if (rc) DVEC_ERR(dvec_err, rc); + + dvec_deinit(&dvec); +} |
