mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-12-21 01:50:53 +00:00
302 lines
10 KiB
C
302 lines
10 KiB
C
|
/*
|
||
|
darray.h
|
||
|
|
||
|
Dynamic arrays
|
||
|
|
||
|
Copyright (C) 2020 Bill Currie <bill@taniwha.org>
|
||
|
|
||
|
Author: Bill Currie <bill@taniwha.org>
|
||
|
Date: 2020/02/17
|
||
|
|
||
|
This program is free software; you can redistribute it and/or
|
||
|
modify it under the terms of the GNU General Public License
|
||
|
as published by the Free Software Foundation; either version 2
|
||
|
of the License, or (at your option) any later version.
|
||
|
|
||
|
This program 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 General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License
|
||
|
along with this program; if not, write to:
|
||
|
|
||
|
Free Software Foundation, Inc.
|
||
|
59 Temple Place - Suite 330
|
||
|
Boston, MA 02111-1307, USA
|
||
|
|
||
|
*/
|
||
|
|
||
|
#ifndef __pr_type_h
|
||
|
#define __pr_type_h
|
||
|
|
||
|
/** \defgroup darray Dynamic Arrays
|
||
|
\ingroup utils
|
||
|
|
||
|
Dynamic array container object
|
||
|
*/
|
||
|
///@{
|
||
|
|
||
|
/** The structure defs for a dynamic array with elements of the given type.
|
||
|
|
||
|
This is just the defs of a struct delcaration: it is useless on its own.
|
||
|
The intended usage is something like:
|
||
|
|
||
|
typedef struct dynamic_array_s DARRAY_TYPE(int) dynamic_array_t;
|
||
|
|
||
|
This allows full flexibility in just how the actual type is used.
|
||
|
|
||
|
The \a size field is the number of elements currently in the array, and
|
||
|
the \a maxSize field is the number of elements the array can hold without
|
||
|
being resized.
|
||
|
|
||
|
The \a grow field specifies the number of elements by which \a maxSize is
|
||
|
to grow when the array needs to be resized. Setting this to 0 prevents
|
||
|
resizing and any attempt to do so is a fatal error.
|
||
|
|
||
|
\param ele_type The type to use for the element array, which is accessed
|
||
|
by the \a a field.
|
||
|
\hideinitializer
|
||
|
*/
|
||
|
#define DARRAY_TYPE(ele_type) \
|
||
|
{ \
|
||
|
size_t size; \
|
||
|
size_t maxSize; \
|
||
|
size_t grow; \
|
||
|
ele_type *a; \
|
||
|
}
|
||
|
|
||
|
/** Clear the array.
|
||
|
|
||
|
If the array can grow, its backing will be freed and maxSize and a reset,
|
||
|
otherwise maxSize and a are left untouched.
|
||
|
|
||
|
\param array *Address* of the array to be modified (ie, pointer to the
|
||
|
array struct instance, not the instance itself: use & for
|
||
|
static instances of the array struct).
|
||
|
\hideinitializer
|
||
|
*/
|
||
|
#define DARRAY_CLEAR(array) \
|
||
|
do { \
|
||
|
__auto_type ar = (array); \
|
||
|
free (ar->a); \
|
||
|
ar->size = 0; \
|
||
|
if (ar->grow) { \
|
||
|
ar->maxSize = 0; \
|
||
|
ar->a = 0; \
|
||
|
} \
|
||
|
} while (0)
|
||
|
|
||
|
/** Set the size of the array.
|
||
|
|
||
|
If the new size is greater than maxSize, and the array can grow (grow is
|
||
|
non-zero), then maxSize will be increased to the smallest multiple of grow
|
||
|
greater than or equal to size (ie, maxSize >= size, maxSize % grow == 0).
|
||
|
|
||
|
Attempting to increase maxSize on an array that cannot grow is an error:
|
||
|
it is assumed that the array struct does not own the backing memory.
|
||
|
|
||
|
\param array *Address* of the array to be modified (ie, pointer to the
|
||
|
array struct instance, not the instance itself: use & for
|
||
|
static instances of the array struct).
|
||
|
\param newSize The new size of the array: newly opened slots are
|
||
|
uninitialized, but old slots retain their values.
|
||
|
\hideinitializer
|
||
|
*/
|
||
|
#define DARRAY_RESIZE(array, newSize) \
|
||
|
do { \
|
||
|
__auto_type ar = (array); \
|
||
|
size_t ns = (newSize); \
|
||
|
if (__builtin_expect (ns > ar->maxSize, 0)) { \
|
||
|
if (__builtin_expect (!ar->grow, 0)) { \
|
||
|
Sys_Error ("Attempt to grow fixed-size darray: %s:%d", \
|
||
|
__FILE__, __LINE__); \
|
||
|
} \
|
||
|
ar->maxSize = ar->grow * ((ns + ar->grow - 1) / ar->grow); \
|
||
|
ar->a = realloc (ar->a, ar->maxSize * sizeof (*ar->a)); \
|
||
|
if (__builtin_expect (!ar->a, 0)) { \
|
||
|
Sys_Error ("failed to realloc darray: %s:%d", \
|
||
|
__FILE__, __LINE__); \
|
||
|
} \
|
||
|
} \
|
||
|
ar->size = ns; \
|
||
|
} while (0)
|
||
|
|
||
|
/** Append a value to the end of the array.
|
||
|
|
||
|
The array is grown by one and the value written to the newly opened slot.
|
||
|
|
||
|
If the new array size is greater than maxSize and the array can be grown,
|
||
|
the array backing will be resized to the next multiple of grow.
|
||
|
|
||
|
Attempting to grow an array that cannot grow is an error: it is assumed
|
||
|
that the array struct does not own the backing memory.
|
||
|
|
||
|
\param array *Address* of the array to be modified (ie, pointer to the
|
||
|
array struct instance, not the instance itself: use & for
|
||
|
static instances of the array struct).
|
||
|
\param value The value to be appended to the array. Must be of a type
|
||
|
compatible with that used for creating the array struct.
|
||
|
\return The appended value: can be assigned to another compatible
|
||
|
value.
|
||
|
\hideinitializer
|
||
|
*/
|
||
|
#define DARRAY_APPEND(array, value) \
|
||
|
({ \
|
||
|
__auto_type ar = (array); \
|
||
|
__auto_type ob = (value); \
|
||
|
size_t sz = ar->size; \
|
||
|
DARRAY_RESIZE (ar, ar->size + 1); \
|
||
|
ar->a[sz] = ob; \
|
||
|
})
|
||
|
|
||
|
/** Open a hole in the array for bulk copying of data.
|
||
|
|
||
|
The array is grown by the requested size, opening a hole at the specified
|
||
|
index. Values beyond the index are copied to just after the newly opened
|
||
|
hole.
|
||
|
|
||
|
If the new array size is greater than maxSize and the array can be grown,
|
||
|
the array backing will be resized to the next multiple of grow.
|
||
|
|
||
|
Attempting to grow an array that cannot grow is an error: it is assumed
|
||
|
that the array struct does not own the backing memory.
|
||
|
|
||
|
\param array *Address* of the array to be modified (ie, pointer to the
|
||
|
array struct instance, not the instance itself: use & for
|
||
|
static instances of the array struct).
|
||
|
\param index The index at which the hole will begin.
|
||
|
\param space The sized of the hole to be opened, in array elements.
|
||
|
\return The *address* of the newly opened hole: can be passed to
|
||
|
memcpy and friends.
|
||
|
|
||
|
memcpy (DARRAY_OPEN_AT(array, index, size), data,
|
||
|
size * sizeof (*data));
|
||
|
\hideinitializer
|
||
|
*/
|
||
|
#define DARRAY_OPEN_AT(array, index, space) \
|
||
|
({ \
|
||
|
__auto_type ar = (array); \
|
||
|
size_t po = (index); \
|
||
|
size_t sp = (space); \
|
||
|
if (__builtin_expect (po > ar->size, 0)) { \
|
||
|
Sys_Error ("Attempt to insert elements outside darray: " \
|
||
|
"%s:%d", __FILE__, __LINE__); \
|
||
|
} \
|
||
|
DARRAY_RESIZE (ar, ar->size + sp); \
|
||
|
memmove (&ar->a[po + sp], &ar->a[po], \
|
||
|
(ar->size - po) * sizeof (*ar->a)); \
|
||
|
&ar->a[po]; \
|
||
|
})
|
||
|
|
||
|
/** Insert a value into the array at the specified index.
|
||
|
|
||
|
The array is grown by one at the specified index and the value written
|
||
|
to the newly opened slot. Values beyond the index are copied to just
|
||
|
after the newly opened slot.
|
||
|
|
||
|
If the new array size is greater than maxSize and the array can be grown,
|
||
|
the array backing will be resized to the next multiple of grow.
|
||
|
|
||
|
Attempting to grow an array that cannot grow is an error: it is assumed
|
||
|
that the array struct does not own the backing memory.
|
||
|
|
||
|
Attempting to insert a value beyond one past the end of the array is an
|
||
|
error (inserting at index = size is valid).
|
||
|
|
||
|
\param array *Address* of the array to be modified (ie, pointer to the
|
||
|
array struct instance, not the instance itself: use & for
|
||
|
static instances of the array struct).
|
||
|
\param value The value to be inserted into the array. Must be of a type
|
||
|
compatible with that used for creating the array struct.
|
||
|
\param index The index at which the value will be inserted
|
||
|
\return The inserted value: can be assigned to another compatible
|
||
|
value.
|
||
|
\hideinitializer
|
||
|
*/
|
||
|
#define DARRAY_INSERT_AT(array, value, index) \
|
||
|
({ \
|
||
|
__auto_type ar = (array); \
|
||
|
__auto_type ob = (value); \
|
||
|
*DARRAY_OPEN_AT (ar, index, 1) = ob; \
|
||
|
})
|
||
|
|
||
|
/** Close a segment of an array.
|
||
|
|
||
|
The values beyond the segment are copied to the beginning of the segment
|
||
|
and the array size reduced by the size of the segment. All but the first
|
||
|
one of the values previously in the segment are lost and gone forever.
|
||
|
|
||
|
Attempting to close a segment that extends outside the array is an error.
|
||
|
|
||
|
\param array *Address* of the array to be modified (ie, pointer to the
|
||
|
array struct instance, not the instance itself: use & for
|
||
|
static instances of the array struct).
|
||
|
\param index The index of the beginning of the segment.
|
||
|
\param count The number of values in the segment.
|
||
|
\return The single value at the beginning of the segment: can be
|
||
|
assigned to another compatible value.
|
||
|
\hideinitializer
|
||
|
*/
|
||
|
#define DARRAY_CLOSE_AT(array, index, count) \
|
||
|
({ \
|
||
|
__auto_type ar = (array); \
|
||
|
size_t po = (index); \
|
||
|
size_t co = (count); \
|
||
|
if (__builtin_expect (po + co > ar->size \
|
||
|
|| po >= ar->size, 0)) { \
|
||
|
Sys_Error ("Attempt to remove elements outside darray: " \
|
||
|
"%s:%d", __FILE__, __LINE__); \
|
||
|
} \
|
||
|
__auto_type ob = ar->a[po]; \
|
||
|
memmove (&ar->a[po], &ar->a[po + co], \
|
||
|
(ar->size - po - co) * sizeof (ob)); \
|
||
|
ar->size -= co; \
|
||
|
ob; \
|
||
|
})
|
||
|
|
||
|
/** Remove a value from an array at the specified index.
|
||
|
|
||
|
The values beyond the index are moved down to fill the hole left by the
|
||
|
single value and the array size reduced by one.
|
||
|
|
||
|
Attempting to remove a value from beyond the array is an error.
|
||
|
|
||
|
\param array *Address* of the array to be modified (ie, pointer to the
|
||
|
array struct instance, not the instance itself: use & for
|
||
|
static instances of the array struct).
|
||
|
\param index The index of the value to be removed.
|
||
|
\return The removed value: can be assigned to another compatible
|
||
|
value.
|
||
|
\hideinitializer
|
||
|
*/
|
||
|
#define DARRAY_REMOVE_AT(array, index) \
|
||
|
({ \
|
||
|
__auto_type ar = (array); \
|
||
|
DARRAY_CLOSE_AT (ar, index, 1); \
|
||
|
})
|
||
|
|
||
|
/** Remove (pop) a value from the end of an array.
|
||
|
|
||
|
The size of the array size reduced by one.
|
||
|
|
||
|
Attempting to remove a value from an empty array is an error.
|
||
|
|
||
|
\param array *Address* of the array to be modified (ie, pointer to the
|
||
|
array struct instance, not the instance itself: use & for
|
||
|
static instances of the array struct).
|
||
|
\return The removed value: can be assigned to another compatible
|
||
|
value.
|
||
|
\hideinitializer
|
||
|
*/
|
||
|
#define DARRAY_REMOVE(array) \
|
||
|
({ \
|
||
|
__auto_type ar = (array); \
|
||
|
DARRAY_CLOSE_AT (ar, ar->size - 1, 1); \
|
||
|
})
|
||
|
|
||
|
///@}
|
||
|
|
||
|
#endif//__pr_type_h
|