1
0
Fork 0
mirror of https://github.com/yshui/picom.git synced 2025-04-21 18:03:02 -04:00

utils: add dynarr

An sds-like dynamic array implementation.

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
This commit is contained in:
Yuxuan Shui 2024-06-04 16:44:55 +01:00
parent ba3093a913
commit 9579cde5bd
No known key found for this signature in database
GPG key ID: D3A4405BE6CC17F4

168
src/utils/dynarr.h Normal file
View file

@ -0,0 +1,168 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
#pragma once
#include <stddef.h>
#include "misc.h"
/// Dynamic array implementation, design is similar to sds [0].
///
/// Note this is not very type safe, be sure to annotate wherever you use this. And the
/// array won't have a fixed address in memory.
///
/// [0]: https://github.com/antirez/sds
struct dynarr_header {
size_t len;
size_t cap;
};
static inline void *dynarr_new_impl(size_t size, size_t nelem) {
struct dynarr_header *ret = calloc(1, sizeof(struct dynarr_header) + size * nelem);
allocchk(ret);
ret->len = 0;
ret->cap = nelem;
return ret + 1;
}
static inline void *dynarr_reserve_impl(size_t size, void *arr, size_t nelem) {
struct dynarr_header *hdr = (struct dynarr_header *)arr - 1;
if (hdr->len + nelem > hdr->cap) {
hdr->cap = max2(max2(1, hdr->cap * 2), hdr->len + nelem);
auto new_hdr = realloc(hdr, sizeof(struct dynarr_header) + hdr->cap * size);
allocchk(new_hdr);
hdr = new_hdr;
}
return hdr + 1;
}
static inline void *dynarr_shrink_to_fit_impl(size_t size, void *arr) {
struct dynarr_header *hdr = (struct dynarr_header *)arr - 1;
if (hdr->len < hdr->cap) {
hdr->cap = hdr->len;
auto new_hdr = realloc(hdr, sizeof(struct dynarr_header) + hdr->cap * size);
allocchk(new_hdr);
hdr = new_hdr;
}
return hdr + 1;
}
static inline void dynarr_remove_impl(size_t size, void *arr, size_t idx) {
struct dynarr_header *hdr = (struct dynarr_header *)arr - 1;
BUG_ON(idx >= hdr->len);
memmove((char *)arr + idx * size, (char *)arr + (idx + 1) * size,
(hdr->len - idx - 1) * size);
hdr->len--;
}
static inline void dynarr_remove_swap_impl(size_t size, void *arr, size_t idx) {
struct dynarr_header *hdr = (struct dynarr_header *)arr - 1;
BUG_ON(idx >= hdr->len);
hdr->len--;
if (idx != hdr->len) {
memcpy((char *)arr + idx * size, (char *)arr + hdr->len * size, size);
}
}
/// Create a new dynamic array with capacity `cap` for type `type`.
#define dynarr_new(type, cap) ((type *)dynarr_new_impl(sizeof(type), cap))
/// Free a dynamic array, destructing each element with `dtor`.
#define dynarr_free(arr, dtor) \
do { \
dynarr_clear(arr, dtor); \
free((struct dynarr_header *)(arr)-1); \
(arr) = NULL; \
} while (0)
#define dynarr_free_pod(arr) dynarr_free(arr, (void (*)(typeof(arr)))NULL)
/// Expand the capacity of the array so it can hold at least `nelem` more elements
#define dynarr_reserve(arr, nelem) \
((arr) = dynarr_reserve_impl(sizeof(typeof(*(arr))), (void *)(arr), (nelem)))
/// Resize the array to `newlen`. If `newlen` is greater than the current length, the
/// new elements will be initialized with `init`. If `newlen` is less than the current
/// length, the excess elements will be destructed with `dtor`.
#define dynarr_resize(arr, newlen, init, dtor) \
do { \
BUG_ON((arr) == NULL); \
if ((newlen) > dynarr_cap(arr)) { \
dynarr_reserve((arr), (newlen)-dynarr_cap(arr)); \
} \
if ((init) != NULL) { \
for (size_t i = dynarr_len(arr); i < (newlen); i++) { \
(init)((arr) + i); \
} \
} \
if ((dtor) != NULL) { \
for (size_t i = (newlen); i < dynarr_len(arr); i++) { \
(dtor)((arr) + i); \
} \
} \
dynarr_len(arr) = (newlen); \
} while (0)
/// Resize the array to `newlen`, for types that can be trivially constructed and
/// destructed.
#define dynarr_resize_pod(arr, newlen) \
dynarr_resize(arr, newlen, (void (*)(typeof(arr)))NULL, (void (*)(typeof(arr)))NULL)
/// Shrink the capacity of the array to its length.
#define dynarr_shrink_to_fit(arr) \
((arr) = dynarr_shrink_to_fit_impl(sizeof(typeof(*(arr))), (void *)(arr)))
/// Push an element to the end of the array
#define dynarr_push(arr, item) \
do { \
dynarr_reserve(arr, 1); \
(arr)[dynarr_len(arr)++] = (item); \
} while (0)
/// Pop an element from the end of the array
#define dynarr_pop(arr) ((arr)[--dynarr_len(arr)])
/// Remove an element from the array by shifting the rest of the array forward.
#define dynarr_remove(arr, idx) \
dynarr_remove_impl(sizeof(typeof(*(arr))), (void *)(arr), idx)
/// Remove an element from the array by swapping it with the last element.
#define dynarr_remove_swap(arr, idx) \
dynarr_remove_swap_impl(sizeof(typeof(*(arr))), (void *)(arr), idx)
/// Return the length of the array
#define dynarr_len(arr) (((struct dynarr_header *)(arr)-1)->len)
/// Return the capacity of the array
#define dynarr_cap(arr) (((struct dynarr_header *)(arr)-1)->cap)
/// Return the last element of the array
#define dynarr_last(arr) ((arr)[dynarr_len(arr) - 1])
/// Return the pointer just past the last element of the array
#define dynarr_end(arr) ((arr) + dynarr_len(arr))
/// Return whether the array is empty
#define dynarr_is_empty(arr) (dynarr_len(arr) == 0)
/// Clear the array, destructing each element with `dtor`.
#define dynarr_clear(arr, dtor) \
do { \
if ((dtor) != NULL) { \
for (size_t i = 0; i < dynarr_len(arr); i++) { \
(dtor)((arr) + i); \
} \
} \
dynarr_len(arr) = 0; \
} while (0)
#define dynarr_clear_pod(arr) dynarr_clear(arr, (void (*)(typeof(arr)))NULL)
/// Extend the array by copying `n` elements from `other`
#define dynarr_extend_from(arr, other, n) \
do { \
if ((n) > 0) { \
dynarr_reserve(arr, n); \
memcpy(dynarr_end(arr), other, sizeof(typeof(*(arr))[(n)])); \
dynarr_len(arr) += (n); \
} \
} while (0)
/// Extend the array by `n` elements, initializing them with `init`
#define dynarr_extend_with(arr, init, n) \
do { \
if ((n) > 0) { \
dynarr_resize(arr, dynarr_len(arr) + (n), init, \
(void (*)(typeof(arr)))NULL); \
} \
} while (0)
#define dynarr_foreach(arr, i) for (typeof(arr)(i) = (arr); (i) < dynarr_end(arr); (i)++)
#define dynarr_foreach_rev(arr, i) \
for (typeof(arr)(i) = dynarr_end(arr) - 1; (i) >= (arr); (i)--)