/* * Copyright © 2020 Benjamin Otte * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Authors: Benjamin Otte */ #include G_BEGIN_DECLS #ifndef GDK_ARRAY_TYPE_NAME #define GDK_ARRAY_TYPE_NAME GdkArray #endif #ifndef GDK_ARRAY_NAME #define GDK_ARRAY_NAME gdk_array #endif #ifndef GDK_ARRAY_ELEMENT_TYPE #define GDK_ARRAY_ELEMENT_TYPE gpointer #endif #ifdef GDK_ARRAY_PREALLOC #if GDK_ARRAY_PREALLOC == 0 #undef GDK_ARRAY_PREALLOC #endif #endif #ifdef GDK_ARRAY_NULL_TERMINATED #define GDK_ARRAY_REAL_SIZE(_size) ((_size) + 1) #define GDK_ARRAY_MAX_SIZE (G_MAXSIZE / sizeof (_T_) - 1) #else #define GDK_ARRAY_REAL_SIZE(_size) (_size) #define GDK_ARRAY_MAX_SIZE (G_MAXSIZE / sizeof (_T_)) #endif /* make this readable */ #define _T_ GDK_ARRAY_ELEMENT_TYPE #define GdkArray GDK_ARRAY_TYPE_NAME #define gdk_array_paste_more(GDK_ARRAY_NAME, func_name) GDK_ARRAY_NAME ## _ ## func_name #define gdk_array_paste(GDK_ARRAY_NAME, func_name) gdk_array_paste_more (GDK_ARRAY_NAME, func_name) #define gdk_array(func_name) gdk_array_paste (GDK_ARRAY_NAME, func_name) typedef struct GdkArray GdkArray; struct GdkArray { _T_ *start; _T_ *end; _T_ *end_allocation; #ifdef GDK_ARRAY_PREALLOC _T_ preallocated[GDK_ARRAY_REAL_SIZE(GDK_ARRAY_PREALLOC)]; #endif }; /* no G_GNUC_UNUSED here, if you don't use an array type, remove it. */ static inline void gdk_array(init) (GdkArray *self) { #ifdef GDK_ARRAY_PREALLOC self->start = self->preallocated; self->end = self->start; self->end_allocation = self->start + GDK_ARRAY_PREALLOC; #ifdef GDK_ARRAY_NULL_TERMINATED *self->start = *(_T_[1]) { 0 }; #endif #else self->start = NULL; self->end = NULL; self->end_allocation = NULL; #endif } G_GNUC_UNUSED static inline gsize gdk_array(get_capacity) (const GdkArray *self) { return self->end_allocation - self->start; } G_GNUC_UNUSED static inline gsize gdk_array(get_size) (const GdkArray *self) { return self->end - self->start; } static inline void gdk_array(free_elements) (_T_ *start, _T_ *end) { #ifdef GDK_ARRAY_FREE_FUNC _T_ *e; for (e = start; e < end; e++) #ifdef GDK_ARRAY_BY_VALUE GDK_ARRAY_FREE_FUNC (e); #else GDK_ARRAY_FREE_FUNC (*e); #endif #endif } /* no G_GNUC_UNUSED here */ static inline void gdk_array(clear) (GdkArray *self) { gdk_array(free_elements) (self->start, self->end); #ifdef GDK_ARRAY_PREALLOC if (self->start != self->preallocated) #endif g_free (self->start); gdk_array(init) (self); } /* * gdk_array_steal: * @self: the array * * Steals all data in the array and clears the array. * * If you need to know the size of the data, you should query it * beforehand. * * Returns: The array's data **/ G_GNUC_UNUSED static inline _T_ * gdk_array(steal) (GdkArray *self) { _T_ *result; #ifdef GDK_ARRAY_PREALLOC if (self->start == self->preallocated) { gsize size = GDK_ARRAY_REAL_SIZE (gdk_array(get_size) (self)); result = g_new (_T_, size); memcpy (result, self->preallocated, sizeof (_T_) * size); } else #endif result = self->start; gdk_array(init) (self); return result; } G_GNUC_UNUSED static inline _T_ * gdk_array(get_data) (const GdkArray *self) { return self->start; } G_GNUC_UNUSED static inline _T_ * gdk_array(index) (const GdkArray *self, gsize pos) { return self->start + pos; } G_GNUC_UNUSED static inline gboolean gdk_array(is_empty) (const GdkArray *self) { return self->end == self->start; } G_GNUC_UNUSED static inline void gdk_array(reserve) (GdkArray *self, gsize n) { gsize new_capacity, size, capacity; if (G_UNLIKELY (n > GDK_ARRAY_MAX_SIZE)) g_error ("requesting array size of %zu, but maximum size is %zu", n, GDK_ARRAY_MAX_SIZE); capacity = gdk_array(get_capacity) (self); if (n <= capacity) return; size = gdk_array(get_size) (self); /* capacity * 2 can overflow, that's why we MAX() */ new_capacity = MAX (GDK_ARRAY_REAL_SIZE (n), capacity * 2); #ifdef GDK_ARRAY_PREALLOC if (self->start == self->preallocated) { self->start = g_new (_T_, new_capacity); memcpy (self->start, self->preallocated, sizeof (_T_) * GDK_ARRAY_REAL_SIZE (size)); } else #endif #ifdef GDK_ARRAY_NULL_TERMINATED if (self->start == NULL) { self->start = g_new (_T_, new_capacity); *self->start = *(_T_[1]) { 0 }; } else #endif self->start = g_renew (_T_, self->start, new_capacity); self->end = self->start + size; self->end_allocation = self->start + new_capacity; #ifdef GDK_ARRAY_NULL_TERMINATED self->end_allocation--; #endif } G_GNUC_UNUSED static inline void gdk_array(splice) (GdkArray *self, gsize pos, gsize removed, gboolean stolen, #ifdef GDK_ARRAY_BY_VALUE const _T_ *additions, #else _T_ *additions, #endif gsize added) { gsize size; gsize remaining; size = gdk_array(get_size) (self); g_assert (pos + removed <= size); remaining = size - pos - removed; if (!stolen) gdk_array(free_elements) (gdk_array(index) (self, pos), gdk_array(index) (self, pos + removed)); gdk_array(reserve) (self, size - removed + added); if (GDK_ARRAY_REAL_SIZE (remaining) && removed != added) memmove (gdk_array(index) (self, pos + added), gdk_array(index) (self, pos + removed), GDK_ARRAY_REAL_SIZE (remaining) * sizeof (_T_)); if (added) { if (additions) memcpy (gdk_array(index) (self, pos), additions, added * sizeof (_T_)); #ifndef GDK_ARRAY_NO_MEMSET else memset (gdk_array(index) (self, pos), 0, added * sizeof (_T_)); #endif } /* might overflow, but does the right thing */ self->end += added - removed; } G_GNUC_UNUSED static void gdk_array(set_size) (GdkArray *self, gsize new_size) { gsize old_size = gdk_array(get_size) (self); if (new_size > old_size) gdk_array(splice) (self, old_size, 0, FALSE, NULL, new_size - old_size); else gdk_array(splice) (self, new_size, old_size - new_size, FALSE, NULL, 0); } G_GNUC_UNUSED static void gdk_array(append) (GdkArray *self, #ifdef GDK_ARRAY_BY_VALUE _T_ *value) #else _T_ value) #endif { gdk_array(splice) (self, gdk_array(get_size) (self), 0, FALSE, #ifdef GDK_ARRAY_BY_VALUE value, #else &value, #endif 1); } #ifdef GDK_ARRAY_BY_VALUE G_GNUC_UNUSED static _T_ * gdk_array(get) (const GdkArray *self, gsize pos) { return gdk_array(index) (self, pos); } #else G_GNUC_UNUSED static _T_ gdk_array(get) (const GdkArray *self, gsize pos) { return *gdk_array(index) (self, pos); } #endif #ifndef GDK_ARRAY_NO_UNDEF #undef _T_ #undef GdkArray #undef gdk_array_paste_more #undef gdk_array_paste #undef gdk_array #undef GDK_ARRAY_REAL_SIZE #undef GDK_ARRAY_MAX_SIZE #undef GDK_ARRAY_BY_VALUE #undef GDK_ARRAY_ELEMENT_TYPE #undef GDK_ARRAY_FREE_FUNC #undef GDK_ARRAY_NAME #undef GDK_ARRAY_NULL_TERMINATED #undef GDK_ARRAY_PREALLOC #undef GDK_ARRAY_TYPE_NAME #undef GDK_ARRAY_NO_MEMSET #endif G_END_DECLS