libdvec-c

C memory vector library
git clone https://git.sinitax.com/sinitax/libdvec-c
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

dvec.c (10684B)


      1#include "dvec.h"
      2
      3#include <stddef.h>
      4#include <string.h>
      5
      6#define ERR(x) ((x) >= 0 ? DVEC_BAD : (x))
      7
      8struct sort_order_user_proxy {
      9	dvec_sort_order_fn order_fn;
     10	const void *user;
     11};
     12
     13int
     14dvec_init(struct dvec *dvec, size_t dsize, size_t cap,
     15	const struct allocator *allocator)
     16{
     17	int rc = 0;
     18
     19	LIBDVEC_ABORT_ON_ARGS(!dvec || !dsize || !allocator);
     20
     21	dvec->allocator = allocator;
     22	dvec->dsize = dsize;
     23	dvec->cap = cap;
     24	dvec->len = 0;
     25	dvec->data = NULL;
     26	dvec->locked = false;
     27	dvec->fused = false;
     28	if (dvec->cap) {
     29		dvec->data = allocator->alloc(allocator, cap * dsize, &rc);
     30		LIBDVEC_ABORT_ON_ALLOC(!dvec->data);
     31		if (!dvec->data) return ERR(rc);
     32	}
     33
     34	return DVEC_OK;
     35}
     36
     37int
     38dvec_deinit(struct dvec *dvec)
     39{
     40	int rc;
     41
     42	LIBDVEC_ABORT_ON_ARGS(!dvec);
     43
     44	rc = dvec->allocator->free(dvec->allocator, dvec->data);
     45	LIBDVEC_ABORT_ON_ALLOC(rc);
     46
     47	return rc >= 0 ? DVEC_OK : ERR(rc);
     48}
     49
     50struct dvec *
     51dvec_alloc(size_t dsize, size_t cap,
     52	const struct allocator *allocator, int *rc)
     53{
     54	struct dvec *dvec;
     55	int _rc;
     56
     57	LIBDVEC_ABORT_ON_ARGS(!allocator);
     58
     59	dvec = allocator->alloc(allocator, sizeof(struct dvec), &_rc);
     60	LIBDVEC_ABORT_ON_ALLOC(!dvec);
     61	if (!dvec) {
     62		if (rc) *rc = ERR(_rc);
     63		return NULL;
     64	}
     65
     66	_rc = dvec_init(dvec, dsize, cap, allocator);
     67	if (_rc < 0) {
     68		if (rc) *rc = _rc;
     69		allocator->free(allocator, dvec);
     70		return NULL;
     71	}
     72
     73	return dvec;
     74}
     75
     76static size_t
     77aligned_size(size_t size, size_t dsize)
     78{
     79	return size + (size % dsize ? dsize - size % dsize : 0);
     80}
     81
     82struct dvec *
     83dvec_alloc_fused(size_t dsize, size_t cap,
     84	const struct allocator *allocator, int *rc)
     85{
     86	struct dvec *dvec;
     87	size_t off;
     88	int _rc = 0;
     89
     90	LIBDVEC_ABORT_ON_ARGS(!allocator);
     91
     92	off = aligned_size(sizeof(struct dvec), dsize);
     93	dvec = allocator->alloc(allocator, off + cap * dsize, &_rc);
     94	if (!dvec) {
     95		if (_rc) *rc = ERR(_rc);
     96		return NULL;
     97	}
     98
     99	dvec->allocator = allocator;
    100	dvec->dsize = dsize;
    101	dvec->cap = cap;
    102	dvec->len = 0;
    103	dvec->data = (uint8_t *) dvec + off;
    104	dvec->locked = false;
    105	dvec->fused = true;
    106
    107	return dvec;
    108}
    109
    110int
    111dvec_free(struct dvec *dvec)
    112{
    113	const struct allocator *allocator;
    114	int rc;
    115
    116	if (!dvec) return DVEC_OK;
    117
    118	allocator = dvec->allocator;
    119
    120	if (dvec->fused) {
    121		rc = allocator->free(allocator, dvec);
    122		if (rc < 0) return rc;
    123	} else {
    124		rc = dvec_deinit(dvec);
    125		if (rc) return rc;
    126
    127		rc = allocator->free(allocator, dvec);
    128		LIBDVEC_ABORT_ON_ALLOC(rc);
    129		if (rc < 0) return rc;
    130	}
    131
    132	return DVEC_OK;
    133}
    134
    135int
    136dvec_copy(struct dvec *dst, struct dvec *src)
    137{
    138	int rc;
    139
    140	LIBDVEC_ABORT_ON_ARGS(!dst || !src || dst->dsize != src->dsize);
    141
    142	rc = dvec_reserve(dst, src->len);
    143	if (rc) return rc;
    144
    145	dst->len = src->len;
    146	memcpy(dst->data, src->data, src->len * src->dsize);
    147
    148	return DVEC_OK;
    149}
    150
    151void
    152dvec_move(struct dvec *dst, struct dvec *src)
    153{
    154	LIBDVEC_ABORT_ON_ARGS(!dst || !src);
    155
    156	memcpy(dst, src, sizeof(struct dvec));
    157}
    158
    159void
    160dvec_swap(struct dvec *dst, struct dvec *src)
    161{
    162	struct dvec tmp;
    163
    164	LIBDVEC_ABORT_ON_ARGS(!dst || !src);
    165
    166	memcpy(&tmp, dst, sizeof(struct dvec));
    167	memcpy(dst, src, sizeof(struct dvec));
    168	memcpy(src, &tmp, sizeof(struct dvec));
    169}
    170
    171void
    172dvec_clear(struct dvec *dvec)
    173{
    174	LIBDVEC_ABORT_ON_ARGS(!dvec);
    175
    176	dvec->len = 0;
    177}
    178
    179int
    180dvec_reserve(struct dvec *dvec, size_t len)
    181{
    182	void *data;
    183	size_t cap, off;
    184	int rc = 1;
    185
    186	LIBDVEC_ABORT_ON_ARGS(!dvec);
    187
    188	if (len <= dvec->cap) return DVEC_OK;
    189
    190	if (dvec->locked) return DVEC_LOCKED;
    191
    192	cap = 2 * dvec->cap;
    193	if (len > cap) cap = len;
    194
    195	if (dvec->fused) {
    196		off = aligned_size(sizeof(struct dvec), dvec->dsize);
    197		data = dvec->allocator->realloc(dvec->allocator, dvec,
    198			off + dvec->cap * dvec->dsize, &rc);
    199		LIBDVEC_ABORT_ON_ALLOC(!data);
    200		if (!data) return ERR(rc);
    201		dvec = data;
    202		dvec->data = (uint8_t *) dvec + off;
    203	} else {
    204		data = dvec->allocator->realloc(dvec->allocator,
    205			dvec->data, cap * dvec->dsize, &rc);
    206		LIBDVEC_ABORT_ON_ALLOC(!data);
    207		if (!data) return ERR(rc);
    208		dvec->data = data;
    209	}
    210
    211	dvec->cap = cap;
    212
    213	return DVEC_OK;
    214}
    215
    216int
    217dvec_shrink(struct dvec *dvec)
    218{
    219	void *data;
    220	size_t cap, off;
    221	int rc = 0;
    222
    223	LIBDVEC_ABORT_ON_ARGS(!dvec);
    224
    225	if (dvec->locked) return DVEC_LOCKED;
    226
    227	cap = dvec->len;
    228
    229	if (dvec->fused) {
    230		off = aligned_size(sizeof(struct dvec), dvec->dsize);
    231		data = dvec->allocator->realloc(dvec->allocator, dvec,
    232			off + cap * dvec->dsize, &rc);
    233		LIBDVEC_ABORT_ON_ALLOC(!data);
    234		if (!data) return ERR(rc);
    235		dvec = data;
    236		dvec->data = (uint8_t *) dvec + off;
    237	} else if (!dvec->len) {
    238		rc = dvec->allocator->free(dvec->allocator, dvec->data);
    239		LIBDVEC_ABORT_ON_ALLOC(rc);
    240		if (rc < 0) return rc;
    241	} else {
    242		data = dvec->allocator->realloc(dvec->allocator,
    243			&dvec->data, cap * dvec->dsize, &rc);
    244		LIBDVEC_ABORT_ON_ALLOC(!data);
    245		if (!data) return ERR(rc);
    246		dvec->data = data;
    247	}
    248
    249	dvec->cap = cap;
    250
    251	return DVEC_OK;
    252}
    253
    254int
    255dvec_add(struct dvec *dvec, size_t index, size_t len)
    256{
    257	int rc;
    258
    259	LIBDVEC_ABORT_ON_ARGS(!dvec || index > dvec->len);
    260
    261	rc = dvec_reserve(dvec, dvec->len + len);
    262	if (rc) return rc;
    263
    264	if (index < dvec->len) {
    265		memmove(dvec->data + (index + len) * dvec->dsize,
    266			dvec->data + index * dvec->dsize,
    267			(dvec->len - index) * dvec->dsize);
    268	}
    269
    270	dvec->len += len;
    271
    272	return DVEC_OK;
    273}
    274
    275void
    276dvec_rm(struct dvec *dvec, size_t index, size_t count)
    277{
    278	LIBDVEC_ABORT_ON_ARGS(!dvec || index + count > dvec->len);
    279
    280	if (index + count < dvec->len) {
    281		memmove(dvec->data + index * dvec->dsize,
    282			dvec->data + (index + count) * dvec->dsize,
    283			(dvec->len - (index + count)) * dvec->dsize);
    284	}
    285
    286	dvec->len -= count;
    287}
    288
    289void
    290dvec_replace(struct dvec *dvec, size_t index, const void *data, size_t count)
    291{
    292	LIBDVEC_ABORT_ON_ARGS(!dvec || !data || index + count > dvec->len);
    293
    294	memcpy(dvec->data + index * dvec->dsize, data, count * dvec->dsize);
    295}
    296
    297void *
    298dvec_iter_fwd(const struct dvec *dvec, const void *p)
    299{
    300	LIBDVEC_ABORT_ON_ARGS(!dvec);
    301	LIBDVEC_ABORT_ON_ARGS(p && (p < dvec->data));
    302	LIBDVEC_ABORT_ON_ARGS(p && (p >= dvec->data + dvec->len * dvec->dsize));
    303	LIBDVEC_ABORT_ON_ARGS(p && ((size_t) (p - dvec->data) % dvec->dsize));
    304
    305	if (p == NULL) {
    306		if (dvec->len > 0)
    307			return dvec->data;
    308		else
    309			return NULL;
    310	} else if ((uint8_t *) p < dvec->data + dvec->dsize * (dvec->len - 1)) {
    311		return (uint8_t *) p + dvec->dsize;
    312	} else {
    313		return NULL;
    314	}
    315}
    316
    317void *
    318dvec_iter_bwd(const struct dvec *dvec, const void *p)
    319{
    320	LIBDVEC_ABORT_ON_ARGS(!dvec);
    321	LIBDVEC_ABORT_ON_ARGS(p && (p < dvec->data));
    322	LIBDVEC_ABORT_ON_ARGS(p && (p >= dvec->data + dvec->len * dvec->dsize));
    323	LIBDVEC_ABORT_ON_ARGS(p && ((size_t) (p - dvec->data) % dvec->dsize));
    324
    325	if (p == NULL) {
    326		if (dvec->len > 0)
    327			return dvec->data + (dvec->len - 1) * dvec->dsize;
    328		else
    329			return NULL;
    330	} else if ((uint8_t *) p > dvec->data) {
    331		return (uint8_t *) p - dvec->dsize;
    332	} else {
    333		return NULL;
    334	}
    335}
    336
    337static void
    338dvec_quick_sort_swap(void *a, void *b, void *tmp, size_t dsize)
    339{
    340	memcpy(tmp, a, dsize);
    341	memcpy(a, b, dsize);
    342	memcpy(b, tmp, dsize);
    343}
    344
    345int
    346dvec_quick_sort(struct dvec *dvec, const struct allocator *allocator,
    347	void *tmp, bool reverse, dvec_sort_order_fn in_order, const void *user)
    348{
    349	const size_t dsize = dvec->dsize;
    350	struct dvec stack;
    351	uint8_t *start, *end;
    352	uint8_t *lo, *hi, *pivot;
    353	bool lo_order, hi_order;
    354	bool modified = false;
    355	int rc;
    356
    357	if (!dvec->len) return DVEC_OK;
    358
    359	rc = dvec_init(&stack, sizeof(uint8_t *), 32, allocator);
    360	if (rc) return rc;
    361
    362	*(uint8_t **)dvec_push(&stack) = dvec_front(dvec);
    363	*(uint8_t **)dvec_push(&stack) = dvec_back(dvec);
    364
    365	while (!dvec_empty(&stack)) {
    366		end = *(uint8_t **)dvec_pop(&stack);
    367		start = *(uint8_t **)dvec_pop(&stack);
    368
    369		if (start == end) continue;
    370
    371		if (start + dsize >= end) {
    372			if (!in_order(start, end, user)) {
    373				dvec_quick_sort_swap(start, end, tmp, dsize);
    374				modified = true;
    375			}
    376			continue;
    377		}
    378
    379		lo = start;
    380		hi = end;
    381		pivot = start + (size_t) (end - start) / dsize / 2 * dsize;
    382
    383		while (lo < hi) {
    384			lo_order = lo != pivot && in_order(lo, pivot, user);
    385			hi_order = hi != pivot && in_order(pivot, hi, user);
    386			if (!lo_order && !hi_order) {
    387				if (lo == pivot) pivot = hi;
    388				else if (hi == pivot) pivot = lo;
    389				dvec_quick_sort_swap(lo, hi, tmp, dsize);
    390				modified = true;
    391			}
    392			if (lo_order) lo += dsize;
    393			if (hi_order) hi -= dsize;
    394		}
    395
    396		if (start != pivot) {
    397			*(uint8_t **)dvec_push(&stack) = start;
    398			*(uint8_t **)dvec_push(&stack) = pivot - dsize;
    399		}
    400
    401		if (end != pivot) {
    402			*(uint8_t **)dvec_push(&stack) = pivot + dsize;
    403			*(uint8_t **)dvec_push(&stack) = end;
    404		}
    405	}
    406
    407	dvec_deinit(&stack);
    408
    409	return modified ? DVEC_MODIFIED : DVEC_OK;
    410}
    411
    412static bool
    413dvec_quick_sort_ex_order(const void *p1, const void *p2, const void *user)
    414{
    415	const struct sort_order_user_proxy *user_proxy = user;
    416	const void *d1 = *(void **)p1, *d2 = *(void **)p2;
    417
    418	return user_proxy->order_fn(d1, d2, user_proxy->user);
    419}
    420
    421int
    422dvec_quick_sort_ex(struct dvec *dvec, const struct allocator *allocator,
    423	void *tmp, bool reverse, dvec_sort_order_fn in_order, const void *user)
    424{
    425	const struct sort_order_user_proxy user_proxy = {
    426		.order_fn = in_order,
    427		.user = user
    428	};
    429	struct dvec ptrmap;
    430	void *p, **pp, **dst, **src;
    431	size_t dsize;
    432	int rc;
    433
    434	if (!dvec->len) return DVEC_OK;
    435
    436	dsize = dvec->dsize;
    437
    438	rc = dvec_init(&ptrmap, sizeof(void *), dvec->len, allocator);
    439	if (rc) return rc;
    440
    441	for (DVEC_ITER(dvec, p))
    442		*(void **)dvec_push(&ptrmap) = p;
    443
    444	rc = dvec_quick_sort(&ptrmap, allocator, tmp, reverse,
    445		dvec_quick_sort_ex_order, &user_proxy);
    446	if (rc) return rc;
    447
    448	for (DVEC_ITER(&ptrmap, pp)) {
    449		if (!*pp || dvec_idx(&ptrmap, pp) == dvec_idx(dvec, *pp))
    450			continue;
    451		dst = pp;
    452		memcpy(tmp, *dst, dsize);
    453		while (1) {
    454			src = dvec_at(&ptrmap, dvec_idx(dvec, *dst));
    455			if (!*src) break;
    456			memcpy(*dst, *src, dsize);
    457			*dst = NULL;
    458			dst = src;
    459		}
    460		memcpy(*dst, tmp, dsize);
    461	}
    462
    463	rc = dvec_deinit(&ptrmap);
    464	if (rc) return rc;
    465
    466	return DVEC_OK;
    467}
    468
    469ssize_t
    470dvec_binary_search(struct dvec *dvec, dvec_search_cmp_fn search_cmp,
    471	void *user, ssize_t *lower, ssize_t *higher)
    472{
    473	size_t min, max, pos;
    474	int rc;
    475
    476	min = 0;
    477	max = dvec->len;
    478	if (lower) *lower = -1;
    479	if (higher) *higher = -1;
    480
    481	while (min < max) {
    482		pos = min + (max - min) / 2;
    483		rc = search_cmp(dvec_at(dvec, pos), user);
    484		if (!rc) {
    485			if (lower && pos > 0)
    486				*lower = (ssize_t) pos - 1;
    487			if (higher && pos < dvec->len - 1)
    488				*higher = (ssize_t) pos + 1;
    489			return (ssize_t) pos;
    490		} else if (rc > 0) {
    491			if (pos + 1 == max) {
    492				if (lower) *lower = (ssize_t) min;
    493				if (higher && min < dvec->len - 1)
    494					*higher = (ssize_t) min + 1;
    495				break;
    496			}
    497			min = pos + 1;
    498		} else {
    499			if (min == pos) {
    500				if (higher) *higher = (ssize_t) min;
    501				if (lower && min > 0)
    502					*lower = (ssize_t) min - 1;
    503				break;
    504			}
    505			max = pos;
    506		}
    507	}
    508
    509	return -1;
    510}