diff --git a/Makefile.djgpp b/Makefile.djgpp index 76020b1..e348072 100644 --- a/Makefile.djgpp +++ b/Makefile.djgpp @@ -33,7 +33,7 @@ INSTALL = install DEBUG = -g ## CHANGE THIS ## -TARGET = roo-e +TARGET = roo_e SRCDIR = . OBJDIR = obj BINDIR = bin @@ -41,7 +41,7 @@ BINDIR = bin # CFLAGS, LDFLAGS, CPPFLAGS, PREFIX can be overriden on CLI CFLAGS := $(DEBUG) -CFLAGS += -I$(SRCDIR)/shared -I$(SRCDIR)/roo-e/src +CFLAGS += -I$(SRCDIR)/shared -I$(SRCDIR)/roo_e/src CPPFLAGS := LDFLAGS := LDLIBS := @@ -78,9 +78,8 @@ ALL_LDLIBS := $(LDLIBS) -lc # Source, Binaries, Dependencies -SRC := $(shell find $(SRCDIR)/roo-e -type f -name '*.c') -SRC += $(shell find $(SRCDIR)/shared -type f -name '*.c') -SRC += $(shell find $(SRCDIR)/test -type f -name '*.c') +SRC := $(shell find $(SRCDIR)/roo_e/src -type f -name '*.c') +SRC += $(shell find $(SRCDIR)/test/src -type f -name '*.c') OBJ := $(patsubst $(SRCDIR)/%,$(OBJDIR)/%,$(SRC:.c=.o)) DEP := $(OBJ:.o=.d) BIN := $(BINDIR)/$(TARGET) diff --git a/build.sh b/build.sh index 353fde1..427c3ec 100755 --- a/build.sh +++ b/build.sh @@ -1,4 +1,4 @@ -#!/bin/bash -e +#!/bin/bash -ex # # Roo/E, the Kangaroo Punch Portable GUI Toolkit @@ -24,21 +24,21 @@ # -# This requires DJGPP for Linux in /opt/cross/djgpp -# -# See: https://github.com/andrewwutw/build-djgpp +# This requires Toolchains from: +# https://skunkworks.kangaroopunch.com/skunkworks/toolchains [[ -d obj ]] && rm -rf obj mkdir -p \ - obj/shared/thirdparty/memwatch \ - obj/roo-e/src/platform \ - obj/roo-e/src/gui/widgets \ + obj/roo_e/src/thirdparty/memwatch \ + obj/roo_e/src/platform \ + obj/roo_e/src/gui/widgets \ obj/test/src -source /opt/cross/djgpp/setenv +#source <(../toolchains/toolchains.sh use x86 dos) +source ../toolchains/x-tools/djgpp/setenv make -f Makefile.djgpp -[[ -f bin/roo-e ]] && rm bin/roo-e -[[ -f bin/grx ]] && rm bin/grx +[[ -f bin/roo_e ]] && rm bin/roo_e +[[ -f bin/grx ]] && rm bin/grx diff --git a/font/font.pro b/font/font.pro deleted file mode 100644 index 6921f3b..0000000 --- a/font/font.pro +++ /dev/null @@ -1,45 +0,0 @@ -# -# Kangaroo Punch MultiPlayer Game Server Mark II -# Copyright (C) 2020-2021 Scott Duensing -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# - -TEMPLATE = app -CONFIG -= qt -CONFIG += console - -DESTDIR = $$OUT_PWD/bin -SHARED = $$PWD/../shared -CLIENT = $$PWD/../client/src -SYSTEM = $$CLIENT/system -GUI = $$CLIENT/gui - -DEFINES += FONT_DEBUGGING - -INCLUDEPATH += \ - $$SHARED \ - $$SHARED/thirdparty \ - $$CLIENT \ - $$SYSTEM \ - $$GUI - -HEADERS = \ - $$SHARED/stddclmr.h \ - $$SHARED/thirdparty/memwatch/memwatch.h \ - $$SHARED/thirdparty/stb_image.h - -SOURCES = \ - $$SHARED/thirdparty/memwatch/memwatch.c \ - src/main.c diff --git a/master.pro b/master.pro deleted file mode 100644 index 78ac7da..0000000 --- a/master.pro +++ /dev/null @@ -1,40 +0,0 @@ -# -# Roo/E, the Kangaroo Punch Portable GUI Toolkit -# Copyright (C) 2022 Scott Duensing -# -# http://kangaroopunch.com -# -# -# This file is part of Roo/E. -# -# Roo/E is free software: you can redistribute it and/or modify it under the -# terms of the GNU Affero General Public License as published by the Free -# Software Foundation, either version 3 of the License, or (at your option) -# any later version. -# -# Roo/E is distributed in the hope that it will be useful, but WITHOUT ANY -# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU Affero General Public License -# along with Roo/E. If not, see . -# - - -TEMPLATE = subdirs -CONFIG *= ORDERED - -SUBDIRS = \ - roo-e \ - test - -OTHER_FILES = \ - .gitattributes \ - .gitignore \ - agpl-3.0.txt \ - LICENSE \ - build.sh \ - Makefile.djgpp \ - test.sh \ - test.conf diff --git a/roo_e/roo-e.pro b/roo_e/roo-e.pro deleted file mode 100644 index e38466c..0000000 --- a/roo_e/roo-e.pro +++ /dev/null @@ -1,105 +0,0 @@ -# -# Roo/E, the Kangaroo Punch Portable GUI Toolkit -# Copyright (C) 2022 Scott Duensing -# -# http://kangaroopunch.com -# -# -# This file is part of Roo/E. -# -# Roo/E is free software: you can redistribute it and/or modify it under the -# terms of the GNU Affero General Public License as published by the Free -# Software Foundation, either version 3 of the License, or (at your option) -# any later version. -# -# Roo/E is distributed in the hope that it will be useful, but WITHOUT ANY -# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU Affero General Public License -# along with Roo/E. If not, see . -# - - -TEMPLATE = lib -CONFIG -= \ - app_bundle \ - qt -CONFIG += \ - console \ - c99 \ - staticlib - -CONFIG += BACKEND_SDL2 -#CONFIG += BACKEND_DJGPP - -SHARED = $$PWD/../shared - -DESTDIR = $$OUT_PWD/bin - -INCLUDEPATH += \ - $$PWD/src \ - $$SHARED - -HEADERS += \ - $$SHARED/macros.h \ - $$SHARED/array.h \ - $$SHARED/memory.h \ - $$SHARED/stddclmr.h \ - $$SHARED/thirdparty/memwatch/memwatch.h \ - $$SHARED/thirdparty/stb_ds.h \ - $$SHARED/log.h \ - $$SHARED/util.h \ - src/gui/font.h \ - src/gui/gui-all.h \ - src/gui/gui.h \ - src/gui/image.h \ - src/gui/surface.h \ - src/gui/widgets/button.h \ - src/gui/widgets/checkbox.h \ - src/gui/widgets/frame.h \ - src/gui/widgets/label.h \ - src/gui/widgets/picture.h \ - src/gui/widgets/radio.h \ - src/gui/widgets/hscroll.h \ - src/gui/widgets/scroll.h \ - src/gui/widgets/vscroll.h \ - src/gui/wmwindow.h \ - src/os.h \ - src/platform/platform.h \ - src/thirdparty/stb_image.h - -SOURCES += \ - $$SHARED/array.c \ - $$SHARED/memory.c \ - $$SHARED/thirdparty/memwatch/memwatch.c \ - $$SHARED/log.c \ - $$SHARED/util.c \ - src/gui/font.c \ - src/gui/gui.c \ - src/gui/image.c \ - src/gui/surface.c \ - src/gui/widgets/button.c \ - src/gui/widgets/checkbox.c \ - src/gui/widgets/frame.c \ - src/gui/widgets/label.c \ - src/gui/widgets/picture.c \ - src/gui/widgets/radio.c \ - src/gui/widgets/hscroll.c \ - src/gui/widgets/scroll.c \ - src/gui/widgets/vscroll.c \ - src/gui/wmwindow.c \ - src/platform/djgpp.c \ - src/platform/sdl2.c - -BACKEND_SDL2 { - DEFINES += BACKEND_SDL2 - LIBS += \ - -lSDL2 -} - -BACKEND_DJGPP { - DEFINES += BACKEND_DJGPP -} - diff --git a/roo_e/src/gui/font.c b/roo_e/src/gui/font.c index 90b3149..eec3af5 100644 --- a/roo_e/src/gui/font.c +++ b/roo_e/src/gui/font.c @@ -121,7 +121,10 @@ FontT *fontLoad(char *filename) { return NULL; } +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-result" fread(buffer, size, 1, in); +#pragma GCC diagnostic pop fclose(in); font = fontFromRAMLoad(buffer); diff --git a/roo_e/src/gui/gui.c b/roo_e/src/gui/gui.c index e20748d..2d1e415 100644 --- a/roo_e/src/gui/gui.c +++ b/roo_e/src/gui/gui.c @@ -133,7 +133,10 @@ void guiShutdown(void) { while (hmlen(_widgetCatalog) > 0) { if (_widgetCatalog[0].value->unregister) _widgetCatalog[0].value->unregister(NULL); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-value" hmdel(_widgetCatalog, _widgetCatalog[0].key); +#pragma GCC diagnostic pop } hmfree(_widgetCatalog); diff --git a/roo_e/src/gui/surface.c b/roo_e/src/gui/surface.c index dd8376f..257ad11 100644 --- a/roo_e/src/gui/surface.c +++ b/roo_e/src/gui/surface.c @@ -413,12 +413,15 @@ SurfaceT *surfaceLoad(char *filename) { if (in) { NEW(SurfaceT, i); if (!i) return NULL; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-result" fread(&i->width, sizeof(uint16_t), 1, in); fread(&i->height, sizeof(uint16_t), 1, in); fread(&i->scanline, sizeof(size_t), 1, in); fread(&i->bytes, sizeof(size_t), 1, in); i->buffer.bits8 = (uint8_t *)malloc(i->bytes); fread(i->buffer.bits8, i->bytes, 1, in); +#pragma GCC diagnostic pop fclose(in); } } diff --git a/roo_e/src/gui/widgets/checkbox.c b/roo_e/src/gui/widgets/checkbox.c index 3fd4af8..fec9116 100644 --- a/roo_e/src/gui/widgets/checkbox.c +++ b/roo_e/src/gui/widgets/checkbox.c @@ -68,7 +68,7 @@ CheckboxT *checkboxCreate(char *label, uint8_t value, ...) { memset(c, 0, sizeof(CheckboxT)); c->value = value; - c->label = labelCreate(LABEL_ALIGN_LEFT, f, label); + c->label = labelCreate(LABEL_ALIGN_LEFT, f, label); // Gets attached to widget tree during paint. if (!c->label) { DEL(c); return NULL; diff --git a/roo_e/src/gui/wmwindow.c b/roo_e/src/gui/wmwindow.c index cc20eb6..ac513ed 100644 --- a/roo_e/src/gui/wmwindow.c +++ b/roo_e/src/gui/wmwindow.c @@ -515,8 +515,6 @@ void windowMove(WindowT *w, uint16_t x, uint16_t y) { static void windowPaint(struct WidgetS *widget, ...) { int16_t x = 0; int16_t y; - int16_t px; - int16_t py; int16_t xi; int16_t yi; int16_t xc; @@ -676,10 +674,6 @@ void wmRender(void) { DamageListT **damageList = NULL; DamageListT *damageItem = NULL; - // ***TODO*** We still calculate the desktop in the damage list below. - // There's some odd off-by-one when rendering that needs fixed. For now - // we just draw the desktop and then window contents. - // Update all minimized windows. surfaceSet(__guiBackBuffer); for (i=0; ireg->paint(widget, WINDOW_PAINT_NORMAL); } + surfaceSet(__guiBackBuffer); + // This is global so we can click on them later. _iconCount = 0; - // Draw the desktop. - surfaceSet(__guiBackBuffer); - surfaceClear(GUI_CYAN); - // Do we have windows? if (arrlen(_windowList) > 0) { @@ -928,6 +920,12 @@ void wmRender(void) { } } while (found == 1); + // ***TODO*** Something is off by one somewhere. The last column + // and row are not being rendered / blitted. + // Hack around it for now. + surfaceLineH(0, videoDisplayWidthGet() - 1, videoDisplayHeightGet() - 1, GUI_BLACK); + surfaceLineV(videoDisplayWidthGet() - 1, 0, videoDisplayHeightGet() - 1, GUI_BLACK); + // Draw the damage list. i = 1; for (k=0; krect.x, damageList[k]->rect.y, damageList[k]->rect.x2, damageList[k]->rect.y2, GUI_CYAN); + surfaceBoxFilled(damageList[k]->rect.x, damageList[k]->rect.y, damageList[k]->rect.x2 - 1, damageList[k]->rect.y2 - 1, GUI_CYAN); } /* @@ -1003,7 +1001,6 @@ void wmUpdate(EventT *event) { int16_t x2; int16_t y2; uint8_t onResize = 0; - WidgetT *widget; WidgetT *widgetOver = NULL; WindowT *win = NULL; ClickRawInputT rawEvent = { 0 }; diff --git a/roo_e/src/thirdparty/memwatch/memwatch.c b/roo_e/src/thirdparty/memwatch/memwatch.c index ae0f6ab..0c36e3a 100644 --- a/roo_e/src/thirdparty/memwatch/memwatch.c +++ b/roo_e/src/thirdparty/memwatch/memwatch.c @@ -829,6 +829,8 @@ void* mwUnmark( void *p, const char *file, unsigned line ) { ** Abort/Retry/Ignore handlers ***********************************************************************/ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-result" static int mwARI( const char *estr ) { char inbuf[81]; int c; @@ -843,6 +845,7 @@ static int mwARI( const char *estr ) { if( c == 'I' || c == 'i' ) return MW_ARI_IGNORE; return MW_ARI_ABORT; } +#pragma GCC diagnostic pop /* standard ARI handler (exported) */ int mwAriHandler( const char *estr ) { diff --git a/roo_e/src/thirdparty/stb_ds.h b/roo_e/src/thirdparty/stb_ds.h index ebd6161..d69a682 100644 --- a/roo_e/src/thirdparty/stb_ds.h +++ b/roo_e/src/thirdparty/stb_ds.h @@ -4,11 +4,11 @@ dynamic arrays and hash tables for C (also works in C++). For a gentle introduction: - http://nothings.org/stb_ds + http://nothings.org/stb_ds To use this library, do this in *one* C or C++ file: - #define STB_DS_IMPLEMENTATION - #include "stb_ds.h" + #define STB_DS_IMPLEMENTATION + #include "stb_ds.h" TABLE OF CONTENTS @@ -25,7 +25,7 @@ COMPILE-TIME OPTIONS #define STBDS_NO_SHORT_NAMES - This flag needs to be set globally. + This flag needs to be set globally. By default stb_ds exposes shorter function names that are not qualified with the "stbds_" prefix. If these names conflict with the names in your @@ -33,7 +33,7 @@ COMPILE-TIME OPTIONS #define STBDS_SIPHASH_2_4 - This flag only needs to be set in the file containing #define STB_DS_IMPLEMENTATION. + This flag only needs to be set in the file containing #define STB_DS_IMPLEMENTATION. By default stb_ds.h hashes using a weaker variant of SipHash and a custom hash for 4- and 8-byte keys. On 64-bit platforms, you can define the above flag to force @@ -44,7 +44,7 @@ COMPILE-TIME OPTIONS #define STBDS_REALLOC(context,ptr,size) better_realloc #define STBDS_FREE(context,ptr) better_free - These defines only need to be set in the file containing #define STB_DS_IMPLEMENTATION. + These defines only need to be set in the file containing #define STB_DS_IMPLEMENTATION. By default stb_ds uses stdlib realloc() and free() for memory management. You can substitute your own functions instead by defining these symbols. You must either @@ -53,11 +53,11 @@ COMPILE-TIME OPTIONS #define STBDS_UNIT_TESTS - Defines a function stbds_unit_tests() that checks the functioning of the data structures. + Defines a function stbds_unit_tests() that checks the functioning of the data structures. Note that on older versions of gcc (e.g. 5.x.x) you may need to build with '-std=c++0x' - (or equivalentally '-std=c++11') when using anonymous structures as seen on the web - page or in STBDS_UNIT_TESTS. + (or equivalentally '-std=c++11') when using anonymous structures as seen on the web + page or in STBDS_UNIT_TESTS. LICENSE @@ -68,7 +68,7 @@ DOCUMENTATION Dynamic Arrays - Non-function interface: + Non-function interface: Declare an empty dynamic array of type T T* foo = NULL; @@ -293,77 +293,77 @@ DOCUMENTATION NOTES * These data structures are realloc'd when they grow, and the macro - "functions" write to the provided pointer. This means: (a) the pointer - must be an lvalue, and (b) the pointer to the data structure is not - stable, and you must maintain it the same as you would a realloc'd - pointer. For example, if you pass a pointer to a dynamic array to a - function which updates it, the function must return back the new - pointer to the caller. This is the price of trying to do this in C. + "functions" write to the provided pointer. This means: (a) the pointer + must be an lvalue, and (b) the pointer to the data structure is not + stable, and you must maintain it the same as you would a realloc'd + pointer. For example, if you pass a pointer to a dynamic array to a + function which updates it, the function must return back the new + pointer to the caller. This is the price of trying to do this in C. * The following are the only functions that are thread-safe on a single data - structure, i.e. can be run in multiple threads simultaneously on the same - data structure - hmlen shlen - hmlenu shlenu - hmget_ts shget_ts - hmgeti_ts shgeti_ts - hmgets_ts shgets_ts + structure, i.e. can be run in multiple threads simultaneously on the same + data structure + hmlen shlen + hmlenu shlenu + hmget_ts shget_ts + hmgeti_ts shgeti_ts + hmgets_ts shgets_ts * You iterate over the contents of a dynamic array and a hashmap in exactly - the same way, using arrlen/hmlen/shlen: + the same way, using arrlen/hmlen/shlen: for (i=0; i < arrlen(foo); ++i) ... foo[i] ... * All operations except arrins/arrdel are O(1) amortized, but individual - operations can be slow, so these data structures may not be suitable - for real time use. Dynamic arrays double in capacity as needed, so - elements are copied an average of once. Hash tables double/halve - their size as needed, with appropriate hysteresis to maintain O(1) - performance. + operations can be slow, so these data structures may not be suitable + for real time use. Dynamic arrays double in capacity as needed, so + elements are copied an average of once. Hash tables double/halve + their size as needed, with appropriate hysteresis to maintain O(1) + performance. NOTES - DYNAMIC ARRAY * If you know how long a dynamic array is going to be in advance, you can avoid - extra memory allocations by using arrsetlen to allocate it to that length in - advance and use foo[n] while filling it out, or arrsetcap to allocate the memory - for that length and use arrput/arrpush as normal. + extra memory allocations by using arrsetlen to allocate it to that length in + advance and use foo[n] while filling it out, or arrsetcap to allocate the memory + for that length and use arrput/arrpush as normal. * Unlike some other versions of the dynamic array, this version should - be safe to use with strict-aliasing optimizations. + be safe to use with strict-aliasing optimizations. NOTES - HASH MAP * For compilers other than GCC and clang (e.g. Visual Studio), for hmput/hmget/hmdel - and variants, the key must be an lvalue (so the macro can take the address of it). - Extensions are used that eliminate this requirement if you're using C99 and later - in GCC or clang, or if you're using C++ in GCC. But note that this can make your - code less portable. + and variants, the key must be an lvalue (so the macro can take the address of it). + Extensions are used that eliminate this requirement if you're using C99 and later + in GCC or clang, or if you're using C++ in GCC. But note that this can make your + code less portable. * To test for presence of a key in a hashmap, just do 'hmgeti(foo,key) >= 0'. * The iteration order of your data in the hashmap is determined solely by the - order of insertions and deletions. In particular, if you never delete, new - keys are always added at the end of the array. This will be consistent - across all platforms and versions of the library. However, you should not - attempt to serialize the internal hash table, as the hash is not consistent - between different platforms, and may change with future versions of the library. + order of insertions and deletions. In particular, if you never delete, new + keys are always added at the end of the array. This will be consistent + across all platforms and versions of the library. However, you should not + attempt to serialize the internal hash table, as the hash is not consistent + between different platforms, and may change with future versions of the library. * Use sh_new_arena() for string hashmaps that you never delete from. Initialize - with NULL if you're managing the memory for your strings, or your strings are - never freed (at least until the hashmap is freed). Otherwise, use sh_new_strdup(). - @TODO: make an arena variant that garbage collects the strings with a trivial - copy collector into a new arena whenever the table shrinks / rebuilds. Since - current arena recommendation is to only use arena if it never deletes, then - this can just replace current arena implementation. + with NULL if you're managing the memory for your strings, or your strings are + never freed (at least until the hashmap is freed). Otherwise, use sh_new_strdup(). + @TODO: make an arena variant that garbage collects the strings with a trivial + copy collector into a new arena whenever the table shrinks / rebuilds. Since + current arena recommendation is to only use arena if it never deletes, then + this can just replace current arena implementation. * If adversarial input is a serious concern and you're on a 64-bit platform, - enable STBDS_SIPHASH_2_4 (see the 'Compile-time options' section), and pass - a strong random number to stbds_rand_seed. + enable STBDS_SIPHASH_2_4 (see the 'Compile-time options' section), and pass + a strong random number to stbds_rand_seed. * The default value for the hash table is stored in foo[-1], so if you - use code like 'hmget(T,k)->value = 5' you can accidentally overwrite - the value stored by hmdefault if 'k' is not present. + use code like 'hmget(T,k)->value = 5' you can accidentally overwrite + the value stored by hmdefault if 'k' is not present. CREDITS @@ -373,15 +373,15 @@ CREDITS github:HeroicKatora -- arraddn() reworking Bugfixes: - Andy Durdin - Shane Liesegang - Vinh Truong - Andreas Molzer - github:hashitaku - github:srdjanstipic - Macoy Madson - Andreas Vennstrom - Tobias Mansfield-Williams + Andy Durdin + Shane Liesegang + Vinh Truong + Andreas Molzer + github:hashitaku + github:srdjanstipic + Macoy Madson + Andreas Vennstrom + Tobias Mansfield-Williams */ #ifdef STBDS_UNIT_TESTS @@ -768,16 +768,16 @@ void *stbds_arrgrowf(void *a, size_t elemsize, size_t addlen, size_t min_cap) // compute the minimum capacity needed if (min_len > min_cap) - min_cap = min_len; + min_cap = min_len; if (min_cap <= stbds_arrcap(a)) - return a; + return a; // increase needed capacity to guarantee O(1) amortized if (min_cap < 2 * stbds_arrcap(a)) - min_cap = 2 * stbds_arrcap(a); + min_cap = 2 * stbds_arrcap(a); else if (min_cap < 4) - min_cap = 4; + min_cap = 4; //if (num_prev < 65536) if (a) prev_allocs[num_prev++] = (int *) ((char *) a+1); //if (num_prev == 2201) @@ -786,11 +786,11 @@ void *stbds_arrgrowf(void *a, size_t elemsize, size_t addlen, size_t min_cap) //if (num_prev < 65536) prev_allocs[num_prev++] = (int *) (char *) b; b = (char *) b + sizeof(stbds_array_header); if (a == NULL) { - stbds_header(b)->length = 0; - stbds_header(b)->hash_table = 0; - stbds_header(b)->temp = 0; + stbds_header(b)->length = 0; + stbds_header(b)->hash_table = 0; + stbds_header(b)->temp = 0; } else { - STBDS_STATS(++stbds_array_grow); + STBDS_STATS(++stbds_array_grow); } stbds_header(b)->capacity = min_cap; @@ -875,8 +875,8 @@ static size_t stbds_log2(size_t slot_count) { size_t n=0; while (slot_count > 1) { - slot_count >>= 1; - ++n; + slot_count >>= 1; + ++n; } return n; } @@ -915,7 +915,7 @@ static stbds_hash_index *stbds_make_hash_index(size_t slot_count, stbds_hash_ind t->used_count_shrink_threshold = slot_count* 6/16; // if table is only 6/16th full, shrink #endif // Following statistics were measured on a Core i7-6700 @ 4.00Ghz, compiled with clang 7.0.1 -O2 - // Note that the larger tables have high variance as they were run fewer times + // Note that the larger tables have high variance as they were run fewer times // A1 A2 B1 C1 // 0.10ms : 0.10ms : 0.10ms : 0.11ms : 2,000 inserts creating 2K table // 0.96ms : 0.95ms : 0.97ms : 1.04ms : 20,000 inserts creating 20K table @@ -930,54 +930,54 @@ static stbds_hash_index *stbds_make_hash_index(size_t slot_count, stbds_hash_ind // 192.11ms : 194.39ms : 196.38ms : 195.73ms : 500,000 inserts & deletes in 200M table if (slot_count <= STBDS_BUCKET_LENGTH) - t->used_count_shrink_threshold = 0; + t->used_count_shrink_threshold = 0; // to avoid infinite loop, we need to guarantee that at least one slot is empty and will terminate probes STBDS_ASSERT(t->used_count_threshold + t->tombstone_count_threshold < t->slot_count); STBDS_STATS(++stbds_hash_alloc); if (ot) { - t->string = ot->string; - // reuse old seed so we can reuse old hashes so below "copy out old data" doesn't do any hashing - t->seed = ot->seed; + t->string = ot->string; + // reuse old seed so we can reuse old hashes so below "copy out old data" doesn't do any hashing + t->seed = ot->seed; } else { - size_t a,b,temp; - memset(&t->string, 0, sizeof(t->string)); - t->seed = stbds_hash_seed; - // LCG - // in 32-bit, a = 2147001325 b = 715136305 - // in 64-bit, a = 2862933555777941757 b = 3037000493 - stbds_load_32_or_64(a,temp, 2147001325, 0x27bb2ee6, 0x87b0b0fd); - stbds_load_32_or_64(b,temp, 715136305, 0, 0xb504f32d); - stbds_hash_seed = stbds_hash_seed * a + b; + size_t a,b,temp; + memset(&t->string, 0, sizeof(t->string)); + t->seed = stbds_hash_seed; + // LCG + // in 32-bit, a = 2147001325 b = 715136305 + // in 64-bit, a = 2862933555777941757 b = 3037000493 + stbds_load_32_or_64(a,temp, 2147001325, 0x27bb2ee6, 0x87b0b0fd); + stbds_load_32_or_64(b,temp, 715136305, 0, 0xb504f32d); + stbds_hash_seed = stbds_hash_seed * a + b; } { - size_t i,j; - for (i=0; i < slot_count >> STBDS_BUCKET_SHIFT; ++i) { - stbds_hash_bucket *b = &t->storage[i]; - for (j=0; j < STBDS_BUCKET_LENGTH; ++j) - b->hash[j] = STBDS_HASH_EMPTY; - for (j=0; j < STBDS_BUCKET_LENGTH; ++j) - b->index[j] = STBDS_INDEX_EMPTY; - } + size_t i,j; + for (i=0; i < slot_count >> STBDS_BUCKET_SHIFT; ++i) { + stbds_hash_bucket *b = &t->storage[i]; + for (j=0; j < STBDS_BUCKET_LENGTH; ++j) + b->hash[j] = STBDS_HASH_EMPTY; + for (j=0; j < STBDS_BUCKET_LENGTH; ++j) + b->index[j] = STBDS_INDEX_EMPTY; + } } // copy out the old data, if any if (ot) { - size_t i,j; - t->used_count = ot->used_count; - for (i=0; i < ot->slot_count >> STBDS_BUCKET_SHIFT; ++i) { - stbds_hash_bucket *ob = &ot->storage[i]; - for (j=0; j < STBDS_BUCKET_LENGTH; ++j) { - if (STBDS_INDEX_IN_USE(ob->index[j])) { - size_t hash = ob->hash[j]; - size_t pos = stbds_probe_position(hash, t->slot_count, t->slot_count_log2); - size_t step = STBDS_BUCKET_LENGTH; - STBDS_STATS(++stbds_rehash_items); - for (;;) { - size_t limit,z; - stbds_hash_bucket *bucket; - bucket = &t->storage[pos >> STBDS_BUCKET_SHIFT]; - STBDS_STATS(++stbds_rehash_probes); + size_t i,j; + t->used_count = ot->used_count; + for (i=0; i < ot->slot_count >> STBDS_BUCKET_SHIFT; ++i) { + stbds_hash_bucket *ob = &ot->storage[i]; + for (j=0; j < STBDS_BUCKET_LENGTH; ++j) { + if (STBDS_INDEX_IN_USE(ob->index[j])) { + size_t hash = ob->hash[j]; + size_t pos = stbds_probe_position(hash, t->slot_count, t->slot_count_log2); + size_t step = STBDS_BUCKET_LENGTH; + STBDS_STATS(++stbds_rehash_items); + for (;;) { + size_t limit,z; + stbds_hash_bucket *bucket; + bucket = &t->storage[pos >> STBDS_BUCKET_SHIFT]; + STBDS_STATS(++stbds_rehash_probes); for (z=pos & STBDS_BUCKET_MASK; z < STBDS_BUCKET_LENGTH; ++z) { if (bucket->hash[z] == 0) { @@ -1017,7 +1017,7 @@ size_t stbds_hash_string(char *str, size_t seed) { size_t hash = seed; while (*str) - hash = STBDS_ROTATE_LEFT(hash, 9) + (unsigned char) *str++; + hash = STBDS_ROTATE_LEFT(hash, 9) + (unsigned char) *str++; // Thomas Wang 64-to-32 bit mix function, hopefully also works in 32 bits hash ^= seed; @@ -1071,16 +1071,16 @@ static size_t stbds_siphash_bytes(void *p, size_t len, size_t seed) #endif #define STBDS_SIPROUND() \ - do { \ - v0 += v1; v1 = STBDS_ROTATE_LEFT(v1, 13); v1 ^= v0; v0 = STBDS_ROTATE_LEFT(v0,STBDS_SIZE_T_BITS/2); \ - v2 += v3; v3 = STBDS_ROTATE_LEFT(v3, 16); v3 ^= v2; \ - v2 += v1; v1 = STBDS_ROTATE_LEFT(v1, 17); v1 ^= v2; v2 = STBDS_ROTATE_LEFT(v2,STBDS_SIZE_T_BITS/2); \ - v0 += v3; v3 = STBDS_ROTATE_LEFT(v3, 21); v3 ^= v0; \ - } while (0) + do { \ + v0 += v1; v1 = STBDS_ROTATE_LEFT(v1, 13); v1 ^= v0; v0 = STBDS_ROTATE_LEFT(v0,STBDS_SIZE_T_BITS/2); \ + v2 += v3; v3 = STBDS_ROTATE_LEFT(v3, 16); v3 ^= v2; \ + v2 += v1; v1 = STBDS_ROTATE_LEFT(v1, 17); v1 ^= v2; v2 = STBDS_ROTATE_LEFT(v2,STBDS_SIZE_T_BITS/2); \ + v0 += v3; v3 = STBDS_ROTATE_LEFT(v3, 21); v3 ^= v0; \ + } while (0) for (i=0; i+sizeof(size_t) <= len; i += sizeof(size_t), d += sizeof(size_t)) { - data = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24); - data |= (size_t) (d[4] | (d[5] << 8) | (d[6] << 16) | (d[7] << 24)) << 16 << 16; // discarded if size_t == 4 + data = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24); + data |= (size_t) (d[4] | (d[5] << 8) | (d[6] << 16) | (d[7] << 24)) << 16 << 16; // discarded if size_t == 4 v3 ^= data; for (j=0; j < STBDS_SIPHASH_C_ROUNDS; ++j) @@ -1089,22 +1089,22 @@ static size_t stbds_siphash_bytes(void *p, size_t len, size_t seed) } data = len << (STBDS_SIZE_T_BITS-8); switch (len - i) { - case 7: data |= ((size_t) d[6] << 24) << 24; // fall through - case 6: data |= ((size_t) d[5] << 20) << 20; // fall through - case 5: data |= ((size_t) d[4] << 16) << 16; // fall through - case 4: data |= (d[3] << 24); // fall through - case 3: data |= (d[2] << 16); // fall through - case 2: data |= (d[1] << 8); // fall through - case 1: data |= d[0]; // fall through - case 0: break; + case 7: data |= ((size_t) d[6] << 24) << 24; // fall through + case 6: data |= ((size_t) d[5] << 20) << 20; // fall through + case 5: data |= ((size_t) d[4] << 16) << 16; // fall through + case 4: data |= (d[3] << 24); // fall through + case 3: data |= (d[2] << 16); // fall through + case 2: data |= (d[1] << 8); // fall through + case 1: data |= d[0]; // fall through + case 0: break; } v3 ^= data; for (j=0; j < STBDS_SIPHASH_C_ROUNDS; ++j) - STBDS_SIPROUND(); + STBDS_SIPROUND(); v0 ^= data; v2 ^= 0xff; for (j=0; j < STBDS_SIPHASH_D_ROUNDS; ++j) - STBDS_SIPROUND(); + STBDS_SIPROUND(); #ifdef STBDS_SIPHASH_2_4 return v0^v1^v2^v3; @@ -1121,77 +1121,77 @@ size_t stbds_hash_bytes(void *p, size_t len, size_t seed) unsigned char *d = (unsigned char *) p; if (len == 4) { - unsigned int hash = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24); - #if 0 - // HASH32-A Bob Jenkin's hash function w/o large constants - hash ^= seed; - hash -= (hash<<6); - hash ^= (hash>>17); - hash -= (hash<<9); - hash ^= seed; - hash ^= (hash<<4); - hash -= (hash<<3); - hash ^= (hash<<10); - hash ^= (hash>>15); - #elif 1 - // HASH32-BB Bob Jenkin's presumably-accidental version of Thomas Wang hash with rotates turned into shifts. - // Note that converting these back to rotates makes it run a lot slower, presumably due to collisions, so I'm - // not really sure what's going on. - hash ^= seed; - hash = (hash ^ 61) ^ (hash >> 16); - hash = hash + (hash << 3); - hash = hash ^ (hash >> 4); - hash = hash * 0x27d4eb2d; - hash ^= seed; - hash = hash ^ (hash >> 15); - #else // HASH32-C - Murmur3 - hash ^= seed; - hash *= 0xcc9e2d51; - hash = (hash << 17) | (hash >> 15); - hash *= 0x1b873593; - hash ^= seed; - hash = (hash << 19) | (hash >> 13); - hash = hash*5 + 0xe6546b64; - hash ^= hash >> 16; - hash *= 0x85ebca6b; - hash ^= seed; - hash ^= hash >> 13; - hash *= 0xc2b2ae35; - hash ^= hash >> 16; - #endif - // Following statistics were measured on a Core i7-6700 @ 4.00Ghz, compiled with clang 7.0.1 -O2 - // Note that the larger tables have high variance as they were run fewer times - // HASH32-A // HASH32-BB // HASH32-C - // 0.10ms // 0.10ms // 0.10ms : 2,000 inserts creating 2K table - // 0.96ms // 0.95ms // 0.99ms : 20,000 inserts creating 20K table - // 14.69ms // 14.43ms // 14.97ms : 200,000 inserts creating 200K table - // 199.99ms // 195.36ms // 202.05ms : 2,000,000 inserts creating 2M table - // 2234.84ms // 2187.74ms // 2240.38ms : 20,000,000 inserts creating 20M table - // 55.68ms // 53.72ms // 57.31ms : 500,000 inserts & deletes in 2K table - // 63.43ms // 61.99ms // 65.73ms : 500,000 inserts & deletes in 20K table - // 80.04ms // 77.96ms // 81.83ms : 500,000 inserts & deletes in 200K table - // 100.42ms // 97.40ms // 102.39ms : 500,000 inserts & deletes in 2M table - // 119.71ms // 120.59ms // 121.63ms : 500,000 inserts & deletes in 20M table - // 185.28ms // 195.15ms // 187.74ms : 500,000 inserts & deletes in 200M table - // 15.58ms // 14.79ms // 15.52ms : 200,000 inserts creating 200K table with varying key spacing + unsigned int hash = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24); + #if 0 + // HASH32-A Bob Jenkin's hash function w/o large constants + hash ^= seed; + hash -= (hash<<6); + hash ^= (hash>>17); + hash -= (hash<<9); + hash ^= seed; + hash ^= (hash<<4); + hash -= (hash<<3); + hash ^= (hash<<10); + hash ^= (hash>>15); + #elif 1 + // HASH32-BB Bob Jenkin's presumably-accidental version of Thomas Wang hash with rotates turned into shifts. + // Note that converting these back to rotates makes it run a lot slower, presumably due to collisions, so I'm + // not really sure what's going on. + hash ^= seed; + hash = (hash ^ 61) ^ (hash >> 16); + hash = hash + (hash << 3); + hash = hash ^ (hash >> 4); + hash = hash * 0x27d4eb2d; + hash ^= seed; + hash = hash ^ (hash >> 15); + #else // HASH32-C - Murmur3 + hash ^= seed; + hash *= 0xcc9e2d51; + hash = (hash << 17) | (hash >> 15); + hash *= 0x1b873593; + hash ^= seed; + hash = (hash << 19) | (hash >> 13); + hash = hash*5 + 0xe6546b64; + hash ^= hash >> 16; + hash *= 0x85ebca6b; + hash ^= seed; + hash ^= hash >> 13; + hash *= 0xc2b2ae35; + hash ^= hash >> 16; + #endif + // Following statistics were measured on a Core i7-6700 @ 4.00Ghz, compiled with clang 7.0.1 -O2 + // Note that the larger tables have high variance as they were run fewer times + // HASH32-A // HASH32-BB // HASH32-C + // 0.10ms // 0.10ms // 0.10ms : 2,000 inserts creating 2K table + // 0.96ms // 0.95ms // 0.99ms : 20,000 inserts creating 20K table + // 14.69ms // 14.43ms // 14.97ms : 200,000 inserts creating 200K table + // 199.99ms // 195.36ms // 202.05ms : 2,000,000 inserts creating 2M table + // 2234.84ms // 2187.74ms // 2240.38ms : 20,000,000 inserts creating 20M table + // 55.68ms // 53.72ms // 57.31ms : 500,000 inserts & deletes in 2K table + // 63.43ms // 61.99ms // 65.73ms : 500,000 inserts & deletes in 20K table + // 80.04ms // 77.96ms // 81.83ms : 500,000 inserts & deletes in 200K table + // 100.42ms // 97.40ms // 102.39ms : 500,000 inserts & deletes in 2M table + // 119.71ms // 120.59ms // 121.63ms : 500,000 inserts & deletes in 20M table + // 185.28ms // 195.15ms // 187.74ms : 500,000 inserts & deletes in 200M table + // 15.58ms // 14.79ms // 15.52ms : 200,000 inserts creating 200K table with varying key spacing - return (((size_t) hash << 16 << 16) | hash) ^ seed; + return (((size_t) hash << 16 << 16) | hash) ^ seed; } else if (len == 8 && sizeof(size_t) == 8) { - size_t hash = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24); - hash |= (size_t) (d[4] | (d[5] << 8) | (d[6] << 16) | (d[7] << 24)) << 16 << 16; // avoid warning if size_t == 4 - hash ^= seed; - hash = (~hash) + (hash << 21); - hash ^= STBDS_ROTATE_RIGHT(hash,24); - hash *= 265; - hash ^= STBDS_ROTATE_RIGHT(hash,14); - hash ^= seed; - hash *= 21; - hash ^= STBDS_ROTATE_RIGHT(hash,28); - hash += (hash << 31); - hash = (~hash) + (hash << 18); - return hash; + size_t hash = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24); + hash |= (size_t) (d[4] | (d[5] << 8) | (d[6] << 16) | (d[7] << 24)) << 16 << 16; // avoid warning if size_t == 4 + hash ^= seed; + hash = (~hash) + (hash << 21); + hash ^= STBDS_ROTATE_RIGHT(hash,24); + hash *= 265; + hash ^= STBDS_ROTATE_RIGHT(hash,14); + hash ^= seed; + hash *= 21; + hash ^= STBDS_ROTATE_RIGHT(hash,28); + hash += (hash << 31); + hash = (~hash) + (hash << 18); + return hash; } else { - return stbds_siphash_bytes(p,len,seed); + return stbds_siphash_bytes(p,len,seed); } #endif } @@ -1203,9 +1203,9 @@ size_t stbds_hash_bytes(void *p, size_t len, size_t seed) static int stbds_is_key_equal(void *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode, size_t i) { if (mode >= STBDS_HM_STRING) - return 0==strcmp((char *) key, * (char **) ((char *) a + elemsize*i + keyoffset)); + return 0==strcmp((char *) key, * (char **) ((char *) a + elemsize*i + keyoffset)); else - return 0==memcmp(key, (char *) a + elemsize*i + keyoffset, keysize); + return 0==memcmp(key, (char *) a + elemsize*i + keyoffset, keysize); } #define STBDS_HASH_TO_ARR(x,elemsize) ((char*) (x) - (elemsize)) @@ -1217,13 +1217,13 @@ void stbds_hmfree_func(void *a, size_t elemsize) { if (a == NULL) return; if (stbds_hash_table(a) != NULL) { - if (stbds_hash_table(a)->string.mode == STBDS_SH_STRDUP) { - size_t i; - // skip 0th element, which is default - for (i=1; i < stbds_header(a)->length; ++i) - STBDS_FREE(NULL, *(char**) ((char *) a + elemsize*i)); - } - stbds_strreset(&stbds_hash_table(a)->string); + if (stbds_hash_table(a)->string.mode == STBDS_SH_STRDUP) { + size_t i; + // skip 0th element, which is default + for (i=1; i < stbds_header(a)->length; ++i) + STBDS_FREE(NULL, *(char**) ((char *) a + elemsize*i)); + } + stbds_strreset(&stbds_hash_table(a)->string); } STBDS_FREE(NULL, stbds_header(a)->hash_table); STBDS_FREE(NULL, stbds_header(a)); @@ -1244,8 +1244,8 @@ static ptrdiff_t stbds_hm_find_slot(void *a, size_t elemsize, void *key, size_t pos = stbds_probe_position(hash, table->slot_count, table->slot_count_log2); for (;;) { - STBDS_STATS(++stbds_hash_probes); - bucket = &table->storage[pos >> STBDS_BUCKET_SHIFT]; + STBDS_STATS(++stbds_hash_probes); + bucket = &table->storage[pos >> STBDS_BUCKET_SHIFT]; // start searching from pos to end of bucket, this should help performance on small hash tables that fit in cache for (i=pos & STBDS_BUCKET_MASK; i < STBDS_BUCKET_LENGTH; ++i) { @@ -1282,30 +1282,30 @@ void * stbds_hmget_key_ts(void *a, size_t elemsize, void *key, size_t keysize, p { size_t keyoffset = 0; if (a == NULL) { - // make it non-empty so we can return a temp - a = stbds_arrgrowf(0, elemsize, 0, 1); - stbds_header(a)->length += 1; - memset(a, 0, elemsize); - *temp = STBDS_INDEX_EMPTY; - // adjust a to point after the default element - return STBDS_ARR_TO_HASH(a,elemsize); + // make it non-empty so we can return a temp + a = stbds_arrgrowf(0, elemsize, 0, 1); + stbds_header(a)->length += 1; + memset(a, 0, elemsize); + *temp = STBDS_INDEX_EMPTY; + // adjust a to point after the default element + return STBDS_ARR_TO_HASH(a,elemsize); } else { - stbds_hash_index *table; - void *raw_a = STBDS_HASH_TO_ARR(a,elemsize); - // adjust a to point to the default element - table = (stbds_hash_index *) stbds_header(raw_a)->hash_table; - if (table == 0) { - *temp = -1; - } else { - ptrdiff_t slot = stbds_hm_find_slot(a, elemsize, key, keysize, keyoffset, mode); - if (slot < 0) { - *temp = STBDS_INDEX_EMPTY; - } else { - stbds_hash_bucket *b = &table->storage[slot >> STBDS_BUCKET_SHIFT]; - *temp = b->index[slot & STBDS_BUCKET_MASK]; - } - } - return a; + stbds_hash_index *table; + void *raw_a = STBDS_HASH_TO_ARR(a,elemsize); + // adjust a to point to the default element + table = (stbds_hash_index *) stbds_header(raw_a)->hash_table; + if (table == 0) { + *temp = -1; + } else { + ptrdiff_t slot = stbds_hm_find_slot(a, elemsize, key, keysize, keyoffset, mode); + if (slot < 0) { + *temp = STBDS_INDEX_EMPTY; + } else { + stbds_hash_bucket *b = &table->storage[slot >> STBDS_BUCKET_SHIFT]; + *temp = b->index[slot & STBDS_BUCKET_MASK]; + } + } + return a; } } @@ -1324,10 +1324,10 @@ void * stbds_hmput_default(void *a, size_t elemsize) // a has a hash table but no entries, because of shmode <- grow // a has entries <- do nothing if (a == NULL || stbds_header(STBDS_HASH_TO_ARR(a,elemsize))->length == 0) { - a = stbds_arrgrowf(a ? STBDS_HASH_TO_ARR(a,elemsize) : NULL, elemsize, 0, 1); - stbds_header(a)->length += 1; - memset(a, 0, elemsize); - a=STBDS_ARR_TO_HASH(a,elemsize); + a = stbds_arrgrowf(a ? STBDS_HASH_TO_ARR(a,elemsize) : NULL, elemsize, 0, 1); + stbds_header(a)->length += 1; + memset(a, 0, elemsize); + a=STBDS_ARR_TO_HASH(a,elemsize); } return a; } @@ -1341,11 +1341,11 @@ void *stbds_hmput_key(void *a, size_t elemsize, void *key, size_t keysize, int m stbds_hash_index *table; if (a == NULL) { - a = stbds_arrgrowf(0, elemsize, 0, 1); - memset(a, 0, elemsize); - stbds_header(a)->length += 1; - // adjust a to point AFTER the default element - a = STBDS_ARR_TO_HASH(a,elemsize); + a = stbds_arrgrowf(0, elemsize, 0, 1); + memset(a, 0, elemsize); + stbds_header(a)->length += 1; + // adjust a to point AFTER the default element + a = STBDS_ARR_TO_HASH(a,elemsize); } // adjust a to point to the default element @@ -1355,8 +1355,8 @@ void *stbds_hmput_key(void *a, size_t elemsize, void *key, size_t keysize, int m table = (stbds_hash_index *) stbds_header(a)->hash_table; if (table == NULL || table->used_count >= table->used_count_threshold) { - stbds_hash_index *nt; - size_t slot_count; + stbds_hash_index *nt; + size_t slot_count; slot_count = (table == NULL) ? STBDS_BUCKET_LENGTH : table->slot_count*2; nt = stbds_make_hash_index(slot_count, table); @@ -1370,11 +1370,11 @@ void *stbds_hmput_key(void *a, size_t elemsize, void *key, size_t keysize, int m // we iterate hash table explicitly because we want to track if we saw a tombstone { - size_t hash = mode >= STBDS_HM_STRING ? stbds_hash_string((char*)key,table->seed) : stbds_hash_bytes(key, keysize,table->seed); - size_t step = STBDS_BUCKET_LENGTH; - size_t pos; - ptrdiff_t tombstone = -1; - stbds_hash_bucket *bucket; + size_t hash = mode >= STBDS_HM_STRING ? stbds_hash_string((char*)key,table->seed) : stbds_hash_bytes(key, keysize,table->seed); + size_t step = STBDS_BUCKET_LENGTH; + size_t pos; + ptrdiff_t tombstone = -1; + stbds_hash_bucket *bucket; // stored hash values are forbidden from being 0, so we can detect empty slots to early out quickly if (hash < 2) hash += 2; @@ -1427,11 +1427,11 @@ void *stbds_hmput_key(void *a, size_t elemsize, void *key, size_t keysize, int m pos &= (table->slot_count-1); } found_empty_slot: - if (tombstone >= 0) { - pos = tombstone; - --table->tombstone_count; - } - ++table->used_count; + if (tombstone >= 0) { + pos = tombstone; + --table->tombstone_count; + } + ++table->used_count; { ptrdiff_t i = (ptrdiff_t) stbds_arrlen(a); @@ -1472,32 +1472,32 @@ void * stbds_shmode_func(size_t elemsize, int mode) void * stbds_hmdel_key(void *a, size_t elemsize, void *key, size_t keysize, size_t keyoffset, int mode) { if (a == NULL) { - return 0; + return 0; } else { - stbds_hash_index *table; - void *raw_a = STBDS_HASH_TO_ARR(a,elemsize); - table = (stbds_hash_index *) stbds_header(raw_a)->hash_table; - stbds_temp(raw_a) = 0; - if (table == 0) { - return a; - } else { - ptrdiff_t slot; - slot = stbds_hm_find_slot(a, elemsize, key, keysize, keyoffset, mode); - if (slot < 0) - return a; - else { - stbds_hash_bucket *b = &table->storage[slot >> STBDS_BUCKET_SHIFT]; - int i = slot & STBDS_BUCKET_MASK; - ptrdiff_t old_index = b->index[i]; - ptrdiff_t final_index = (ptrdiff_t) stbds_arrlen(raw_a)-1-1; // minus one for the raw_a vs a, and minus one for 'last' - STBDS_ASSERT(slot < (ptrdiff_t) table->slot_count); - --table->used_count; - ++table->tombstone_count; - stbds_temp(raw_a) = 1; - STBDS_ASSERT(table->used_count >= 0); - //STBDS_ASSERT(table->tombstone_count < table->slot_count/4); - b->hash[i] = STBDS_HASH_DELETED; - b->index[i] = STBDS_INDEX_DELETED; + stbds_hash_index *table; + void *raw_a = STBDS_HASH_TO_ARR(a,elemsize); + table = (stbds_hash_index *) stbds_header(raw_a)->hash_table; + stbds_temp(raw_a) = 0; + if (table == 0) { + return a; + } else { + ptrdiff_t slot; + slot = stbds_hm_find_slot(a, elemsize, key, keysize, keyoffset, mode); + if (slot < 0) + return a; + else { + stbds_hash_bucket *b = &table->storage[slot >> STBDS_BUCKET_SHIFT]; + int i = slot & STBDS_BUCKET_MASK; + ptrdiff_t old_index = b->index[i]; + ptrdiff_t final_index = (ptrdiff_t) stbds_arrlen(raw_a)-1-1; // minus one for the raw_a vs a, and minus one for 'last' + STBDS_ASSERT(slot < (ptrdiff_t) table->slot_count); + --table->used_count; + ++table->tombstone_count; + stbds_temp(raw_a) = 1; + STBDS_ASSERT(table->used_count >= 0); + //STBDS_ASSERT(table->tombstone_count < table->slot_count/4); + b->hash[i] = STBDS_HASH_DELETED; + b->index[i] = STBDS_INDEX_DELETED; if (mode == STBDS_HM_STRING && table->string.mode == STBDS_SH_STRDUP) STBDS_FREE(NULL, *(char**) ((char *) a+elemsize*old_index)); @@ -1559,8 +1559,8 @@ char *stbds_stralloc(stbds_string_arena *a, char *str) char *p; size_t len = strlen(str)+1; if (len > a->remaining) { - // compute the next blocksize - size_t blocksize = a->block; + // compute the next blocksize + size_t blocksize = a->block; // size is 512, 512, 1024, 1024, 2048, 2048, 4096, 4096, etc., so that // there are log(SIZE) allocations to free when we destroy the table @@ -1607,9 +1607,9 @@ void stbds_strreset(stbds_string_arena *a) stbds_string_block *x,*y; x = a->storage; while (x) { - y = x->next; - STBDS_FREE(NULL, x); - x = y; + y = x->next; + STBDS_FREE(NULL, x); + x = y; } memset(a, 0, sizeof(*a)); } @@ -1667,27 +1667,27 @@ void stbds_unit_tests(void) STBDS_ASSERT(arrlen(arr)==0); for (i=0; i < 20000; i += 50) { - for (j=0; j < i; ++j) - arrpush(arr,j); - arrfree(arr); + for (j=0; j < i; ++j) + arrpush(arr,j); + arrfree(arr); } for (i=0; i < 4; ++i) { - arrpush(arr,1); arrpush(arr,2); arrpush(arr,3); arrpush(arr,4); - arrdel(arr,i); - arrfree(arr); - arrpush(arr,1); arrpush(arr,2); arrpush(arr,3); arrpush(arr,4); - arrdelswap(arr,i); - arrfree(arr); + arrpush(arr,1); arrpush(arr,2); arrpush(arr,3); arrpush(arr,4); + arrdel(arr,i); + arrfree(arr); + arrpush(arr,1); arrpush(arr,2); arrpush(arr,3); arrpush(arr,4); + arrdelswap(arr,i); + arrfree(arr); } for (i=0; i < 5; ++i) { - arrpush(arr,1); arrpush(arr,2); arrpush(arr,3); arrpush(arr,4); - stbds_arrins(arr,i,5); - STBDS_ASSERT(arr[i] == 5); - if (i < 4) - STBDS_ASSERT(arr[4] == 4); - arrfree(arr); + arrpush(arr,1); arrpush(arr,2); arrpush(arr,3); arrpush(arr,4); + stbds_arrins(arr,i,5); + STBDS_ASSERT(arr[i] == 5); + if (i < 4) + STBDS_ASSERT(arr[4] == 4); + arrfree(arr); } i = 1; @@ -1696,30 +1696,30 @@ void stbds_unit_tests(void) STBDS_ASSERT(hmgeti(intmap, i) == -1); STBDS_ASSERT(hmget (intmap, i) == -2); for (i=0; i < testsize; i+=2) - hmput(intmap, i, i*5); + hmput(intmap, i, i*5); for (i=0; i < testsize; i+=1) { - if (i & 1) STBDS_ASSERT(hmget(intmap, i) == -2 ); - else STBDS_ASSERT(hmget(intmap, i) == i*5); - if (i & 1) STBDS_ASSERT(hmget_ts(intmap, i, temp) == -2 ); - else STBDS_ASSERT(hmget_ts(intmap, i, temp) == i*5); + if (i & 1) STBDS_ASSERT(hmget(intmap, i) == -2 ); + else STBDS_ASSERT(hmget(intmap, i) == i*5); + if (i & 1) STBDS_ASSERT(hmget_ts(intmap, i, temp) == -2 ); + else STBDS_ASSERT(hmget_ts(intmap, i, temp) == i*5); } for (i=0; i < testsize; i+=2) - hmput(intmap, i, i*3); + hmput(intmap, i, i*3); for (i=0; i < testsize; i+=1) - if (i & 1) STBDS_ASSERT(hmget(intmap, i) == -2 ); - else STBDS_ASSERT(hmget(intmap, i) == i*3); + if (i & 1) STBDS_ASSERT(hmget(intmap, i) == -2 ); + else STBDS_ASSERT(hmget(intmap, i) == i*3); for (i=2; i < testsize; i+=4) - hmdel(intmap, i); // delete half the entries + hmdel(intmap, i); // delete half the entries for (i=0; i < testsize; i+=1) - if (i & 3) STBDS_ASSERT(hmget(intmap, i) == -2 ); - else STBDS_ASSERT(hmget(intmap, i) == i*3); + if (i & 3) STBDS_ASSERT(hmget(intmap, i) == -2 ); + else STBDS_ASSERT(hmget(intmap, i) == i*3); for (i=0; i < testsize; i+=1) - hmdel(intmap, i); // delete the rest of the entries + hmdel(intmap, i); // delete the rest of the entries for (i=0; i < testsize; i+=1) - STBDS_ASSERT(hmget(intmap, i) == -2 ); + STBDS_ASSERT(hmget(intmap, i) == -2 ); hmfree(intmap); for (i=0; i < testsize; i+=2) - hmput(intmap, i, i*3); + hmput(intmap, i, i*3); hmfree(intmap); #if defined(__clang__) || defined(__GNUC__) @@ -1735,117 +1735,117 @@ void stbds_unit_tests(void) #endif for (i=0; i < testsize; ++i) - stralloc(&sa, strkey(i)); + stralloc(&sa, strkey(i)); strreset(&sa); { - s.key = "a", s.value = 1; - shputs(strmap, s); - STBDS_ASSERT(*strmap[0].key == 'a'); - STBDS_ASSERT(strmap[0].key == s.key); - STBDS_ASSERT(strmap[0].value == s.value); - shfree(strmap); + s.key = "a", s.value = 1; + shputs(strmap, s); + STBDS_ASSERT(*strmap[0].key == 'a'); + STBDS_ASSERT(strmap[0].key == s.key); + STBDS_ASSERT(strmap[0].value == s.value); + shfree(strmap); } { - s.key = "a", s.value = 1; - sh_new_strdup(strmap); - shputs(strmap, s); - STBDS_ASSERT(*strmap[0].key == 'a'); - STBDS_ASSERT(strmap[0].key != s.key); - STBDS_ASSERT(strmap[0].value == s.value); - shfree(strmap); + s.key = "a", s.value = 1; + sh_new_strdup(strmap); + shputs(strmap, s); + STBDS_ASSERT(*strmap[0].key == 'a'); + STBDS_ASSERT(strmap[0].key != s.key); + STBDS_ASSERT(strmap[0].value == s.value); + shfree(strmap); } { - s.key = "a", s.value = 1; - sh_new_arena(strmap); - shputs(strmap, s); - STBDS_ASSERT(*strmap[0].key == 'a'); - STBDS_ASSERT(strmap[0].key != s.key); - STBDS_ASSERT(strmap[0].value == s.value); - shfree(strmap); + s.key = "a", s.value = 1; + sh_new_arena(strmap); + shputs(strmap, s); + STBDS_ASSERT(*strmap[0].key == 'a'); + STBDS_ASSERT(strmap[0].key != s.key); + STBDS_ASSERT(strmap[0].value == s.value); + shfree(strmap); } for (j=0; j < 2; ++j) { - STBDS_ASSERT(shgeti(strmap,"foo") == -1); - if (j == 0) - sh_new_strdup(strmap); - else - sh_new_arena(strmap); - STBDS_ASSERT(shgeti(strmap,"foo") == -1); - shdefault(strmap, -2); - STBDS_ASSERT(shgeti(strmap,"foo") == -1); - for (i=0; i < testsize; i+=2) - shput(strmap, strkey(i), i*3); - for (i=0; i < testsize; i+=1) - if (i & 1) STBDS_ASSERT(shget(strmap, strkey(i)) == -2 ); - else STBDS_ASSERT(shget(strmap, strkey(i)) == i*3); - for (i=2; i < testsize; i+=4) - shdel(strmap, strkey(i)); // delete half the entries - for (i=0; i < testsize; i+=1) - if (i & 3) STBDS_ASSERT(shget(strmap, strkey(i)) == -2 ); - else STBDS_ASSERT(shget(strmap, strkey(i)) == i*3); - for (i=0; i < testsize; i+=1) - shdel(strmap, strkey(i)); // delete the rest of the entries - for (i=0; i < testsize; i+=1) - STBDS_ASSERT(shget(strmap, strkey(i)) == -2 ); - shfree(strmap); + STBDS_ASSERT(shgeti(strmap,"foo") == -1); + if (j == 0) + sh_new_strdup(strmap); + else + sh_new_arena(strmap); + STBDS_ASSERT(shgeti(strmap,"foo") == -1); + shdefault(strmap, -2); + STBDS_ASSERT(shgeti(strmap,"foo") == -1); + for (i=0; i < testsize; i+=2) + shput(strmap, strkey(i), i*3); + for (i=0; i < testsize; i+=1) + if (i & 1) STBDS_ASSERT(shget(strmap, strkey(i)) == -2 ); + else STBDS_ASSERT(shget(strmap, strkey(i)) == i*3); + for (i=2; i < testsize; i+=4) + shdel(strmap, strkey(i)); // delete half the entries + for (i=0; i < testsize; i+=1) + if (i & 3) STBDS_ASSERT(shget(strmap, strkey(i)) == -2 ); + else STBDS_ASSERT(shget(strmap, strkey(i)) == i*3); + for (i=0; i < testsize; i+=1) + shdel(strmap, strkey(i)); // delete the rest of the entries + for (i=0; i < testsize; i+=1) + STBDS_ASSERT(shget(strmap, strkey(i)) == -2 ); + shfree(strmap); } { - struct { char *key; char value; } *hash = NULL; - char name[4] = "jen"; - shput(hash, "bob" , 'h'); - shput(hash, "sally" , 'e'); - shput(hash, "fred" , 'l'); - shput(hash, "jen" , 'x'); - shput(hash, "doug" , 'o'); + struct { char *key; char value; } *hash = NULL; + char name[4] = "jen"; + shput(hash, "bob" , 'h'); + shput(hash, "sally" , 'e'); + shput(hash, "fred" , 'l'); + shput(hash, "jen" , 'x'); + shput(hash, "doug" , 'o'); shput(hash, name , 'l'); shfree(hash); } for (i=0; i < testsize; i += 2) { - stbds_struct s = { i,i*2,i*3,i*4 }; - hmput(map, s, i*5); + stbds_struct s = { i,i*2,i*3,i*4 }; + hmput(map, s, i*5); } for (i=0; i < testsize; i += 1) { - stbds_struct s = { i,i*2,i*3 ,i*4 }; - stbds_struct t = { i,i*2,i*3+1,i*4 }; - if (i & 1) STBDS_ASSERT(hmget(map, s) == 0); - else STBDS_ASSERT(hmget(map, s) == i*5); - if (i & 1) STBDS_ASSERT(hmget_ts(map, s, temp) == 0); - else STBDS_ASSERT(hmget_ts(map, s, temp) == i*5); - //STBDS_ASSERT(hmget(map, t.key) == 0); + stbds_struct s = { i,i*2,i*3 ,i*4 }; + stbds_struct t = { i,i*2,i*3+1,i*4 }; + if (i & 1) STBDS_ASSERT(hmget(map, s) == 0); + else STBDS_ASSERT(hmget(map, s) == i*5); + if (i & 1) STBDS_ASSERT(hmget_ts(map, s, temp) == 0); + else STBDS_ASSERT(hmget_ts(map, s, temp) == i*5); + //STBDS_ASSERT(hmget(map, t.key) == 0); } for (i=0; i < testsize; i += 2) { - stbds_struct s = { i,i*2,i*3,i*4 }; - hmputs(map2, s); + stbds_struct s = { i,i*2,i*3,i*4 }; + hmputs(map2, s); } hmfree(map); for (i=0; i < testsize; i += 1) { - stbds_struct s = { i,i*2,i*3,i*4 }; - stbds_struct t = { i,i*2,i*3+1,i*4 }; - if (i & 1) STBDS_ASSERT(hmgets(map2, s.key).d == 0); - else STBDS_ASSERT(hmgets(map2, s.key).d == i*4); - //STBDS_ASSERT(hmgetp(map2, t.key) == 0); + stbds_struct s = { i,i*2,i*3,i*4 }; + stbds_struct t = { i,i*2,i*3+1,i*4 }; + if (i & 1) STBDS_ASSERT(hmgets(map2, s.key).d == 0); + else STBDS_ASSERT(hmgets(map2, s.key).d == i*4); + //STBDS_ASSERT(hmgetp(map2, t.key) == 0); } hmfree(map2); for (i=0; i < testsize; i += 2) { - stbds_struct2 s = { { i,i*2 }, i*3,i*4, i*5 }; - hmputs(map3, s); + stbds_struct2 s = { { i,i*2 }, i*3,i*4, i*5 }; + hmputs(map3, s); } for (i=0; i < testsize; i += 1) { - stbds_struct2 s = { { i,i*2}, i*3, i*4, i*5 }; - stbds_struct2 t = { { i,i*2}, i*3+1, i*4, i*5 }; - if (i & 1) STBDS_ASSERT(hmgets(map3, s.key).d == 0); - else STBDS_ASSERT(hmgets(map3, s.key).d == i*5); - //STBDS_ASSERT(hmgetp(map3, t.key) == 0); + stbds_struct2 s = { { i,i*2}, i*3, i*4, i*5 }; + stbds_struct2 t = { { i,i*2}, i*3+1, i*4, i*5 }; + if (i & 1) STBDS_ASSERT(hmgets(map3, s.key).d == 0); + else STBDS_ASSERT(hmgets(map3, s.key).d == i*5); + //STBDS_ASSERT(hmgetp(map3, t.key) == 0); } #endif } diff --git a/test/src/main.c b/test/src/main.c index 68d8e6b..353cc99 100644 --- a/test/src/main.c +++ b/test/src/main.c @@ -52,11 +52,11 @@ int main(int argc, char *argv[]) { memoryStartup(argv[0]); logOpenByHandle(memoryLogHandleGet()); - if (guiStartup(800, 600, 16) == SUCCESS) { + if (guiStartup(800, 600, 8) == SUCCESS) { for (i=1; i<4; i++) { sprintf(title, "Testing %d", i); - w = windowCreate(i * 50, i * 50, 600, 400, title, WIN_STANDARD, 640, 480); + w = windowCreate(i * 50, i * 50, 500, 300, title, WIN_STANDARD, 640, 480); l = labelCreate(LABEL_ALIGN_LEFT, __guiFontVGA8x16, "Label"); labelColorSet(l, __guiBaseColors[i], GUI_BLACK); diff --git a/test/test.pro b/test/test.pro deleted file mode 100644 index c7df217..0000000 --- a/test/test.pro +++ /dev/null @@ -1,45 +0,0 @@ -# -# Roo/E, the Kangaroo Punch Portable GUI Toolkit -# Copyright (C) 2022 Scott Duensing -# -# http://kangaroopunch.com -# -# -# This file is part of Roo/E. -# -# Roo/E is free software: you can redistribute it and/or modify it under the -# terms of the GNU Affero General Public License as published by the Free -# Software Foundation, either version 3 of the License, or (at your option) -# any later version. -# -# Roo/E is distributed in the hope that it will be useful, but WITHOUT ANY -# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU Affero General Public License -# along with Roo/E. If not, see . -# - - -TEMPLATE = app -CONFIG -= \ - app_bundle \ - qt -CONFIG += \ - console \ - c99 - -DESTDIR = $$OUT_PWD/bin - -INCLUDEPATH += \ - $$PWD/../roo-e/src \ - $$PWD/../shared \ - $$PWD/src - -SOURCES += \ - src/main.c - -LIBS += \ - $$PWD/../build-master-debug/roo-e/bin/libroo-e.a \ - -lSDL2