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.
3306 lines
104 KiB
3306 lines
104 KiB
5 years ago
|
/* 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.
|
||
|
------------------------------------------------------------------------------
|
||
|
*/
|