From 5576a5044f596fa7508f1cd0d20a7135772ac3e1 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Sat, 31 May 2014 12:12:34 -0700 Subject: [PATCH 1/6] initial check-in --- stretchy_buffer.h | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 stretchy_buffer.h diff --git a/stretchy_buffer.h b/stretchy_buffer.h new file mode 100644 index 0000000..fd76e59 --- /dev/null +++ b/stretchy_buffer.h @@ -0,0 +1,28 @@ +// 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]) + +#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__sbmaybegrow(a,n) (stb__sbneedgrow(a,(n)) ? stb__sbgrow(a,n) : 0) +#define stb__sbgrow(a,n) stb__sbgrowf((void **) &(a), (n), sizeof(*(a))) + +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); + if (p) { + if (!*arr) ((int *) p)[1] = 0; + *arr = (void *) ((int *) p + 2); + stb__sbm(*arr) = m; + } +} From 666596ed89a41be2ed45351c0d68407a465159c2 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Sat, 31 May 2014 13:00:39 -0700 Subject: [PATCH 2/6] untested rewrite of stretchy_buffer relying on implicit cast from void* --- stretchy_buffer.h | 148 ++++++++++++++++++++++++++++++++++++++----- tests/stb.dsp | 4 ++ tests/stretchy_buffer_test.c | 1 + 3 files changed, 136 insertions(+), 17 deletions(-) create mode 100644 tests/stretchy_buffer_test.c diff --git a/stretchy_buffer.h b/stretchy_buffer.h index fd76e59..47711b3 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)))) -static void stb__sbgrowf(void **arr, int increment, int itemsize) +#include + +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 5b9e1a6..95710f2 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 0000000..5ced5bc --- /dev/null +++ b/tests/stretchy_buffer_test.c @@ -0,0 +1 @@ +#include "stretchy_buffer.h" \ No newline at end of file From cf72af77885fc9b53b197c34f9e23532bcb557af Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Sat, 31 May 2014 13:22:56 -0700 Subject: [PATCH 3/6] header guards, fix faux-null-ptr constant, tweak docs --- stretchy_buffer.h | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/stretchy_buffer.h b/stretchy_buffer.h index 47711b3..a638493 100644 --- a/stretchy_buffer.h +++ b/stretchy_buffer.h @@ -16,12 +16,14 @@ // - 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). +// 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: // @@ -98,6 +100,9 @@ // 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 @@ -137,6 +142,7 @@ static void * stb__sbgrowf(void *arr, int increment, int itemsize) #ifdef STRETCHY_BUFFER_OUT_OF_MEMORY STRETCHY_BUFFER_OUT_OF_MEMORY ; #endif - return (void *) 8; // try to force a NULL pointer exception later + return (void *) (2*sizeof(int)); // try to force a NULL pointer exception later } } +#endif // STB_STRETCHY_BUFFER_H_INCLUDED From 509f9777e53086e0d4f566b5483084160b693b69 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Sat, 31 May 2014 13:34:37 -0700 Subject: [PATCH 4/6] minor doc improvement --- stretchy_buffer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stretchy_buffer.h b/stretchy_buffer.h index a638493..5e8dc94 100644 --- a/stretchy_buffer.h +++ b/stretchy_buffer.h @@ -61,7 +61,7 @@ // 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 +// 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 From 2bf4326350badbf345c618040f2815ab4b33cda6 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Sat, 31 May 2014 13:39:00 -0700 Subject: [PATCH 5/6] cleanup runtime output/error detection of stb.h unit tests --- tests/stb.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/stb.c b/tests/stb.c index 0bf1767..e0731d6 100644 --- a/tests/stb.c +++ b/tests/stb.c @@ -1552,7 +1552,12 @@ int main(int argc, char **argv) for (n=1; n < 20; ++n) malloc(1 << n); - stb_("Finished stb.c with %d errors.", count); + printf("Finished stb.c with %d errors.\n", count); + + #ifdef _MSC_VER + if (count) + __asm int 3; + #endif return 0; } From 4e8cade2cf0732ec692d5c7ada15312307a4e34f Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Sat, 31 May 2014 13:46:13 -0700 Subject: [PATCH 6/6] move image_test into main tests directory --- tests/{image_test => }/image_test.dsp | 16 ++++++++-------- tests/stb.dsw | 7 +++++-- 2 files changed, 13 insertions(+), 10 deletions(-) rename tests/{image_test => }/image_test.dsp (73%) diff --git a/tests/image_test/image_test.dsp b/tests/image_test.dsp similarity index 73% rename from tests/image_test/image_test.dsp rename to tests/image_test.dsp index ef5c3a4..05146e0 100644 --- a/tests/image_test/image_test.dsp +++ b/tests/image_test.dsp @@ -48,8 +48,8 @@ BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 !ELSEIF "$(CFG)" == "image_test - Win32 Debug" @@ -63,16 +63,16 @@ LINK32=link.exe # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\.." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept !ENDIF @@ -82,11 +82,11 @@ LINK32=link.exe # Name "image_test - Win32 Debug" # Begin Source File -SOURCE=..\image_test.c +SOURCE=.\image_test.c # End Source File # Begin Source File -SOURCE=..\..\stb_image.c +SOURCE=..\stb_image.c # End Source File # Begin Source File diff --git a/tests/stb.dsw b/tests/stb.dsw index c07ce5a..d22498a 100644 --- a/tests/stb.dsw +++ b/tests/stb.dsw @@ -3,7 +3,7 @@ Microsoft Developer Studio Workspace File, Format Version 6.00 ############################################################################### -Project: "image_test"=.\image_test\image_test.dsp - Package Owner=<4> +Project: "image_test"=.\image_test.dsp - Package Owner=<4> Package=<5> {{{ @@ -15,7 +15,7 @@ Package=<4> ############################################################################### -Project: "make_readme"=..\tools\make_readme\make_readme.dsp - Package Owner=<4> +Project: "make_readme"=..\tools\make_readme.dsp - Package Owner=<4> Package=<5> {{{ @@ -38,6 +38,9 @@ Package=<4> Begin Project Dependency Project_Dep_Name stb_cpp End Project Dependency + Begin Project Dependency + Project_Dep_Name image_test + End Project Dependency }}} ###############################################################################