diff --git a/.github/workflows/ci-fuzz.yml b/.github/workflows/ci-fuzz.yml new file mode 100644 index 0000000..332fca9 --- /dev/null +++ b/.github/workflows/ci-fuzz.yml @@ -0,0 +1,23 @@ +name: CIFuzz +on: [pull_request] +jobs: + Fuzzing: + runs-on: ubuntu-latest + steps: + - name: Build Fuzzers + uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master + with: + oss-fuzz-project-name: 'stb' + dry-run: false + - name: Run Fuzzers + uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master + with: + oss-fuzz-project-name: 'stb' + fuzz-seconds: 900 + dry-run: false + - name: Upload Crash + uses: actions/upload-artifact@v1 + if: failure() + with: + name: artifacts + path: ./out/artifacts diff --git a/.travis.yml b/.travis.yml index 3b71802..c2ad947 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,7 @@ language: C +arch: + - AMD64 + - ppc64le install: true script: - cd tests diff --git a/README.md b/README.md index deace3c..731b632 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,8 @@ by Jorge L. "VinoBS" Rodriguez, and stb_sprintf by Jeff Roberts. library | lastest version | category | LoC | description --------------------- | ---- | -------- | --- | -------------------------------- -**[stb_vorbis.c](stb_vorbis.c)** | 1.20 | audio | 5563 | decode ogg vorbis files from file/memory to float/16-bit signed output +<<<<<<< HEAD +**[stb_vorbis.c](stb_vorbis.c)** | 1.21 | audio | 5569 | decode ogg vorbis files from file/memory to float/16-bit signed output **[stb_image.h](stb_image.h)** | 2.26 | graphics | 7762 | image loading/decoding from file/memory: JPG, PNG, TGA, BMP, PSD, GIF, HDR, PIC, PNM **[stb_truetype.h](stb_truetype.h)** | 1.24 | graphics | 5011 | parse, decode, and rasterize characters from truetype fonts **[stb_image_write.h](stb_image_write.h)** | 1.15 | graphics | 1690 | image writing to disk: PNG, TGA, BMP, JPG, HDR @@ -43,7 +44,7 @@ library | lastest version | category | LoC | description **[stb_include.h](stb_include.h)** | 0.02 | misc | 295 | implement recursive #include support, particularly for GLSL Total libraries: 21 -Total lines of C code: 56524 +Total lines of C code: 56530 FAQ diff --git a/deprecated/stb.h b/deprecated/stb.h index 06f3168..80fa202 100644 --- a/deprecated/stb.h +++ b/deprecated/stb.h @@ -6132,7 +6132,7 @@ static char **readdir_raw(char *dir, int return_subdirs, char *mask) char buffer[4096], with_slash[4096]; size_t n; - #ifdef _MSC_VER + #ifdef WIN32 stb__wchar *ws; struct _wfinddata_t data; #ifdef _WIN64 @@ -6142,7 +6142,7 @@ static char **readdir_raw(char *dir, int return_subdirs, char *mask) const long none = -1; long z; #endif - #else // !_MSC_VER + #else // !WIN32 const DIR *none = NULL; DIR *z; #endif @@ -6159,7 +6159,7 @@ static char **readdir_raw(char *dir, int return_subdirs, char *mask) if (!stb_strscpy(with_slash,buffer,sizeof(with_slash))) return NULL; - #ifdef _MSC_VER + #ifdef WIN32 if (!stb_strscpy(buffer+n,"*.*",sizeof(buffer)-n)) return NULL; ws = stb__from_utf8(buffer); @@ -6170,7 +6170,7 @@ static char **readdir_raw(char *dir, int return_subdirs, char *mask) if (z != none) { int nonempty = STB_TRUE; - #ifndef _MSC_VER + #ifndef WIN32 struct dirent *data = readdir(z); nonempty = (data != NULL); #endif @@ -6179,7 +6179,7 @@ static char **readdir_raw(char *dir, int return_subdirs, char *mask) do { int is_subdir; - #ifdef _MSC_VER + #ifdef WIN32 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"); @@ -6207,13 +6207,13 @@ static char **readdir_raw(char *dir, int return_subdirs, char *mask) } } } - #ifdef _MSC_VER + #ifdef WIN32 while (0 == _wfindnext(z, &data)); #else while ((data = readdir(z)) != NULL); #endif } - #ifdef _MSC_VER + #ifdef WIN32 _findclose(z); #else closedir(z); @@ -13059,1356 +13059,6 @@ char * stb__string_constant(char *file, int line, char *x) #endif // STB_DEFINE #endif // !STB_DEBUG && !STB_ALWAYS_H - -#ifdef STB_STUA -#error "STUA is no longer supported" -////////////////////////////////////////////////////////////////////////// -// -// stua: little scripting language -// -// define STB_STUA to compile it -// -// see http://nothings.org/stb/stb_stua.html for documentation -// -// basic parsing model: -// -// lexical analysis -// use stb_lex() to parse tokens; keywords get their own tokens -// -// parsing: -// recursive descent parser. too much of a hassle to make an unambiguous -// LR(1) grammar, and one-pass generation is clumsier (recursive descent -// makes it easier to e.g. compile nested functions). on the other hand, -// dictionary syntax required hackery to get extra lookahead. -// -// codegen: -// output into an evaluation tree, using array indices as 'pointers' -// -// run: -// traverse the tree; support for 'break/continue/return' is tricky -// -// garbage collection: -// stu__mark and sweep; explicit stack with non-stu__compile_global_scope roots - -typedef stb_int32 stua_obj; - -typedef stb_idict stua_dict; - -STB_EXTERN void stua_run_script(char *s); -STB_EXTERN void stua_uninit(void); - -extern stua_obj stua_globals; - -STB_EXTERN double stua_number(stua_obj z); - -STB_EXTERN stua_obj stua_getnil(void); -STB_EXTERN stua_obj stua_getfalse(void); -STB_EXTERN stua_obj stua_gettrue(void); -STB_EXTERN stua_obj stua_string(char *z); -STB_EXTERN stua_obj stua_make_number(double d); -STB_EXTERN stua_obj stua_box(int type, void *data, int size); - -enum -{ - STUA_op_negate=129, - STUA_op_shl, STUA_op_ge, - STUA_op_shr, STUA_op_le, - STUA_op_shru, - STUA_op_last -}; - -#define STUA_NO_VALUE 2 // equivalent to a tagged NULL -STB_EXTERN stua_obj (*stua_overload)(int op, stua_obj a, stua_obj b, stua_obj c); - -STB_EXTERN stua_obj stua_error(char *err, ...); - -STB_EXTERN stua_obj stua_pushroot(stua_obj o); -STB_EXTERN void stua_poproot ( void ); - - -#ifdef STB_DEFINE -// INTERPRETER - -// 31-bit floating point implementation -// force the (1 << 30) bit (2nd highest bit) to be zero by re-biasing the exponent; -// then shift and set the bottom bit - -static stua_obj stu__floatp(float *f) -{ - unsigned int n = *(unsigned int *) f; - unsigned int e = n & (0xff << 23); - - assert(sizeof(int) == 4 && sizeof(float) == 4); - - if (!e) // zero? - n = n; // no change - else if (e < (64 << 23)) // underflow of the packed encoding? - n = (n & 0x80000000); // signed 0 - else if (e > (190 << 23)) // overflow of the encoding? (or INF or NAN) - n = (n & 0x80000000) + (127 << 23); // new INF encoding - else - n -= 0x20000000; - - // now we need to shuffle the bits so that the spare bit is at the bottom - assert((n & 0x40000000) == 0); - return (n & 0x80000000) + (n << 1) + 1; -} - -static unsigned char stu__getfloat_addend[256]; -static float stu__getfloat(stua_obj v) -{ - unsigned int n; - unsigned int e = ((unsigned int) v) >> 24; - - n = (int) v >> 1; // preserve high bit - n += stu__getfloat_addend[e] << 24; - return *(float *) &n; -} - -stua_obj stua_float(float f) -{ - return stu__floatp(&f); -} - -static void stu__float_init(void) -{ - int i; - stu__getfloat_addend[0] = 0; // do nothing to biased exponent of 0 - for (i=1; i < 127; ++i) - stu__getfloat_addend[i] = 32; // undo the -0x20000000 - stu__getfloat_addend[127] = 64; // convert packed INF to INF (0x3f -> 0x7f) - - for (i=0; i < 128; ++i) // for signed floats, remove the bit we just shifted down - stu__getfloat_addend[128+i] = stu__getfloat_addend[i] - 64; -} - -// Tagged data type implementation - - // TAGS: -#define stu__int_tag 0 // of 2 bits // 00 int -#define stu__float_tag 1 // of 1 bit // 01 float -#define stu__ptr_tag 2 // of 2 bits // 10 boxed - // 11 float - -#define stu__tag(x) ((x) & 3) -#define stu__number(x) (stu__tag(x) != stu__ptr_tag) -#define stu__isint(x) (stu__tag(x) == stu__int_tag) - -#define stu__int(x) ((x) >> 2) -#define stu__float(x) (stu__getfloat(x)) - -#define stu__makeint(v) ((v)*4+stu__int_tag) - -// boxed data, and tag support for boxed data - -enum -{ - STU___float = 1, STU___int = 2, - STU___number = 3, STU___string = 4, - STU___function = 5, STU___dict = 6, - STU___boolean = 7, STU___error = 8, -}; - -// boxed data -#define STU__BOX short type, stua_gc -typedef struct stu__box { STU__BOX; } stu__box; - -stu__box stu__nil = { 0, 1 }; -stu__box stu__true = { STU___boolean, 1, }; -stu__box stu__false = { STU___boolean, 1, }; - -#define stu__makeptr(v) ((stua_obj) (v) + stu__ptr_tag) - -#define stua_nil stu__makeptr(&stu__nil) -#define stua_true stu__makeptr(&stu__true) -#define stua_false stu__makeptr(&stu__false) - -stua_obj stua_getnil(void) { return stua_nil; } -stua_obj stua_getfalse(void) { return stua_false; } -stua_obj stua_gettrue(void) { return stua_true; } - -#define stu__ptr(x) ((stu__box *) ((x) - stu__ptr_tag)) - -#define stu__checkt(t,x) ((t) == STU___float ? ((x) & 1) == stu__float_tag : \ - (t) == STU___int ? stu__isint(x) : \ - (t) == STU___number ? stu__number(x) : \ - stu__tag(x) == stu__ptr_tag && stu__ptr(x)->type == (t)) - -typedef struct -{ - STU__BOX; - void *ptr; -} stu__wrapper; - -// implementation of a 'function' or function + closure - -typedef struct stu__func -{ - STU__BOX; - stua_obj closure_source; // 0 - regular function; 4 - C function - // if closure, pointer to source function - union { - stua_obj closure_data; // partial-application data - void *store; // pointer to free that holds 'code' - stua_obj (*func)(stua_dict *context); - } f; - // closure ends here - short *code; - int num_param; - stua_obj *param; // list of parameter strings -} stu__func; - -// apply this to 'short *code' to get at data -#define stu__const(f) ((stua_obj *) (f)) - -static void stu__free_func(stu__func *f) -{ - if (f->closure_source == 0) free(f->f.store); - if ((stb_uint) f->closure_source <= 4) free(f->param); - free(f); -} - -#define stu__pd(x) ((stua_dict *) stu__ptr(x)) -#define stu__pw(x) ((stu__wrapper *) stu__ptr(x)) -#define stu__pf(x) ((stu__func *) stu__ptr(x)) - - -// garbage-collection - - -static stu__box ** stu__gc_ptrlist; -static stua_obj * stu__gc_root_stack; - -stua_obj stua_pushroot(stua_obj o) { stb_arr_push(stu__gc_root_stack, o); return o; } -void stua_poproot ( void ) { stb_arr_pop(stu__gc_root_stack); } - -static stb_sdict *stu__strings; -static void stu__mark(stua_obj z) -{ - int i; - stu__box *p = stu__ptr(z); - if (p->stua_gc == 1) return; // already marked - assert(p->stua_gc == 0); - p->stua_gc = 1; - switch(p->type) { - case STU___function: { - stu__func *f = (stu__func *) p; - if ((stb_uint) f->closure_source <= 4) { - if (f->closure_source == 0) { - for (i=1; i <= f->code[0]; ++i) - if (!stu__number(((stua_obj *) f->code)[-i])) - stu__mark(((stua_obj *) f->code)[-i]); - } - for (i=0; i < f->num_param; ++i) - stu__mark(f->param[i]); - } else { - stu__mark(f->closure_source); - stu__mark(f->f.closure_data); - } - break; - } - case STU___dict: { - stua_dict *e = (stua_dict *) p; - for (i=0; i < e->limit; ++i) - if (e->table[i].k != STB_IEMPTY && e->table[i].k != STB_IDEL) { - if (!stu__number(e->table[i].k)) stu__mark((int) e->table[i].k); - if (!stu__number(e->table[i].v)) stu__mark((int) e->table[i].v); - } - break; - } - } -} - -static int stu__num_allocs, stu__size_allocs; -static stua_obj stu__flow_val = stua_nil; // used for break & return - -static void stua_gc(int force) -{ - int i; - if (!force && stu__num_allocs == 0 && stu__size_allocs == 0) return; - stu__num_allocs = stu__size_allocs = 0; - //printf("[gc]\n"); - - // clear marks - for (i=0; i < stb_arr_len(stu__gc_ptrlist); ++i) - stu__gc_ptrlist[i]->stua_gc = 0; - - // stu__mark everything reachable - stu__nil.stua_gc = stu__true.stua_gc = stu__false.stua_gc = 1; - stu__mark(stua_globals); - if (!stu__number(stu__flow_val)) - stu__mark(stu__flow_val); - for (i=0; i < stb_arr_len(stu__gc_root_stack); ++i) - if (!stu__number(stu__gc_root_stack[i])) - stu__mark(stu__gc_root_stack[i]); - - // sweep unreachables - for (i=0; i < stb_arr_len(stu__gc_ptrlist);) { - stu__box *z = stu__gc_ptrlist[i]; - if (!z->stua_gc) { - switch (z->type) { - case STU___dict: stb_idict_destroy((stua_dict *) z); break; - case STU___error: free(((stu__wrapper *) z)->ptr); break; - case STU___string: stb_sdict_remove(stu__strings, (char*) ((stu__wrapper *) z)->ptr, NULL); free(z); break; - case STU___function: stu__free_func((stu__func *) z); break; - } - // swap in the last item over this, and repeat - z = stb_arr_pop(stu__gc_ptrlist); - stu__gc_ptrlist[i] = z; - } else - ++i; - } -} - -static void stu__consider_gc(stua_obj x) -{ - if (stu__size_allocs < 100000) return; - if (stu__num_allocs < 10 && stu__size_allocs < 1000000) return; - stb_arr_push(stu__gc_root_stack, x); - stua_gc(0); - stb_arr_pop(stu__gc_root_stack); -} - -static stua_obj stu__makeobj(int type, void *data, int size, int safe_to_gc) -{ - stua_obj x = stu__makeptr(data); - ((stu__box *) data)->type = type; - stb_arr_push(stu__gc_ptrlist, (stu__box *) data); - stu__num_allocs += 1; - stu__size_allocs += size; - if (safe_to_gc) stu__consider_gc(x); - return x; -} - -stua_obj stua_box(int type, void *data, int size) -{ - stu__wrapper *p = (stu__wrapper *) malloc(sizeof(*p)); - p->ptr = data; - return stu__makeobj(type, p, size, 0); -} - -// a stu string can be directly compared for equality, because -// they go into a hash table -stua_obj stua_string(char *z) -{ - stu__wrapper *b = (stu__wrapper *) stb_sdict_get(stu__strings, z); - if (b == NULL) { - int o = stua_box(STU___string, NULL, strlen(z) + sizeof(*b)); - b = stu__pw(o); - stb_sdict_add(stu__strings, z, b); - stb_sdict_getkey(stu__strings, z, (char **) &b->ptr); - } - return stu__makeptr(b); -} - -// stb_obj dictionary is just an stb_idict -static void stu__set(stua_dict *d, stua_obj k, stua_obj v) -{ if (stb_idict_set(d, k, v)) stu__size_allocs += 8; } - -static stua_obj stu__get(stua_dict *d, stua_obj k, stua_obj res) -{ - stb_idict_get_flag(d, k, &res); - return res; -} - -static stua_obj make_string(char *z, int len) -{ - stua_obj s; - char temp[256], *q = (char *) stb_temp(temp, len+1), *p = q; - while (len > 0) { - if (*z == '\\') { - if (z[1] == 'n') *p = '\n'; - else if (z[1] == 'r') *p = '\r'; - else if (z[1] == 't') *p = '\t'; - else *p = z[1]; - p += 1; z += 2; len -= 2; - } else { - *p++ = *z++; len -= 1; - } - } - *p = 0; - s = stua_string(q); - stb_tempfree(temp, q); - return s; -} - -enum token_names -{ - T__none=128, - ST_shl = STUA_op_shl, ST_ge = STUA_op_ge, - ST_shr = STUA_op_shr, ST_le = STUA_op_le, - ST_shru = STUA_op_shru, STU__negate = STUA_op_negate, - ST__reset_numbering = STUA_op_last, - ST_white, - ST_id, ST_float, ST_decimal, ST_hex, ST_char,ST_string, ST_number, - // make sure the keywords come _AFTER_ ST_id, so stb_lex prefer them - ST_if, ST_while, ST_for, ST_eq, ST_nil, - ST_then, ST_do, ST_in, ST_ne, ST_true, - ST_else, ST_break, ST_let, ST_and, ST_false, - ST_elseif, ST_continue, ST_into, ST_or, ST_repeat, - ST_end, ST_as, ST_return, ST_var, ST_func, - ST_catch, ST__frame, - ST__max_terminals, - - STU__defaultparm, STU__seq, -}; - -static stua_dict * stu__globaldict; - stua_obj stua_globals; - -static enum -{ - FLOW_normal, FLOW_continue, FLOW_break, FLOW_return, FLOW_error, -} stu__flow; - -stua_obj stua_error(char *z, ...) -{ - stua_obj a; - char temp[4096], *x; - va_list v; va_start(v,z); vsprintf(temp, z, v); va_end(v); - x = stb_p_strdup(temp); - a = stua_box(STU___error, x, strlen(x)); - stu__flow = FLOW_error; - stu__flow_val = a; - return stua_nil; -} - -double stua_number(stua_obj z) -{ - return stu__tag(z) == stu__int_tag ? stu__int(z) : stu__float(z); -} - -stua_obj stua_make_number(double d) -{ - double e = floor(d); - if (e == d && e < (1 << 29) && e >= -(1 << 29)) - return stu__makeint((int) e); - else - return stua_float((float) d); -} - -stua_obj (*stua_overload)(int op, stua_obj a, stua_obj b, stua_obj c) = NULL; - -static stua_obj stu__op(int op, stua_obj a, stua_obj b, stua_obj c) -{ - stua_obj r = STUA_NO_VALUE; - if (op == '+') { - if (stu__checkt(STU___string, a) && stu__checkt(STU___string, b)) { - ;// @TODO: string concatenation - } else if (stu__checkt(STU___function, a) && stu__checkt(STU___dict, b)) { - stu__func *f = (stu__func *) malloc(12); - assert(offsetof(stu__func, code)==12); - f->closure_source = a; - f->f.closure_data = b; - return stu__makeobj(STU___function, f, 16, 1); - } - } - if (stua_overload) r = stua_overload(op,a,b,c); - if (stu__flow != FLOW_error && r == STUA_NO_VALUE) - stua_error("Typecheck for operator %d", op), r=stua_nil; - return r; -} - -#define STU__EVAL2(a,b) \ - a = stu__eval(stu__f[n+1]); if (stu__flow) break; stua_pushroot(a); \ - b = stu__eval(stu__f[n+2]); stua_poproot(); if (stu__flow) break; - -#define STU__FB(op) \ - STU__EVAL2(a,b) \ - if (stu__tag(a) == stu__int_tag && stu__tag(b) == stu__int_tag) \ - return ((a) op (b)); \ - if (stu__number(a) && stu__number(b)) \ - return stua_make_number(stua_number(a) op stua_number(b)); \ - return stu__op(stu__f[n], a,b, stua_nil) - -#define STU__F(op) \ - STU__EVAL2(a,b) \ - if (stu__number(a) && stu__number(b)) \ - return stua_make_number(stua_number(a) op stua_number(b)); \ - return stu__op(stu__f[n], a,b, stua_nil) - -#define STU__I(op) \ - STU__EVAL2(a,b) \ - if (stu__tag(a) == stu__int_tag && stu__tag(b) == stu__int_tag) \ - return stu__makeint(stu__int(a) op stu__int(b)); \ - return stu__op(stu__f[n], a,b, stua_nil) - -#define STU__C(op) \ - STU__EVAL2(a,b) \ - if (stu__number(a) && stu__number(b)) \ - return (stua_number(a) op stua_number(b)) ? stua_true : stua_false; \ - return stu__op(stu__f[n], a,b, stua_nil) - -#define STU__CE(op) \ - STU__EVAL2(a,b) \ - return (a op b) ? stua_true : stua_false - -static short *stu__f; -static stua_obj stu__f_obj; -static stua_dict *stu__c; -static stua_obj stu__funceval(stua_obj fo, stua_obj co); - -static int stu__cond(stua_obj x) -{ - if (stu__flow) return 0; - if (!stu__checkt(STU___boolean, x)) - x = stu__op('!', x, stua_nil, stua_nil); - if (x == stua_true ) return 1; - if (x == stua_false) return 0; - stu__flow = FLOW_error; - return 0; -} - -// had to manually eliminate tailcall recursion for debugging complex stuff -#define TAILCALL(x) n = (x); goto top; -static stua_obj stu__eval(int n) -{ -top: - if (stu__flow >= FLOW_return) return stua_nil; // is this needed? - if (n < 0) return stu__const(stu__f)[n]; - assert(n != 0 && n != 1); - switch (stu__f[n]) { - stua_obj a,b,c; - case ST_catch: a = stu__eval(stu__f[n+1]); - if (stu__flow == FLOW_error) { a=stu__flow_val; stu__flow = FLOW_normal; } - return a; - case ST_var: b = stu__eval(stu__f[n+2]); if (stu__flow) break; - stu__set(stu__c, stu__const(stu__f)[stu__f[n+1]], b); - return b; - case STU__seq: stu__eval(stu__f[n+1]); if (stu__flow) break; - TAILCALL(stu__f[n+2]); - case ST_if: if (!stu__cond(stu__eval(stu__f[n+1]))) return stua_nil; - TAILCALL(stu__f[n+2]); - case ST_else: a = stu__cond(stu__eval(stu__f[n+1])); - TAILCALL(stu__f[n + 2 + !a]); - #define STU__HANDLE_BREAK \ - if (stu__flow >= FLOW_break) { \ - if (stu__flow == FLOW_break) { \ - a = stu__flow_val; \ - stu__flow = FLOW_normal; \ - stu__flow_val = stua_nil; \ - return a; \ - } \ - return stua_nil; \ - } - case ST_as: stu__eval(stu__f[n+3]); - STU__HANDLE_BREAK - // fallthrough! - case ST_while: a = stua_nil; stua_pushroot(a); - while (stu__cond(stu__eval(stu__f[n+1]))) { - stua_poproot(); - a = stu__eval(stu__f[n+2]); - STU__HANDLE_BREAK - stu__flow = FLOW_normal; // clear 'continue' flag - stua_pushroot(a); - if (stu__f[n+3]) stu__eval(stu__f[n+3]); - STU__HANDLE_BREAK - stu__flow = FLOW_normal; // clear 'continue' flag - } - stua_poproot(); - return a; - case ST_break: stu__flow = FLOW_break; stu__flow_val = stu__eval(stu__f[n+1]); break; - case ST_continue:stu__flow = FLOW_continue; break; - case ST_return: stu__flow = FLOW_return; stu__flow_val = stu__eval(stu__f[n+1]); break; - case ST__frame: return stu__f_obj; - case '[': STU__EVAL2(a,b); - if (stu__checkt(STU___dict, a)) - return stu__get(stu__pd(a), b, stua_nil); - return stu__op(stu__f[n], a, b, stua_nil); - case '=': a = stu__eval(stu__f[n+2]); if (stu__flow) break; - n = stu__f[n+1]; - if (stu__f[n] == ST_id) { - if (!stb_idict_update(stu__c, stu__const(stu__f)[stu__f[n+1]], a)) - if (!stb_idict_update(stu__globaldict, stu__const(stu__f)[stu__f[n+1]], a)) - return stua_error("Assignment to undefined variable"); - } else if (stu__f[n] == '[') { - stua_pushroot(a); - b = stu__eval(stu__f[n+1]); if (stu__flow) { stua_poproot(); break; } - stua_pushroot(b); - c = stu__eval(stu__f[n+2]); stua_poproot(); stua_poproot(); - if (stu__flow) break; - if (!stu__checkt(STU___dict, b)) return stua_nil; - stu__set(stu__pd(b), c, a); - } else { - return stu__op(stu__f[n], stu__eval(n), a, stua_nil); - } - return a; - case STU__defaultparm: - a = stu__eval(stu__f[n+2]); - stu__flow = FLOW_normal; - if (stb_idict_add(stu__c, stu__const(stu__f)[stu__f[n+1]], a)) - stu__size_allocs += 8; - return stua_nil; - case ST_id: a = stu__get(stu__c, stu__const(stu__f)[stu__f[n+1]], STUA_NO_VALUE); // try local variable - return a != STUA_NO_VALUE // else try stu__compile_global_scope variable - ? a : stu__get(stu__globaldict, stu__const(stu__f)[stu__f[n+1]], stua_nil); - case STU__negate:a = stu__eval(stu__f[n+1]); if (stu__flow) break; - return stu__isint(a) ? -a : stu__op(stu__f[n], a, stua_nil, stua_nil); - case '~': a = stu__eval(stu__f[n+1]); if (stu__flow) break; - return stu__isint(a) ? (~a)&~3 : stu__op(stu__f[n], a, stua_nil, stua_nil); - case '!': a = stu__eval(stu__f[n+1]); if (stu__flow) break; - a = stu__cond(a); if (stu__flow) break; - return a ? stua_true : stua_false; - case ST_eq: STU__CE(==); case ST_le: STU__C(<=); case '<': STU__C(<); - case ST_ne: STU__CE(!=); case ST_ge: STU__C(>=); case '>': STU__C(>); - case '+' : STU__FB(+); case '*': STU__F(*); case '&': STU__I(&); case ST_shl: STU__I(<<); - case '-' : STU__FB(-); case '/': STU__F(/); case '|': STU__I(|); case ST_shr: STU__I(>>); - case '%': STU__I(%); case '^': STU__I(^); - case ST_shru: STU__EVAL2(a,b); - if (stu__tag(a) == stu__int_tag && stu__tag(b) == stu__int_tag) - return stu__makeint((unsigned) stu__int(a) >> stu__int(b)); - return stu__op(stu__f[n], a,b, stua_nil); - case ST_and: a = stu__eval(stu__f[n+1]); b = stu__cond(a); if (stu__flow) break; - return a ? stu__eval(stu__f[n+2]) : a; - case ST_or : a = stu__eval(stu__f[n+1]); b = stu__cond(a); if (stu__flow) break; - return a ? b : stu__eval(stu__f[n+2]); - case'(':case':': STU__EVAL2(a,b); - if (!stu__checkt(STU___function, a)) - return stu__op(stu__f[n], a,b, stua_nil); - if (!stu__checkt(STU___dict, b)) - return stua_nil; - if (stu__f[n] == ':') - b = stu__makeobj(STU___dict, stb_idict_copy(stu__pd(b)), stb_idict_memory_usage(stu__pd(b)), 0); - a = stu__funceval(a,b); - return a; - case '{' : { - stua_dict *d; - d = stb_idict_new_size(stu__f[n+1] > 40 ? 64 : 16); - if (d == NULL) - return stua_nil; // breakpoint fodder - c = stu__makeobj(STU___dict, d, 32, 1); - stua_pushroot(c); - a = stu__f[n+1]; - for (b=0; b < a; ++b) { - stua_obj x = stua_pushroot(stu__eval(stu__f[n+2 + b*2 + 0])); - stua_obj y = stu__eval(stu__f[n+2 + b*2 + 1]); - stua_poproot(); - if (stu__flow) { stua_poproot(); return stua_nil; } - stu__set(d, x, y); - } - stua_poproot(); - return c; - } - default: if (stu__f[n] < 0) return stu__const(stu__f)[stu__f[n]]; - assert(0); /* NOTREACHED */ // internal error! - } - return stua_nil; -} - -int stb__stua_nesting; -static stua_obj stu__funceval(stua_obj fo, stua_obj co) -{ - stu__func *f = stu__pf(fo); - stua_dict *context = stu__pd(co); - int i,j; - stua_obj p; - short *tf = stu__f; // save previous function - stua_dict *tc = stu__c; - - if (stu__flow == FLOW_error) return stua_nil; - assert(stu__flow == FLOW_normal); - - stua_pushroot(fo); - stua_pushroot(co); - stu__consider_gc(stua_nil); - - while ((stb_uint) f->closure_source > 4) { - // add data from closure to context - stua_dict *e = (stua_dict *) stu__pd(f->f.closure_data); - for (i=0; i < e->limit; ++i) - if (e->table[i].k != STB_IEMPTY && e->table[i].k != STB_IDEL) - if (stb_idict_add(context, e->table[i].k, e->table[i].v)) - stu__size_allocs += 8; - // use add so if it's already defined, we don't override it; that way - // explicit parameters win over applied ones, and most recent applications - // win over previous ones - f = stu__pf(f->closure_source); - } - - for (j=0, i=0; i < f->num_param; ++i) - // if it doesn't already exist, add it from the numbered parameters - if (stb_idict_add(context, f->param[i], stu__get(context, stu__int(j), stua_nil))) - ++j; - - // @TODO: if (stu__get(context, stu__int(f->num_param+1)) != STUA_NO_VALUE) // error: too many parameters - // @TODO: ditto too few parameters - - if (f->closure_source == 4) - p = f->f.func(context); - else { - stu__f = f->code, stu__c = context; - stu__f_obj = co; - ++stb__stua_nesting; - if (stu__f[1]) - p = stu__eval(stu__f[1]); - else - p = stua_nil; - --stb__stua_nesting; - stu__f = tf, stu__c = tc; // restore previous function - if (stu__flow == FLOW_return) { - stu__flow = FLOW_normal; - p = stu__flow_val; - stu__flow_val = stua_nil; - } - } - - stua_poproot(); - stua_poproot(); - - return p; -} - -// Parser - -static int stu__tok; -static stua_obj stu__tokval; - -static char *stu__curbuf, *stu__bufstart; - -static stb_matcher *stu__lex_matcher; - -static unsigned char stu__prec[ST__max_terminals], stu__end[ST__max_terminals]; - -static void stu__nexttoken(void) -{ - int len; - -retry: - stu__tok = stb_lex(stu__lex_matcher, stu__curbuf, &len); - if (stu__tok == 0) - return; - switch(stu__tok) { - case ST_white : stu__curbuf += len; goto retry; - case T__none : stu__tok = *stu__curbuf; break; - case ST_string: stu__tokval = make_string(stu__curbuf+1, len-2); break; - case ST_id : stu__tokval = make_string(stu__curbuf, len); break; - case ST_hex : stu__tokval = stu__makeint(strtol(stu__curbuf+2,NULL,16)); stu__tok = ST_number; break; - case ST_decimal: stu__tokval = stu__makeint(strtol(stu__curbuf ,NULL,10)); stu__tok = ST_number; break; - case ST_float : stu__tokval = stua_float((float) atof(stu__curbuf)) ; stu__tok = ST_number; break; - case ST_char : stu__tokval = stu__curbuf[2] == '\\' ? stu__curbuf[3] : stu__curbuf[2]; - if (stu__curbuf[3] == 't') stu__tokval = '\t'; - if (stu__curbuf[3] == 'n') stu__tokval = '\n'; - if (stu__curbuf[3] == 'r') stu__tokval = '\r'; - stu__tokval = stu__makeint(stu__tokval); - stu__tok = ST_number; - break; - } - stu__curbuf += len; -} - -static struct { int stu__tok; char *regex; } stu__lexemes[] = -{ - ST_white , "([ \t\n\r]|/\\*(.|\n)*\\*/|//[^\r\n]*([\r\n]|$))+", - ST_id , "[_a-zA-Z][_a-zA-Z0-9]*", - ST_hex , "0x[0-9a-fA-F]+", - ST_decimal, "[0-9]+[0-9]*", - ST_float , "[0-9]+\\.?[0-9]*([eE][-+]?[0-9]+)?", - ST_float , "\\.[0-9]+([eE][-+]?[0-9]+)?", - ST_char , "c'(\\\\.|[^\\'])'", - ST_string , "\"(\\\\.|[^\\\"\n\r])*\"", - ST_string , "\'(\\\\.|[^\\\'\n\r])*\'", - - #define stua_key4(a,b,c,d) ST_##a, #a, ST_##b, #b, ST_##c, #c, ST_##d, #d, - stua_key4(if,then,else,elseif) stua_key4(while,do,for,in) - stua_key4(func,var,let,break) stua_key4(nil,true,false,end) - stua_key4(return,continue,as,repeat) stua_key4(_frame,catch,catch,catch) - - ST_shl, "<<", ST_and, "&&", ST_eq, "==", ST_ge, ">=", - ST_shr, ">>", ST_or , "||", ST_ne, "!=", ST_le, "<=", - ST_shru,">>>", ST_into, "=>", - T__none, ".", -}; - -typedef struct -{ - stua_obj *data; // constants being compiled - short *code; // code being compiled - stua_dict *locals; - short *non_local_refs; -} stu__comp_func; - -static stu__comp_func stu__pfunc; -static stu__comp_func *func_stack = NULL; -static void stu__push_func_comp(void) -{ - stb_arr_push(func_stack, stu__pfunc); - stu__pfunc.data = NULL; - stu__pfunc.code = NULL; - stu__pfunc.locals = stb_idict_new_size(16); - stu__pfunc.non_local_refs = NULL; - stb_arr_push(stu__pfunc.code, 0); // number of data items - stb_arr_push(stu__pfunc.code, 1); // starting execution address -} - -static void stu__pop_func_comp(void) -{ - stb_arr_free(stu__pfunc.code); - stb_arr_free(stu__pfunc.data); - stb_idict_destroy(stu__pfunc.locals); - stb_arr_free(stu__pfunc.non_local_refs); - stu__pfunc = stb_arr_pop(func_stack); -} - -// if an id is a reference to an outer lexical scope, this -// function returns the "name" of it, and updates the stack -// structures to make sure the names are propagated in. -static int stu__nonlocal_id(stua_obj var_obj) -{ - stua_obj dummy, var = var_obj; - int i, n = stb_arr_len(func_stack), j,k; - if (stb_idict_get_flag(stu__pfunc.locals, var, &dummy)) return 0; - for (i=n-1; i > 1; --i) { - if (stb_idict_get_flag(func_stack[i].locals, var, &dummy)) - break; - } - if (i <= 1) return 0; // stu__compile_global_scope - j = i; // need to access variable from j'th frame - for (i=0; i < stb_arr_len(stu__pfunc.non_local_refs); ++i) - if (stu__pfunc.non_local_refs[i] == j) return j-n; - stb_arr_push(stu__pfunc.non_local_refs, j-n); - // now make sure all the parents propagate it down - for (k=n-1; k > 1; --k) { - if (j-k >= 0) return j-n; // comes direct from this parent - for(i=0; i < stb_arr_len(func_stack[k].non_local_refs); ++i) - if (func_stack[k].non_local_refs[i] == j-k) - return j-n; - stb_arr_push(func_stack[k].non_local_refs, j-k); - } - assert (k != 1); - - return j-n; -} - -static int stu__off(void) { return stb_arr_len(stu__pfunc.code); } -static void stu__cc(int a) -{ - assert(a >= -2000 && a < 5000); - stb_arr_push(stu__pfunc.code, a); -} -static int stu__cc1(int a) { stu__cc(a); return stu__off()-1; } -static int stu__cc2(int a, int b) { stu__cc(a); stu__cc(b); return stu__off()-2; } -static int stu__cc3(int a, int b, int c) { - if (a == '=') assert(c != 0); - stu__cc(a); stu__cc(b); stu__cc(c); return stu__off()-3; } -static int stu__cc4(int a, int b, int c, int d) { stu__cc(a); stu__cc(b); stu__cc(c); stu__cc(d); return stu__off()-4; } - -static int stu__cdv(stua_obj p) -{ - int i; - assert(p != STUA_NO_VALUE); - for (i=0; i < stb_arr_len(stu__pfunc.data); ++i) - if (stu__pfunc.data[i] == p) - break; - if (i == stb_arr_len(stu__pfunc.data)) - stb_arr_push(stu__pfunc.data, p); - return ~i; -} - -static int stu__cdt(void) -{ - int z = stu__cdv(stu__tokval); - stu__nexttoken(); - return z; -} - -static int stu__seq(int a, int b) -{ - return !a ? b : !b ? a : stu__cc3(STU__seq, a,b); -} - -static char stu__comp_err_str[1024]; -static int stu__comp_err_line; -static int stu__err(char *str, ...) -{ - va_list v; - char *s = stu__bufstart; - stu__comp_err_line = 1; - while (s < stu__curbuf) { - if (s[0] == '\n' || s[0] == '\r') { - if (s[0]+s[1] == '\n' + '\r') ++s; - ++stu__comp_err_line; - } - ++s; - } - va_start(v, str); - vsprintf(stu__comp_err_str, str, v); - va_end(v); - return 0; -} - -static int stu__accept(int p) -{ - if (stu__tok != p) return 0; - stu__nexttoken(); - return 1; -} - -static int stu__demand(int p) -{ - if (stu__accept(p)) return 1; - return stu__err("Didn't find expected stu__tok"); -} - -static int stu__demandv(int p, stua_obj *val) -{ - if (stu__tok == p || p==0) { - *val = stu__tokval; - stu__nexttoken(); - return 1; - } else - return 0; -} - -static int stu__expr(int p); -int stu__nexpr(int p) { stu__nexttoken(); return stu__expr(p); } -static int stu__statements(int once, int as); - -static int stu__parse_if(void) // parse both ST_if and ST_elseif -{ - int b,c,a; - a = stu__nexpr(1); if (!a) return 0; - if (!stu__demand(ST_then)) return stu__err("expecting THEN"); - b = stu__statements(0,0); if (!b) return 0; - if (b == 1) b = -1; - - if (stu__tok == ST_elseif) { - return stu__parse_if(); - } else if (stu__accept(ST_else)) { - c = stu__statements(0,0); if (!c) return 0; - if (!stu__demand(ST_end)) return stu__err("expecting END after else clause"); - return stu__cc4(ST_else, a, b, c); - } else { - if (!stu__demand(ST_end)) return stu__err("expecting END in if statement"); - return stu__cc3(ST_if, a, b); - } -} - -int stu__varinit(int z, int in_globals) -{ - int a,b; - stu__nexttoken(); - while (stu__demandv(ST_id, &b)) { - if (!stb_idict_add(stu__pfunc.locals, b, 1)) - if (!in_globals) return stu__err("Redefined variable %s.", stu__pw(b)->ptr); - if (stu__accept('=')) { - a = stu__expr(1); if (!a) return 0; - } else - a = stu__cdv(stua_nil); - z = stu__seq(z, stu__cc3(ST_var, stu__cdv(b), a)); - if (!stu__accept(',')) break; - } - return z; -} - -static int stu__compile_unary(int z, int outparm, int require_inparm) -{ - int op = stu__tok, a, b; - stu__nexttoken(); - if (outparm) { - if (require_inparm || (stu__tok && stu__tok != ST_end && stu__tok != ST_else && stu__tok != ST_elseif && stu__tok !=';')) { - a = stu__expr(1); if (!a) return 0; - } else - a = stu__cdv(stua_nil); - b = stu__cc2(op, a); - } else - b = stu__cc1(op); - return stu__seq(z,b); -} - -static int stu__assign(void) -{ - int z; - stu__accept(ST_let); - z = stu__expr(1); if (!z) return 0; - if (stu__accept('=')) { - int y,p = (z >= 0 ? stu__pfunc.code[z] : 0); - if (z < 0 || (p != ST_id && p != '[')) return stu__err("Invalid lvalue in assignment"); - y = stu__assign(); if (!y) return 0; - z = stu__cc3('=', z, y); - } - return z; -} - -static int stu__statements(int once, int stop_while) -{ - int a,b, c, z=0; - for(;;) { - switch (stu__tok) { - case ST_if : a = stu__parse_if(); if (!a) return 0; - z = stu__seq(z, a); - break; - case ST_while : if (stop_while) return (z ? z:1); - a = stu__nexpr(1); if (!a) return 0; - if (stu__accept(ST_as)) c = stu__statements(0,0); else c = 0; - if (!stu__demand(ST_do)) return stu__err("expecting DO"); - b = stu__statements(0,0); if (!b) return 0; - if (!stu__demand(ST_end)) return stu__err("expecting END"); - if (b == 1) b = -1; - z = stu__seq(z, stu__cc4(ST_while, a, b, c)); - break; - case ST_repeat : stu__nexttoken(); - c = stu__statements(0,1); if (!c) return 0; - if (!stu__demand(ST_while)) return stu__err("expecting WHILE"); - a = stu__expr(1); if (!a) return 0; - if (!stu__demand(ST_do)) return stu__err("expecting DO"); - b = stu__statements(0,0); if (!b) return 0; - if (!stu__demand(ST_end)) return stu__err("expecting END"); - if (b == 1) b = -1; - z = stu__seq(z, stu__cc4(ST_as, a, b, c)); - break; - case ST_catch : a = stu__nexpr(1); if (!a) return 0; - z = stu__seq(z, stu__cc2(ST_catch, a)); - break; - case ST_var : z = stu__varinit(z,0); break; - case ST_return : z = stu__compile_unary(z,1,1); break; - case ST_continue:z = stu__compile_unary(z,0,0); break; - case ST_break : z = stu__compile_unary(z,1,0); break; - case ST_into : if (z == 0 && !once) return stu__err("=> cannot be first statement in block"); - a = stu__nexpr(99); - b = (a >= 0? stu__pfunc.code[a] : 0); - if (a < 0 || (b != ST_id && b != '[')) return stu__err("Invalid lvalue on right side of =>"); - z = stu__cc3('=', a, z); - break; - default : if (stu__end[stu__tok]) return once ? 0 : (z ? z:1); - a = stu__assign(); if (!a) return 0; - stu__accept(';'); - if (stu__tok && !stu__end[stu__tok]) { - if (a < 0) - return stu__err("Constant has no effect"); - if (stu__pfunc.code[a] != '(' && stu__pfunc.code[a] != '=') - return stu__err("Expression has no effect"); - } - z = stu__seq(z, a); - break; - } - if (!z) return 0; - stu__accept(';'); - if (once && stu__tok != ST_into) return z; - } -} - -static int stu__postexpr(int z, int p); -static int stu__dictdef(int end, int *count) -{ - int z,n=0,i,flags=0; - short *dict=NULL; - stu__nexttoken(); - while (stu__tok != end) { - if (stu__tok == ST_id) { - stua_obj id = stu__tokval; - stu__nexttoken(); - if (stu__tok == '=') { - flags |= 1; - stb_arr_push(dict, stu__cdv(id)); - z = stu__nexpr(1); if (!z) return 0; - } else { - z = stu__cc2(ST_id, stu__cdv(id)); - z = stu__postexpr(z,1); if (!z) return 0; - flags |= 2; - stb_arr_push(dict, stu__cdv(stu__makeint(n++))); - } - } else { - z = stu__expr(1); if (!z) return 0; - flags |= 2; - stb_arr_push(dict, stu__cdv(stu__makeint(n++))); - } - if (end != ')' && flags == 3) { z=stu__err("can't mix initialized and uninitialized defs"); goto done;} - stb_arr_push(dict, z); - if (!stu__accept(',')) break; - } - if (!stu__demand(end)) - return stu__err(end == ')' ? "Expecting ) at end of function call" - : "Expecting } at end of dictionary definition"); - z = stu__cc2('{', stb_arr_len(dict)/2); - for (i=0; i < stb_arr_len(dict); ++i) - stu__cc(dict[i]); - if (count) *count = n; -done: - stb_arr_free(dict); - return z; -} - -static int stu__comp_id(void) -{ - int z,d; - d = stu__nonlocal_id(stu__tokval); - if (d == 0) - return z = stu__cc2(ST_id, stu__cdt()); - // access a non-local frame by naming it with the appropriate int - assert(d < 0); - z = stu__cdv(d); // relative frame # is the 'variable' in our local frame - z = stu__cc2(ST_id, z); // now access that dictionary - return stu__cc3('[', z, stu__cdt()); // now access the variable from that dir -} - -static stua_obj stu__funcdef(stua_obj *id, stua_obj *func); -static int stu__expr(int p) -{ - int z; - // unary - switch (stu__tok) { - case ST_number: z = stu__cdt(); break; - case ST_string: z = stu__cdt(); break; // @TODO - string concatenation like C - case ST_id : z = stu__comp_id(); break; - case ST__frame: z = stu__cc1(ST__frame); stu__nexttoken(); break; - case ST_func : z = stu__funcdef(NULL,NULL); break; - case ST_if : z = stu__parse_if(); break; - case ST_nil : z = stu__cdv(stua_nil); stu__nexttoken(); break; - case ST_true : z = stu__cdv(stua_true); stu__nexttoken(); break; - case ST_false : z = stu__cdv(stua_false); stu__nexttoken(); break; - case '-' : z = stu__nexpr(99); if (z) z=stu__cc2(STU__negate,z); else return z; break; - case '!' : z = stu__nexpr(99); if (z) z=stu__cc2('!',z); else return z; break; - case '~' : z = stu__nexpr(99); if (z) z=stu__cc2('~',z); else return z; break; - case '{' : z = stu__dictdef('}', NULL); break; - default : return stu__err("Unexpected token"); - case '(' : stu__nexttoken(); z = stu__statements(0,0); if (!stu__demand(')')) return stu__err("Expecting )"); - } - return stu__postexpr(z,p); -} - -static int stu__postexpr(int z, int p) -{ - int q; - // postfix - while (stu__tok == '(' || stu__tok == '[' || stu__tok == '.') { - if (stu__accept('.')) { - // MUST be followed by a plain identifier! use [] for other stuff - if (stu__tok != ST_id) return stu__err("Must follow . with plain name; try [] instead"); - z = stu__cc3('[', z, stu__cdv(stu__tokval)); - stu__nexttoken(); - } else if (stu__accept('[')) { - while (stu__tok != ']') { - int r = stu__expr(1); if (!r) return 0; - z = stu__cc3('[', z, r); - if (!stu__accept(',')) break; - } - if (!stu__demand(']')) return stu__err("Expecting ]"); - } else { - int n, p = stu__dictdef(')', &n); if (!p) return 0; - #if 0 // this is incorrect! - if (z > 0 && stu__pfunc.code[z] == ST_id) { - stua_obj q = stu__get(stu__globaldict, stu__pfunc.data[-stu__pfunc.code[z+1]-1], stua_nil); - if (stu__checkt(STU___function, q)) - if ((stu__pf(q))->num_param != n) - return stu__err("Incorrect number of parameters"); - } - #endif - z = stu__cc3('(', z, p); - } - } - // binop - this implementation taken from lcc - for (q=stu__prec[stu__tok]; q >= p; --q) { - while (stu__prec[stu__tok] == q) { - int o = stu__tok, y = stu__nexpr(p+1); if (!y) return 0; - z = stu__cc3(o,z,y); - } - } - return z; -} - -static stua_obj stu__finish_func(stua_obj *param, int start) -{ - int n, size; - stu__func *f = (stu__func *) malloc(sizeof(*f)); - f->closure_source = 0; - f->num_param = stb_arr_len(param); - f->param = (int *) stb_copy(param, f->num_param * sizeof(*f->param)); - size = stb_arr_storage(stu__pfunc.code) + stb_arr_storage(stu__pfunc.data) + sizeof(*f) + 8; - f->f.store = malloc(stb_arr_storage(stu__pfunc.code) + stb_arr_storage(stu__pfunc.data)); - f->code = (short *) ((char *) f->f.store + stb_arr_storage(stu__pfunc.data)); - memcpy(f->code, stu__pfunc.code, stb_arr_storage(stu__pfunc.code)); - f->code[1] = start; - f->code[0] = stb_arr_len(stu__pfunc.data); - for (n=0; n < f->code[0]; ++n) - ((stua_obj *) f->code)[-1-n] = stu__pfunc.data[n]; - return stu__makeobj(STU___function, f, size, 0); -} - -static int stu__funcdef(stua_obj *id, stua_obj *result) -{ - int n,z=0,i,q; - stua_obj *param = NULL; - short *nonlocal; - stua_obj v,f=stua_nil; - assert(stu__tok == ST_func); - stu__nexttoken(); - if (id) { - if (!stu__demandv(ST_id, id)) return stu__err("Expecting function name"); - } else - stu__accept(ST_id); - if (!stu__demand('(')) return stu__err("Expecting ( for function parameter"); - stu__push_func_comp(); - while (stu__tok != ')') { - if (!stu__demandv(ST_id, &v)) { z=stu__err("Expecting parameter name"); goto done; } - stb_idict_add(stu__pfunc.locals, v, 1); - if (stu__tok == '=') { - n = stu__nexpr(1); if (!n) { z=0; goto done; } - z = stu__seq(z, stu__cc3(STU__defaultparm, stu__cdv(v), n)); - } else - stb_arr_push(param, v); - if (!stu__accept(',')) break; - } - if (!stu__demand(')')) { z=stu__err("Expecting ) at end of parameter list"); goto done; } - n = stu__statements(0,0); if (!n) { z=0; goto done; } - if (!stu__demand(ST_end)) { z=stu__err("Expecting END at end of function"); goto done; } - if (n == 1) n = 0; - n = stu__seq(z,n); - f = stu__finish_func(param, n); - if (result) { *result = f; z=1; stu__pop_func_comp(); } - else { - nonlocal = stu__pfunc.non_local_refs; - stu__pfunc.non_local_refs = NULL; - stu__pop_func_comp(); - z = stu__cdv(f); - if (nonlocal) { // build a closure with references to the needed frames - short *initcode = NULL; - for (i=0; i < stb_arr_len(nonlocal); ++i) { - int k = nonlocal[i], p; - stb_arr_push(initcode, stu__cdv(k)); - if (k == -1) p = stu__cc1(ST__frame); - else { p = stu__cdv(stu__makeint(k+1)); p = stu__cc2(ST_id, p); } - stb_arr_push(initcode, p); - } - q = stu__cc2('{', stb_arr_len(nonlocal)); - for (i=0; i < stb_arr_len(initcode); ++i) - stu__cc(initcode[i]); - z = stu__cc3('+', z, q); - stb_arr_free(initcode); - } - stb_arr_free(nonlocal); - } -done: - stb_arr_free(param); - if (!z) stu__pop_func_comp(); - return z; -} - -static int stu__compile_global_scope(void) -{ - stua_obj o; - int z=0; - - stu__push_func_comp(); - while (stu__tok != 0) { - if (stu__tok == ST_func) { - stua_obj id, f; - if (!stu__funcdef(&id,&f)) - goto error; - stu__set(stu__globaldict, id, f); - } else if (stu__tok == ST_var) { - z = stu__varinit(z,1); if (!z) goto error; - } else { - int y = stu__statements(1,0); if (!y) goto error; - z = stu__seq(z,y); - } - stu__accept(';'); - } - o = stu__finish_func(NULL, z); - stu__pop_func_comp(); - - o = stu__funceval(o, stua_globals); // initialize stu__globaldict - if (stu__flow == FLOW_error) - printf("Error: %s\n", ((stu__wrapper *) stu__ptr(stu__flow_val))->ptr); - return 1; -error: - stu__pop_func_comp(); - return 0; -} - -stua_obj stu__myprint(stua_dict *context) -{ - stua_obj x = stu__get(context, stua_string("x"), stua_nil); - if ((x & 1) == stu__float_tag) printf("%f", stu__getfloat(x)); - else if (stu__tag(x) == stu__int_tag) printf("%d", stu__int(x)); - else { - stu__wrapper *s = stu__pw(x); - if (s->type == STU___string || s->type == STU___error) - printf("%s", s->ptr); - else if (s->type == STU___dict) printf("{{dictionary}}"); - else if (s->type == STU___function) printf("[[function]]"); - else - printf("[[ERROR:%s]]", s->ptr); - } - return x; -} - -void stua_init(void) -{ - if (!stu__globaldict) { - int i; - stua_obj s; - stu__func *f; - - stu__prec[ST_and] = stu__prec[ST_or] = 1; - stu__prec[ST_eq ] = stu__prec[ST_ne] = stu__prec[ST_le] = - stu__prec[ST_ge] = stu__prec['>' ] = stu__prec['<'] = 2; - stu__prec[':'] = 3; - stu__prec['&'] = stu__prec['|'] = stu__prec['^'] = 4; - stu__prec['+'] = stu__prec['-'] = 5; - stu__prec['*'] = stu__prec['/'] = stu__prec['%'] = - stu__prec[ST_shl]= stu__prec[ST_shr]= stu__prec[ST_shru]= 6; - - stu__end[')'] = stu__end[ST_end] = stu__end[ST_else] = 1; - stu__end[ST_do] = stu__end[ST_elseif] = 1; - - stu__float_init(); - stu__lex_matcher = stb_lex_matcher(); - for (i=0; i < sizeof(stu__lexemes)/sizeof(stu__lexemes[0]); ++i) - stb_lex_item(stu__lex_matcher, stu__lexemes[i].regex, stu__lexemes[i].stu__tok); - - stu__globaldict = stb_idict_new_size(64); - stua_globals = stu__makeobj(STU___dict, stu__globaldict, 0,0); - stu__strings = stb_sdict_new(0); - - stu__curbuf = stu__bufstart = "func _print(x) end\n" - "func print()\n var x=0 while _frame[x] != nil as x=x+1 do _print(_frame[x]) end end\n"; - stu__nexttoken(); - if (!stu__compile_global_scope()) - printf("Compile error in line %d: %s\n", stu__comp_err_line, stu__comp_err_str); - - s = stu__get(stu__globaldict, stua_string("_print"), stua_nil); - if (stu__tag(s) == stu__ptr_tag && stu__ptr(s)->type == STU___function) { - f = stu__pf(s); - free(f->f.store); - f->closure_source = 4; - f->f.func = stu__myprint; - f->code = NULL; - } - } -} - -void stua_uninit(void) -{ - if (stu__globaldict) { - stb_idict_remove_all(stu__globaldict); - stb_arr_setlen(stu__gc_root_stack, 0); - stua_gc(1); - stb_idict_destroy(stu__globaldict); - stb_sdict_delete(stu__strings); - stb_matcher_free(stu__lex_matcher); - stb_arr_free(stu__gc_ptrlist); - stb_arr_free(func_stack); - stb_arr_free(stu__gc_root_stack); - stu__globaldict = NULL; - } -} - -void stua_run_script(char *s) -{ - stua_init(); - - stu__curbuf = stu__bufstart = s; - stu__nexttoken(); - - stu__flow = FLOW_normal; - - if (!stu__compile_global_scope()) - printf("Compile error in line %d: %s\n", stu__comp_err_line, stu__comp_err_str); - stua_gc(1); -} -#endif // STB_DEFINE -#endif // STB_STUA - #undef STB_EXTERN #endif // STB_INCLUDE_STB_H diff --git a/stb_ds.h b/stb_ds.h index 4911b94..f6ec239 100644 --- a/stb_ds.h +++ b/stb_ds.h @@ -104,7 +104,7 @@ DOCUMENTATION moving the rest of the array over. Returns b. arrinsn: - void arrins(T* a, int p, int n); + void arrinsn(T* a, int p, int n); Inserts n uninitialized items into array a starting at a[p], moving the rest of the array over. @@ -123,7 +123,7 @@ DOCUMENTATION Deletes the element at a[p], moving the rest of the array over. arrdeln: - void arrdel(T* a, int p, int n); + void arrdeln(T* a, int p, int n); Deletes n elements starting at a[p], moving the rest of the array over. arrdelswap: diff --git a/stb_image.h b/stb_image.h index f2e3430..e6207e5 100644 --- a/stb_image.h +++ b/stb_image.h @@ -111,7 +111,7 @@ RECENT REVISION HISTORY: Josh Tobin Matthew Gregan github:poppolopoppo Julian Raschke Gregory Mullen Christian Floisand github:darealshinji Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007 - Brad Weinberger Matvey Cherevko [reserved] + Brad Weinberger Matvey Cherevko github:mosra Luca Sas Alexander Veselov Zack Middleton [reserved] Ryan C. Gordon [reserved] [reserved] DO NOT ADD YOUR NAME HERE @@ -310,11 +310,10 @@ RECENT REVISION HISTORY: // // iPhone PNG support: // -// By default we convert iphone-formatted PNGs back to RGB, even though -// they are internally encoded differently. You can disable this conversion -// by calling stbi_convert_iphone_png_to_rgb(0), in which case -// you will always just get the native iphone "format" through (which -// is BGR stored in RGB). +// We optionally support converting iPhone-formatted PNGs (which store +// premultiplied BGRA) back to RGB, even though they're internally encoded +// differently. To enable this conversion, call +// stbi_convert_iphone_png_to_rgb(1). // // Call stbi_set_unpremultiply_on_load(1) as well to force a divide per // pixel to remove any premultiplied alpha *only* if the image file explicitly @@ -518,6 +517,8 @@ STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); // as above, but only applies to images loaded on the thread that calls the function // this function is only available if your compiler supports thread-local variables; // calling it will fail to link if your compiler doesn't +STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply); +STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert); STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip); // ZLIB client - used by PNG, available for other purposes @@ -3821,6 +3822,10 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp else decode_n = z->s->img_n; + // nothing to do if no components requested; check this now to avoid + // accessing uninitialized coutput[0] later + if (decode_n <= 0) { stbi__cleanup_jpeg(z); return NULL; } + // resample and color-convert { int k; @@ -4923,19 +4928,46 @@ static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int return 1; } -static int stbi__unpremultiply_on_load = 0; -static int stbi__de_iphone_flag = 0; +static int stbi__unpremultiply_on_load_global = 0; +static int stbi__de_iphone_flag_global = 0; STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) { - stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply; + stbi__unpremultiply_on_load_global = flag_true_if_should_unpremultiply; } STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) { - stbi__de_iphone_flag = flag_true_if_should_convert; + stbi__de_iphone_flag_global = flag_true_if_should_convert; } +#ifndef STBI_THREAD_LOCAL +#define stbi__unpremultiply_on_load stbi__unpremultiply_on_load_global +#define stbi__de_iphone_flag stbi__de_iphone_flag_global +#else +static STBI_THREAD_LOCAL int stbi__unpremultiply_on_load_local, stbi__unpremultiply_on_load_set; +static STBI_THREAD_LOCAL int stbi__de_iphone_flag_local, stbi__de_iphone_flag_set; + +STBIDEF void stbi__unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply; + stbi__unpremultiply_on_load_set = 1; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag_local = flag_true_if_should_convert; + stbi__de_iphone_flag_set = 1; +} + +#define stbi__unpremultiply_on_load (stbi__unpremultiply_on_load_set \ + ? stbi__unpremultiply_on_load_local \ + : stbi__unpremultiply_on_load_global) +#define stbi__de_iphone_flag (stbi__de_iphone_flag_set \ + ? stbi__de_iphone_flag_local \ + : stbi__de_iphone_flag_global) +#endif // STBI_THREAD_LOCAL + static void stbi__de_iphone(stbi__png *z) { stbi__context *s = z->s; @@ -5316,6 +5348,32 @@ typedef struct int extra_read; } stbi__bmp_data; +static int stbi__bmp_set_mask_defaults(stbi__bmp_data *info, int compress) +{ + // BI_BITFIELDS specifies masks explicitly, don't override + if (compress == 3) + return 1; + + if (compress == 0) { + if (info->bpp == 16) { + info->mr = 31u << 10; + info->mg = 31u << 5; + info->mb = 31u << 0; + } else if (info->bpp == 32) { + info->mr = 0xffu << 16; + info->mg = 0xffu << 8; + info->mb = 0xffu << 0; + info->ma = 0xffu << 24; + info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 + } else { + // otherwise, use defaults, which is all-0 + info->mr = info->mg = info->mb = info->ma = 0; + } + return 1; + } + return 0; // error +} + static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) { int hsz; @@ -5343,6 +5401,8 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) if (hsz != 12) { int compress = stbi__get32le(s); if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); + if (compress >= 4) return stbi__errpuc("BMP JPEG/PNG", "BMP type not supported: unsupported compression"); // this includes PNG/JPEG modes + if (compress == 3 && info->bpp != 16 && info->bpp != 32) return stbi__errpuc("bad BMP", "bad BMP"); // bitfields requires 16 or 32 bits/pixel stbi__get32le(s); // discard sizeof stbi__get32le(s); // discard hres stbi__get32le(s); // discard vres @@ -5357,17 +5417,7 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) } if (info->bpp == 16 || info->bpp == 32) { if (compress == 0) { - if (info->bpp == 32) { - info->mr = 0xffu << 16; - info->mg = 0xffu << 8; - info->mb = 0xffu << 0; - info->ma = 0xffu << 24; - info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 - } else { - info->mr = 31u << 10; - info->mg = 31u << 5; - info->mb = 31u << 0; - } + stbi__bmp_set_mask_defaults(info, compress); } else if (compress == 3) { info->mr = stbi__get32le(s); info->mg = stbi__get32le(s); @@ -5382,6 +5432,7 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) return stbi__errpuc("bad BMP", "bad BMP"); } } else { + // V4/V5 header int i; if (hsz != 108 && hsz != 124) return stbi__errpuc("bad BMP", "bad BMP"); @@ -5389,6 +5440,8 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) info->mg = stbi__get32le(s); info->mb = stbi__get32le(s); info->ma = stbi__get32le(s); + if (compress != 3) // override mr/mg/mb unless in BI_BITFIELDS mode, as per docs + stbi__bmp_set_mask_defaults(info, compress); stbi__get32le(s); // discard color space for (i=0; i < 12; ++i) stbi__get32le(s); // discard color space parameters @@ -6862,9 +6915,10 @@ static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, } if (delays) { - *delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers ); - if (!*delays) + int *new_delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers ); + if (!new_delays) return stbi__load_gif_main_outofmem(&g, out, delays); + *delays = new_delays; delays_size = layers * sizeof(int); } } else { @@ -7200,9 +7254,10 @@ static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) info.all_a = 255; p = stbi__bmp_parse_header(s, &info); - stbi__rewind( s ); - if (p == NULL) + if (p == NULL) { + stbi__rewind( s ); return 0; + } if (x) *x = s->img_x; if (y) *y = s->img_y; if (comp) { @@ -7474,7 +7529,6 @@ static int stbi__pnm_is16(stbi__context *s) return 1; return 0; } - #endif static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) diff --git a/stb_image_write.h b/stb_image_write.h index 6889b8d..271d4ac 100644 --- a/stb_image_write.h +++ b/stb_image_write.h @@ -178,7 +178,7 @@ STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality); -#ifdef STBI_WINDOWS_UTF8 +#ifdef STBIW_WINDOWS_UTF8 STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); #endif #endif @@ -285,7 +285,7 @@ static void stbi__stdio_write(void *context, void *data, int size) fwrite(data,1,size,(FILE*) context); } -#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) +#if defined(_WIN32) && defined(STBIW_WINDOWS_UTF8) #ifdef __cplusplus #define STBIW_EXTERN extern "C" #else @@ -296,25 +296,25 @@ STBIW_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned in STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) { - return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); + return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); } #endif static FILE *stbiw__fopen(char const *filename, char const *mode) { FILE *f; -#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) +#if defined(_WIN32) && defined(STBIW_WINDOWS_UTF8) wchar_t wMode[64]; wchar_t wFilename[1024]; - if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename))) + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename))) return 0; - if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode))) + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode))) return 0; #if defined(_MSC_VER) && _MSC_VER >= 1400 - if (0 != _wfopen_s(&f, wFilename, wMode)) - f = 0; + if (0 != _wfopen_s(&f, wFilename, wMode)) + f = 0; #else f = _wfopen(wFilename, wMode); #endif @@ -622,6 +622,8 @@ STBIWDEF int stbi_write_tga(char const *filename, int x, int y, int comp, const #define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) +#ifndef STBI_WRITE_NO_STDIO + static void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) { int exponent; @@ -756,7 +758,7 @@ static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, f char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"; s->func(s->context, header, sizeof(header)-1); -#ifdef __STDC_WANT_SECURE_LIB__ +#ifdef __STDC_LIB_EXT1__ len = sprintf_s(buffer, sizeof(buffer), "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); #else len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); @@ -777,7 +779,6 @@ STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, return stbi_write_hdr_core(&s, x, y, comp, (float *) data); } -#ifndef STBI_WRITE_NO_STDIO STBIWDEF int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) { stbi__write_context s = { 0 }; @@ -1635,7 +1636,7 @@ STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const add HDR output fix monochrome BMP 0.95 (2014-08-17) - add monochrome TGA output + add monochrome TGA output 0.94 (2014-05-31) rename private functions to avoid conflicts with stb_image.h 0.93 (2014-05-27) diff --git a/stb_textedit.h b/stb_textedit.h index 41afb9e..5bb3a57 100644 --- a/stb_textedit.h +++ b/stb_textedit.h @@ -1,4 +1,4 @@ -// stb_textedit.h - v1.13 - public domain - Sean Barrett +// stb_textedit.h - v1.14 - public domain - Sean Barrett // Development of this library was sponsored by RAD Game Tools // // This C header file implements the guts of a multi-line text-editing @@ -29,6 +29,7 @@ // // VERSION HISTORY // +// 1.14 ( ) page up/down, various fixes // 1.13 (2019-02-07) fix bug in undo size management // 1.12 (2018-01-29) user can change STB_TEXTEDIT_KEYTYPE, fix redo to avoid crash // 1.11 (2017-03-03) fix HOME on last line, dragging off single-line textfield @@ -52,6 +53,7 @@ // Ulf Winklemann: move-by-word in 1.1 // Fabian Giesen: secondary key inputs in 1.5 // Martins Mozeiko: STB_TEXTEDIT_memmove in 1.6 +// Louis Schnellbach: page up/down in 1.14 // // Bugfixes: // Scott Graham @@ -142,6 +144,8 @@ // STB_TEXTEDIT_K_RIGHT keyboard input to move cursor right // STB_TEXTEDIT_K_UP keyboard input to move cursor up // STB_TEXTEDIT_K_DOWN keyboard input to move cursor down +// STB_TEXTEDIT_K_PGUP keyboard input to move cursor up a page +// STB_TEXTEDIT_K_PGDOWN keyboard input to move cursor down a page // STB_TEXTEDIT_K_LINESTART keyboard input to move cursor to start of line // e.g. HOME // STB_TEXTEDIT_K_LINEEND keyboard input to move cursor to end of line // e.g. END // STB_TEXTEDIT_K_TEXTSTART keyboard input to move cursor to start of text // e.g. ctrl-HOME @@ -164,10 +168,6 @@ // STB_TEXTEDIT_K_TEXTSTART2 secondary keyboard input to move cursor to start of text // STB_TEXTEDIT_K_TEXTEND2 secondary keyboard input to move cursor to end of text // -// Todo: -// STB_TEXTEDIT_K_PGUP keyboard input to move cursor up a page -// STB_TEXTEDIT_K_PGDOWN keyboard input to move cursor down a page -// // Keyboard input must be encoded as a single integer value; e.g. a character code // and some bitflags that represent shift states. to simplify the interface, SHIFT must // be a bitflag, so we can test the shifted state of cursor movements to allow selection, @@ -331,6 +331,10 @@ typedef struct // each textfield keeps its own insert mode state. to keep an app-wide // insert mode, copy this value in/out of the app state + int row_count_per_page; + // page size in number of row. + // this value MUST be set to >0 for pageup or pagedown in multilines documents. + ///////////////////// // // private data @@ -708,9 +712,7 @@ static int stb_textedit_paste_internal(STB_TEXTEDIT_STRING *str, STB_TexteditSta state->has_preferred_x = 0; return 1; } - // remove the undo since we didn't actually insert the characters - if (state->undostate.undo_point) - --state->undostate.undo_point; + // note: paste failure will leave deleted selection, may be restored with an undo (see https://github.com/nothings/stb/issues/734 for details) return 0; } @@ -849,12 +851,16 @@ retry: break; case STB_TEXTEDIT_K_DOWN: - case STB_TEXTEDIT_K_DOWN | STB_TEXTEDIT_K_SHIFT: { + case STB_TEXTEDIT_K_DOWN | STB_TEXTEDIT_K_SHIFT: + case STB_TEXTEDIT_K_PGDOWN: + case STB_TEXTEDIT_K_PGDOWN | STB_TEXTEDIT_K_SHIFT: { StbFindState find; StbTexteditRow row; - int i, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0; + int i, j, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0; + int is_page = (key & ~STB_TEXTEDIT_K_SHIFT) == STB_TEXTEDIT_K_PGDOWN; + int row_count = is_page ? state->row_count_per_page : 1; - if (state->single_line) { + if (!is_page && state->single_line) { // on windows, up&down in single-line behave like left&right key = STB_TEXTEDIT_K_RIGHT | (key & STB_TEXTEDIT_K_SHIFT); goto retry; @@ -863,17 +869,20 @@ retry: if (sel) stb_textedit_prep_selection_at_cursor(state); else if (STB_TEXT_HAS_SELECTION(state)) - stb_textedit_move_to_last(str,state); + stb_textedit_move_to_last(str, state); // compute current position of cursor point stb_textedit_clamp(str, state); stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); - // now find character position down a row - if (find.length) { - float goal_x = state->has_preferred_x ? state->preferred_x : find.x; - float x; + for (j = 0; j < row_count; ++j) { + float x, goal_x = state->has_preferred_x ? state->preferred_x : find.x; int start = find.first_char + find.length; + + if (find.length == 0) + break; + + // now find character position down a row state->cursor = start; STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor); x = row.x0; @@ -895,17 +904,25 @@ retry: if (sel) state->select_end = state->cursor; + + // go to next line + find.first_char = find.first_char + find.length; + find.length = row.num_chars; } break; } case STB_TEXTEDIT_K_UP: - case STB_TEXTEDIT_K_UP | STB_TEXTEDIT_K_SHIFT: { + case STB_TEXTEDIT_K_UP | STB_TEXTEDIT_K_SHIFT: + case STB_TEXTEDIT_K_PGUP: + case STB_TEXTEDIT_K_PGUP | STB_TEXTEDIT_K_SHIFT: { StbFindState find; StbTexteditRow row; - int i, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0; + int i, j, prev_scan, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0; + int is_page = (key & ~STB_TEXTEDIT_K_SHIFT) == STB_TEXTEDIT_K_PGUP; + int row_count = is_page ? state->row_count_per_page : 1; - if (state->single_line) { + if (!is_page && state->single_line) { // on windows, up&down become left&right key = STB_TEXTEDIT_K_LEFT | (key & STB_TEXTEDIT_K_SHIFT); goto retry; @@ -920,11 +937,14 @@ retry: stb_textedit_clamp(str, state); stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); - // can only go up if there's a previous row - if (find.prev_first != find.first_char) { + for (j = 0; j < row_count; ++j) { + float x, goal_x = state->has_preferred_x ? state->preferred_x : find.x; + + // can only go up if there's a previous row + if (find.prev_first == find.first_char) + break; + // now find character position up a row - float goal_x = state->has_preferred_x ? state->preferred_x : find.x; - float x; state->cursor = find.prev_first; STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor); x = row.x0; @@ -946,6 +966,14 @@ retry: if (sel) state->select_end = state->cursor; + + // go to previous line + // (we need to scan previous line the hard way. maybe we could expose this as a new API function?) + prev_scan = find.prev_first > 0 ? find.prev_first - 1 : 0; + while (prev_scan > 0 && STB_TEXTEDIT_GETCHAR(str, prev_scan - 1) != STB_TEXTEDIT_NEWLINE) + --prev_scan; + find.first_char = find.prev_first; + find.prev_first = prev_scan; } break; } @@ -1069,10 +1097,6 @@ retry: state->has_preferred_x = 0; break; } - -// @TODO: -// STB_TEXTEDIT_K_PGUP - move cursor up a page -// STB_TEXTEDIT_K_PGDOWN - move cursor down a page } } @@ -1337,6 +1361,7 @@ static void stb_textedit_clear_state(STB_TexteditState *state, int is_single_lin state->initialized = 1; state->single_line = (unsigned char) is_single_line; state->insert_mode = 0; + state->row_count_per_page = 0; } // API initialize diff --git a/stb_tilemap_editor.h b/stb_tilemap_editor.h index 9b77364..fbd3388 100644 --- a/stb_tilemap_editor.h +++ b/stb_tilemap_editor.h @@ -1,4 +1,4 @@ -// stb_tilemap_editor.h - v0.41 - Sean Barrett - http://nothings.org/stb +// stb_tilemap_editor.h - v0.42 - Sean Barrett - http://nothings.org/stb // placed in the public domain - not copyrighted - first released 2014-09 // // Embeddable tilemap editor for C/C++ @@ -275,6 +275,7 @@ // either approach allows cut&pasting between levels.) // // REVISION HISTORY +// 0.42 fix compilation errors // 0.41 fix warnings // 0.40 fix warning // 0.39 fix warning @@ -317,6 +318,8 @@ // Bugfixes: // Ryan Whitworth // Eugene Opalev +// Rob Loach +// github:wernsey // // LICENSE // @@ -1821,6 +1824,8 @@ static int stbte__button(int colormode, const char *label, int x, int y, int tex int x0=x,y0=y, x1=x+width,y1=y+STBTE__BUTTON_HEIGHT; int s = STBTE__BUTTON_INTERNAL_SPACING; + if(!disabled) stbte__hittest(x0,y0,x1,y1,id); + if (stbte__ui.event == STBTE__paint) stbte__draw_textbox(x0,y0,x1,y1, (char*) label,s+textoff,s, colormode, STBTE__INDEX_FOR_ID(id,disabled,toggled)); if (disabled) @@ -1833,6 +1838,8 @@ static int stbte__button_icon(int colormode, char ch, int x, int y, int width, i int x0=x,y0=y, x1=x+width,y1=y+STBTE__BUTTON_HEIGHT; int s = STBTE__BUTTON_INTERNAL_SPACING; + stbte__hittest(x0,y0,x1,y1,id); + if (stbte__ui.event == STBTE__paint) { char label[2] = { ch, 0 }; int pad = (9 - stbte__get_char_width(ch))/2; @@ -1846,6 +1853,7 @@ static int stbte__button_icon(int colormode, char ch, int x, int y, int width, i static int stbte__minibutton(int colormode, int x, int y, int ch, int id) { int x0 = x, y0 = y, x1 = x+8, y1 = y+7; + stbte__hittest(x0,y0,x1,y1,id); if (stbte__ui.event == STBTE__paint) { char str[2] = { (char)ch, 0 }; stbte__draw_textbox(x0,y0,x1,y1, str,1,0,colormode, STBTE__INDEX_FOR_ID(id,0,0)); @@ -1856,6 +1864,7 @@ static int stbte__minibutton(int colormode, int x, int y, int ch, int id) static int stbte__layerbutton(int x, int y, int ch, int id, int toggled, int disabled, int colormode) { int x0 = x, y0 = y, x1 = x+10, y1 = y+11; + if(!disabled) stbte__hittest(x0,y0,x1,y1,id); if (stbte__ui.event == STBTE__paint) { char str[2] = { (char)ch, 0 }; int off = (9-stbte__get_char_width(ch))/2; @@ -1869,6 +1878,7 @@ static int stbte__layerbutton(int x, int y, int ch, int id, int toggled, int dis static int stbte__microbutton(int x, int y, int size, int id, int colormode) { int x0 = x, y0 = y, x1 = x+size, y1 = y+size; + stbte__hittest(x0,y0,x1,y1,id); if (stbte__ui.event == STBTE__paint) { stbte__draw_box(x0,y0,x1,y1, colormode, STBTE__INDEX_FOR_ID(id,0,0)); } @@ -1878,6 +1888,7 @@ static int stbte__microbutton(int x, int y, int size, int id, int colormode) static int stbte__microbutton_dragger(int x, int y, int size, int id, int *pos) { int x0 = x, y0 = y, x1 = x+size, y1 = y+size; + stbte__hittest(x0,y0,x1,y1,id); switch (stbte__ui.event) { case STBTE__paint: stbte__draw_box(x0,y0,x1,y1, STBTE__cexpander, STBTE__INDEX_FOR_ID(id,0,0)); @@ -1908,6 +1919,8 @@ static int stbte__category_button(const char *label, int x, int y, int width, in int x0=x,y0=y, x1=x+width,y1=y+STBTE__BUTTON_HEIGHT; int s = STBTE__BUTTON_INTERNAL_SPACING; + stbte__hittest(x0,y0,x1,y1,id); + if (stbte__ui.event == STBTE__paint) stbte__draw_textbox(x0,y0,x1,y1, (char*) label, s,s, STBTE__ccategory_button, STBTE__INDEX_FOR_ID(id,0,toggled)); @@ -1927,6 +1940,7 @@ static int stbte__slider(int x0, int w, int y, int range, int *value, int id) { int x1 = x0+w; int pos = *value * w / (range+1); + stbte__hittest(x0,y-2,x1,y+3,id); int event_mouse_move = STBTE__change; switch (stbte__ui.event) { case STBTE__paint: @@ -1969,6 +1983,7 @@ static int stbte__float_control(int x0, int y0, int w, float minv, float maxv, f { int x1 = x0+w; int y1 = y0+11; + stbte__hittest(x0,y0,x1,y1,id); switch (stbte__ui.event) { case STBTE__paint: { char text[32]; @@ -1980,7 +1995,7 @@ static int stbte__float_control(int x0, int y0, int w, float minv, float maxv, f case STBTE__rightdown: if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) stbte__activate(id); - return STBTE__begin; + return STBTE__begin; break; case STBTE__leftup: case STBTE__rightup: @@ -2020,7 +2035,6 @@ static int stbte__float_control(int x0, int y0, int w, float minv, float maxv, f static void stbte__scrollbar(int x, int y0, int y1, int *val, int v0, int v1, int num_vis, int id) { - int over; int thumbpos; if (v1 - v0 <= num_vis) return; @@ -2029,7 +2043,7 @@ static void stbte__scrollbar(int x, int y0, int y1, int *val, int v0, int v1, in thumbpos = y0+2 + (y1-y0-4) * *val / (v1 - v0 - num_vis); if (thumbpos < y0) thumbpos = y0; if (thumbpos >= y1) thumbpos = y1; - over = stbte__hittest(x-1,y0,x+2,y1,id); + stbte__hittest(x-1,y0,x+2,y1,id); switch (stbte__ui.event) { case STBTE__paint: stbte__draw_rect(x,y0,x+1,y1, stbte__color_table[STBTE__cscrollbar][STBTE__text][STBTE__idle]); @@ -2807,6 +2821,10 @@ static void stbte__drag_update(stbte_tilemap *tm, int mapx, int mapy, int copy_p int ox,oy,i,deleted=0,written=0; short temp[STBTE_MAX_LAYERS]; short *data = NULL; + + STBTE__NOTUSED(deleted); + STBTE__NOTUSED(written); + if (!stbte__ui.shift) { ox = mapx - stbte__ui.drag_x; oy = mapy - stbte__ui.drag_y; @@ -2928,6 +2946,9 @@ static void stbte__tile_paint(stbte_tilemap *tm, int sx, int sy, int mapx, int m { int i; int id = STBTE__IDMAP(mapx,mapy); + int x0=sx, y0=sy; + int x1=sx+tm->spacing_x, y1=sy+tm->spacing_y; + stbte__hittest(x0,y0,x1,y1, id); short *data = tm->data[mapy][mapx]; short temp[STBTE_MAX_LAYERS]; @@ -2996,7 +3017,7 @@ static void stbte__tile_paint(stbte_tilemap *tm, int sx, int sy, int mapx, int m i = layer; if (i == tm->solo_layer || (!tm->layerinfo[i].hidden && tm->solo_layer < 0)) if (data[i] >= 0) - STBTE_DRAW_TILE(x0,y0, (unsigned short) data[i], 0, tm->props[mapy][mapx]); + STBTE_DRAW_TILE(sx,sy, (unsigned short) data[i], 0, tm->props[mapy][mapx]); } } @@ -3492,11 +3513,14 @@ static void stbte__categories(stbte_tilemap *tm, int x0, int y0, int w, int h) static void stbte__tile_in_palette(stbte_tilemap *tm, int x, int y, int slot) { + stbte__tileinfo *t = &tm->tiles[slot]; + int x0=x, y0=y, x1 = x+tm->palette_spacing_x - 1, y1 = y+tm->palette_spacing_y; int id = STBTE__ID(STBTE__palette, slot); + stbte__hittest(x0,y0,x1,y1, id); switch (stbte__ui.event) { case STBTE__paint: stbte__draw_rect(x,y,x+tm->palette_spacing_x-1,y+tm->palette_spacing_x-1, STBTE_COLOR_TILEPALETTE_BACKGROUND); - STBTE_DRAW_TILE(x,y,t->id, slot == tm->cur_tile,0); + STBTE_DRAW_TILE(x,y,id, slot == tm->cur_tile,0); if (slot == tm->cur_tile) stbte__draw_frame_delayed(x-1,y-1,x+tm->palette_spacing_x,y+tm->palette_spacing_y, STBTE_COLOR_TILEPALETTE_OUTLINE); break; @@ -3565,6 +3589,7 @@ static void stbte__props_panel(stbte_tilemap *tm, int x0, int y0, int w, int h) my = stbte__ui.select_y0; p = tm->props[my][mx]; data = tm->data[my][mx]; + STBTE__NOTUSED(data); for (i=0; i < STBTE_MAX_PROPERTIES; ++i) { unsigned int n = STBTE_PROP_TYPE(i, data, p); if (n) { @@ -3644,8 +3669,9 @@ static void stbte__props_panel(stbte_tilemap *tm, int x0, int y0, int w, int h) } } -static int stbte__cp_mode, stbte__cp_aspect, stbte__cp_state, stbte__cp_index, stbte__save, stbte__cp_altered, stbte__color_copy; +static int stbte__cp_mode, stbte__cp_aspect, stbte__save, stbte__cp_altered; #ifdef STBTE__COLORPICKER +static int stbte__cp_state, stbte__cp_index, stbte__color_copy; static void stbte__dump_colorstate(void) { int i,j,k; diff --git a/stb_truetype.h b/stb_truetype.h index e06b867..c3c4f8e 100644 --- a/stb_truetype.h +++ b/stb_truetype.h @@ -2454,8 +2454,6 @@ static stbtt_int32 stbtt__GetGlyphClass(stbtt_uint8 *classDefTable, int glyph) if (glyph >= startGlyphID && glyph < startGlyphID + glyphCount) return (stbtt_int32)ttUSHORT(classDef1ValueArray + 2 * (glyph - startGlyphID)); - - classDefTable = classDef1ValueArray + 2 * glyphCount; } break; case 2: { @@ -2478,8 +2476,6 @@ static stbtt_int32 stbtt__GetGlyphClass(stbtt_uint8 *classDefTable, int glyph) else return (stbtt_int32)ttUSHORT(classRangeRecord + 4); } - - classDefTable = classRangeRecords + 6 * classRangeCount; } break; default: { @@ -4486,35 +4482,35 @@ static float stbtt__cuberoot( float x ) return (float) STBTT_pow( x,1.0f/3.0f); } -// x^3 + c*x^2 + b*x + a = 0 +// x^3 + a*x^2 + b*x + c = 0 static int stbtt__solve_cubic(float a, float b, float c, float* r) { - float s = -a / 3; - float p = b - a*a / 3; - float q = a * (2*a*a - 9*b) / 27 + c; + float s = -a / 3; + float p = b - a*a / 3; + float q = a * (2*a*a - 9*b) / 27 + c; float p3 = p*p*p; - float d = q*q + 4*p3 / 27; - if (d >= 0) { - float z = (float) STBTT_sqrt(d); - float u = (-q + z) / 2; - float v = (-q - z) / 2; - u = stbtt__cuberoot(u); - v = stbtt__cuberoot(v); - r[0] = s + u + v; - return 1; - } else { - float u = (float) STBTT_sqrt(-p/3); - float v = (float) STBTT_acos(-STBTT_sqrt(-27/p3) * q / 2) / 3; // p3 must be negative, since d is negative - float m = (float) STBTT_cos(v); + float d = q*q + 4*p3 / 27; + if (d >= 0) { + float z = (float) STBTT_sqrt(d); + float u = (-q + z) / 2; + float v = (-q - z) / 2; + u = stbtt__cuberoot(u); + v = stbtt__cuberoot(v); + r[0] = s + u + v; + return 1; + } else { + float u = (float) STBTT_sqrt(-p/3); + float v = (float) STBTT_acos(-STBTT_sqrt(-27/p3) * q / 2) / 3; // p3 must be negative, since d is negative + float m = (float) STBTT_cos(v); float n = (float) STBTT_cos(v-3.141592/2)*1.732050808f; - r[0] = s + u * 2 * m; - r[1] = s - u * (m + n); - r[2] = s - u * (m - n); + r[0] = s + u * 2 * m; + r[1] = s - u * (m + n); + r[2] = s - u * (m - n); //STBTT_assert( STBTT_fabs(((r[0]+a)*r[0]+b)*r[0]+c) < 0.05f); // these asserts may not be safe at all scales, though they're in bezier t parameter units so maybe? //STBTT_assert( STBTT_fabs(((r[1]+a)*r[1]+b)*r[1]+c) < 0.05f); //STBTT_assert( STBTT_fabs(((r[2]+a)*r[2]+b)*r[2]+c) < 0.05f); - return 3; + return 3; } } diff --git a/stb_vorbis.c b/stb_vorbis.c index a8cbfa6..534e689 100644 --- a/stb_vorbis.c +++ b/stb_vorbis.c @@ -1,4 +1,4 @@ -// Ogg Vorbis audio decoder - v1.20 - public domain +// Ogg Vorbis audio decoder - v1.22 - public domain // http://nothings.org/stb_vorbis/ // // Original version written by Sean Barrett in 2007. @@ -29,12 +29,15 @@ // Bernhard Wodo Evan Balster github:alxprd // Tom Beaumont Ingo Leitgeb Nicolas Guillemot // Phillip Bennefall Rohit Thiago Goulart -// github:manxorist saga musix github:infatum +// github:manxorist Saga Musix github:infatum // Timur Gagiev Maxwell Koo Peter Waller // github:audinowho Dougall Johnson David Reid // github:Clownacy Pedro J. Estebanez Remi Verschelde +// AnthoFoxo github:morlat Gabriel Ravier // // Partial history: +// 1.22 - - various small fixes +// 1.21 - 2021-07-02 - fix bug for files with no comments // 1.20 - 2020-07-11 - several small fixes // 1.19 - 2020-02-05 - warnings // 1.18 - 2020-02-02 - fix seek bugs; parse header comments; misc warnings etc. @@ -220,6 +223,12 @@ extern int stb_vorbis_decode_frame_pushdata( // channel. In other words, (*output)[0][0] contains the first sample from // the first channel, and (*output)[1][0] contains the first sample from // the second channel. +// +// *output points into stb_vorbis's internal output buffer storage; these +// buffers are owned by stb_vorbis and application code should not free +// them or modify their contents. They are transient and will be overwritten +// once you ask for more data to get decoded, so be sure to grab any data +// you need before then. extern void stb_vorbis_flush_pushdata(stb_vorbis *f); // inform stb_vorbis that your next datablock will not be contiguous with @@ -579,7 +588,7 @@ enum STBVorbisError #if defined(_MSC_VER) || defined(__MINGW32__) #include #endif - #if defined(__linux__) || defined(__linux) || defined(__EMSCRIPTEN__) || defined(__NEWLIB__) + #if defined(__linux__) || defined(__linux) || defined(__sun__) || defined(__EMSCRIPTEN__) || defined(__NEWLIB__) #include #endif #else // STB_VORBIS_NO_CRT @@ -646,6 +655,12 @@ typedef signed int int32; typedef float codetype; +#ifdef _MSC_VER +#define STBV_NOTUSED(v) (void)(v) +#else +#define STBV_NOTUSED(v) (void)sizeof(v) +#endif + // @NOTE // // Some arrays below are tagged "//varies", which means it's actually @@ -1046,7 +1061,7 @@ static float float32_unpack(uint32 x) uint32 sign = x & 0x80000000; uint32 exp = (x & 0x7fe00000) >> 21; double res = sign ? -(double)mantissa : (double)mantissa; - return (float) ldexp((float)res, exp-788); + return (float) ldexp((float)res, (int)exp-788); } @@ -1077,6 +1092,7 @@ static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) // find the first entry for (k=0; k < n; ++k) if (len[k] < NO_CODE) break; if (k == n) { assert(c->sorted_entries == 0); return TRUE; } + assert(len[k] < 32); // no error return required, code reading lens checks this // add to the list add_entry(c, 0, k, m++, len[k], values); // add all available leaves @@ -1090,6 +1106,7 @@ static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) uint32 res; int z = len[i], y; if (z == NO_CODE) continue; + assert(z < 32); // no error return required, code reading lens checks this // find lowest available leaf (should always be earliest, // which is what the specification calls for) // note that this property, and the fact we can never have @@ -1099,12 +1116,10 @@ static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) while (z > 0 && !available[z]) --z; if (z == 0) { return FALSE; } res = available[z]; - assert(z >= 0 && z < 32); available[z] = 0; add_entry(c, bit_reverse(res), i, m++, len[i], values); // propagate availability up the tree if (z != len[i]) { - assert(len[i] >= 0 && len[i] < 32); for (y=len[i]; y > z; --y) { assert(available[y] == 0); available[y] = res + (1 << (32-y)); @@ -2577,34 +2592,33 @@ static void imdct_step3_inner_s_loop_ld654(int n, float *e, int i_off, float *A, while (z > base) { float k00,k11; + float l00,l11; - k00 = z[-0] - z[-8]; - k11 = z[-1] - z[-9]; - z[-0] = z[-0] + z[-8]; - z[-1] = z[-1] + z[-9]; - z[-8] = k00; - z[-9] = k11 ; + k00 = z[-0] - z[ -8]; + k11 = z[-1] - z[ -9]; + l00 = z[-2] - z[-10]; + l11 = z[-3] - z[-11]; + z[ -0] = z[-0] + z[ -8]; + z[ -1] = z[-1] + z[ -9]; + z[ -2] = z[-2] + z[-10]; + z[ -3] = z[-3] + z[-11]; + z[ -8] = k00; + z[ -9] = k11; + z[-10] = (l00+l11) * A2; + z[-11] = (l11-l00) * A2; - k00 = z[ -2] - z[-10]; - k11 = z[ -3] - z[-11]; - z[ -2] = z[ -2] + z[-10]; - z[ -3] = z[ -3] + z[-11]; - z[-10] = (k00+k11) * A2; - z[-11] = (k11-k00) * A2; - - k00 = z[-12] - z[ -4]; // reverse to avoid a unary negation + k00 = z[ -4] - z[-12]; k11 = z[ -5] - z[-13]; + l00 = z[ -6] - z[-14]; + l11 = z[ -7] - z[-15]; z[ -4] = z[ -4] + z[-12]; z[ -5] = z[ -5] + z[-13]; - z[-12] = k11; - z[-13] = k00; - - k00 = z[-14] - z[ -6]; // reverse to avoid a unary negation - k11 = z[ -7] - z[-15]; z[ -6] = z[ -6] + z[-14]; z[ -7] = z[ -7] + z[-15]; - z[-14] = (k00+k11) * A2; - z[-15] = (k00-k11) * A2; + z[-12] = k11; + z[-13] = -k00; + z[-14] = (l11-l00) * A2; + z[-15] = (l00+l11) * -A2; iter_54(z); iter_54(z-8); @@ -3069,6 +3083,7 @@ static int do_floor(vorb *f, Mapping *map, int i, int n, float *target, YTYPE *f for (q=1; q < g->values; ++q) { j = g->sorted_order[q]; #ifndef STB_VORBIS_NO_DEFER_FLOOR + STBV_NOTUSED(step2_flag); if (finalY[j] >= 0) #else if (step2_flag[j]) @@ -3171,6 +3186,7 @@ static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start, // WINDOWING + STBV_NOTUSED(left_end); n = f->blocksize[m->blockflag]; map = &f->mapping[m->mapping]; @@ -3368,7 +3384,7 @@ static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start, // this isn't to spec, but spec would require us to read ahead // and decode the size of all current frames--could be done, // but presumably it's not a commonly used feature - f->current_loc = -n2; // start of first frame is positioned for discard + f->current_loc = 0u - n2; // start of first frame is positioned for discard (NB this is an intentional unsigned overflow/wrap-around) // we might have to discard samples "from" the next frame too, // if we're lapping a large block then a small at the start? f->discard_samples_deferred = n - right_end; @@ -3642,8 +3658,12 @@ static int start_decoder(vorb *f) f->vendor[len] = (char)'\0'; //user comments f->comment_list_length = get32_packet(f); - f->comment_list = (char**)setup_malloc(f, sizeof(char*) * (f->comment_list_length)); - if (f->comment_list == NULL) return error(f, VORBIS_outofmem); + f->comment_list = NULL; + if (f->comment_list_length > 0) + { + f->comment_list = (char**) setup_malloc(f, sizeof(char*) * (f->comment_list_length)); + if (f->comment_list == NULL) return error(f, VORBIS_outofmem); + } for(i=0; i < f->comment_list_length; ++i) { len = get32_packet(f); @@ -3865,8 +3885,7 @@ static int start_decoder(vorb *f) unsigned int div=1; for (k=0; k < c->dimensions; ++k) { int off = (z / div) % c->lookup_values; - float val = mults[off]; - val = mults[off]*c->delta_value + c->minimum_value + last; + float val = mults[off]*c->delta_value + c->minimum_value + last; c->multiplicands[j*c->dimensions + k] = val; if (c->sequence_p) last = val; @@ -3949,7 +3968,7 @@ static int start_decoder(vorb *f) if (g->class_masterbooks[j] >= f->codebook_count) return error(f, VORBIS_invalid_setup); } for (k=0; k < 1 << g->class_subclasses[j]; ++k) { - g->subclass_books[j][k] = get_bits(f,8)-1; + g->subclass_books[j][k] = (int16)get_bits(f,8)-1; if (g->subclass_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup); } } @@ -4507,6 +4526,7 @@ stb_vorbis *stb_vorbis_open_pushdata( *error = VORBIS_need_more_data; else *error = p.error; + vorbis_deinit(&p); return NULL; } f = vorbis_alloc(&p); @@ -4564,7 +4584,7 @@ static uint32 vorbis_find_page(stb_vorbis *f, uint32 *end, uint32 *last) header[i] = get8(f); if (f->eof) return 0; if (header[4] != 0) goto invalid; - goal = header[22] + (header[23] << 8) + (header[24]<<16) + (header[25]<<24); + goal = header[22] + (header[23] << 8) + (header[24]<<16) + ((uint32)header[25]<<24); for (i=22; i < 26; ++i) header[i] = 0; crc = 0; @@ -4968,7 +4988,7 @@ unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f) // set. whoops! break; } - previous_safe = last_page_loc+1; + //previous_safe = last_page_loc+1; // NOTE: not used after this point, but note for debugging last_page_loc = stb_vorbis_get_file_offset(f); } @@ -5079,7 +5099,10 @@ stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, const st stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, int *error, const stb_vorbis_alloc *alloc) { stb_vorbis *f, p; - if (data == NULL) return NULL; + if (!data) { + if (error) *error = VORBIS_unexpected_eof; + return NULL; + } vorbis_init(&p, alloc); p.stream = (uint8 *) data; p.stream_end = (uint8 *) data + len; @@ -5154,11 +5177,11 @@ static void copy_samples(short *dest, float *src, int len) static void compute_samples(int mask, short *output, int num_c, float **data, int d_offset, int len) { - #define BUFFER_SIZE 32 - float buffer[BUFFER_SIZE]; - int i,j,o,n = BUFFER_SIZE; + #define STB_BUFFER_SIZE 32 + float buffer[STB_BUFFER_SIZE]; + int i,j,o,n = STB_BUFFER_SIZE; check_endianness(); - for (o = 0; o < len; o += BUFFER_SIZE) { + for (o = 0; o < len; o += STB_BUFFER_SIZE) { memset(buffer, 0, sizeof(buffer)); if (o + n > len) n = len - o; for (j=0; j < num_c; ++j) { @@ -5175,16 +5198,17 @@ static void compute_samples(int mask, short *output, int num_c, float **data, in output[o+i] = v; } } + #undef STB_BUFFER_SIZE } static void compute_stereo_samples(short *output, int num_c, float **data, int d_offset, int len) { - #define BUFFER_SIZE 32 - float buffer[BUFFER_SIZE]; - int i,j,o,n = BUFFER_SIZE >> 1; + #define STB_BUFFER_SIZE 32 + float buffer[STB_BUFFER_SIZE]; + int i,j,o,n = STB_BUFFER_SIZE >> 1; // o is the offset in the source data check_endianness(); - for (o = 0; o < len; o += BUFFER_SIZE >> 1) { + for (o = 0; o < len; o += STB_BUFFER_SIZE >> 1) { // o2 is the offset in the output data int o2 = o << 1; memset(buffer, 0, sizeof(buffer)); @@ -5214,6 +5238,7 @@ static void compute_stereo_samples(short *output, int num_c, float **data, int d output[o2+i] = v; } } + #undef STB_BUFFER_SIZE } static void convert_samples_short(int buf_c, short **buffer, int b_offset, int data_c, float **data, int d_offset, int samples) @@ -5286,8 +5311,6 @@ int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short float **outputs; int len = num_shorts / channels; int n=0; - int z = f->channels; - if (z > channels) z = channels; while (n < len) { int k = f->channel_buffer_end - f->channel_buffer_start; if (n+k >= len) k = len - n; @@ -5306,8 +5329,6 @@ int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, in { float **outputs; int n=0; - int z = f->channels; - if (z > channels) z = channels; while (n < len) { int k = f->channel_buffer_end - f->channel_buffer_start; if (n+k >= len) k = len - n; diff --git a/tests/ossfuzz.sh b/tests/ossfuzz.sh index 2af98f5..4b8fd27 100755 --- a/tests/ossfuzz.sh +++ b/tests/ossfuzz.sh @@ -17,9 +17,13 @@ cp $SRC/stb/tests/stb_png.dict $OUT/stb_png_read_fuzzer.dict tar xvzf $SRC/stb/jpg.tar.gz --directory $SRC/stb/tests tar xvzf $SRC/stb/gif.tar.gz --directory $SRC/stb/tests +unzip $SRC/stb/bmp.zip -d $SRC/stb/tests +unzip $SRC/stb/tga.zip -d $SRC/stb/tests -find $SRC/stb/tests -name "*.png" -o -name "*.jpg" -o -name ".gif" | \ - xargs zip $OUT/stbi_read_fuzzer_seed_corpus.zip +find $SRC/stb/tests -name "*.png" -o -name "*.jpg" -o -name "*.gif" \ + -o -name "*.bmp" -o -name "*.tga" -o -name "*.TGA" \ + -o -name "*.ppm" -o -name "*.pgm" \ + | xargs zip $OUT/stbi_read_fuzzer_seed_corpus.zip echo "" >> $SRC/stb/tests/gif.dict cat $SRC/stb/tests/gif.dict $SRC/stb/tests/stb_png.dict > $OUT/stbi_read_fuzzer.dict diff --git a/tests/pbm/basi0g16.pgm b/tests/pbm/basi0g16.pgm new file mode 100644 index 0000000..7241243 Binary files /dev/null and b/tests/pbm/basi0g16.pgm differ diff --git a/tests/pbm/basi2c16.ppm b/tests/pbm/basi2c16.ppm new file mode 100644 index 0000000..f2913bb Binary files /dev/null and b/tests/pbm/basi2c16.ppm differ diff --git a/tests/pbm/cdfn2c08.ppm b/tests/pbm/cdfn2c08.ppm new file mode 100644 index 0000000..1a9e0f0 Binary files /dev/null and b/tests/pbm/cdfn2c08.ppm differ diff --git a/tests/pbm/cdun2c08.ppm b/tests/pbm/cdun2c08.ppm new file mode 100644 index 0000000..2d7202b Binary files /dev/null and b/tests/pbm/cdun2c08.ppm differ diff --git a/tests/pbm/comment.pgm b/tests/pbm/comment.pgm new file mode 100644 index 0000000..aa9dc71 Binary files /dev/null and b/tests/pbm/comment.pgm differ diff --git a/tests/pbm/ctfn0g04.pgm b/tests/pbm/ctfn0g04.pgm new file mode 100644 index 0000000..284f870 Binary files /dev/null and b/tests/pbm/ctfn0g04.pgm differ