You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3305 lines
104 KiB
3305 lines
104 KiB
/* stb_lib.h - v1.00 - http://nothings.org/stb |
|
no warranty is offered or implied; use this code at your own risk |
|
|
|
============================================================================ |
|
You MUST |
|
|
|
#define STB_LIB_IMPLEMENTATION |
|
|
|
in EXACTLY _one_ C or C++ file that includes this header, BEFORE the |
|
include, like this: |
|
|
|
#define STB_LIB_IMPLEMENTATION |
|
#include "stblib_files.h" |
|
|
|
All other files should just #include "stblib_files.h" without the #define. |
|
============================================================================ |
|
|
|
LICENSE |
|
|
|
See end of file for license information. |
|
|
|
CREDITS |
|
|
|
Written by Sean Barrett. |
|
|
|
Fixes: |
|
Philipp Wiesemann Robert Nix |
|
r-lyeh blackpawn |
|
github:Mojofreem Ryan Whitworth |
|
Vincent Isambart Mike Sartain |
|
Eugene Opalev Tim Sjostrand |
|
github:infatum Dave Butler |
|
*/ |
|
|
|
#ifndef STB_INCLUDE_STB_LIB_H |
|
|
|
#include <stdarg.h> |
|
|
|
#if defined(_WIN32) && !defined(__MINGW32__) |
|
#ifndef _CRT_SECURE_NO_WARNINGS |
|
#define _CRT_SECURE_NO_WARNINGS |
|
#endif |
|
#ifndef _CRT_NONSTDC_NO_DEPRECATE |
|
#define _CRT_NONSTDC_NO_DEPRECATE |
|
#endif |
|
#ifndef _CRT_NON_CONFORMING_SWPRINTFS |
|
#define _CRT_NON_CONFORMING_SWPRINTFS |
|
#endif |
|
#if !defined(_MSC_VER) || _MSC_VER > 1700 |
|
#include <intrin.h> // _BitScanReverse |
|
#endif |
|
#endif |
|
|
|
#include <stdlib.h> // stdlib could have min/max |
|
#include <stdio.h> // need FILE |
|
#include <string.h> // stb_define_hash needs memcpy/memset |
|
#include <time.h> // stb_dirtree |
|
|
|
typedef unsigned char stb_uchar; |
|
typedef unsigned char stb_uint8; |
|
typedef unsigned int stb_uint; |
|
typedef unsigned short stb_uint16; |
|
typedef short stb_int16; |
|
typedef signed char stb_int8; |
|
#if defined(STB_USE_LONG_FOR_32_BIT_INT) || defined(STB_LONG32) |
|
typedef unsigned long stb_uint32; |
|
typedef long stb_int32; |
|
#else |
|
typedef unsigned int stb_uint32; |
|
typedef int stb_int32; |
|
#endif |
|
typedef char stb__testsize2_16[sizeof(stb_uint16)==2 ? 1 : -1]; |
|
typedef char stb__testsize2_32[sizeof(stb_uint32)==4 ? 1 : -1]; |
|
|
|
#ifdef _MSC_VER |
|
typedef unsigned __int64 stb_uint64; |
|
typedef __int64 stb_int64; |
|
#define STB_IMM_UINT64(literalui64) (literalui64##ui64) |
|
#else |
|
// ?? |
|
typedef unsigned long long stb_uint64; |
|
typedef long long stb_int64; |
|
#define STB_IMM_UINT64(literalui64) (literalui64##ULL) |
|
#endif |
|
typedef char stb__testsize2_64[sizeof(stb_uint64)==8 ? 1 : -1]; |
|
|
|
#ifdef __cplusplus |
|
#define STB_EXTERN extern "C" |
|
#else |
|
#define STB_EXTERN extern |
|
#endif |
|
|
|
// check for well-known debug defines |
|
#if defined(DEBUG) || defined(_DEBUG) || defined(DBG) |
|
#ifndef NDEBUG |
|
#define STB_DEBUG |
|
#endif |
|
#endif |
|
|
|
#ifdef STB_DEBUG |
|
#include <assert.h> |
|
#endif |
|
#endif // STB_INCLUDE_STB_LIB_H |
|
|
|
#ifdef STB_LIB_IMPLEMENTATION |
|
#include <assert.h> |
|
#include <stdarg.h> |
|
#include <stddef.h> |
|
#include <ctype.h> |
|
#include <math.h> |
|
#ifndef _WIN32 |
|
#include <unistd.h> |
|
#else |
|
#include <io.h> // _mktemp |
|
#include <direct.h> // _rmdir |
|
#endif |
|
#include <sys/types.h> // stat()/_stat() |
|
#include <sys/stat.h> // stat()/_stat() |
|
#endif |
|
|
|
////////////////////////////////////////////////////////////////////////////// |
|
// |
|
// Miscellany |
|
// |
|
|
|
#ifdef _WIN32 |
|
#define stb_stricmp(a,b) stricmp(a,b) |
|
#define stb_strnicmp(a,b,n) strnicmp(a,b,n) |
|
#else |
|
#define stb_stricmp(a,b) strcasecmp(a,b) |
|
#define stb_strnicmp(a,b,n) strncasecmp(a,b,n) |
|
#endif |
|
|
|
#ifndef STB_INCLUDE_STB_LIB_H |
|
STB_EXTERN void stb_fatal(char *fmt, ...); |
|
STB_EXTERN void stb_swap(void *p, void *q, size_t sz); |
|
STB_EXTERN double stb_linear_remap(double x, double x_min, double x_max, |
|
double out_min, double out_max); |
|
|
|
#define stb_arrcount(x) (sizeof(x)/sizeof((x)[0])) |
|
#define stb_lerp(t,a,b) ( (a) + (t) * (float) ((b)-(a)) ) |
|
#define stb_unlerp(t,a,b) ( ((t) - (a)) / (float) ((b) - (a)) ) |
|
|
|
#endif |
|
|
|
|
|
#ifdef STB_LIB_IMPLEMENTATION |
|
void stb_fatal(char *s, ...) |
|
{ |
|
va_list a; |
|
va_start(a,s); |
|
fputs("Fatal error: ", stderr); |
|
vfprintf(stderr, s, a); |
|
va_end(a); |
|
fputs("\n", stderr); |
|
#ifdef STB_DEBUG |
|
#ifdef _MSC_VER |
|
#ifndef _WIN64 |
|
__asm int 3; // trap to debugger! |
|
#else |
|
__debugbreak(); |
|
#endif |
|
#else |
|
__builtin_trap(); |
|
#endif |
|
#endif |
|
exit(1); |
|
} |
|
|
|
typedef struct { char d[4]; } stb__4; |
|
typedef struct { char d[8]; } stb__8; |
|
|
|
// optimize the small cases, though you shouldn't be calling this for those! |
|
void stb_swap(void *p, void *q, size_t sz) |
|
{ |
|
char buffer[256]; |
|
if (p == q) return; |
|
if (sz == 4) { |
|
stb__4 temp = * ( stb__4 *) p; |
|
* (stb__4 *) p = * ( stb__4 *) q; |
|
* (stb__4 *) q = temp; |
|
return; |
|
} else if (sz == 8) { |
|
stb__8 temp = * ( stb__8 *) p; |
|
* (stb__8 *) p = * ( stb__8 *) q; |
|
* (stb__8 *) q = temp; |
|
return; |
|
} |
|
|
|
while (sz > sizeof(buffer)) { |
|
stb_swap(p, q, sizeof(buffer)); |
|
p = (char *) p + sizeof(buffer); |
|
q = (char *) q + sizeof(buffer); |
|
sz -= sizeof(buffer); |
|
} |
|
|
|
memcpy(buffer, p , sz); |
|
memcpy(p , q , sz); |
|
memcpy(q , buffer, sz); |
|
} |
|
|
|
#ifdef stb_linear_remap |
|
#undef stb_linear_remap |
|
#endif |
|
|
|
double stb_linear_remap(double x, double x_min, double x_max, |
|
double out_min, double out_max) |
|
{ |
|
return stb_lerp(stb_unlerp(x,x_min,x_max),out_min,out_max); |
|
} |
|
|
|
#define stb_linear_remap(t,a,b,c,d) stb_lerp(stb_unlerp(t,a,b),c,d) |
|
#endif // STB_LIB_IMPLEMENTATION |
|
|
|
#ifndef STB_INCLUDE_STB_LIB_H |
|
// avoid unnecessary function call, but define function so its address can be taken |
|
#ifndef stb_linear_remap |
|
#define stb_linear_remap(t,a,b,c,d) stb_lerp(stb_unlerp(t,a,b),c,d) |
|
#endif |
|
#endif |
|
|
|
////////////////////////////////////////////////////////////////////////////// |
|
// |
|
// cross-platform snprintf because they keep changing that, |
|
// and with old compilers without vararg macros we can't write |
|
// a macro wrapper to fix it up |
|
|
|
#ifndef STB_INCLUDE_STB_LIB_H |
|
STB_EXTERN int stb_snprintf(char *s, size_t n, const char *fmt, ...); |
|
STB_EXTERN int stb_vsnprintf(char *s, size_t n, const char *fmt, va_list v); |
|
STB_EXTERN char *stb_sprintf(const char *fmt, ...); |
|
#endif |
|
|
|
#ifdef STB_LIB_IMPLEMENTATION |
|
|
|
int stb_vsnprintf(char *s, size_t n, const char *fmt, va_list v) |
|
{ |
|
int res; |
|
#ifdef _WIN32 |
|
// Could use "_vsnprintf_s(s, n, _TRUNCATE, fmt, v)" ? |
|
res = _vsnprintf(s,n,fmt,v); |
|
#else |
|
res = vsnprintf(s,n,fmt,v); |
|
#endif |
|
if (n) s[n-1] = 0; |
|
// Unix returns length output would require, Windows returns negative when truncated. |
|
return (res >= (int) n || res < 0) ? -1 : res; |
|
} |
|
|
|
int stb_snprintf(char *s, size_t n, const char *fmt, ...) |
|
{ |
|
int res; |
|
va_list v; |
|
va_start(v,fmt); |
|
res = stb_vsnprintf(s, n, fmt, v); |
|
va_end(v); |
|
return res; |
|
} |
|
|
|
char *stb_sprintf(const char *fmt, ...) |
|
{ |
|
static char buffer[1024]; |
|
va_list v; |
|
va_start(v,fmt); |
|
stb_vsnprintf(buffer,1024,fmt,v); |
|
va_end(v); |
|
return buffer; |
|
} |
|
#endif |
|
|
|
////////////////////////////////////////////////////////////////////////////// |
|
// |
|
// Windows UTF8 filename handling |
|
// |
|
// Windows stupidly treats 8-bit filenames as some dopey code page, |
|
// rather than utf-8. If we want to use utf8 filenames, we have to |
|
// convert them to WCHAR explicitly and call WCHAR versions of the |
|
// file functions. So, ok, we do. |
|
|
|
|
|
#ifndef STB_INCLUDE_STB_LIB_H |
|
#ifdef _WIN32 |
|
#define stb__fopen(x,y) _wfopen((const wchar_t *)stb__from_utf8(x), (const wchar_t *)stb__from_utf8_alt(y)) |
|
#define stb__windows(x,y) x |
|
#else |
|
#define stb__fopen(x,y) fopen(x,y) |
|
#define stb__windows(x,y) y |
|
#endif |
|
|
|
|
|
typedef unsigned short stb__wchar; |
|
|
|
STB_EXTERN stb__wchar * stb_from_utf8(stb__wchar *buffer, char *str, int n); |
|
STB_EXTERN char * stb_to_utf8 (char *buffer, stb__wchar *str, int n); |
|
|
|
STB_EXTERN stb__wchar *stb__from_utf8(char *str); |
|
STB_EXTERN stb__wchar *stb__from_utf8_alt(char *str); |
|
STB_EXTERN char *stb__to_utf8(stb__wchar *str); |
|
#endif |
|
|
|
#ifdef STB_LIB_IMPLEMENTATION |
|
stb__wchar * stb_from_utf8(stb__wchar *buffer, char *ostr, int n) |
|
{ |
|
unsigned char *str = (unsigned char *) ostr; |
|
stb_uint32 c; |
|
int i=0; |
|
--n; |
|
while (*str) { |
|
if (i >= n) |
|
return NULL; |
|
if (!(*str & 0x80)) |
|
buffer[i++] = *str++; |
|
else if ((*str & 0xe0) == 0xc0) { |
|
if (*str < 0xc2) return NULL; |
|
c = (*str++ & 0x1f) << 6; |
|
if ((*str & 0xc0) != 0x80) return NULL; |
|
buffer[i++] = c + (*str++ & 0x3f); |
|
} else if ((*str & 0xf0) == 0xe0) { |
|
if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return NULL; |
|
if (*str == 0xed && str[1] > 0x9f) return NULL; // str[1] < 0x80 is checked below |
|
c = (*str++ & 0x0f) << 12; |
|
if ((*str & 0xc0) != 0x80) return NULL; |
|
c += (*str++ & 0x3f) << 6; |
|
if ((*str & 0xc0) != 0x80) return NULL; |
|
buffer[i++] = c + (*str++ & 0x3f); |
|
} else if ((*str & 0xf8) == 0xf0) { |
|
if (*str > 0xf4) return NULL; |
|
if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return NULL; |
|
if (*str == 0xf4 && str[1] > 0x8f) return NULL; // str[1] < 0x80 is checked below |
|
c = (*str++ & 0x07) << 18; |
|
if ((*str & 0xc0) != 0x80) return NULL; |
|
c += (*str++ & 0x3f) << 12; |
|
if ((*str & 0xc0) != 0x80) return NULL; |
|
c += (*str++ & 0x3f) << 6; |
|
if ((*str & 0xc0) != 0x80) return NULL; |
|
c += (*str++ & 0x3f); |
|
// utf-8 encodings of values used in surrogate pairs are invalid |
|
if ((c & 0xFFFFF800) == 0xD800) return NULL; |
|
if (c >= 0x10000) { |
|
c -= 0x10000; |
|
if (i + 2 > n) return NULL; |
|
buffer[i++] = 0xD800 | (0x3ff & (c >> 10)); |
|
buffer[i++] = 0xDC00 | (0x3ff & (c )); |
|
} |
|
} else |
|
return NULL; |
|
} |
|
buffer[i] = 0; |
|
return buffer; |
|
} |
|
|
|
char * stb_to_utf8(char *buffer, stb__wchar *str, int n) |
|
{ |
|
int i=0; |
|
--n; |
|
while (*str) { |
|
if (*str < 0x80) { |
|
if (i+1 > n) return NULL; |
|
buffer[i++] = (char) *str++; |
|
} else if (*str < 0x800) { |
|
if (i+2 > n) return NULL; |
|
buffer[i++] = 0xc0 + (*str >> 6); |
|
buffer[i++] = 0x80 + (*str & 0x3f); |
|
str += 1; |
|
} else if (*str >= 0xd800 && *str < 0xdc00) { |
|
stb_uint32 c; |
|
if (i+4 > n) return NULL; |
|
c = ((str[0] - 0xd800) << 10) + ((str[1]) - 0xdc00) + 0x10000; |
|
buffer[i++] = 0xf0 + (c >> 18); |
|
buffer[i++] = 0x80 + ((c >> 12) & 0x3f); |
|
buffer[i++] = 0x80 + ((c >> 6) & 0x3f); |
|
buffer[i++] = 0x80 + ((c ) & 0x3f); |
|
str += 2; |
|
} else if (*str >= 0xdc00 && *str < 0xe000) { |
|
return NULL; |
|
} else { |
|
if (i+3 > n) return NULL; |
|
buffer[i++] = 0xe0 + (*str >> 12); |
|
buffer[i++] = 0x80 + ((*str >> 6) & 0x3f); |
|
buffer[i++] = 0x80 + ((*str ) & 0x3f); |
|
str += 1; |
|
} |
|
} |
|
buffer[i] = 0; |
|
return buffer; |
|
} |
|
|
|
stb__wchar *stb__from_utf8(char *str) |
|
{ |
|
static stb__wchar buffer[4096]; |
|
return stb_from_utf8(buffer, str, 4096); |
|
} |
|
|
|
stb__wchar *stb__from_utf8_alt(char *str) |
|
{ |
|
static stb__wchar buffer[4096]; |
|
return stb_from_utf8(buffer, str, 4096); |
|
} |
|
|
|
char *stb__to_utf8(stb__wchar *str) |
|
{ |
|
static char buffer[4096]; |
|
return stb_to_utf8(buffer, str, 4096); |
|
} |
|
#endif |
|
|
|
////////////////////////////////////////////////////////////////////////////// |
|
// |
|
// qsort Compare Routines |
|
// NOT THREAD SAFE |
|
|
|
#ifndef STB_INCLUDE_STB_LIB_H |
|
STB_EXTERN int (*stb_intcmp(int offset))(const void *a, const void *b); |
|
STB_EXTERN int (*stb_qsort_strcmp(int offset))(const void *a, const void *b); |
|
STB_EXTERN int (*stb_qsort_stricmp(int offset))(const void *a, const void *b); |
|
STB_EXTERN int (*stb_floatcmp(int offset))(const void *a, const void *b); |
|
STB_EXTERN int (*stb_doublecmp(int offset))(const void *a, const void *b); |
|
STB_EXTERN int (*stb_ucharcmp(int offset))(const void *a, const void *b); |
|
STB_EXTERN int (*stb_charcmp(int offset))(const void *a, const void *b); |
|
#endif |
|
|
|
#ifdef STB_LIB_IMPLEMENTATION |
|
static int stb__intcmpoffset, stb__ucharcmpoffset, stb__strcmpoffset; |
|
static int stb__floatcmpoffset, stb__doublecmpoffset, stb__charcmpoffset; |
|
|
|
int stb__intcmp(const void *a, const void *b) |
|
{ |
|
const int p = *(const int *) ((const char *) a + stb__intcmpoffset); |
|
const int q = *(const int *) ((const char *) b + stb__intcmpoffset); |
|
return p < q ? -1 : p > q; |
|
} |
|
|
|
int stb__ucharcmp(const void *a, const void *b) |
|
{ |
|
const int p = *(const unsigned char *) ((const char *) a + stb__ucharcmpoffset); |
|
const int q = *(const unsigned char *) ((const char *) b + stb__ucharcmpoffset); |
|
return p < q ? -1 : p > q; |
|
} |
|
|
|
int stb__charcmp(const void *a, const void *b) |
|
{ |
|
const int p = *(const char *) ((const char *) a + stb__ucharcmpoffset); |
|
const int q = *(const char *) ((const char *) b + stb__ucharcmpoffset); |
|
return p < q ? -1 : p > q; |
|
} |
|
|
|
int stb__floatcmp(const void *a, const void *b) |
|
{ |
|
const float p = *(const float *) ((const char *) a + stb__floatcmpoffset); |
|
const float q = *(const float *) ((const char *) b + stb__floatcmpoffset); |
|
return p < q ? -1 : p > q; |
|
} |
|
|
|
int stb__doublecmp(const void *a, const void *b) |
|
{ |
|
const double p = *(const double *) ((const char *) a + stb__doublecmpoffset); |
|
const double q = *(const double *) ((const char *) b + stb__doublecmpoffset); |
|
return p < q ? -1 : p > q; |
|
} |
|
|
|
int stb__qsort_strcmp(const void *a, const void *b) |
|
{ |
|
const char *p = *(const char **) ((const char *) a + stb__strcmpoffset); |
|
const char *q = *(const char **) ((const char *) b + stb__strcmpoffset); |
|
return strcmp(p,q); |
|
} |
|
|
|
int stb__qsort_stricmp(const void *a, const void *b) |
|
{ |
|
const char *p = *(const char **) ((const char *) a + stb__strcmpoffset); |
|
const char *q = *(const char **) ((const char *) b + stb__strcmpoffset); |
|
return stb_stricmp(p,q); |
|
} |
|
|
|
int (*stb_intcmp(int offset))(const void *, const void *) |
|
{ |
|
stb__intcmpoffset = offset; |
|
return &stb__intcmp; |
|
} |
|
|
|
int (*stb_ucharcmp(int offset))(const void *, const void *) |
|
{ |
|
stb__ucharcmpoffset = offset; |
|
return &stb__ucharcmp; |
|
} |
|
|
|
int (*stb_charcmp(int offset))(const void *, const void *) |
|
{ |
|
stb__charcmpoffset = offset; |
|
return &stb__ucharcmp; |
|
} |
|
|
|
int (*stb_qsort_strcmp(int offset))(const void *, const void *) |
|
{ |
|
stb__strcmpoffset = offset; |
|
return &stb__qsort_strcmp; |
|
} |
|
|
|
int (*stb_qsort_stricmp(int offset))(const void *, const void *) |
|
{ |
|
stb__strcmpoffset = offset; |
|
return &stb__qsort_stricmp; |
|
} |
|
|
|
int (*stb_floatcmp(int offset))(const void *, const void *) |
|
{ |
|
stb__floatcmpoffset = offset; |
|
return &stb__floatcmp; |
|
} |
|
|
|
int (*stb_doublecmp(int offset))(const void *, const void *) |
|
{ |
|
stb__doublecmpoffset = offset; |
|
return &stb__doublecmp; |
|
} |
|
#endif |
|
|
|
////////////////////////////////////////////////////////////////////////////// |
|
// |
|
// String Processing |
|
// |
|
|
|
#ifndef STB_INCLUDE_STB_LIB_H |
|
#define stb_prefixi(s,t) (0==stb_strnicmp((s),(t),strlen(t))) |
|
|
|
enum stb_splitpath_flag |
|
{ |
|
STB_PATH = 1, |
|
STB_FILE = 2, |
|
STB_EXT = 4, |
|
STB_PATH_FILE = STB_PATH + STB_FILE, |
|
STB_FILE_EXT = STB_FILE + STB_EXT, |
|
STB_EXT_NO_PERIOD = 8, |
|
}; |
|
|
|
STB_EXTERN char * stb_skipwhite(char *s); |
|
STB_EXTERN char * stb_trimwhite(char *s); |
|
STB_EXTERN char * stb_skipnewline(char *s); |
|
STB_EXTERN char * stb_strncpy(char *s, char *t, int n); |
|
STB_EXTERN char * stb_substr(char *t, int n); |
|
STB_EXTERN char * stb_duplower(char *s); |
|
STB_EXTERN void stb_tolower (char *s); |
|
STB_EXTERN char * stb_strchr2 (char *s, char p1, char p2); |
|
STB_EXTERN char * stb_strrchr2(char *s, char p1, char p2); |
|
STB_EXTERN char * stb_strtok(char *output, char *src, char *delimit); |
|
STB_EXTERN char * stb_strtok_keep(char *output, char *src, char *delimit); |
|
STB_EXTERN char * stb_strtok_invert(char *output, char *src, char *allowed); |
|
STB_EXTERN char * stb_dupreplace(char *s, char *find, char *replace); |
|
STB_EXTERN void stb_replaceinplace(char *s, char *find, char *replace); |
|
STB_EXTERN char * stb_splitpath(char *output, char *src, int flag); |
|
STB_EXTERN char * stb_splitpathdup(char *src, int flag); |
|
STB_EXTERN char * stb_replacedir(char *output, char *src, char *dir); |
|
STB_EXTERN char * stb_replaceext(char *output, char *src, char *ext); |
|
STB_EXTERN void stb_fixpath(char *path); |
|
STB_EXTERN char * stb_shorten_path_readable(char *path, int max_len); |
|
STB_EXTERN int stb_suffix (char *s, char *t); |
|
STB_EXTERN int stb_suffixi(char *s, char *t); |
|
STB_EXTERN int stb_prefix (char *s, char *t); |
|
STB_EXTERN char * stb_strichr(char *s, char t); |
|
STB_EXTERN char * stb_stristr(char *s, char *t); |
|
STB_EXTERN int stb_prefix_count(char *s, char *t); |
|
STB_EXTERN const char * stb_plural(int n); // "s" or "" |
|
STB_EXTERN size_t stb_strscpy(char *d, const char *s, size_t n); |
|
|
|
STB_EXTERN char **stb_tokens(char *src, char *delimit, int *count); |
|
STB_EXTERN char **stb_tokens_nested(char *src, char *delimit, int *count, char *nest_in, char *nest_out); |
|
STB_EXTERN char **stb_tokens_nested_empty(char *src, char *delimit, int *count, char *nest_in, char *nest_out); |
|
STB_EXTERN char **stb_tokens_allowempty(char *src, char *delimit, int *count); |
|
STB_EXTERN char **stb_tokens_stripwhite(char *src, char *delimit, int *count); |
|
STB_EXTERN char **stb_tokens_withdelim(char *src, char *delimit, int *count); |
|
STB_EXTERN char **stb_tokens_quoted(char *src, char *delimit, int *count); |
|
// with 'quoted', allow delimiters to appear inside quotation marks, and don't |
|
// strip whitespace inside them (and we delete the quotation marks unless they |
|
// appear back to back, in which case they're considered escaped) |
|
#endif // STB_INCLUDE_STB_LIB_H |
|
|
|
#ifdef STB_LIB_IMPLEMENTATION |
|
#include <ctype.h> |
|
|
|
size_t stb_strscpy(char *d, const char *s, size_t n) |
|
{ |
|
size_t len = strlen(s); |
|
if (len >= n) { |
|
if (n) d[0] = 0; |
|
return 0; |
|
} |
|
strcpy(d,s); |
|
return len + 1; |
|
} |
|
|
|
const char *stb_plural(int n) |
|
{ |
|
return n == 1 ? "" : "s"; |
|
} |
|
|
|
int stb_prefix(char *s, char *t) |
|
{ |
|
while (*t) |
|
if (*s++ != *t++) |
|
return 0; |
|
return 1; |
|
} |
|
|
|
int stb_prefix_count(char *s, char *t) |
|
{ |
|
int c=0; |
|
while (*t) { |
|
if (*s++ != *t++) |
|
break; |
|
++c; |
|
} |
|
return c; |
|
} |
|
|
|
int stb_suffix(char *s, char *t) |
|
{ |
|
size_t n = strlen(s); |
|
size_t m = strlen(t); |
|
if (m <= n) |
|
return 0 == strcmp(s+n-m, t); |
|
else |
|
return 0; |
|
} |
|
|
|
int stb_suffixi(char *s, char *t) |
|
{ |
|
size_t n = strlen(s); |
|
size_t m = strlen(t); |
|
if (m <= n) |
|
return 0 == stb_stricmp(s+n-m, t); |
|
else |
|
return 0; |
|
} |
|
|
|
// originally I was using this table so that I could create known sentinel |
|
// values--e.g. change whitetable[0] to be true if I was scanning for whitespace, |
|
// and false if I was scanning for nonwhite. I don't appear to be using that |
|
// functionality anymore (I do for tokentable, though), so just replace it |
|
// with isspace() |
|
char *stb_skipwhite(char *s) |
|
{ |
|
while (isspace((unsigned char) *s)) ++s; |
|
return s; |
|
} |
|
|
|
char *stb_skipnewline(char *s) |
|
{ |
|
if (s[0] == '\r' || s[0] == '\n') { |
|
if (s[0]+s[1] == '\r' + '\n') ++s; |
|
++s; |
|
} |
|
return s; |
|
} |
|
|
|
char *stb_trimwhite(char *s) |
|
{ |
|
int i,n; |
|
s = stb_skipwhite(s); |
|
n = (int) strlen(s); |
|
for (i=n-1; i >= 0; --i) |
|
if (!isspace(s[i])) |
|
break; |
|
s[i+1] = 0; |
|
return s; |
|
} |
|
|
|
char *stb_strncpy(char *s, char *t, int n) |
|
{ |
|
strncpy(s,t,n); |
|
s[n-1] = 0; |
|
return s; |
|
} |
|
|
|
char *stb_substr(char *t, int n) |
|
{ |
|
char *a; |
|
int z = (int) strlen(t); |
|
if (z < n) n = z; |
|
a = (char *) malloc(n+1); |
|
strncpy(a,t,n); |
|
a[n] = 0; |
|
return a; |
|
} |
|
|
|
char *stb_duplower(char *s) |
|
{ |
|
char *p = strdup(s), *q = p; |
|
while (*q) { |
|
*q = tolower(*q); |
|
++q; |
|
} |
|
return p; |
|
} |
|
|
|
void stb_tolower(char *s) |
|
{ |
|
while (*s) { |
|
*s = tolower(*s); |
|
++s; |
|
} |
|
} |
|
|
|
char *stb_strchr2(char *s, char x, char y) |
|
{ |
|
for(; *s; ++s) |
|
if (*s == x || *s == y) |
|
return s; |
|
return NULL; |
|
} |
|
|
|
char *stb_strrchr2(char *s, char x, char y) |
|
{ |
|
char *r = NULL; |
|
for(; *s; ++s) |
|
if (*s == x || *s == y) |
|
r = s; |
|
return r; |
|
} |
|
|
|
char *stb_strichr(char *s, char t) |
|
{ |
|
if (tolower(t) == toupper(t)) |
|
return strchr(s,t); |
|
return stb_strchr2(s, (char) tolower(t), (char) toupper(t)); |
|
} |
|
|
|
char *stb_stristr(char *s, char *t) |
|
{ |
|
size_t n = strlen(t); |
|
char *z; |
|
if (n==0) return s; |
|
while ((z = stb_strichr(s, *t)) != NULL) { |
|
if (0==stb_strnicmp(z, t, n)) |
|
return z; |
|
s = z+1; |
|
} |
|
return NULL; |
|
} |
|
|
|
static char *stb_strtok_raw(char *output, char *src, char *delimit, int keep, int invert) |
|
{ |
|
if (invert) { |
|
while (*src && strchr(delimit, *src) != NULL) { |
|
*output++ = *src++; |
|
} |
|
} else { |
|
while (*src && strchr(delimit, *src) == NULL) { |
|
*output++ = *src++; |
|
} |
|
} |
|
*output = 0; |
|
if (keep) |
|
return src; |
|
else |
|
return *src ? src+1 : src; |
|
} |
|
|
|
char *stb_strtok(char *output, char *src, char *delimit) |
|
{ |
|
return stb_strtok_raw(output, src, delimit, 0, 0); |
|
} |
|
|
|
char *stb_strtok_keep(char *output, char *src, char *delimit) |
|
{ |
|
return stb_strtok_raw(output, src, delimit, 1, 0); |
|
} |
|
|
|
char *stb_strtok_invert(char *output, char *src, char *delimit) |
|
{ |
|
return stb_strtok_raw(output, src, delimit, 1,1); |
|
} |
|
|
|
static char **stb_tokens_raw(char *src_, char *delimit, int *count, |
|
int stripwhite, int allow_empty, char *start, char *end) |
|
{ |
|
int nested = 0; |
|
unsigned char *src = (unsigned char *) src_; |
|
static char stb_tokentable[256]; // rely on static initializion to 0 |
|
static char stable[256],etable[256]; |
|
char *out; |
|
char **result; |
|
int num=0; |
|
unsigned char *s; |
|
|
|
s = (unsigned char *) delimit; while (*s) stb_tokentable[*s++] = 1; |
|
if (start) { |
|
s = (unsigned char *) start; while (*s) stable[*s++] = 1; |
|
s = (unsigned char *) end; if (s) while (*s) stable[*s++] = 1; |
|
s = (unsigned char *) end; if (s) while (*s) etable[*s++] = 1; |
|
} |
|
stable[0] = 1; |
|
|
|
// two passes through: the first time, counting how many |
|
s = (unsigned char *) src; |
|
while (*s) { |
|
// state: just found delimiter |
|
// skip further delimiters |
|
if (!allow_empty) { |
|
stb_tokentable[0] = 0; |
|
while (stb_tokentable[*s]) |
|
++s; |
|
if (!*s) break; |
|
} |
|
++num; |
|
// skip further non-delimiters |
|
stb_tokentable[0] = 1; |
|
if (stripwhite == 2) { // quoted strings |
|
while (!stb_tokentable[*s]) { |
|
if (*s != '"') |
|
++s; |
|
else { |
|
++s; |
|
if (*s == '"') |
|
++s; // "" -> ", not start a string |
|
else { |
|
// begin a string |
|
while (*s) { |
|
if (s[0] == '"') { |
|
if (s[1] == '"') s += 2; // "" -> " |
|
else { ++s; break; } // terminating " |
|
} else |
|
++s; |
|
} |
|
} |
|
} |
|
} |
|
} else |
|
while (nested || !stb_tokentable[*s]) { |
|
if (stable[*s]) { |
|
if (!*s) break; |
|
if (end ? etable[*s] : nested) |
|
--nested; |
|
else |
|
++nested; |
|
} |
|
++s; |
|
} |
|
if (allow_empty) { |
|
if (*s) ++s; |
|
} |
|
} |
|
// now num has the actual count... malloc our output structure |
|
// need space for all the strings: strings won't be any longer than |
|
// original input, since for every '\0' there's at least one delimiter |
|
result = (char **) malloc(sizeof(*result) * (num+1) + (s-src+1)); |
|
if (result == NULL) return result; |
|
out = (char *) (result + (num+1)); |
|
// second pass: copy out the data |
|
s = (unsigned char *) src; |
|
num = 0; |
|
nested = 0; |
|
while (*s) { |
|
char *last_nonwhite; |
|
// state: just found delimiter |
|
// skip further delimiters |
|
if (!allow_empty) { |
|
stb_tokentable[0] = 0; |
|
if (stripwhite) |
|
while (stb_tokentable[*s] || isspace(*s)) |
|
++s; |
|
else |
|
while (stb_tokentable[*s]) |
|
++s; |
|
} else if (stripwhite) { |
|
while (isspace(*s)) ++s; |
|
} |
|
if (!*s) break; |
|
// we're past any leading delimiters and whitespace |
|
result[num] = out; |
|
++num; |
|
// copy non-delimiters |
|
stb_tokentable[0] = 1; |
|
last_nonwhite = out-1; |
|
if (stripwhite == 2) { |
|
while (!stb_tokentable[*s]) { |
|
if (*s != '"') { |
|
if (!isspace(*s)) last_nonwhite = out; |
|
*out++ = *s++; |
|
} else { |
|
++s; |
|
if (*s == '"') { |
|
if (!isspace(*s)) last_nonwhite = out; |
|
*out++ = *s++; // "" -> ", not start string |
|
} else { |
|
// begin a quoted string |
|
while (*s) { |
|
if (s[0] == '"') { |
|
if (s[1] == '"') { *out++ = *s; s += 2; } |
|
else { ++s; break; } // terminating " |
|
} else |
|
*out++ = *s++; |
|
} |
|
last_nonwhite = out-1; // all in quotes counts as non-white |
|
} |
|
} |
|
} |
|
} else { |
|
while (nested || !stb_tokentable[*s]) { |
|
if (!isspace(*s)) last_nonwhite = out; |
|
if (stable[*s]) { |
|
if (!*s) break; |
|
if (end ? etable[*s] : nested) |
|
--nested; |
|
else |
|
++nested; |
|
} |
|
*out++ = *s++; |
|
} |
|
} |
|
|
|
if (stripwhite) // rewind to last non-whitespace char |
|
out = last_nonwhite+1; |
|
*out++ = '\0'; |
|
|
|
if (*s) ++s; // skip delimiter |
|
} |
|
s = (unsigned char *) delimit; while (*s) stb_tokentable[*s++] = 0; |
|
if (start) { |
|
s = (unsigned char *) start; while (*s) stable[*s++] = 1; |
|
s = (unsigned char *) end; if (s) while (*s) stable[*s++] = 1; |
|
s = (unsigned char *) end; if (s) while (*s) etable[*s++] = 1; |
|
} |
|
if (count != NULL) *count = num; |
|
result[num] = 0; |
|
return result; |
|
} |
|
|
|
char **stb_tokens(char *src, char *delimit, int *count) |
|
{ |
|
return stb_tokens_raw(src,delimit,count,0,0,0,0); |
|
} |
|
|
|
char **stb_tokens_nested(char *src, char *delimit, int *count, char *nest_in, char *nest_out) |
|
{ |
|
return stb_tokens_raw(src,delimit,count,0,0,nest_in,nest_out); |
|
} |
|
|
|
char **stb_tokens_nested_empty(char *src, char *delimit, int *count, char *nest_in, char *nest_out) |
|
{ |
|
return stb_tokens_raw(src,delimit,count,0,1,nest_in,nest_out); |
|
} |
|
|
|
char **stb_tokens_allowempty(char *src, char *delimit, int *count) |
|
{ |
|
return stb_tokens_raw(src,delimit,count,0,1,0,0); |
|
} |
|
|
|
char **stb_tokens_stripwhite(char *src, char *delimit, int *count) |
|
{ |
|
return stb_tokens_raw(src,delimit,count,1,1,0,0); |
|
} |
|
|
|
char **stb_tokens_quoted(char *src, char *delimit, int *count) |
|
{ |
|
return stb_tokens_raw(src,delimit,count,2,1,0,0); |
|
} |
|
|
|
char *stb_dupreplace(char *src, char *find, char *replace) |
|
{ |
|
size_t len_find = strlen(find); |
|
size_t len_replace = strlen(replace); |
|
int count = 0; |
|
|
|
char *s,*p,*q; |
|
|
|
s = strstr(src, find); |
|
if (s == NULL) return strdup(src); |
|
do { |
|
++count; |
|
s = strstr(s + len_find, find); |
|
} while (s != NULL); |
|
|
|
p = (char *) malloc(strlen(src) + count * (len_replace - len_find) + 1); |
|
if (p == NULL) return p; |
|
q = p; |
|
s = src; |
|
for (;;) { |
|
char *t = strstr(s, find); |
|
if (t == NULL) { |
|
strcpy(q,s); |
|
assert(strlen(p) == strlen(src) + count*(len_replace-len_find)); |
|
return p; |
|
} |
|
memcpy(q, s, t-s); |
|
q += t-s; |
|
memcpy(q, replace, len_replace); |
|
q += len_replace; |
|
s = t + len_find; |
|
} |
|
} |
|
|
|
void stb_replaceinplace(char *src, char *find, char *replace) |
|
{ |
|
size_t len_find = strlen(find); |
|
size_t len_replace = strlen(replace); |
|
int delta; |
|
|
|
char *s,*p,*q; |
|
|
|
delta = len_replace - len_find; |
|
assert(delta <= 0); |
|
if (delta > 0) return; |
|
|
|
p = strstr(src, find); |
|
if (p == NULL) return; |
|
|
|
s = q = p; |
|
while (*s) { |
|
memcpy(q, replace, len_replace); |
|
p += len_find; |
|
q += len_replace; |
|
s = strstr(p, find); |
|
if (s == NULL) s = p + strlen(p); |
|
memmove(q, p, s-p); |
|
q += s-p; |
|
p = s; |
|
} |
|
*q = 0; |
|
} |
|
|
|
void stb_fixpath(char *path) |
|
{ |
|
for(; *path; ++path) |
|
if (*path == '\\') |
|
*path = '/'; |
|
} |
|
|
|
void stb__add_section(char *buffer, char *data, int curlen, int newlen) |
|
{ |
|
if (newlen < curlen) { |
|
int z1 = newlen >> 1, z2 = newlen-z1; |
|
memcpy(buffer, data, z1-1); |
|
buffer[z1-1] = '.'; |
|
buffer[z1-0] = '.'; |
|
memcpy(buffer+z1+1, data+curlen-z2+1, z2-1); |
|
} else |
|
memcpy(buffer, data, curlen); |
|
} |
|
|
|
char * stb_shorten_path_readable(char *path, int len) |
|
{ |
|
static char buffer[1024]; |
|
int n = strlen(path),n1,n2,r1,r2; |
|
char *s; |
|
if (n <= len) return path; |
|
if (len > 1024) return path; |
|
s = stb_strrchr2(path, '/', '\\'); |
|
if (s) { |
|
n1 = s - path + 1; |
|
n2 = n - n1; |
|
++s; |
|
} else { |
|
n1 = 0; |
|
n2 = n; |
|
s = path; |
|
} |
|
// now we need to reduce r1 and r2 so that they fit in len |
|
if (n1 < len>>1) { |
|
r1 = n1; |
|
r2 = len - r1; |
|
} else if (n2 < len >> 1) { |
|
r2 = n2; |
|
r1 = len - r2; |
|
} else { |
|
r1 = n1 * len / n; |
|
r2 = n2 * len / n; |
|
if (r1 < len>>2) r1 = len>>2, r2 = len-r1; |
|
if (r2 < len>>2) r2 = len>>2, r1 = len-r2; |
|
} |
|
assert(r1 <= n1 && r2 <= n2); |
|
if (n1) |
|
stb__add_section(buffer, path, n1, r1); |
|
stb__add_section(buffer+r1, s, n2, r2); |
|
buffer[len] = 0; |
|
return buffer; |
|
} |
|
|
|
static char *stb__splitpath_raw(char *buffer, char *path, int flag) |
|
{ |
|
int len=0,x,y, n = (int) strlen(path), f1,f2; |
|
char *s = stb_strrchr2(path, '/', '\\'); |
|
char *t = strrchr(path, '.'); |
|
if (s && t && t < s) t = NULL; |
|
if (s) ++s; |
|
|
|
if (flag == STB_EXT_NO_PERIOD) |
|
flag |= STB_EXT; |
|
|
|
if (!(flag & (STB_PATH | STB_FILE | STB_EXT))) return NULL; |
|
|
|
f1 = s == NULL ? 0 : s-path; // start of filename |
|
f2 = t == NULL ? n : t-path; // just past end of filename |
|
|
|
if (flag & STB_PATH) { |
|
x = 0; if (f1 == 0 && flag == STB_PATH) len=2; |
|
} else if (flag & STB_FILE) { |
|
x = f1; |
|
} else { |
|
x = f2; |
|
if (flag & STB_EXT_NO_PERIOD) |
|
if (buffer[x] == '.') |
|
++x; |
|
} |
|
|
|
if (flag & STB_EXT) |
|
y = n; |
|
else if (flag & STB_FILE) |
|
y = f2; |
|
else |
|
y = f1; |
|
|
|
if (buffer == NULL) { |
|
buffer = (char *) malloc(y-x + len + 1); |
|
if (!buffer) return NULL; |
|
} |
|
|
|
if (len) { strcpy(buffer, "./"); return buffer; } |
|
strncpy(buffer, path+x, y-x); |
|
buffer[y-x] = 0; |
|
return buffer; |
|
} |
|
|
|
char *stb_splitpath(char *output, char *src, int flag) |
|
{ |
|
return stb__splitpath_raw(output, src, flag); |
|
} |
|
|
|
char *stb_splitpathdup(char *src, int flag) |
|
{ |
|
return stb__splitpath_raw(NULL, src, flag); |
|
} |
|
|
|
char *stb_replacedir(char *output, char *src, char *dir) |
|
{ |
|
char buffer[4096]; |
|
stb_splitpath(buffer, src, STB_FILE | STB_EXT); |
|
if (dir) |
|
sprintf(output, "%s/%s", dir, buffer); |
|
else |
|
strcpy(output, buffer); |
|
return output; |
|
} |
|
|
|
char *stb_replaceext(char *output, char *src, char *ext) |
|
{ |
|
char buffer[4096]; |
|
stb_splitpath(buffer, src, STB_PATH | STB_FILE); |
|
if (ext) |
|
sprintf(output, "%s.%s", buffer, ext[0] == '.' ? ext+1 : ext); |
|
else |
|
strcpy(output, buffer); |
|
return output; |
|
} |
|
#endif |
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////// |
|
// |
|
// stb_arr |
|
// |
|
// An stb_arr is directly useable as a pointer (use the actual type in your |
|
// definition), but when it resizes, it returns a new pointer and you can't |
|
// use the old one, so you have to be careful to copy-in-out as necessary. |
|
// |
|
// Use a NULL pointer as a 0-length array. |
|
// |
|
// float *my_array = NULL, *temp; |
|
// |
|
// // add elements on the end one at a time |
|
// stb_arr_push(my_array, 0.0f); |
|
// stb_arr_push(my_array, 1.0f); |
|
// stb_arr_push(my_array, 2.0f); |
|
// |
|
// assert(my_array[1] == 2.0f); |
|
// |
|
// // add an uninitialized element at the end, then assign it |
|
// *stb_arr_add(my_array) = 3.0f; |
|
// |
|
// // add three uninitialized elements at the end |
|
// temp = stb_arr_addn(my_array,3); |
|
// temp[0] = 4.0f; |
|
// temp[1] = 5.0f; |
|
// temp[2] = 6.0f; |
|
// |
|
// assert(my_array[5] == 5.0f); |
|
// |
|
// // remove the last one |
|
// stb_arr_pop(my_array); |
|
// |
|
// assert(stb_arr_len(my_array) == 6); |
|
|
|
|
|
#ifndef STB_INCLUDE_STB_LIB_H |
|
|
|
// simple functions written on top of other functions |
|
#define stb_arr_empty(a) ( stb_arr_len(a) == 0 ) |
|
#define stb_arr_add(a) ( stb_arr_addn((a),1) ) |
|
#define stb_arr_push(a,v) ( *stb_arr_add(a)=(v) ) |
|
|
|
typedef struct |
|
{ |
|
int len, limit; |
|
unsigned int signature; |
|
unsigned int padding; // make it a multiple of 16 so preserve alignment mod 16 |
|
} stb__arr; |
|
|
|
#define stb_arr_signature 0x51bada7b // ends with 0123 in decimal |
|
|
|
// access the header block stored before the data |
|
#define stb_arrhead(a) /*lint --e(826)*/ (((stb__arr *) (a)) - 1) |
|
#define stb_arrhead2(a) /*lint --e(826)*/ (((stb__arr *) (a)) - 1) |
|
|
|
#ifdef STB_DEBUG |
|
#define stb_arr_check(a) assert(!a || stb_arrhead(a)->signature == stb_arr_signature) |
|
#define stb_arr_check2(a) assert(!a || stb_arrhead2(a)->signature == stb_arr_signature) |
|
#else |
|
#define stb_arr_check(a) ((void) 0) |
|
#define stb_arr_check2(a) ((void) 0) |
|
#endif |
|
|
|
// ARRAY LENGTH |
|
|
|
// get the array length; special case if pointer is NULL |
|
#define stb_arr_len(a) (a ? stb_arrhead(a)->len : 0) |
|
#define stb_arr_len2(a) ((stb__arr *) (a) ? stb_arrhead2(a)->len : 0) |
|
#define stb_arr_lastn(a) (stb_arr_len(a)-1) |
|
|
|
// check whether a given index is valid -- tests 0 <= i < stb_arr_len(a) |
|
#define stb_arr_valid(a,i) (a ? (int) (i) < stb_arrhead(a)->len : 0) |
|
|
|
// change the array length so is is exactly N entries long, creating |
|
// uninitialized entries as needed |
|
#define stb_arr_setlen(a,n) \ |
|
(stb__arr_setlen((void **) &(a), sizeof(a[0]), (n))) |
|
|
|
// change the array length so that N is a valid index (that is, so |
|
// it is at least N entries long), creating uninitialized entries as needed |
|
#define stb_arr_makevalid(a,n) \ |
|
(stb_arr_len(a) < (n)+1 ? stb_arr_setlen(a,(n)+1),(a) : (a)) |
|
|
|
// remove the last element of the array, returning it |
|
#define stb_arr_pop(a) ((stb_arr_check(a), (a))[--stb_arrhead(a)->len]) |
|
|
|
// access the last element in the array |
|
#define stb_arr_last(a) ((stb_arr_check(a), (a))[stb_arr_len(a)-1]) |
|
|
|
// is iterator at end of list? |
|
#define stb_arr_end(a,i) ((i) >= &(a)[stb_arr_len(a)]) |
|
|
|
// (internal) change the allocated length of the array |
|
#define stb_arr__grow(a,n) (stb_arr_check(a), stb_arrhead(a)->len += (n)) |
|
|
|
// add N new unitialized elements to the end of the array |
|
#define stb_arr__addn(a,n) /*lint --e(826)*/ \ |
|
((stb_arr_len(a)+(n) > stb_arrcurmax(a)) \ |
|
? (stb__arr_addlen((void **) &(a),sizeof(*a),(n)),0) \ |
|
: ((stb_arr__grow(a,n), 0))) |
|
|
|
// add N new unitialized elements to the end of the array, and return |
|
// a pointer to the first new one |
|
#define stb_arr_addn(a,n) (stb_arr__addn((a),n),(a)+stb_arr_len(a)-(n)) |
|
|
|
// add N new uninitialized elements starting at index 'i' |
|
#define stb_arr_insertn(a,i,n) (stb__arr_insertn((void **) &(a), sizeof(*a), i, n)) |
|
|
|
// insert an element at i |
|
#define stb_arr_insert(a,i,v) (stb__arr_insertn((void **) &(a), sizeof(*a), i, 1), ((a)[i] = v)) |
|
|
|
// delete N elements from the middle starting at index 'i' |
|
#define stb_arr_deleten(a,i,n) (stb__arr_deleten((void **) &(a), sizeof(*a), i, n)) |
|
|
|
// delete the i'th element |
|
#define stb_arr_delete(a,i) stb_arr_deleten(a,i,1) |
|
|
|
// delete the i'th element, swapping down from the end |
|
#define stb_arr_fastdelete(a,i) \ |
|
(stb_swap(&a[i], &a[stb_arrhead(a)->len-1], sizeof(*a)), stb_arr_pop(a)) |
|
|
|
|
|
// ARRAY STORAGE |
|
|
|
// get the array maximum storage; special case if NULL |
|
#define stb_arrcurmax(a) (a ? stb_arrhead(a)->limit : 0) |
|
#define stb_arrcurmax2(a) (a ? stb_arrhead2(a)->limit : 0) |
|
|
|
// set the maxlength of the array to n in anticipation of further growth |
|
#define stb_arr_setsize(a,n) (stb_arr_check(a), stb__arr_setsize((void **) &(a),sizeof((a)[0]),n)) |
|
|
|
// make sure maxlength is large enough for at least N new allocations |
|
#define stb_arr_atleast(a,n) (stb_arr_len(a)+(n) > stb_arrcurmax(a) \ |
|
? stb_arr_setsize((a), (n)) : 0) |
|
|
|
// make a copy of a given array (copies contents via 'memcpy'!) |
|
#define stb_arr_copy(a) stb__arr_copy(a, sizeof((a)[0])) |
|
|
|
// compute the storage needed to store all the elements of the array |
|
#define stb_arr_storage(a) (stb_arr_len(a) * sizeof((a)[0])) |
|
|
|
#define stb_arr_for(v,arr) for((v)=(arr); (v) < (arr)+stb_arr_len(arr); ++(v)) |
|
|
|
// IMPLEMENTATION |
|
|
|
STB_EXTERN void stb_arr_free_(void **p); |
|
STB_EXTERN void *stb__arr_copy_(void *p, int elem_size); |
|
STB_EXTERN void stb__arr_setsize_(void **p, int size, int limit); |
|
STB_EXTERN void stb__arr_setlen_(void **p, int size, int newlen); |
|
STB_EXTERN void stb__arr_addlen_(void **p, int size, int addlen); |
|
STB_EXTERN void stb__arr_deleten_(void **p, int size, int loc, int n); |
|
STB_EXTERN void stb__arr_insertn_(void **p, int size, int loc, int n); |
|
|
|
#define stb_arr_free(p) stb_arr_free_((void **) &(p)) |
|
|
|
#ifndef STBLIB_MALLOC_WRAPPER // @Todo |
|
#define stb__arr_setsize stb__arr_setsize_ |
|
#define stb__arr_setlen stb__arr_setlen_ |
|
#define stb__arr_addlen stb__arr_addlen_ |
|
#define stb__arr_deleten stb__arr_deleten_ |
|
#define stb__arr_insertn stb__arr_insertn_ |
|
#define stb__arr_copy stb__arr_copy_ |
|
#else |
|
#define stb__arr_addlen(p,s,n) stb__arr_addlen_(p,s,n,__FILE__,__LINE__) |
|
#define stb__arr_setlen(p,s,n) stb__arr_setlen_(p,s,n,__FILE__,__LINE__) |
|
#define stb__arr_setsize(p,s,n) stb__arr_setsize_(p,s,n,__FILE__,__LINE__) |
|
#define stb__arr_deleten(p,s,i,n) stb__arr_deleten_(p,s,i,n,__FILE__,__LINE__) |
|
#define stb__arr_insertn(p,s,i,n) stb__arr_insertn_(p,s,i,n,__FILE__,__LINE__) |
|
#define stb__arr_copy(p,s) stb__arr_copy_(p,s,__FILE__,__LINE__) |
|
#endif |
|
#endif // STB_INCLUDE_STB_LIB_H |
|
|
|
#ifdef STB_LIB_IMPLEMENTATION |
|
void stb_arr_malloc(void **target, void *context) |
|
{ |
|
stb__arr *q = (stb__arr *) malloc(sizeof(*q)); |
|
q->len = q->limit = 0; |
|
q->signature = stb_arr_signature; |
|
*target = (void *) (q+1); |
|
} |
|
|
|
static void * stb__arr_malloc(int size) |
|
{ |
|
return malloc(size); |
|
} |
|
|
|
void * stb__arr_copy_(void *p, int elem_size) |
|
{ |
|
stb__arr *q; |
|
if (p == NULL) return p; |
|
q = (stb__arr *) malloc(sizeof(*q) + elem_size * stb_arrhead2(p)->limit); |
|
stb_arr_check2(p); |
|
memcpy(q, stb_arrhead2(p), sizeof(*q) + elem_size * stb_arrhead2(p)->len); |
|
return q+1; |
|
} |
|
|
|
void stb_arr_free_(void **pp) |
|
{ |
|
void *p = *pp; |
|
stb_arr_check2(p); |
|
if (p) { |
|
stb__arr *q = stb_arrhead2(p); |
|
free(q); |
|
} |
|
*pp = NULL; |
|
} |
|
|
|
static void stb__arrsize_(void **pp, int size, int limit, int len) |
|
{ |
|
void *p = *pp; |
|
stb__arr *a; |
|
stb_arr_check2(p); |
|
if (p == NULL) { |
|
if (len == 0 && size == 0) return; |
|
a = (stb__arr *) stb__arr_malloc(sizeof(*a) + size*limit); |
|
a->limit = limit; |
|
a->len = len; |
|
a->signature = stb_arr_signature; |
|
} else { |
|
a = stb_arrhead2(p); |
|
a->len = len; |
|
if (a->limit < limit) { |
|
void *p; |
|
if (a->limit >= 4 && limit < a->limit * 2) |
|
limit = a->limit * 2; |
|
p = realloc(a, sizeof(*a) + limit*size); |
|
if (p) { |
|
a = (stb__arr *) p; |
|
a->limit = limit; |
|
} else { |
|
// throw an error! |
|
} |
|
} |
|
} |
|
a->len = a->len < a->limit ? a->len : a->limit; |
|
*pp = a+1; |
|
} |
|
|
|
void stb__arr_setsize_(void **pp, int size, int limit) |
|
{ |
|
void *p = *pp; |
|
stb_arr_check2(p); |
|
stb__arrsize_(pp, size, limit, stb_arr_len2(p)); |
|
} |
|
|
|
void stb__arr_setlen_(void **pp, int size, int newlen) |
|
{ |
|
void *p = *pp; |
|
stb_arr_check2(p); |
|
if (stb_arrcurmax2(p) < newlen || p == NULL) { |
|
stb__arrsize_(pp, size, newlen, newlen); |
|
} else { |
|
stb_arrhead2(p)->len = newlen; |
|
} |
|
} |
|
|
|
void stb__arr_addlen_(void **p, int size, int addlen) |
|
{ |
|
stb__arr_setlen_(p, size, stb_arr_len2(*p) + addlen); |
|
} |
|
|
|
void stb__arr_insertn_(void **pp, int size, int i, int n) |
|
{ |
|
void *p = *pp; |
|
if (n) { |
|
int z; |
|
|
|
if (p == NULL) { |
|
stb__arr_addlen_(pp, size, n); |
|
return; |
|
} |
|
|
|
z = stb_arr_len2(p); |
|
stb__arr_addlen_(&p, size, n); |
|
memmove((char *) p + (i+n)*size, (char *) p + i*size, size * (z-i)); |
|
} |
|
*pp = p; |
|
} |
|
|
|
void stb__arr_deleten_(void **pp, int size, int i, int n) |
|
{ |
|
void *p = *pp; |
|
if (n) { |
|
memmove((char *) p + i*size, (char *) p + (i+n)*size, size * (stb_arr_len2(p)-(i+n))); |
|
stb_arrhead2(p)->len -= n; |
|
} |
|
*pp = p; |
|
} |
|
#endif |
|
|
|
////////////////////////////////////////////////////////////////////////////// |
|
// |
|
// Hashing |
|
// |
|
// typical use for this is to make a power-of-two hash table. |
|
// |
|
// let N = size of table (2^n) |
|
// let H = stb_hash(str) |
|
// let S = stb_rehash(H) | 1 |
|
// |
|
// then hash probe sequence P(i) for i=0..N-1 |
|
// P(i) = (H + S*i) & (N-1) |
|
// |
|
// the idea is that H has 32 bits of hash information, but the |
|
// table has only, say, 2^20 entries so only uses 20 of the bits. |
|
// then by rehashing the original H we get 2^12 different probe |
|
// sequences for a given initial probe location. (So it's optimal |
|
// for 64K tables and its optimality decreases past that.) |
|
// |
|
// ok, so I've added something that generates _two separate_ |
|
// 32-bit hashes simultaneously which should scale better to |
|
// very large tables. |
|
|
|
#ifndef STB_INCLUDE_STB_LIB_H |
|
STB_EXTERN unsigned int stb_hash(char *str); |
|
STB_EXTERN unsigned int stb_hashptr(void *p); |
|
STB_EXTERN unsigned int stb_hashlen(char *str, int len); |
|
STB_EXTERN unsigned int stb_rehash_improved(unsigned int v); |
|
STB_EXTERN unsigned int stb_hash_fast(void *p, int len); |
|
STB_EXTERN unsigned int stb_hash2(char *str, unsigned int *hash2_ptr); |
|
STB_EXTERN unsigned int stb_hash_number(unsigned int hash); |
|
|
|
#define stb_rehash(x) ((x) + ((x) >> 6) + ((x) >> 19)) |
|
#endif // STB_INCLUDE_STB_LIB_H |
|
|
|
#ifdef STB_LIB_IMPLEMENTATION |
|
unsigned int stb_hash(char *str) |
|
{ |
|
unsigned int hash = 0; |
|
while (*str) |
|
hash = (hash << 7) + (hash >> 25) + *str++; |
|
return hash + (hash >> 16); |
|
} |
|
|
|
unsigned int stb_hashlen(char *str, int len) |
|
{ |
|
unsigned int hash = 0; |
|
while (len-- > 0 && *str) |
|
hash = (hash << 7) + (hash >> 25) + *str++; |
|
return hash + (hash >> 16); |
|
} |
|
|
|
unsigned int stb_hashptr(void *p) |
|
{ |
|
unsigned int x = (unsigned int)(size_t) p; |
|
|
|
// typically lacking in low bits and high bits |
|
x = stb_rehash(x); |
|
x += x << 16; |
|
|
|
// pearson's shuffle |
|
x ^= x << 3; |
|
x += x >> 5; |
|
x ^= x << 2; |
|
x += x >> 15; |
|
x ^= x << 10; |
|
return stb_rehash(x); |
|
} |
|
|
|
unsigned int stb_rehash_improved(unsigned int v) |
|
{ |
|
return stb_hashptr((void *)(size_t) v); |
|
} |
|
|
|
unsigned int stb_hash2(char *str, unsigned int *hash2_ptr) |
|
{ |
|
unsigned int hash1 = 0x3141592c; |
|
unsigned int hash2 = 0x77f044ed; |
|
while (*str) { |
|
hash1 = (hash1 << 7) + (hash1 >> 25) + *str; |
|
hash2 = (hash2 << 11) + (hash2 >> 21) + *str; |
|
++str; |
|
} |
|
*hash2_ptr = hash2 + (hash1 >> 16); |
|
return hash1 + (hash2 >> 16); |
|
} |
|
|
|
// Paul Hsieh hash |
|
#define stb__get16_slow(p) ((p)[0] + ((p)[1] << 8)) |
|
#if defined(_MSC_VER) |
|
#define stb__get16(p) (*((unsigned short *) (p))) |
|
#else |
|
#define stb__get16(p) stb__get16_slow(p) |
|
#endif |
|
|
|
unsigned int stb_hash_fast(void *p, int len) |
|
{ |
|
unsigned char *q = (unsigned char *) p; |
|
unsigned int hash = len; |
|
|
|
if (len <= 0 || q == NULL) return 0; |
|
|
|
/* Main loop */ |
|
if (((int)(size_t) q & 1) == 0) { |
|
for (;len > 3; len -= 4) { |
|
unsigned int val; |
|
hash += stb__get16(q); |
|
val = (stb__get16(q+2) << 11); |
|
hash = (hash << 16) ^ hash ^ val; |
|
q += 4; |
|
hash += hash >> 11; |
|
} |
|
} else { |
|
for (;len > 3; len -= 4) { |
|
unsigned int val; |
|
hash += stb__get16_slow(q); |
|
val = (stb__get16_slow(q+2) << 11); |
|
hash = (hash << 16) ^ hash ^ val; |
|
q += 4; |
|
hash += hash >> 11; |
|
} |
|
} |
|
|
|
/* Handle end cases */ |
|
switch (len) { |
|
case 3: hash += stb__get16_slow(q); |
|
hash ^= hash << 16; |
|
hash ^= q[2] << 18; |
|
hash += hash >> 11; |
|
break; |
|
case 2: hash += stb__get16_slow(q); |
|
hash ^= hash << 11; |
|
hash += hash >> 17; |
|
break; |
|
case 1: hash += q[0]; |
|
hash ^= hash << 10; |
|
hash += hash >> 1; |
|
break; |
|
case 0: break; |
|
} |
|
|
|
/* Force "avalanching" of final 127 bits */ |
|
hash ^= hash << 3; |
|
hash += hash >> 5; |
|
hash ^= hash << 4; |
|
hash += hash >> 17; |
|
hash ^= hash << 25; |
|
hash += hash >> 6; |
|
|
|
return hash; |
|
} |
|
|
|
unsigned int stb_hash_number(unsigned int hash) |
|
{ |
|
hash ^= hash << 3; |
|
hash += hash >> 5; |
|
hash ^= hash << 4; |
|
hash += hash >> 17; |
|
hash ^= hash << 25; |
|
hash += hash >> 6; |
|
return hash; |
|
} |
|
#endif |
|
|
|
////////////////////////////////////////////////////////////////////////////// |
|
// |
|
// Instantiated data structures |
|
// |
|
// This is an attempt to implement a templated data structure. |
|
// |
|
// Hash table: call stb_define_hash(TYPE,N,KEY,K1,K2,HASH,VALUE) |
|
// TYPE -- will define a structure type containing the hash table |
|
// N -- the name, will prefix functions named: |
|
// N create |
|
// N destroy |
|
// N get |
|
// N set, N add, N update, |
|
// N remove |
|
// KEY -- the type of the key. 'x == y' must be valid |
|
// K1,K2 -- keys never used by the app, used as flags in the hashtable |
|
// HASH -- a piece of code ending with 'return' that hashes key 'k' |
|
// VALUE -- the type of the value. 'x = y' must be valid |
|
// |
|
// Note that stb_define_hash_base can be used to define more sophisticated |
|
// hash tables, e.g. those that make copies of the key or use special |
|
// comparisons (e.g. strcmp). |
|
|
|
#define STB_(prefix,name) stb__##prefix##name |
|
#define STB__(prefix,name) prefix##name |
|
#define STB__use(x) x |
|
#define STB__skip(x) |
|
|
|
#define stb_declare_hash(PREFIX,TYPE,N,KEY,VALUE) \ |
|
typedef struct stb__st_##TYPE TYPE;\ |
|
PREFIX int STB__(N, init)(TYPE *h, int count);\ |
|
PREFIX int STB__(N, memory_usage)(TYPE *h);\ |
|
PREFIX TYPE * STB__(N, create)(void);\ |
|
PREFIX TYPE * STB__(N, copy)(TYPE *h);\ |
|
PREFIX void STB__(N, destroy)(TYPE *h);\ |
|
PREFIX int STB__(N,get_flag)(TYPE *a, KEY k, VALUE *v);\ |
|
PREFIX VALUE STB__(N,get)(TYPE *a, KEY k);\ |
|
PREFIX int STB__(N, set)(TYPE *a, KEY k, VALUE v);\ |
|
PREFIX int STB__(N, add)(TYPE *a, KEY k, VALUE v);\ |
|
PREFIX int STB__(N, update)(TYPE*a,KEY k,VALUE v);\ |
|
PREFIX int STB__(N, remove)(TYPE *a, KEY k, VALUE *v); |
|
|
|
#define STB_nocopy(x) (x) |
|
#define STB_nodelete(x) 0 |
|
#define STB_nofields |
|
#define STB_nonullvalue(x) |
|
#define STB_nullvalue(x) x |
|
#define STB_safecompare(x) x |
|
#define STB_nosafe(x) |
|
#define STB_noprefix |
|
|
|
#ifdef __GNUC__ |
|
#define STB__nogcc(x) |
|
#else |
|
#define STB__nogcc(x) x |
|
#endif |
|
|
|
#define stb_define_hash_base(PREFIX,TYPE,FIELDS,N,NC,LOAD_FACTOR, \ |
|
KEY,EMPTY,DEL,COPY,DISPOSE,SAFE, \ |
|
VCOMPARE,CCOMPARE,HASH, \ |
|
VALUE,HASVNULL,VNULL) \ |
|
\ |
|
typedef struct \ |
|
{ \ |
|
KEY k; \ |
|
VALUE v; \ |
|
} STB_(N,_hashpair); \ |
|
\ |
|
STB__nogcc( typedef struct stb__st_##TYPE TYPE; ) \ |
|
struct stb__st_##TYPE { \ |
|
FIELDS \ |
|
STB_(N,_hashpair) *table; \ |
|
unsigned int mask; \ |
|
int count, limit; \ |
|
int deleted; \ |
|
\ |
|
int delete_threshhold; \ |
|
int grow_threshhold; \ |
|
int shrink_threshhold; \ |
|
unsigned char alloced, has_empty, has_del; \ |
|
VALUE ev; VALUE dv; \ |
|
}; \ |
|
\ |
|
static unsigned int STB_(N, hash)(KEY k) \ |
|
{ \ |
|
HASH \ |
|
} \ |
|
\ |
|
PREFIX int STB__(N, init)(TYPE *h, int count) \ |
|
{ \ |
|
int i; \ |
|
if (count < 4) count = 4; \ |
|
h->limit = count; \ |
|
h->count = 0; \ |
|
h->mask = count-1; \ |
|
h->deleted = 0; \ |
|
h->grow_threshhold = (int) (count * LOAD_FACTOR); \ |
|
h->has_empty = h->has_del = 0; \ |
|
h->alloced = 0; \ |
|
if (count <= 64) \ |
|
h->shrink_threshhold = 0; \ |
|
else \ |
|
h->shrink_threshhold = (int) (count * (LOAD_FACTOR/2.25)); \ |
|
h->delete_threshhold = (int) (count * (1-LOAD_FACTOR)/2); \ |
|
h->table = (STB_(N,_hashpair)*) malloc(sizeof(h->table[0]) * count); \ |
|
if (h->table == NULL) return 0; \ |
|
/* ideally this gets turned into a memset32 automatically */ \ |
|
for (i=0; i < count; ++i) \ |
|
h->table[i].k = EMPTY; \ |
|
return 1; \ |
|
} \ |
|
\ |
|
PREFIX int STB__(N, memory_usage)(TYPE *h) \ |
|
{ \ |
|
return sizeof(*h) + h->limit * sizeof(h->table[0]); \ |
|
} \ |
|
\ |
|
PREFIX TYPE * STB__(N, create)(void) \ |
|
{ \ |
|
TYPE *h = (TYPE *) malloc(sizeof(*h)); \ |
|
if (h) { \ |
|
if (STB__(N, init)(h, 16)) \ |
|
h->alloced = 1; \ |
|
else { free(h); h=NULL; } \ |
|
} \ |
|
return h; \ |
|
} \ |
|
\ |
|
PREFIX void STB__(N, destroy)(TYPE *a) \ |
|
{ \ |
|
int i; \ |
|
for (i=0; i < a->limit; ++i) \ |
|
if (!CCOMPARE(a->table[i].k,EMPTY) && !CCOMPARE(a->table[i].k, DEL)) \ |
|
DISPOSE(a->table[i].k); \ |
|
free(a->table); \ |
|
if (a->alloced) \ |
|
free(a); \ |
|
} \ |
|
\ |
|
static void STB_(N, rehash)(TYPE *a, int count); \ |
|
\ |
|
PREFIX int STB__(N,get_flag)(TYPE *a, KEY k, VALUE *v) \ |
|
{ \ |
|
unsigned int h = STB_(N, hash)(k); \ |
|
unsigned int n = h & a->mask, s; \ |
|
if (CCOMPARE(k,EMPTY)){ if (a->has_empty) *v = a->ev; return a->has_empty;}\ |
|
if (CCOMPARE(k,DEL)) { if (a->has_del ) *v = a->dv; return a->has_del; }\ |
|
if (CCOMPARE(a->table[n].k,EMPTY)) return 0; \ |
|
SAFE(if (!CCOMPARE(a->table[n].k,DEL))) \ |
|
if (VCOMPARE(a->table[n].k,k)) { *v = a->table[n].v; return 1; } \ |
|
s = stb_rehash(h) | 1; \ |
|
for(;;) { \ |
|
n = (n + s) & a->mask; \ |
|
if (CCOMPARE(a->table[n].k,EMPTY)) return 0; \ |
|
SAFE(if (CCOMPARE(a->table[n].k,DEL)) continue;) \ |
|
if (VCOMPARE(a->table[n].k,k)) \ |
|
{ *v = a->table[n].v; return 1; } \ |
|
} \ |
|
} \ |
|
\ |
|
HASVNULL( \ |
|
PREFIX VALUE STB__(N,get)(TYPE *a, KEY k) \ |
|
{ \ |
|
VALUE v; \ |
|
if (STB__(N,get_flag)(a,k,&v)) return v; \ |
|
else return VNULL; \ |
|
} \ |
|
) \ |
|
\ |
|
PREFIX int STB__(N,getkey)(TYPE *a, KEY k, KEY *kout) \ |
|
{ \ |
|
unsigned int h = STB_(N, hash)(k); \ |
|
unsigned int n = h & a->mask, s; \ |
|
if (CCOMPARE(k,EMPTY)||CCOMPARE(k,DEL)) return 0; \ |
|
if (CCOMPARE(a->table[n].k,EMPTY)) return 0; \ |
|
SAFE(if (!CCOMPARE(a->table[n].k,DEL))) \ |
|
if (VCOMPARE(a->table[n].k,k)) { *kout = a->table[n].k; return 1; } \ |
|
s = stb_rehash(h) | 1; \ |
|
for(;;) { \ |
|
n = (n + s) & a->mask; \ |
|
if (CCOMPARE(a->table[n].k,EMPTY)) return 0; \ |
|
SAFE(if (CCOMPARE(a->table[n].k,DEL)) continue;) \ |
|
if (VCOMPARE(a->table[n].k,k)) \ |
|
{ *kout = a->table[n].k; return 1; } \ |
|
} \ |
|
} \ |
|
\ |
|
static int STB_(N,addset)(TYPE *a, KEY k, VALUE v, \ |
|
int allow_new, int allow_old, int copy) \ |
|
{ \ |
|
unsigned int h = STB_(N, hash)(k); \ |
|
unsigned int n = h & a->mask; \ |
|
int b = -1; \ |
|
if (CCOMPARE(k,EMPTY)) { \ |
|
if (a->has_empty ? allow_old : allow_new) { \ |
|
n=a->has_empty; a->ev = v; a->has_empty = 1; return !n; \ |
|
} else return 0; \ |
|
} \ |
|
if (CCOMPARE(k,DEL)) { \ |
|
if (a->has_del ? allow_old : allow_new) { \ |
|
n=a->has_del; a->dv = v; a->has_del = 1; return !n; \ |
|
} else return 0; \ |
|
} \ |
|
if (!CCOMPARE(a->table[n].k, EMPTY)) { \ |
|
unsigned int s; \ |
|
if (CCOMPARE(a->table[n].k, DEL)) \ |
|
b = n; \ |
|
else if (VCOMPARE(a->table[n].k,k)) { \ |
|
if (allow_old) \ |
|
a->table[n].v = v; \ |
|
return !allow_new; \ |
|
} \ |
|
s = stb_rehash(h) | 1; \ |
|
for(;;) { \ |
|
n = (n + s) & a->mask; \ |
|
if (CCOMPARE(a->table[n].k, EMPTY)) break; \ |
|
if (CCOMPARE(a->table[n].k, DEL)) { \ |
|
if (b < 0) b = n; \ |
|
} else if (VCOMPARE(a->table[n].k,k)) { \ |
|
if (allow_old) \ |
|
a->table[n].v = v; \ |
|
return !allow_new; \ |
|
} \ |
|
} \ |
|
} \ |
|
if (!allow_new) return 0; \ |
|
if (b < 0) b = n; else --a->deleted; \ |
|
a->table[b].k = copy ? COPY(k) : k; \ |
|
a->table[b].v = v; \ |
|
++a->count; \ |
|
if (a->count > a->grow_threshhold) \ |
|
STB_(N,rehash)(a, a->limit*2); \ |
|
return 1; \ |
|
} \ |
|
\ |
|
PREFIX int STB__(N, set)(TYPE *a, KEY k, VALUE v){return STB_(N,addset)(a,k,v,1,1,1);}\ |
|
PREFIX int STB__(N, add)(TYPE *a, KEY k, VALUE v){return STB_(N,addset)(a,k,v,1,0,1);}\ |
|
PREFIX int STB__(N, update)(TYPE*a,KEY k,VALUE v){return STB_(N,addset)(a,k,v,0,1,1);}\ |
|
\ |
|
PREFIX int STB__(N, remove)(TYPE *a, KEY k, VALUE *v) \ |
|
{ \ |
|
unsigned int h = STB_(N, hash)(k); \ |
|
unsigned int n = h & a->mask, s; \ |
|
if (CCOMPARE(k,EMPTY)) { if (a->has_empty) { if(v)*v = a->ev; a->has_empty=0; return 1; } return 0; } \ |
|
if (CCOMPARE(k,DEL)) { if (a->has_del ) { if(v)*v = a->dv; a->has_del =0; return 1; } return 0; } \ |
|
if (CCOMPARE(a->table[n].k,EMPTY)) return 0; \ |
|
if (SAFE(CCOMPARE(a->table[n].k,DEL) || ) !VCOMPARE(a->table[n].k,k)) { \ |
|
s = stb_rehash(h) | 1; \ |
|
for(;;) { \ |
|
n = (n + s) & a->mask; \ |
|
if (CCOMPARE(a->table[n].k,EMPTY)) return 0; \ |
|
SAFE(if (CCOMPARE(a->table[n].k, DEL)) continue;) \ |
|
if (VCOMPARE(a->table[n].k,k)) break; \ |
|
} \ |
|
} \ |
|
DISPOSE(a->table[n].k); \ |
|
a->table[n].k = DEL; \ |
|
--a->count; \ |
|
++a->deleted; \ |
|
if (v != NULL) \ |
|
*v = a->table[n].v; \ |
|
if (a->count < a->shrink_threshhold) \ |
|
STB_(N, rehash)(a, a->limit >> 1); \ |
|
else if (a->deleted > a->delete_threshhold) \ |
|
STB_(N, rehash)(a, a->limit); \ |
|
return 1; \ |
|
} \ |
|
\ |
|
PREFIX TYPE * STB__(NC, copy)(TYPE *a) \ |
|
{ \ |
|
int i; \ |
|
TYPE *h = (TYPE *) malloc(sizeof(*h)); \ |
|
if (!h) return NULL; \ |
|
if (!STB__(N, init)(h, a->limit)) { free(h); return NULL; } \ |
|
h->count = a->count; \ |
|
h->deleted = a->deleted; \ |
|
h->alloced = 1; \ |
|
h->ev = a->ev; h->dv = a->dv; \ |
|
h->has_empty = a->has_empty; h->has_del = a->has_del; \ |
|
memcpy(h->table, a->table, h->limit * sizeof(h->table[0])); \ |
|
for (i=0; i < a->limit; ++i) \ |
|
if (!CCOMPARE(h->table[i].k,EMPTY) && !CCOMPARE(h->table[i].k,DEL)) \ |
|
h->table[i].k = COPY(h->table[i].k); \ |
|
return h; \ |
|
} \ |
|
\ |
|
static void STB_(N, rehash)(TYPE *a, int count) \ |
|
{ \ |
|
int i; \ |
|
TYPE b; \ |
|
STB__(N, init)(&b, count); \ |
|
for (i=0; i < a->limit; ++i) \ |
|
if (!CCOMPARE(a->table[i].k,EMPTY) && !CCOMPARE(a->table[i].k,DEL)) \ |
|
STB_(N,addset)(&b, a->table[i].k, a->table[i].v,1,1,0); \ |
|
free(a->table); \ |
|
a->table = b.table; \ |
|
a->mask = b.mask; \ |
|
a->count = b.count; \ |
|
a->limit = b.limit; \ |
|
a->deleted = b.deleted; \ |
|
a->delete_threshhold = b.delete_threshhold; \ |
|
a->grow_threshhold = b.grow_threshhold; \ |
|
a->shrink_threshhold = b.shrink_threshhold; \ |
|
} |
|
|
|
#define STB_equal(a,b) ((a) == (b)) |
|
|
|
#define stb_define_hash(TYPE,N,KEY,EMPTY,DEL,HASH,VALUE) \ |
|
stb_define_hash_base(STB_noprefix, TYPE,STB_nofields,N,NC,0.85f, \ |
|
KEY,EMPTY,DEL,STB_nocopy,STB_nodelete,STB_nosafe, \ |
|
STB_equal,STB_equal,HASH, \ |
|
VALUE,STB_nonullvalue,0) |
|
|
|
#define stb_define_hash_vnull(TYPE,N,KEY,EMPTY,DEL,HASH,VALUE,VNULL) \ |
|
stb_define_hash_base(STB_noprefix, TYPE,STB_nofields,N,NC,0.85f, \ |
|
KEY,EMPTY,DEL,STB_nocopy,STB_nodelete,STB_nosafe, \ |
|
STB_equal,STB_equal,HASH, \ |
|
VALUE,STB_nullvalue,VNULL) |
|
|
|
////////////////////////////////////////////////////////////////////////////// |
|
// |
|
// stb_ptrmap |
|
// |
|
// An stb_ptrmap data structure is an O(1) hash table between pointers. One |
|
// application is to let you store "extra" data associated with pointers, |
|
// which is why it was originally called stb_extra. |
|
|
|
#ifndef STB_INCLUDE_STB_LIB_H |
|
stb_declare_hash(STB_EXTERN, stb_ptrmap, stb_ptrmap_, void *, void *) |
|
stb_declare_hash(STB_EXTERN, stb_idict, stb_idict_, stb_int32, stb_int32) |
|
|
|
STB_EXTERN void stb_ptrmap_delete(stb_ptrmap *e, void (*free_func)(void *)); |
|
STB_EXTERN stb_ptrmap *stb_ptrmap_new(void); |
|
|
|
STB_EXTERN stb_idict * stb_idict_new_size(unsigned int size); |
|
STB_EXTERN void stb_idict_remove_all(stb_idict *e); |
|
#endif // STB_INCLUDE_STB_LIB_H |
|
|
|
#ifdef STB_LIB_IMPLEMENTATION |
|
|
|
#define STB_EMPTY ((void *) 2) |
|
#define STB_EDEL ((void *) 6) |
|
|
|
stb_define_hash_base(STB_noprefix,stb_ptrmap, STB_nofields, stb_ptrmap_,stb_ptrmap_,0.85f, |
|
void *,STB_EMPTY,STB_EDEL,STB_nocopy,STB_nodelete,STB_nosafe, |
|
STB_equal,STB_equal,return stb_hashptr(k);, |
|
void *,STB_nullvalue,NULL) |
|
|
|
stb_ptrmap *stb_ptrmap_new(void) |
|
{ |
|
return stb_ptrmap_create(); |
|
} |
|
|
|
void stb_ptrmap_delete(stb_ptrmap *e, void (*free_func)(void *)) |
|
{ |
|
int i; |
|
if (free_func) |
|
for (i=0; i < e->limit; ++i) |
|
if (e->table[i].k != STB_EMPTY && e->table[i].k != STB_EDEL) { |
|
if (free_func == free) |
|
free(e->table[i].v); // allow STB_MALLOC_WRAPPER to operate |
|
else |
|
free_func(e->table[i].v); |
|
} |
|
stb_ptrmap_destroy(e); |
|
} |
|
|
|
// extra fields needed for stua_dict |
|
#define STB_IEMPTY ((int) 1) |
|
#define STB_IDEL ((int) 3) |
|
stb_define_hash_base(STB_noprefix, stb_idict, STB_nofields, stb_idict_,stb_idict_,0.85f, |
|
stb_int32,STB_IEMPTY,STB_IDEL,STB_nocopy,STB_nodelete,STB_nosafe, |
|
STB_equal,STB_equal, |
|
return stb_rehash_improved(k);,stb_int32,STB_nonullvalue,0) |
|
|
|
stb_idict * stb_idict_new_size(unsigned int size) |
|
{ |
|
stb_idict *e = (stb_idict *) malloc(sizeof(*e)); |
|
if (e) { |
|
// round up to power of 2 |
|
while ((size & (size-1)) != 0) // while more than 1 bit is set |
|
size += (size & ~(size-1)); // add the lowest set bit |
|
stb_idict_init(e, size); |
|
e->alloced = 1; |
|
} |
|
return e; |
|
} |
|
|
|
void stb_idict_remove_all(stb_idict *e) |
|
{ |
|
int n; |
|
for (n=0; n < e->limit; ++n) |
|
e->table[n].k = STB_IEMPTY; |
|
e->has_empty = e->has_del = 0; |
|
} |
|
#endif |
|
|
|
////////////////////////////////////////////////////////////////////////////// |
|
// |
|
// SDICT: Hash Table for Strings (symbol table) |
|
// |
|
// if "use_arena=1", then strings will be copied |
|
// into blocks and never freed until the sdict is freed; |
|
// otherwise they're malloc()ed and free()d on the fly. |
|
// (specify use_arena=1 if you never stb_sdict_remove) |
|
|
|
#ifndef STB_INCLUDE_STB_LIB_H |
|
stb_declare_hash(STB_EXTERN, stb_sdict, stb_sdict_, char *, void *) |
|
|
|
STB_EXTERN stb_sdict * stb_sdict_new(void); |
|
STB_EXTERN stb_sdict * stb_sdict_copy(stb_sdict*); |
|
STB_EXTERN void stb_sdict_delete(stb_sdict *); |
|
STB_EXTERN void * stb_sdict_change(stb_sdict *, char *str, void *p); |
|
STB_EXTERN int stb_sdict_count(stb_sdict *d); |
|
|
|
STB_EXTERN int stb_sdict_internal_limit(stb_sdict *d); |
|
STB_EXTERN char * stb_sdict_internal_key(stb_sdict *d, int n); |
|
STB_EXTERN void * stb_sdict_internal_value(stb_sdict *d, int n); |
|
|
|
#define stb_sdict_for(d,i,q,z) \ |
|
for(i=0; i < stb_sdict_internal_limit(d) ? (q=stb_sdict_internal_key(d,i),z=stb_sdict_internal_value(d,i),1) : 0; ++i) \ |
|
if (q==NULL||q==(void *) 1);else // reversed makes macro friendly |
|
#endif // STB_INCLUDE_STB_LIB_H |
|
|
|
#ifdef STB_LIB_IMPLEMENTATION |
|
|
|
// if in same translation unit, for speed, don't call accessors |
|
#undef stb_sdict_for |
|
#define stb_sdict_for(d,i,q,z) \ |
|
for(i=0; i < (d)->limit ? (q=(d)->table[i].k,z=(d)->table[i].v,1) : 0; ++i) \ |
|
if (q==NULL||q==(void *) 1);else // reversed makes macro friendly |
|
|
|
//#define STB_DEL ((void *) 1) |
|
#define STB_SDEL ((char *) 1) |
|
|
|
stb_define_hash_base(STB_noprefix, stb_sdict, STB_nofields, stb_sdict_,stb_sdictinternal_, 0.85f, |
|
char *, NULL, STB_SDEL, strdup, free, |
|
STB_safecompare, !strcmp, STB_equal, return stb_hash(k);, |
|
void *, STB_nullvalue, NULL) |
|
|
|
int stb_sdict_count(stb_sdict *a) |
|
{ |
|
return a->count; |
|
} |
|
|
|
int stb_sdict_internal_limit(stb_sdict *a) |
|
{ |
|
return a->limit; |
|
} |
|
char* stb_sdict_internal_key(stb_sdict *a, int n) |
|
{ |
|
return a->table[n].k; |
|
} |
|
void* stb_sdict_internal_value(stb_sdict *a, int n) |
|
{ |
|
return a->table[n].v; |
|
} |
|
|
|
stb_sdict * stb_sdict_new(void) |
|
{ |
|
stb_sdict *d = stb_sdict_create(); |
|
if (d == NULL) return NULL; |
|
return d; |
|
} |
|
|
|
stb_sdict* stb_sdict_copy(stb_sdict *old) |
|
{ |
|
return stb_sdictinternal_copy(old); |
|
} |
|
|
|
void stb_sdict_delete(stb_sdict *d) |
|
{ |
|
stb_sdict_destroy(d); |
|
} |
|
|
|
void * stb_sdict_change(stb_sdict *d, char *str, void *p) |
|
{ |
|
void *q = stb_sdict_get(d, str); |
|
stb_sdict_set(d, str, p); |
|
return q; |
|
} |
|
#endif |
|
|
|
////////////////////////////////////////////////////////////////////////////// |
|
// |
|
// File Processing |
|
// |
|
|
|
#ifndef STB_INCLUDE_STB_LIB_H |
|
#ifdef _MSC_VER |
|
#define stb_rename(x,y) _wrename((const wchar_t *)stb__from_utf8(x), (const wchar_t *)stb__from_utf8_alt(y)) |
|
#define stb_mktemp _mktemp |
|
#else |
|
#define stb_mktemp mktemp |
|
#define stb_rename rename |
|
#endif |
|
|
|
#define stb_filec (char *) stb_file |
|
#define stb_fileu (unsigned char *) stb_file |
|
STB_EXTERN void * stb_file(char *filename, size_t *length); |
|
STB_EXTERN size_t stb_filelen(FILE *f); |
|
STB_EXTERN int stb_filewrite(char *filename, void *data, size_t length); |
|
STB_EXTERN int stb_filewritestr(char *filename, char *data); |
|
STB_EXTERN char ** stb_stringfile(char *filename, int *len); |
|
STB_EXTERN char * stb_fgets(char *buffer, int buflen, FILE *f); |
|
STB_EXTERN char * stb_fgets_malloc(FILE *f); |
|
STB_EXTERN int stb_fexists(char *filename); |
|
STB_EXTERN int stb_fcmp(char *s1, char *s2); |
|
STB_EXTERN int stb_feq(char *s1, char *s2); |
|
STB_EXTERN time_t stb_ftimestamp(char *filename); |
|
STB_EXTERN int stb_fullpath(char *abs, int abs_size, char *rel); |
|
|
|
STB_EXTERN int stb_copyfile(char *src, char *dest); |
|
STB_EXTERN int stb_fread(void *data, size_t len, size_t count, void *f); |
|
STB_EXTERN int stb_fwrite(void *data, size_t len, size_t count, void *f); |
|
#endif // STB_INCLUDE_STB_LIB_H |
|
|
|
#ifdef STB_LIB_IMPLEMENTATION |
|
#if defined(_MSC_VER) || defined(__MINGW32__) |
|
#define stb__stat _stat |
|
#else |
|
#define stb__stat stat |
|
#endif |
|
|
|
int stb_fexists(char *filename) |
|
{ |
|
struct stb__stat buf; |
|
return stb__windows( |
|
_wstat((const wchar_t *)stb__from_utf8(filename), &buf), |
|
stat(filename,&buf) |
|
) == 0; |
|
} |
|
|
|
time_t stb_ftimestamp(char *filename) |
|
{ |
|
struct stb__stat buf; |
|
if (stb__windows( |
|
_wstat((const wchar_t *)stb__from_utf8(filename), &buf), |
|
stat(filename,&buf) |
|
) == 0) |
|
{ |
|
return buf.st_mtime; |
|
} else { |
|
return 0; |
|
} |
|
} |
|
|
|
size_t stb_filelen(FILE *f) |
|
{ |
|
size_t len, pos; |
|
pos = ftell(f); |
|
fseek(f, 0, SEEK_END); |
|
len = ftell(f); |
|
fseek(f, pos, SEEK_SET); |
|
return len; |
|
} |
|
|
|
void *stb_file(char *filename, size_t *length) |
|
{ |
|
FILE *f = stb__fopen(filename, "rb"); |
|
char *buffer; |
|
size_t len, len2; |
|
if (!f) return NULL; |
|
len = stb_filelen(f); |
|
buffer = (char *) malloc(len+2); // nul + extra |
|
len2 = fread(buffer, 1, len, f); |
|
if (len2 == len) { |
|
if (length) *length = len; |
|
buffer[len] = 0; |
|
} else { |
|
free(buffer); |
|
buffer = NULL; |
|
} |
|
fclose(f); |
|
return buffer; |
|
} |
|
|
|
int stb_filewrite(char *filename, void *data, size_t length) |
|
{ |
|
FILE *f = stb__fopen(filename, "wb"); |
|
if (f) { |
|
unsigned char *data_ptr = (unsigned char *) data; |
|
size_t remaining = length; |
|
while (remaining > 0) { |
|
size_t len2 = remaining > 65536 ? 65536 : remaining; |
|
size_t len3 = fwrite(data_ptr, 1, len2, f); |
|
if (len2 != len3) { |
|
fprintf(stderr, "Failed while writing %s\n", filename); |
|
break; |
|
} |
|
remaining -= len2; |
|
data_ptr += len2; |
|
} |
|
fclose(f); |
|
} |
|
return f != NULL; |
|
} |
|
|
|
int stb_filewritestr(char *filename, char *data) |
|
{ |
|
return stb_filewrite(filename, data, strlen(data)); |
|
} |
|
|
|
char ** stb_stringfile(char *filename, int *plen) |
|
{ |
|
FILE *f = stb__fopen(filename, "rb"); |
|
char *buffer, **list=NULL, *s; |
|
size_t len, count, i; |
|
|
|
if (!f) return NULL; |
|
len = stb_filelen(f); |
|
buffer = (char *) malloc(len+1); |
|
len = fread(buffer, 1, len, f); |
|
buffer[len] = 0; |
|
fclose(f); |
|
|
|
// two passes through: first time count lines, second time set them |
|
for (i=0; i < 2; ++i) { |
|
s = buffer; |
|
if (i == 1) |
|
list[0] = s; |
|
count = 1; |
|
while (*s) { |
|
if (*s == '\n' || *s == '\r') { |
|
// detect if both cr & lf are together |
|
int crlf = (s[0] + s[1]) == ('\n' + '\r'); |
|
if (i == 1) *s = 0; |
|
if (crlf) ++s; |
|
if (s[1]) { // it's not over yet |
|
if (i == 1) list[count] = s+1; |
|
++count; |
|
} |
|
} |
|
++s; |
|
} |
|
if (i == 0) { |
|
list = (char **) malloc(sizeof(*list) * (count+1) + len+1); |
|
if (!list) return NULL; |
|
list[count] = 0; |
|
// recopy the file so there's just a single allocation to free |
|
memcpy(&list[count+1], buffer, len+1); |
|
free(buffer); |
|
buffer = (char *) &list[count+1]; |
|
if (plen) *plen = count; |
|
} |
|
} |
|
return list; |
|
} |
|
|
|
char * stb_fgets(char *buffer, int buflen, FILE *f) |
|
{ |
|
char *p; |
|
buffer[0] = 0; |
|
p = fgets(buffer, buflen, f); |
|
if (p) { |
|
int n = strlen(p)-1; |
|
if (n >= 0) |
|
if (p[n] == '\n') |
|
p[n] = 0; |
|
} |
|
return p; |
|
} |
|
|
|
char * stb_fgets_malloc(FILE *f) |
|
{ |
|
// avoid reallocing for small strings |
|
char quick_buffer[800]; |
|
quick_buffer[sizeof(quick_buffer)-2] = 0; |
|
if (!fgets(quick_buffer, sizeof(quick_buffer), f)) |
|
return NULL; |
|
|
|
if (quick_buffer[sizeof(quick_buffer)-2] == 0) { |
|
int n = strlen(quick_buffer); |
|
if (n > 0 && quick_buffer[n-1] == '\n') |
|
quick_buffer[n-1] = 0; |
|
return strdup(quick_buffer); |
|
} else { |
|
char *p; |
|
char *a = strdup(quick_buffer); |
|
int len = sizeof(quick_buffer)-1; |
|
|
|
while (!feof(f)) { |
|
if (a[len-1] == '\n') break; |
|
a = (char *) realloc(a, len*2); |
|
p = &a[len]; |
|
p[len-2] = 0; |
|
if (!fgets(p, len, f)) |
|
break; |
|
if (p[len-2] == 0) { |
|
len += strlen(p); |
|
break; |
|
} |
|
len = len + (len-1); |
|
} |
|
if (a[len-1] == '\n') |
|
a[len-1] = 0; |
|
return a; |
|
} |
|
} |
|
|
|
int stb_fullpath(char *abs, int abs_size, char *rel) |
|
{ |
|
#ifdef _MSC_VER |
|
return _fullpath(abs, rel, abs_size) != NULL; |
|
#else |
|
if (rel[0] == '/' || rel[0] == '~') { |
|
if ((int) strlen(rel) >= abs_size) |
|
return 0; |
|
strcpy(abs,rel); |
|
return 1; |
|
} else { |
|
int n; |
|
getcwd(abs, abs_size); |
|
n = strlen(abs); |
|
if (n+(int) strlen(rel)+2 <= abs_size) { |
|
abs[n] = '/'; |
|
strcpy(abs+n+1, rel); |
|
return 1; |
|
} else { |
|
return 0; |
|
} |
|
} |
|
#endif |
|
} |
|
|
|
static int stb_fcmp_core(FILE *f, FILE *g) |
|
{ |
|
char buf1[1024],buf2[1024]; |
|
int n1,n2, res=0; |
|
|
|
while (1) { |
|
n1 = fread(buf1, 1, sizeof(buf1), f); |
|
n2 = fread(buf2, 1, sizeof(buf2), g); |
|
res = memcmp(buf1,buf2,n1 < n2 ? n1 : n2); |
|
if (res) |
|
break; |
|
if (n1 != n2) { |
|
res = n1 < n2 ? -1 : 1; |
|
break; |
|
} |
|
if (n1 == 0) |
|
break; |
|
} |
|
|
|
fclose(f); |
|
fclose(g); |
|
return res; |
|
} |
|
|
|
int stb_fcmp(char *s1, char *s2) |
|
{ |
|
FILE *f = stb__fopen(s1, "rb"); |
|
FILE *g = stb__fopen(s2, "rb"); |
|
|
|
if (f == NULL || g == NULL) { |
|
if (f) fclose(f); |
|
if (g) { |
|
fclose(g); |
|
return 1; |
|
} |
|
return f != NULL; |
|
} |
|
|
|
return stb_fcmp_core(f,g); |
|
} |
|
|
|
int stb_feq(char *s1, char *s2) |
|
{ |
|
FILE *f = stb__fopen(s1, "rb"); |
|
FILE *g = stb__fopen(s2, "rb"); |
|
|
|
if (f == NULL || g == NULL) { |
|
if (f) fclose(f); |
|
if (g) fclose(g); |
|
return f == g; |
|
} |
|
|
|
// feq is faster because it shortcuts if they're different length |
|
if (stb_filelen(f) != stb_filelen(g)) { |
|
fclose(f); |
|
fclose(g); |
|
return 0; |
|
} |
|
|
|
return !stb_fcmp_core(f,g); |
|
} |
|
|
|
int stb_copyfile(char *src, char *dest) |
|
{ |
|
char raw_buffer[1024]; |
|
char *buffer; |
|
int buf_size = 65536; |
|
|
|
FILE *f, *g; |
|
|
|
// if file already exists at destination, do nothing |
|
if (stb_feq(src, dest)) return 1; |
|
|
|
// open file |
|
f = stb__fopen(src, "rb"); |
|
if (f == NULL) return 0; |
|
|
|
// open file for writing |
|
g = stb__fopen(dest, "wb"); |
|
if (g == NULL) { |
|
fclose(f); |
|
return 0; |
|
} |
|
|
|
buffer = (char *) malloc(buf_size); |
|
if (buffer == NULL) { |
|
buffer = raw_buffer; |
|
buf_size = sizeof(raw_buffer); |
|
} |
|
|
|
while (!feof(f)) { |
|
int n = fread(buffer, 1, buf_size, f); |
|
if (n != 0) |
|
fwrite(buffer, 1, n, g); |
|
} |
|
|
|
fclose(f); |
|
if (buffer != raw_buffer) |
|
free(buffer); |
|
|
|
fclose(g); |
|
return 1; |
|
} |
|
|
|
#define stb_fgetc(f) ((unsigned char) fgetc(f)) |
|
|
|
#if 0 |
|
// strip the trailing '/' or '\\' from a directory so we can refer to it |
|
// as a file for _stat() |
|
char *stb_strip_final_slash(char *t) |
|
{ |
|
if (t[0]) { |
|
char *z = t + strlen(t) - 1; |
|
// *z is the last character |
|
if (*z == '\\' || *z == '/') |
|
if (z != t+2 || t[1] != ':') // but don't strip it if it's e.g. "c:/" |
|
*z = 0; |
|
if (*z == '\\') |
|
*z = '/'; // canonicalize to make sure it matches db |
|
} |
|
return t; |
|
} |
|
|
|
char *stb_strip_final_slash_regardless(char *t) |
|
{ |
|
if (t[0]) { |
|
char *z = t + strlen(t) - 1; |
|
// *z is the last character |
|
if (*z == '\\' || *z == '/') |
|
*z = 0; |
|
if (*z == '\\') |
|
*z = '/'; // canonicalize to make sure it matches db |
|
} |
|
return t; |
|
} |
|
#endif |
|
#endif |
|
|
|
////////////////////////////////////////////////////////////////////////////// |
|
// |
|
// Portable directory reading |
|
// |
|
|
|
#ifndef STB_INCLUDE_STB_LIB_H |
|
STB_EXTERN char **stb_readdir_files (char *dir); |
|
STB_EXTERN char **stb_readdir_files_mask(char *dir, char *wild); |
|
STB_EXTERN char **stb_readdir_subdirs(char *dir); |
|
STB_EXTERN char **stb_readdir_subdirs_mask(char *dir, char *wild); |
|
STB_EXTERN void stb_readdir_free (char **files); |
|
STB_EXTERN char **stb_readdir_recursive(char *dir, char *filespec); |
|
STB_EXTERN void stb_delete_directory_recursive(char *dir); |
|
|
|
// forward declare for implementation |
|
STB_EXTERN int stb_wildmatchi(char *expr, char *candidate); |
|
#endif // STB_INCLUDE_STB_LIB_H |
|
|
|
|
|
#ifdef STB_LIB_IMPLEMENTATION |
|
|
|
#ifdef _MSC_VER |
|
#include <io.h> |
|
#else |
|
#include <unistd.h> |
|
#include <dirent.h> |
|
#endif |
|
|
|
void stb_readdir_free(char **files) |
|
{ |
|
char **f2 = files; |
|
int i; |
|
for (i=0; i < stb_arr_len(f2); ++i) |
|
free(f2[i]); |
|
stb_arr_free(f2); |
|
} |
|
|
|
static int isdotdirname(char *name) |
|
{ |
|
if (name[0] == '.') |
|
return (name[1] == '.') ? !name[2] : !name[1]; |
|
return 0; |
|
} |
|
|
|
static char **readdir_raw(char *dir, int return_subdirs, char *mask) |
|
{ |
|
char **results = NULL; |
|
char buffer[4096], with_slash[4096]; |
|
size_t n; |
|
|
|
#ifdef _MSC_VER |
|
stb__wchar *ws; |
|
struct _wfinddata_t data; |
|
#ifdef _WIN64 |
|
const intptr_t none = -1; |
|
intptr_t z; |
|
#else |
|
const long none = -1; |
|
long z; |
|
#endif |
|
#else // !_MSC_VER |
|
const DIR *none = NULL; |
|
DIR *z; |
|
#endif |
|
|
|
n = stb_strscpy(buffer,dir,sizeof(buffer)); |
|
if (!n || n >= sizeof(buffer)) |
|
return NULL; |
|
stb_fixpath(buffer); |
|
n--; |
|
|
|
if (n > 0 && (buffer[n-1] != '/')) { |
|
buffer[n++] = '/'; |
|
} |
|
buffer[n] = 0; |
|
if (!stb_strscpy(with_slash,buffer,sizeof(with_slash))) |
|
return NULL; |
|
|
|
#ifdef _MSC_VER |
|
if (!stb_strscpy(buffer+n,"*.*",sizeof(buffer)-n)) |
|
return NULL; |
|
ws = stb__from_utf8(buffer); |
|
z = _wfindfirst((const wchar_t *)ws, &data); |
|
#else |
|
z = opendir(dir); |
|
#endif |
|
|
|
if (z != none) { |
|
int nonempty = 1; |
|
#ifndef _MSC_VER |
|
struct dirent *data = readdir(z); |
|
nonempty = (data != NULL); |
|
#endif |
|
|
|
if (nonempty) { |
|
|
|
do { |
|
int is_subdir; |
|
#ifdef _MSC_VER |
|
char *name = stb__to_utf8((stb__wchar *)data.name); |
|
if (name == NULL) { |
|
fprintf(stderr, "%s to convert '%S' to %s!\n", "Unable", data.name, "utf8"); |
|
continue; |
|
} |
|
is_subdir = !!(data.attrib & _A_SUBDIR); |
|
#else |
|
char *name = data->d_name; |
|
if (!stb_strscpy(buffer+n,name,sizeof(buffer)-n)) |
|
break; |
|
// Could follow DT_LNK, but would need to check for recursive links. |
|
is_subdir = !!(data->d_type & DT_DIR); |
|
#endif |
|
|
|
if (is_subdir == return_subdirs) { |
|
if (!is_subdir || !isdotdirname(name)) { |
|
if (!mask || stb_wildmatchi(mask, name)) { |
|
char buffer[4096],*p=buffer; |
|
if ( stb_snprintf(buffer, sizeof(buffer), "%s%s", with_slash, name) < 0 ) |
|
break; |
|
if (buffer[0] == '.' && buffer[1] == '/') |
|
p = buffer+2; |
|
stb_arr_push(results, strdup(p)); |
|
} |
|
} |
|
} |
|
} |
|
#ifdef _MSC_VER |
|
while (0 == _wfindnext(z, &data)); |
|
#else |
|
while ((data = readdir(z)) != NULL); |
|
#endif |
|
} |
|
#ifdef _MSC_VER |
|
_findclose(z); |
|
#else |
|
closedir(z); |
|
#endif |
|
} |
|
return results; |
|
} |
|
|
|
char **stb_readdir_files (char *dir) { return readdir_raw(dir, 0, NULL); } |
|
char **stb_readdir_subdirs(char *dir) { return readdir_raw(dir, 1, NULL); } |
|
char **stb_readdir_files_mask(char *dir, char *wild) { return readdir_raw(dir, 0, wild); } |
|
char **stb_readdir_subdirs_mask(char *dir, char *wild) { return readdir_raw(dir, 1, wild); } |
|
|
|
int stb__rec_max=0x7fffffff; |
|
static char **stb_readdir_rec(char **sofar, char *dir, char *filespec) |
|
{ |
|
char **files; |
|
char ** dirs; |
|
char **p; |
|
|
|
if (stb_arr_len(sofar) >= stb__rec_max) return sofar; |
|
|
|
files = stb_readdir_files_mask(dir, filespec); |
|
stb_arr_for(p, files) { |
|
stb_arr_push(sofar, strdup(*p)); |
|
if (stb_arr_len(sofar) >= stb__rec_max) break; |
|
} |
|
stb_readdir_free(files); |
|
if (stb_arr_len(sofar) >= stb__rec_max) return sofar; |
|
|
|
dirs = stb_readdir_subdirs(dir); |
|
stb_arr_for(p, dirs) |
|
sofar = stb_readdir_rec(sofar, *p, filespec); |
|
stb_readdir_free(dirs); |
|
return sofar; |
|
} |
|
|
|
char **stb_readdir_recursive(char *dir, char *filespec) |
|
{ |
|
return stb_readdir_rec(NULL, dir, filespec); |
|
} |
|
|
|
void stb_delete_directory_recursive(char *dir) |
|
{ |
|
char **list = stb_readdir_subdirs(dir); |
|
int i; |
|
for (i=0; i < stb_arr_len(list); ++i) |
|
stb_delete_directory_recursive(list[i]); |
|
stb_arr_free(list); |
|
list = stb_readdir_files(dir); |
|
for (i=0; i < stb_arr_len(list); ++i) |
|
if (!remove(list[i])) { |
|
// on windows, try again after making it writeable; don't ALWAYS |
|
// do this first since that would be slow in the normal case |
|
#ifdef _MSC_VER |
|
_chmod(list[i], _S_IWRITE); |
|
remove(list[i]); |
|
#endif |
|
} |
|
stb_arr_free(list); |
|
stb__windows(_rmdir,rmdir)(dir); |
|
} |
|
#endif |
|
|
|
////////////////////////////////////////////////////////////////////////////// |
|
// |
|
// Checksums: CRC-32, ADLER32, SHA-1 |
|
// |
|
// CRC-32 and ADLER32 allow streaming blocks |
|
// SHA-1 requires either a complete buffer, max size 2^32 - 73 |
|
// or it can checksum directly from a file, max 2^61 |
|
|
|
#ifndef STB_INCLUDE_STB_LIB_H |
|
#define STB_ADLER32_SEED 1 |
|
#define STB_CRC32_SEED 0 // note that we logical NOT this in the code |
|
|
|
STB_EXTERN stb_uint stb_adler32 (stb_uint adler32, stb_uchar *buffer, stb_uint buflen); |
|
STB_EXTERN stb_uint stb_crc32_block(stb_uint crc32 , stb_uchar *buffer, stb_uint buflen); |
|
STB_EXTERN stb_uint stb_crc32 ( stb_uchar *buffer, stb_uint buflen); |
|
|
|
STB_EXTERN void stb_sha1( unsigned char output[20], stb_uchar *buffer, unsigned int len); |
|
STB_EXTERN int stb_sha1_file(unsigned char output[20], char *file); |
|
#endif // STB_INCLUDE_STB_LIB_H |
|
|
|
#ifdef STB_LIB_IMPLEMENTATION |
|
stb_uint stb_crc32_block(stb_uint crc, unsigned char *buffer, stb_uint len) |
|
{ |
|
static stb_uint crc_table[256]; |
|
stb_uint i,j,s; |
|
crc = ~crc; |
|
|
|
if (crc_table[1] == 0) |
|
for(i=0; i < 256; i++) { |
|
for (s=i, j=0; j < 8; ++j) |
|
s = (s >> 1) ^ (s & 1 ? 0xedb88320 : 0); |
|
crc_table[i] = s; |
|
} |
|
for (i=0; i < len; ++i) |
|
crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; |
|
return ~crc; |
|
} |
|
|
|
stb_uint stb_crc32(unsigned char *buffer, stb_uint len) |
|
{ |
|
return stb_crc32_block(0, buffer, len); |
|
} |
|
|
|
stb_uint stb_adler32(stb_uint adler32, stb_uchar *buffer, stb_uint buflen) |
|
{ |
|
const unsigned long ADLER_MOD = 65521; |
|
unsigned long s1 = adler32 & 0xffff, s2 = adler32 >> 16; |
|
unsigned long blocklen, i; |
|
|
|
blocklen = buflen % 5552; |
|
while (buflen) { |
|
for (i=0; i + 7 < blocklen; i += 8) { |
|
s1 += buffer[0], s2 += s1; |
|
s1 += buffer[1], s2 += s1; |
|
s1 += buffer[2], s2 += s1; |
|
s1 += buffer[3], s2 += s1; |
|
s1 += buffer[4], s2 += s1; |
|
s1 += buffer[5], s2 += s1; |
|
s1 += buffer[6], s2 += s1; |
|
s1 += buffer[7], s2 += s1; |
|
|
|
buffer += 8; |
|
} |
|
|
|
for (; i < blocklen; ++i) |
|
s1 += *buffer++, s2 += s1; |
|
|
|
s1 %= ADLER_MOD, s2 %= ADLER_MOD; |
|
buflen -= blocklen; |
|
blocklen = 5552; |
|
} |
|
return (s2 << 16) + s1; |
|
} |
|
|
|
#define stb__big32(c) (((c)[0]<<24) + (c)[1]*65536 + (c)[2]*256 + (c)[3]) |
|
static void stb__sha1(stb_uchar *chunk, stb_uint h[5]) |
|
{ |
|
int i; |
|
stb_uint a,b,c,d,e; |
|
stb_uint w[80]; |
|
|
|
for (i=0; i < 16; ++i) |
|
w[i] = stb__big32(&chunk[i*4]); |
|
for (i=16; i < 80; ++i) { |
|
stb_uint t; |
|
t = w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16]; |
|
w[i] = (t + t) | (t >> 31); |
|
} |
|
|
|
a = h[0]; |
|
b = h[1]; |
|
c = h[2]; |
|
d = h[3]; |
|
e = h[4]; |
|
|
|
#define STB__SHA1(k,f) \ |
|
{ \ |
|
stb_uint temp = (a << 5) + (a >> 27) + (f) + e + (k) + w[i]; \ |
|
e = d; \ |
|
d = c; \ |
|
c = (b << 30) + (b >> 2); \ |
|
b = a; \ |
|
a = temp; \ |
|
} |
|
|
|
i=0; |
|
for (; i < 20; ++i) STB__SHA1(0x5a827999, d ^ (b & (c ^ d)) ); |
|
for (; i < 40; ++i) STB__SHA1(0x6ed9eba1, b ^ c ^ d ); |
|
for (; i < 60; ++i) STB__SHA1(0x8f1bbcdc, (b & c) + (d & (b ^ c)) ); |
|
for (; i < 80; ++i) STB__SHA1(0xca62c1d6, b ^ c ^ d ); |
|
|
|
#undef STB__SHA1 |
|
|
|
h[0] += a; |
|
h[1] += b; |
|
h[2] += c; |
|
h[3] += d; |
|
h[4] += e; |
|
} |
|
|
|
void stb_sha1(stb_uchar output[20], stb_uchar *buffer, stb_uint len) |
|
{ |
|
unsigned char final_block[128]; |
|
stb_uint end_start, final_len, j; |
|
int i; |
|
|
|
stb_uint h[5]; |
|
|
|
h[0] = 0x67452301; |
|
h[1] = 0xefcdab89; |
|
h[2] = 0x98badcfe; |
|
h[3] = 0x10325476; |
|
h[4] = 0xc3d2e1f0; |
|
|
|
// we need to write padding to the last one or two |
|
// blocks, so build those first into 'final_block' |
|
|
|
// we have to write one special byte, plus the 8-byte length |
|
|
|
// compute the block where the data runs out |
|
end_start = len & ~63; |
|
|
|
// compute the earliest we can encode the length |
|
if (((len+9) & ~63) == end_start) { |
|
// it all fits in one block, so fill a second-to-last block |
|
end_start -= 64; |
|
} |
|
|
|
final_len = end_start + 128; |
|
|
|
// now we need to copy the data in |
|
assert(end_start + 128 >= len+9); |
|
assert(end_start < len || len < 64-9); |
|
|
|
j = 0; |
|
if (end_start > len) |
|
j = (stb_uint) - (int) end_start; |
|
|
|
for (; end_start + j < len; ++j) |
|
final_block[j] = buffer[end_start + j]; |
|
final_block[j++] = 0x80; |
|
while (j < 128-5) // 5 byte length, so write 4 extra padding bytes |
|
final_block[j++] = 0; |
|
// big-endian size |
|
final_block[j++] = len >> 29; |
|
final_block[j++] = len >> 21; |
|
final_block[j++] = len >> 13; |
|
final_block[j++] = len >> 5; |
|
final_block[j++] = len << 3; |
|
assert(j == 128 && end_start + j == final_len); |
|
|
|
for (j=0; j < final_len; j += 64) { // 512-bit chunks |
|
if (j+64 >= end_start+64) |
|
stb__sha1(&final_block[j - end_start], h); |
|
else |
|
stb__sha1(&buffer[j], h); |
|
} |
|
|
|
for (i=0; i < 5; ++i) { |
|
output[i*4 + 0] = h[i] >> 24; |
|
output[i*4 + 1] = h[i] >> 16; |
|
output[i*4 + 2] = h[i] >> 8; |
|
output[i*4 + 3] = h[i] >> 0; |
|
} |
|
} |
|
|
|
int stb_sha1_file(stb_uchar output[20], char *file) |
|
{ |
|
int i; |
|
stb_uint64 length=0; |
|
unsigned char buffer[128]; |
|
|
|
FILE *f = stb__fopen(file, "rb"); |
|
stb_uint h[5]; |
|
|
|
if (f == NULL) return 0; // file not found |
|
|
|
h[0] = 0x67452301; |
|
h[1] = 0xefcdab89; |
|
h[2] = 0x98badcfe; |
|
h[3] = 0x10325476; |
|
h[4] = 0xc3d2e1f0; |
|
|
|
for(;;) { |
|
int n = fread(buffer, 1, 64, f); |
|
if (n == 64) { |
|
stb__sha1(buffer, h); |
|
length += n; |
|
} else { |
|
int block = 64; |
|
|
|
length += n; |
|
|
|
buffer[n++] = 0x80; |
|
|
|
// if there isn't enough room for the length, double the block |
|
if (n + 8 > 64) |
|
block = 128; |
|
|
|
// pad to end |
|
memset(buffer+n, 0, block-8-n); |
|
|
|
i = block - 8; |
|
buffer[i++] = (stb_uchar) (length >> 53); |
|
buffer[i++] = (stb_uchar) (length >> 45); |
|
buffer[i++] = (stb_uchar) (length >> 37); |
|
buffer[i++] = (stb_uchar) (length >> 29); |
|
buffer[i++] = (stb_uchar) (length >> 21); |
|
buffer[i++] = (stb_uchar) (length >> 13); |
|
buffer[i++] = (stb_uchar) (length >> 5); |
|
buffer[i++] = (stb_uchar) (length << 3); |
|
assert(i == block); |
|
stb__sha1(buffer, h); |
|
if (block == 128) |
|
stb__sha1(buffer+64, h); |
|
else |
|
assert(block == 64); |
|
break; |
|
} |
|
} |
|
fclose(f); |
|
|
|
for (i=0; i < 5; ++i) { |
|
output[i*4 + 0] = h[i] >> 24; |
|
output[i*4 + 1] = h[i] >> 16; |
|
output[i*4 + 2] = h[i] >> 8; |
|
output[i*4 + 3] = h[i] >> 0; |
|
} |
|
|
|
return 1; |
|
} |
|
#endif // STB_LIB_IMPLEMENTATION |
|
|
|
////////////////////////////////////////////////////////////////////////////// |
|
// |
|
// Random Numbers via Meresenne Twister or LCG |
|
// |
|
|
|
#ifndef STB_INCLUDE_STB_LIB_H |
|
STB_EXTERN unsigned long stb_srandLCG(unsigned long seed); |
|
STB_EXTERN unsigned long stb_randLCG(void); |
|
STB_EXTERN double stb_frandLCG(void); |
|
|
|
STB_EXTERN void stb_srand(unsigned long seed); |
|
STB_EXTERN unsigned long stb_rand(void); |
|
STB_EXTERN double stb_frand(void); |
|
STB_EXTERN void stb_shuffle(void *p, size_t n, size_t sz, |
|
unsigned long seed); |
|
STB_EXTERN void stb_reverse(void *p, size_t n, size_t sz); |
|
|
|
STB_EXTERN unsigned long stb_randLCG_explicit(unsigned long seed); |
|
#endif // STB_INCLUDE_STB_LIB_H |
|
|
|
#ifdef STB_LIB_IMPLEMENTATION |
|
unsigned long stb_randLCG_explicit(unsigned long seed) |
|
{ |
|
return seed * 2147001325 + 715136305; |
|
} |
|
|
|
static unsigned long stb__rand_seed=0; |
|
|
|
unsigned long stb_srandLCG(unsigned long seed) |
|
{ |
|
unsigned long previous = stb__rand_seed; |
|
stb__rand_seed = seed; |
|
return previous; |
|
} |
|
|
|
unsigned long stb_randLCG(void) |
|
{ |
|
stb__rand_seed = stb__rand_seed * 2147001325 + 715136305; // BCPL generator |
|
// shuffle non-random bits to the middle, and xor to decorrelate with seed |
|
return 0x31415926 ^ ((stb__rand_seed >> 16) + (stb__rand_seed << 16)); |
|
} |
|
|
|
double stb_frandLCG(void) |
|
{ |
|
return stb_randLCG() / ((double) (1 << 16) * (1 << 16)); |
|
} |
|
|
|
void stb_shuffle(void *p, size_t n, size_t sz, unsigned long seed) |
|
{ |
|
char *a; |
|
unsigned long old_seed; |
|
int i; |
|
if (seed) |
|
old_seed = stb_srandLCG(seed); |
|
a = (char *) p + (n-1) * sz; |
|
|
|
for (i=n; i > 1; --i) { |
|
int j = stb_randLCG() % i; |
|
stb_swap(a, (char *) p + j * sz, sz); |
|
a -= sz; |
|
} |
|
if (seed) |
|
stb_srandLCG(old_seed); |
|
} |
|
|
|
void stb_reverse(void *p, size_t n, size_t sz) |
|
{ |
|
int i,j = n-1; |
|
for (i=0; i < j; ++i,--j) { |
|
stb_swap((char *) p + i * sz, (char *) p + j * sz, sz); |
|
} |
|
} |
|
|
|
// public domain Mersenne Twister by Michael Brundage |
|
#define STB__MT_LEN 624 |
|
|
|
int stb__mt_index = STB__MT_LEN*sizeof(unsigned long)+1; |
|
unsigned long stb__mt_buffer[STB__MT_LEN]; |
|
|
|
void stb_srand(unsigned long seed) |
|
{ |
|
int i; |
|
unsigned long old = stb_srandLCG(seed); |
|
for (i = 0; i < STB__MT_LEN; i++) |
|
stb__mt_buffer[i] = stb_randLCG(); |
|
stb_srandLCG(old); |
|
stb__mt_index = STB__MT_LEN*sizeof(unsigned long); |
|
} |
|
|
|
#define STB__MT_IA 397 |
|
#define STB__MT_IB (STB__MT_LEN - STB__MT_IA) |
|
#define STB__UPPER_MASK 0x80000000 |
|
#define STB__LOWER_MASK 0x7FFFFFFF |
|
#define STB__MATRIX_A 0x9908B0DF |
|
#define STB__TWIST(b,i,j) ((b)[i] & STB__UPPER_MASK) | ((b)[j] & STB__LOWER_MASK) |
|
#define STB__MAGIC(s) (((s)&1)*STB__MATRIX_A) |
|
|
|
unsigned long stb_rand() |
|
{ |
|
unsigned long * b = stb__mt_buffer; |
|
int idx = stb__mt_index; |
|
unsigned long s,r; |
|
int i; |
|
|
|
if (idx >= STB__MT_LEN*sizeof(unsigned long)) { |
|
if (idx > STB__MT_LEN*sizeof(unsigned long)) |
|
stb_srand(0); |
|
idx = 0; |
|
i = 0; |
|
for (; i < STB__MT_IB; i++) { |
|
s = STB__TWIST(b, i, i+1); |
|
b[i] = b[i + STB__MT_IA] ^ (s >> 1) ^ STB__MAGIC(s); |
|
} |
|
for (; i < STB__MT_LEN-1; i++) { |
|
s = STB__TWIST(b, i, i+1); |
|
b[i] = b[i - STB__MT_IB] ^ (s >> 1) ^ STB__MAGIC(s); |
|
} |
|
|
|
s = STB__TWIST(b, STB__MT_LEN-1, 0); |
|
b[STB__MT_LEN-1] = b[STB__MT_IA-1] ^ (s >> 1) ^ STB__MAGIC(s); |
|
} |
|
stb__mt_index = idx + sizeof(unsigned long); |
|
|
|
r = *(unsigned long *)((unsigned char *)b + idx); |
|
|
|
r ^= (r >> 11); |
|
r ^= (r << 7) & 0x9D2C5680; |
|
r ^= (r << 15) & 0xEFC60000; |
|
r ^= (r >> 18); |
|
|
|
return r; |
|
} |
|
|
|
double stb_frand(void) |
|
{ |
|
return stb_rand() / ((double) (1 << 16) * (1 << 16)); |
|
} |
|
#endif |
|
|
|
////////////////////////////////////////////////////////////////////////////// |
|
// |
|
// wildcards and regexping |
|
// |
|
|
|
#ifndef STB_INCLUDE_STB_LIB_H |
|
STB_EXTERN int stb_wildmatch (char *expr, char *candidate); |
|
STB_EXTERN int stb_wildmatchi(char *expr, char *candidate); |
|
STB_EXTERN int stb_wildfind (char *expr, char *candidate); |
|
STB_EXTERN int stb_wildfindi (char *expr, char *candidate); |
|
#endif // STB_INCLUDE_STB_LIB_H |
|
|
|
#ifdef STB_LIB_IMPLEMENTATION |
|
static int stb__match_qstring(char *candidate, char *qstring, int qlen, int insensitive) |
|
{ |
|
int i; |
|
if (insensitive) { |
|
for (i=0; i < qlen; ++i) |
|
if (qstring[i] == '?') { |
|
if (!candidate[i]) return 0; |
|
} else |
|
if (tolower(qstring[i]) != tolower(candidate[i])) |
|
return 0; |
|
} else { |
|
for (i=0; i < qlen; ++i) |
|
if (qstring[i] == '?') { |
|
if (!candidate[i]) return 0; |
|
} else |
|
if (qstring[i] != candidate[i]) |
|
return 0; |
|
} |
|
return 1; |
|
} |
|
|
|
static int stb__find_qstring(char *candidate, char *qstring, int qlen, int insensitive) |
|
{ |
|
char c; |
|
|
|
int offset=0; |
|
while (*qstring == '?') { |
|
++qstring; |
|
--qlen; |
|
++candidate; |
|
if (qlen == 0) return 0; |
|
if (*candidate == 0) return -1; |
|
} |
|
|
|
c = *qstring++; |
|
--qlen; |
|
if (insensitive) c = tolower(c); |
|
|
|
while (candidate[offset]) { |
|
if (c == (insensitive ? tolower(candidate[offset]) : candidate[offset])) |
|
if (stb__match_qstring(candidate+offset+1, qstring, qlen, insensitive)) |
|
return offset; |
|
++offset; |
|
} |
|
|
|
return -1; |
|
} |
|
|
|
int stb__wildmatch_raw2(char *expr, char *candidate, int search, int insensitive) |
|
{ |
|
int where=0; |
|
int start = -1; |
|
|
|
if (!search) { |
|
// parse to first '*' |
|
if (*expr != '*') |
|
start = 0; |
|
while (*expr != '*') { |
|
if (!*expr) |
|
return *candidate == 0 ? 0 : -1; |
|
if (*expr == '?') { |
|
if (!*candidate) return -1; |
|
} else { |
|
if (insensitive) { |
|
if (tolower(*candidate) != tolower(*expr)) |
|
return -1; |
|
} else |
|
if (*candidate != *expr) |
|
return -1; |
|
} |
|
++candidate, ++expr, ++where; |
|
} |
|
} else { |
|
// 0-length search string |
|
if (!*expr) |
|
return 0; |
|
} |
|
|
|
assert(search || *expr == '*'); |
|
if (!search) |
|
++expr; |
|
|
|
// implicit '*' at this point |
|
|
|
while (*expr) { |
|
int o=0; |
|
// combine redundant * characters |
|
while (expr[0] == '*') ++expr; |
|
|
|
// ok, at this point, expr[-1] == '*', |
|
// and expr[0] != '*' |
|
|
|
if (!expr[0]) return start >= 0 ? start : 0; |
|
|
|
// now find next '*' |
|
o = 0; |
|
while (expr[o] != '*') { |
|
if (expr[o] == 0) |
|
break; |
|
++o; |
|
} |
|
// if no '*', scan to end, then match at end |
|
if (expr[o] == 0 && !search) { |
|
int z; |
|
for (z=0; z < o; ++z) |
|
if (candidate[z] == 0) |
|
return -1; |
|
while (candidate[z]) |
|
++z; |
|
// ok, now check if they match |
|
if (stb__match_qstring(candidate+z-o, expr, o, insensitive)) |
|
return start >= 0 ? start : 0; |
|
return -1; |
|
} else { |
|
// if yes '*', then do stb__find_qmatch on the intervening chars |
|
int n = stb__find_qstring(candidate, expr, o, insensitive); |
|
if (n < 0) |
|
return -1; |
|
if (start < 0) |
|
start = where + n; |
|
expr += o; |
|
candidate += n+o; |
|
} |
|
|
|
if (*expr == 0) { |
|
assert(search); |
|
return start; |
|
} |
|
|
|
assert(*expr == '*'); |
|
++expr; |
|
} |
|
|
|
return start >= 0 ? start : 0; |
|
} |
|
|
|
int stb__wildmatch_raw(char *expr, char *candidate, int search, int insensitive) |
|
{ |
|
char buffer[256]; |
|
// handle multiple search strings |
|
char *s = strchr(expr, ';'); |
|
char *last = expr; |
|
while (s) { |
|
int z; |
|
// need to allow for non-writeable strings... assume they're small |
|
if (s - last < 256) { |
|
stb_strncpy(buffer, last, s-last+1); |
|
z = stb__wildmatch_raw2(buffer, candidate, search, insensitive); |
|
} else { |
|
*s = 0; |
|
z = stb__wildmatch_raw2(last, candidate, search, insensitive); |
|
*s = ';'; |
|
} |
|
if (z >= 0) return z; |
|
last = s+1; |
|
s = strchr(last, ';'); |
|
} |
|
return stb__wildmatch_raw2(last, candidate, search, insensitive); |
|
} |
|
|
|
int stb_wildmatch(char *expr, char *candidate) |
|
{ |
|
return stb__wildmatch_raw(expr, candidate, 0,0) >= 0; |
|
} |
|
|
|
int stb_wildmatchi(char *expr, char *candidate) |
|
{ |
|
return stb__wildmatch_raw(expr, candidate, 0,1) >= 0; |
|
} |
|
|
|
int stb_wildfind(char *expr, char *candidate) |
|
{ |
|
return stb__wildmatch_raw(expr, candidate, 1,0); |
|
} |
|
|
|
int stb_wildfindi(char *expr, char *candidate) |
|
{ |
|
return stb__wildmatch_raw(expr, candidate, 1,1); |
|
} |
|
|
|
#undef STB_LIB_IMPLEMENTATION |
|
#endif // STB_LIB_IMPLEMENTATION |
|
|
|
#ifndef STB_INCLUDE_STB_LIB_H |
|
#define STB_INCLUDE_STB_LIB_H |
|
#undef STB_EXTERN |
|
#endif |
|
|
|
/* |
|
------------------------------------------------------------------------------ |
|
This software is available under 2 licenses -- choose whichever you prefer. |
|
------------------------------------------------------------------------------ |
|
ALTERNATIVE A - MIT License |
|
Copyright (c) 2017 Sean Barrett |
|
Permission is hereby granted, free of charge, to any person obtaining a copy of |
|
this software and associated documentation files (the "Software"), to deal in |
|
the Software without restriction, including without limitation the rights to |
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies |
|
of the Software, and to permit persons to whom the Software is furnished to do |
|
so, subject to the following conditions: |
|
The above copyright notice and this permission notice shall be included in all |
|
copies or substantial portions of the Software. |
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|
SOFTWARE. |
|
------------------------------------------------------------------------------ |
|
ALTERNATIVE B - Public Domain (www.unlicense.org) |
|
This is free and unencumbered software released into the public domain. |
|
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this |
|
software, either in source code form or as a compiled binary, for any purpose, |
|
commercial or non-commercial, and by any means. |
|
In jurisdictions that recognize copyright laws, the author or authors of this |
|
software dedicate any and all copyright interest in the software to the public |
|
domain. We make this dedication for the benefit of the public at large and to |
|
the detriment of our heirs and successors. We intend this dedication to be an |
|
overt act of relinquishment in perpetuity of all present and future rights to |
|
this software under copyright law. |
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
|
------------------------------------------------------------------------------ |
|
*/
|
|
|