diff --git a/stretchy_buffer.h b/stretchy_buffer.h index fd76e595bdce189e8f82c0b5e7f7d69034d4cdea..47711b33fcb99864fa5000014443bc18a4e98ad1 100644 --- a/stretchy_buffer.h +++ b/stretchy_buffer.h @@ -1,28 +1,142 @@ -// stretchy buffer // init: NULL // free: sbfree() // push_back: sbpush() // size: sbcount() // -#define stbcg_sbfree(a) ((a) ? free(stb__sbraw(a)),0 : 0) -#define stbcg_sbpush(a,v) (stb__sbmaybegrow(a,1), (a)[stb__sbn(a)++] = (v)) -#define stbcg_sbcount(a) ((a) ? stb__sbn(a) : 0) -#define stbcg_sbadd(a,n) (stb__sbmaybegrow(a,n), stb__sbn(a)+=(n), &(a)[stb__sbn(a)-(n)]) -#define stbcg_sblast(a) ((a)[stb__sbn(a)-1]) +// 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 super-convenient in C, but it lacks many of the +// abilities of 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[2] access the 2nd (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 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]) -#include #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__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) stb__sbgrowf((void **) &(a), (n), sizeof(*(a))) +#define stb__sbgrow(a,n) ((a) = stb__sbgrowf((a), (n), sizeof(*(a)))) + +#include -static void stb__sbgrowf(void **arr, int increment, int itemsize) +static void * stb__sbgrowf(void *arr, int increment, int itemsize) { - int d = *arr ? 2*stb__sbm(*arr) : 0, n2 = stbcg_sbcount(*arr) + increment; - int m = d > n2 ? d : n2; - void *p = realloc(*arr ? stb__sbraw(*arr) : 0, itemsize * m + sizeof(int)*2); - assert(p); + 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) ((int *) p)[1] = 0; - *arr = (void *) ((int *) p + 2); - stb__sbm(*arr) = m; + 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 *) 8; // try to force a NULL pointer exception later } } diff --git a/tests/stb.dsp b/tests/stb.dsp index 5b9e1a6c4de7c80734292e3fe874b54cc7f90f4a..95710f29ffb12fc66fb3b359a197e232dd65d62e 100644 --- a/tests/stb.dsp +++ b/tests/stb.dsp @@ -102,6 +102,10 @@ SOURCE=..\stb_vorbis.c # End Source File # Begin Source File +SOURCE=.\stretchy_buffer_test.c +# End Source File +# Begin Source File + SOURCE=.\test_truetype.c # End Source File # Begin Source File diff --git a/tests/stretchy_buffer_test.c b/tests/stretchy_buffer_test.c new file mode 100644 index 0000000000000000000000000000000000000000..5ced5bc3abdc2263dc87aaf40064988d7907db15 --- /dev/null +++ b/tests/stretchy_buffer_test.c @@ -0,0 +1 @@ +#include "stretchy_buffer.h" \ No newline at end of file