|
@@ -0,0 +1,148 @@
|
|
|
+// stretchy_buffer.h - v0.9 - public domain
|
|
|
+// a vector<>-like dynamic array for C
|
|
|
+//
|
|
|
+// version history:
|
|
|
+// 0.9 - rewrite to try to avoid strict-aliasing optimization
|
|
|
+// issues, but won't compile as C++
|
|
|
+//
|
|
|
+// The idea:
|
|
|
+//
|
|
|
+// This implements an approximation to C++ vector<> for C, in that it
|
|
|
+// provides a generic definition for dynamic arrays which you can
|
|
|
+// still access in a typesafe way using arr[i] or *(arr+i). However,
|
|
|
+// it is simply a convenience wrapper around the common idiom of
|
|
|
+// of keeping a set of variables (in a struct or globals) which store
|
|
|
+// - pointer to array
|
|
|
+// - the length of the "in-use" part of the array
|
|
|
+// - the current size of the allocated array
|
|
|
+//
|
|
|
+// I find it to be single most useful non-built-in-structure when
|
|
|
+// programming in C (hash tables a close second), but to be clear
|
|
|
+// it lacks many of the capabilities of C++ vector<>: there is no
|
|
|
+// range checking, the object address isn't stable (see next section
|
|
|
+// for details), the set of methods available is small (although
|
|
|
+// the file stb.h has another implementation of stretchy buffers
|
|
|
+// called 'stb_arr' which provides more methods, e.g. for insertion
|
|
|
+// and deletion).
|
|
|
+//
|
|
|
+// How to use:
|
|
|
+//
|
|
|
+// Unlike other stb header file libraries, there is no need to
|
|
|
+// define an _IMPLEMENTATION symbol. Every #include creates as
|
|
|
+// much implementation is needed.
|
|
|
+//
|
|
|
+// stretchy_buffer.h does not define any types, so you do not
|
|
|
+// need to #include it to before defining data types that are
|
|
|
+// stretchy buffers, only in files that *manipulate* stretchy
|
|
|
+// buffers.
|
|
|
+//
|
|
|
+// If you want a stretchy buffer aka dynamic array containing
|
|
|
+// objects of TYPE, declare such an array as:
|
|
|
+//
|
|
|
+// TYPE *myarray = NULL;
|
|
|
+//
|
|
|
+// (There is no typesafe way to distinguish between stretchy
|
|
|
+// buffers and regular arrays/pointers; this is necessary to
|
|
|
+// make ordinary array indexing work on these objects.)
|
|
|
+//
|
|
|
+// Unlike C++ vector<>, the stretchy_buffer has the same
|
|
|
+// semantics as an object that you manually malloc and realloc.
|
|
|
+// The pointer may relocate every time you add a new object
|
|
|
+// to it, so you:
|
|
|
+//
|
|
|
+// 1. can't take long-term pointers to elements of the array
|
|
|
+// 2. have to return the pointer from functions which might expand it
|
|
|
+// (either as a return value or by passing it back)
|
|
|
+//
|
|
|
+// Now you can do the following things with this array:
|
|
|
+//
|
|
|
+// sb_free(TYPE *a) free the array
|
|
|
+// sb_count(TYPE *a) the number of elements in the array
|
|
|
+// sb_push(TYPE *a, TYPE v) adds v on the end of the array, a la push_back
|
|
|
+// sb_add(TYPE *a, int n) adds n uninitialized elements at end of array & returns pointer to first added
|
|
|
+// sb_last(TYPE *a) returns an lvalue of the last item in the array
|
|
|
+// a[n] access the nth (counting from 0) element of the array
|
|
|
+//
|
|
|
+// #define STRETCHY_BUFFER_NO_SHORT_NAMES to only export
|
|
|
+// names of the form 'stb_sb_' if you have a name that would
|
|
|
+// otherwise collide.
|
|
|
+//
|
|
|
+// Note that these are all macros and many of them evaluate
|
|
|
+// their arguments more than once, so the arguments should
|
|
|
+// be side-effect-free.
|
|
|
+//
|
|
|
+// Note that 'TYPE *a' in sb_push and sb_add must be lvalues
|
|
|
+// so that the library can overwrite the existing pointer if
|
|
|
+// the object has to be reallocated.
|
|
|
+//
|
|
|
+// In an out-of-memory condition, the code will try to
|
|
|
+// set up a null-pointer or otherwise-invalid-pointer
|
|
|
+// exception to happen later. It's possible optimizing
|
|
|
+// compilers could detect this write-to-null statically
|
|
|
+// and optimize away some of the code, but it should only
|
|
|
+// be along the failure path. Nevertheless, for more security
|
|
|
+// in the face of such compilers, #define STRETCHY_BUFFER_OUT_OF_MEMORY
|
|
|
+// to a statement such as assert(0) or exit(1) or something
|
|
|
+// to force a failure when out-of-memory occurs.
|
|
|
+//
|
|
|
+// How it works:
|
|
|
+//
|
|
|
+// A long-standing tradition in things like malloc implementations
|
|
|
+// is to store extra data before the beginning of the block returned
|
|
|
+// to the user. The stretchy buffer implementation here uses the
|
|
|
+// same trick; the current-count and current-allocation-size are
|
|
|
+// stored before the beginning of the array returned to the user.
|
|
|
+// (This means you can't directly free() the pointer, because the
|
|
|
+// allocated pointer is different from the type-safe pointer provided
|
|
|
+// to the user.)
|
|
|
+//
|
|
|
+// The details are trivial and implementation is straightforward;
|
|
|
+// the main trick is in realizing in the first place that it's
|
|
|
+// possible to do this in a generic, type-safe way in C.
|
|
|
+
|
|
|
+#ifndef STB_STRETCHY_BUFFER_H_INCLUDED
|
|
|
+#define STB_STRETCHY_BUFFER_H_INCLUDED
|
|
|
+
|
|
|
+#ifndef NO_STRETCHY_BUFFER_SHORT_NAMES
|
|
|
+#define sb_free stb_sb_free
|
|
|
+#define sb_push stb_sb_push
|
|
|
+#define sb_count stb_sb_count
|
|
|
+#define sb_add stb_sb_add
|
|
|
+#define sb_last stb_sb_last
|
|
|
+#endif
|
|
|
+
|
|
|
+#define stb_sb_free(a) ((a) ? free(stb__sbraw(a)),0 : 0)
|
|
|
+#define stb_sb_push(a,v) (stb__sbmaybegrow(a,1), (a)[stb__sbn(a)++] = (v))
|
|
|
+#define stb_sb_count(a) ((a) ? stb__sbn(a) : 0)
|
|
|
+#define stb_sb_add(a,n) (stb__sbmaybegrow(a,n), stb__sbn(a)+=(n), &(a)[stb__sbn(a)-(n)])
|
|
|
+#define stb_sb_last(a) ((a)[stb__sbn(a)-1])
|
|
|
+
|
|
|
+#define stb__sbraw(a) ((int *) (a) - 2)
|
|
|
+#define stb__sbm(a) stb__sbraw(a)[0]
|
|
|
+#define stb__sbn(a) stb__sbraw(a)[1]
|
|
|
+
|
|
|
+#define stb__sbneedgrow(a,n) ((a)==0 || stb__sbn(a)+(n) >= stb__sbm(a))
|
|
|
+#define stb__sbmaybegrow(a,n) (stb__sbneedgrow(a,(n)) ? stb__sbgrow(a,n) : 0)
|
|
|
+#define stb__sbgrow(a,n) ((a) = stb__sbgrowf((a), (n), sizeof(*(a))))
|
|
|
+
|
|
|
+#include <stdlib.h>
|
|
|
+
|
|
|
+static void * stb__sbgrowf(void *arr, int increment, int itemsize)
|
|
|
+{
|
|
|
+ int dbl_cur = arr ? 2*stb__sbm(arr) : 0;
|
|
|
+ int min_needed = stb_sb_count(arr) + increment;
|
|
|
+ int m = dbl_cur > min_needed ? dbl_cur : min_needed;
|
|
|
+ int *p = realloc(arr ? stb__sbraw(arr) : 0, itemsize * m + sizeof(int)*2);
|
|
|
+ if (p) {
|
|
|
+ if (!arr)
|
|
|
+ p[1] = 0;
|
|
|
+ p[0] = m;
|
|
|
+ return p;
|
|
|
+ } else {
|
|
|
+ #ifdef STRETCHY_BUFFER_OUT_OF_MEMORY
|
|
|
+ STRETCHY_BUFFER_OUT_OF_MEMORY ;
|
|
|
+ #endif
|
|
|
+ return (void *) (2*sizeof(int)); // try to force a NULL pointer exception later
|
|
|
+ }
|
|
|
+}
|
|
|
+#endif // STB_STRETCHY_BUFFER_H_INCLUDED
|