array.h (7752B)
1 /* 2 * This file is part of the Lumidify ToolKit (LTK) 3 * Copyright (c) 2020, 2023 lumidify <nobody@lumidify.org> 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a copy 6 * of this software and associated documentation files (the "Software"), to deal 7 * in the Software without restriction, including without limitation the rights 8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 * copies of the Software, and to permit persons to whom the Software is 10 * furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice shall be included in all 13 * copies or substantial portions of the Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 * SOFTWARE. 22 */ 23 24 #ifndef _LTK_ARRAY_H_ 25 #define _LTK_ARRAY_H_ 26 27 #include <stdio.h> 28 #include <stdlib.h> 29 30 #include "util.h" 31 #include "memory.h" 32 33 /* FIXME: make this work on more compilers? */ 34 #if (defined(__GNUC__) || defined(__clang__)) 35 #define LTK_UNUSED_FUNC __attribute__((unused)) 36 #else 37 #define LTK_UNUSED_FUNC 38 #endif 39 40 #define LTK_ARRAY_INIT_DECL_BASE(name, type, storage) \ 41 typedef struct { \ 42 type *buf; \ 43 size_t buf_size; \ 44 size_t len; \ 45 } ltk_array_##name; \ 46 \ 47 LTK_UNUSED_FUNC storage ltk_array_##name *ltk_array_create_##name(size_t initial_len); \ 48 LTK_UNUSED_FUNC storage type ltk_array_pop_##name(ltk_array_##name *ar); \ 49 LTK_UNUSED_FUNC storage void ltk_array_prepare_gap_##name(ltk_array_##name *ar, size_t index, size_t len); \ 50 LTK_UNUSED_FUNC storage void ltk_array_insert_##name(ltk_array_##name *ar, size_t index, type *elem, size_t len); \ 51 LTK_UNUSED_FUNC storage void ltk_array_resize_##name(ltk_array_##name *ar, size_t size); \ 52 LTK_UNUSED_FUNC storage void ltk_array_destroy_##name(ltk_array_##name *ar); \ 53 LTK_UNUSED_FUNC storage void ltk_array_clear_##name(ltk_array_##name *ar); \ 54 LTK_UNUSED_FUNC storage void ltk_array_append_##name(ltk_array_##name *ar, type elem); \ 55 LTK_UNUSED_FUNC storage void ltk_array_destroy_deep_##name(ltk_array_##name *ar, void (*destroy_func)(type)); \ 56 LTK_UNUSED_FUNC storage type ltk_array_get_safe_##name(ltk_array_##name *ar, size_t index); \ 57 LTK_UNUSED_FUNC storage void ltk_array_set_safe_##name(ltk_array_##name *ar, size_t index, type e); 58 59 #define LTK_ARRAY_INIT_IMPL_BASE(name, type, storage) \ 60 LTK_UNUSED_FUNC storage ltk_array_##name * \ 61 ltk_array_create_##name(size_t initial_len) { \ 62 if (initial_len == 0) \ 63 ltk_fatal("Array length is zero\n"); \ 64 ltk_array_##name *ar = ltk_malloc(sizeof(ltk_array_##name)); \ 65 ar->buf = ltk_reallocarray(NULL, initial_len, sizeof(type)); \ 66 ar->buf_size = initial_len; \ 67 ar->len = 0; \ 68 return ar; \ 69 } \ 70 \ 71 LTK_UNUSED_FUNC storage type \ 72 ltk_array_pop_##name(ltk_array_##name *ar) { \ 73 if (ar->len == 0) \ 74 ltk_fatal("Array empty; cannot pop.\n"); \ 75 ar->len--; \ 76 return ar->buf[ar->len]; \ 77 } \ 78 \ 79 /* FIXME: having this function in the public interface is ugly */ \ 80 LTK_UNUSED_FUNC storage void \ 81 ltk_array_prepare_gap_##name(ltk_array_##name *ar, size_t index, size_t len) { \ 82 if (index > ar->len) \ 83 ltk_fatal("Array index out of bounds\n"); \ 84 ltk_array_resize_##name(ar, ar->len + len); \ 85 ar->len += len; \ 86 if (ar->len - len == index) \ 87 return; \ 88 memmove(ar->buf + index + len, ar->buf + index, \ 89 (ar->len - len - index) * sizeof(type)); \ 90 } \ 91 \ 92 LTK_UNUSED_FUNC storage void \ 93 ltk_array_insert_##name(ltk_array_##name *ar, size_t index, type *elem, size_t len) { \ 94 ltk_array_prepare_gap_##name(ar, index, len); \ 95 for (size_t i = 0; i < len; i++) { \ 96 ar->buf[index + i] = elem[i]; \ 97 } \ 98 } \ 99 \ 100 LTK_UNUSED_FUNC storage void \ 101 ltk_array_append_##name(ltk_array_##name *ar, type elem) { \ 102 if (ar->len == ar->buf_size) \ 103 ltk_array_resize_##name(ar, ar->len + 1); \ 104 ar->buf[ar->len++] = elem; \ 105 } \ 106 \ 107 LTK_UNUSED_FUNC storage void \ 108 ltk_array_clear_##name(ltk_array_##name *ar) { \ 109 ar->len = 0; \ 110 ltk_array_resize_##name(ar, 1); \ 111 } \ 112 \ 113 LTK_UNUSED_FUNC storage void \ 114 ltk_array_resize_##name(ltk_array_##name *ar, size_t len) { \ 115 size_t new_size = ideal_array_size(ar->buf_size, len); \ 116 if (new_size != ar->buf_size) { \ 117 ar->buf = ltk_reallocarray(ar->buf, new_size, sizeof(type)); \ 118 ar->buf_size = new_size; \ 119 ar->len = ar->len < new_size ? ar->len : new_size; \ 120 } \ 121 } \ 122 \ 123 LTK_UNUSED_FUNC storage void \ 124 ltk_array_destroy_##name(ltk_array_##name *ar) { \ 125 if (!ar) \ 126 return; \ 127 ltk_free(ar->buf); \ 128 ltk_free(ar); \ 129 } \ 130 \ 131 LTK_UNUSED_FUNC storage void \ 132 ltk_array_destroy_deep_##name(ltk_array_##name *ar, void (*destroy_func)(type)) { \ 133 if (!ar) \ 134 return; \ 135 for (size_t i = 0; i < ar->len; i++) { \ 136 destroy_func(ar->buf[i]); \ 137 } \ 138 ltk_array_destroy_##name(ar); \ 139 } \ 140 \ 141 LTK_UNUSED_FUNC storage type \ 142 ltk_array_get_safe_##name(ltk_array_##name *ar, size_t index) { \ 143 if (index >= ar->len) \ 144 ltk_fatal("Index out of bounds.\n"); \ 145 return ar->buf[index]; \ 146 } \ 147 \ 148 LTK_UNUSED_FUNC storage void \ 149 ltk_array_set_safe_##name(ltk_array_##name *ar, size_t index, type e) { \ 150 if (index >= ar->len) \ 151 ltk_fatal("Index out of bounds.\n"); \ 152 ar->buf[index] = e; \ 153 } 154 155 #define ltk_array(name) ltk_array_##name 156 #define ltk_array_create(name, initial_len) ltk_array_create_##name(initial_len) 157 #define ltk_array_pop(name, ar) ltk_array_pop_##name(ar) 158 #define ltk_array_insert(name, ar, index, elem, len) ltk_array_insert_##name(ar, index, elem, len) 159 #define ltk_array_resize(name, ar, size) ltk_array_resize_##name(ar, size) 160 #define ltk_array_destroy(name, ar) ltk_array_destroy_##name(ar) 161 #define ltk_array_clear(name, ar) ltk_array_clear_##name(ar) 162 #define ltk_array_append(name, ar, elem) ltk_array_append_##name(ar, elem) 163 #define ltk_array_destroy_deep(name, ar, destroy_func) ltk_array_destroy_deep_##name(ar, destroy_func) 164 #define ltk_array_length(ar) ((ar)->len) 165 #define ltk_array_get(ar, index) ((ar)->buf[index]) 166 #define ltk_array_get_safe(name, ar, index) ltk_array_get_safe_##name(ar, index) 167 #define ltk_array_set_safe(name, ar, index, e) ltk_array_set_safe_##name(ar, index, e) 168 169 #define LTK_ARRAY_INIT_DECL(name, type) LTK_ARRAY_INIT_DECL_BASE(name, type,) 170 #define LTK_ARRAY_INIT_IMPL(name, type) LTK_ARRAY_INIT_IMPL_BASE(name, type,) 171 #define LTK_ARRAY_INIT_DECL_STATIC(name, type) LTK_ARRAY_INIT_DECL_BASE(name, type, static) 172 #define LTK_ARRAY_INIT_IMPL_STATIC(name, type) LTK_ARRAY_INIT_IMPL_BASE(name, type, static) 173 174 #endif /* _LTK_ARRAY_H_ */