diff --git a/src/Sourcefile b/src/Sourcefile index a6c33e7d6..3832371d4 100644 --- a/src/Sourcefile +++ b/src/Sourcefile @@ -1,3 +1,4 @@ +qs22j.c string.c d_main.c d_clisrv.c diff --git a/src/d_net.c b/src/d_net.c index 419ef2351..b91ecc5bf 100644 --- a/src/d_net.c +++ b/src/d_net.c @@ -32,6 +32,8 @@ #include "stun.h" #include "byteptr.h" +#include "qs22j.h" + // // NETWORKING // @@ -1471,7 +1473,7 @@ void Command_Ping_f(void) else if (ms_width < 100) ms_width = 2; else ms_width = 3; - qsort(pingv, pingc, sizeof (struct pingcell), &pingcellcmp); + qs22j(pingv, pingc, sizeof (struct pingcell), &pingcellcmp); for (i = 0; i < pingc; ++i) { diff --git a/src/hardware/hw_batching.c b/src/hardware/hw_batching.c index b13ad03ea..8c98a830e 100644 --- a/src/hardware/hw_batching.c +++ b/src/hardware/hw_batching.c @@ -13,6 +13,7 @@ #include "hw_glob.h" #include "hw_batching.h" #include "../i_system.h" +#include "../qs22j.h" // The texture for the next polygon given to HWR_ProcessPolygon. // Set with HWR_SetCurrentTexture. @@ -261,9 +262,9 @@ void HWR_RenderBatches(void) // sort polygons ps_hw_batchsorttime = I_GetPreciseTime(); if (cv_glshaders.value && gl_shadersavailable) - qsort(polygonIndexArray, polygonArraySize, sizeof(unsigned int), comparePolygons); + qs22j(polygonIndexArray, polygonArraySize, sizeof(unsigned int), comparePolygons); else - qsort(polygonIndexArray, polygonArraySize, sizeof(unsigned int), comparePolygonsNoShaders); + qs22j(polygonIndexArray, polygonArraySize, sizeof(unsigned int), comparePolygonsNoShaders); ps_hw_batchsorttime = I_GetPreciseTime() - ps_hw_batchsorttime; // sort order // 1. shader diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 4a769f95d..338bdab45 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -14,6 +14,8 @@ #include "../doomstat.h" +#include "../qs22j.h" + #ifdef HWRENDER #include "hw_glob.h" #include "hw_light.h" @@ -4712,7 +4714,7 @@ static void HWR_SortVisSprites(void) { gl_vsprorder[i] = HWR_GetVisSprite(i); } - qsort(gl_vsprorder, gl_visspritecount, sizeof(gl_vissprite_t*), CompareVisSprites); + qs22j(gl_vsprorder, gl_visspritecount, sizeof(gl_vissprite_t*), CompareVisSprites); } // A drawnode is something that points to a 3D floor, 3D side, or masked @@ -4932,7 +4934,7 @@ static void HWR_CreateDrawNodes(void) // p is the number of stuff to sort // sort the list based on the value of the 'drawcount' member of the drawnodes. - qsort(sortindex, p, sizeof(size_t), CompareDrawNodes); + qs22j(sortindex, p, sizeof(size_t), CompareDrawNodes); // an additional pass is needed to correct the order of consecutive planes in the list. // for each consecutive run of planes in the list, sort that run based on plane height and view height. @@ -4951,7 +4953,7 @@ static void HWR_CreateDrawNodes(void) if (run_end > run_start)// if there are multiple consecutive planes, not just one { // consecutive run of planes found, now sort it - qsort(sortindex + run_start, run_end - run_start + 1, sizeof(size_t), CompareDrawNodePlanes); + qs22j(sortindex + run_start, run_end - run_start + 1, sizeof(size_t), CompareDrawNodePlanes); } run_start = run_end + 1;// continue looking for runs coming right after this one } diff --git a/src/m_menu.c b/src/m_menu.c index 35ef73738..7646e6322 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -94,6 +94,8 @@ int snprintf(char *str, size_t n, const char *fmt, ...); #include "discord.h" #endif +#include "qs22j.h" + #define SKULLXOFF -32 #define LINEHEIGHT 16 #define STRINGHEIGHT 8 @@ -8753,22 +8755,22 @@ void M_SortServerList(void) switch(cv_serversort.value) { case 0: // Ping. - qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_time); + qs22j(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_time); break; case 1: // Modified state. - qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_modified); + qs22j(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_modified); break; case 2: // Most players. - qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_numberofplayer_reverse); + qs22j(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_numberofplayer_reverse); break; case 3: // Least players. - qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_numberofplayer); + qs22j(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_numberofplayer); break; case 4: // Max players. - qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_maxplayer_reverse); + qs22j(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_maxplayer_reverse); break; case 5: // Gametype. - qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_gametypename); + qs22j(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_gametypename); break; } #endif diff --git a/src/qs22j.c b/src/qs22j.c new file mode 100644 index 000000000..53277521e --- /dev/null +++ b/src/qs22j.c @@ -0,0 +1,278 @@ +// License: 0BSD +// +// Copyright 2022 Raymond Gardner +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +// IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +// + +#include +#include +#include "qs22j.h" + +#define INSORTTHRESH 5 // if n < this use insertion sort + // MUST be >= 2 +#define MIDTHRESH 20 // < this use middle as pivot +#define MEDOF3THRESH 50 // < this use median-of-3 as pivot + // larger subfiles use med-of-3-medians + +#define min(a,b) (((a) < (b)) ? (a) : (b)) + +typedef int32_t WORD; +typedef int64_t DWORD; +typedef void *pref_typ; + +// if no uintptr_t, use Bentley-McIlroy trick (undefined behavior) +//#define ptr_to_int(p) (p-(char*)0) +#define ptr_to_int(p) ((uintptr_t)(void *)p) + +#define ASWAP(a, b, t) ((void)(t = a, a = b, b = t)) + +#define SWAP(a, b) if (swap_type) swapf(a, b, size);\ + else do {pref_typ t; ASWAP(*(pref_typ*)(a), *(pref_typ*)(b), t);} while (0) + +#define COMP(a, b) ((*compar)((void *)(a), (void *)(b))) + +static void swapbytes(void *a0, void *b0, size_t n) +{ + char *a = a0, *b = b0, t; + do {ASWAP(*a, *b, t); a++; b++;} while (--n); +} + +static void swapdword(void *a0, void *b0, size_t n) +{ + DWORD *a = a0, *b = b0, t; + ASWAP(*a, *b, t); + (void)n; +} + +static void swapdwords(void *a0, void *b0, size_t n) +{ + DWORD *a = a0, *b = b0, t; + do {ASWAP(*a, *b, t); a++; b++;} while (n -= sizeof(DWORD)); +} + +static void swapword(void *a0, void *b0, size_t n) +{ + WORD *a = a0, *b = b0, t; + ASWAP(*a, *b, t); + (void)n; +} + +static void swapwords(void *a0, void *b0, size_t n) +{ + WORD *a = a0, *b = b0, t; + do {ASWAP(*a, *b, t); a++; b++;} while (n -= sizeof(WORD)); +} + +typedef void (*swapf_typ)(void *, void *, size_t); + +static char *med3(char *a, char *b, char *c, int (*compar)(const void *, const void *)) +{ + return COMP(a, b) < 0 ? + (COMP(b, c) < 0 ? b : COMP(a, c) < 0 ? c : a) : + (COMP(b, c) > 0 ? b : COMP(a, c) > 0 ? c : a); +} + +void qs22j(void *base, size_t nmemb, size_t size, + int (*compar)(const void *, const void *)) +{ + char *stack[2*8*sizeof(size_t)], **sp = stack; // stack and stack pointer + char *left = base; // set up char * base pointer + char *limit = left + nmemb * size; // pointer past end of array + char *i, *ii, *j, *jj; // scan pointers + int ki = 0, kj = 0; + int swap_type = 1; + swapf_typ swapf, vecswapf; + + vecswapf = swapf = swapbytes; + if ((ptr_to_int(left) | size) % sizeof(WORD)) + { + ; // unaligned or not multple of WORD size; swap bytes + } + else if (size == sizeof(DWORD)) + { + swapf = swapdword; + vecswapf = swapdwords; + if (size == sizeof(pref_typ)) + swap_type = 0; + } + else if (size == sizeof(WORD)) + { + swapf = swapword; + vecswapf = swapwords; + if (size == sizeof(pref_typ)) + swap_type = 0; + } + else if ((size % sizeof(DWORD)) == 0) + { + swapf = vecswapf = swapdwords; + } + else if ((size % sizeof(WORD)) == 0) + { + swapf = vecswapf = swapwords; + } + for (;;) + { + nmemb = (limit - left) / size; + for (i = left + size; i < limit && COMP(i - size, i) <= 0; i += size) + ; + + if (i == limit) // if already in order + goto pop; + + if (nmemb >= INSORTTHRESH) // otherwise use insertion sort + { + char *right = limit - size; + // best so far? fewer compares, a few more swaps + char *p = left + (nmemb / 2) * size; + if (nmemb >= MIDTHRESH) + { + char *pleft = left + size; + char *pright = right - size; + + if (nmemb >= MEDOF3THRESH) + { + size_t k = (nmemb / 8) * size; + pleft = med3(pleft, left + k, left + k * 2, compar); + p = med3(p - k, p, p + k, compar); + pright = med3(right - k * 2, right - k, pright, compar); + } + + p = med3(pleft, p, pright, compar); + } + + i = ii = left; // i scans left to right + j = jj = right; // j scans right to left + for (;;) + { + + while (i <= j) + { + if (i != p && ((ki = COMP(i, p)) >= 0)) + { + if (ki) + break; + + if (ii == p) + { + p = i; + } + else if (i != ii) + { + SWAP(i, ii); + } + + ii += size; + } + i += size; + } + + while (i < j) + { + if (j != p && ((kj = COMP(j, p)) <= 0)) + { + if (kj) + break; + + if (jj == p) + { + p = j; + } + else if (j != jj) + { + SWAP(j, jj); + } + + jj -= size; + } + j -= size; + } + + if (i >= j) + break; + + SWAP(i, j); + i += size; + j -= size; + } + + if (p < i) + i -= size; + + if (p != i) + { + SWAP(p, i); + } + + ptrdiff_t lessthan = i - ii; + size_t k = min(lessthan, ii - left); + + if (k) + vecswapf(left, i - k, k); + + ptrdiff_t morethan = jj - i; + k = min(morethan, right - jj); + + if (k) + vecswapf(i + size, limit - k, k); + + if (lessthan > morethan) + { + if (lessthan > 1) + { + sp[0] = left; + sp[1] = left + lessthan; + sp += 2; // increment stack pointer + } + + if (morethan <= 1) + goto pop; + + left = limit - morethan; + } + else + { + if (morethan > 1) + { + sp[0] = limit - morethan; + sp[1] = limit; + sp += 2; // increment stack pointer + } + + if (lessthan <= 1) + goto pop; + + limit = left + lessthan; + } + + } + else // else subfile is small, use insertion sort + { + for (i = left + size; i < limit; i += size) + { + for (j = i; j != left && COMP(j - size, j) > 0; j -= size) + { + SWAP(j - size, j); + } + } +pop: + if (sp != stack) // if any entries on stack + { + sp -= 2; // pop the left and limit + left = sp[0]; + limit = sp[1]; + } + else // else stack empty, done + break; + } + } +} diff --git a/src/qs22j.h b/src/qs22j.h new file mode 100644 index 000000000..95d1c1292 --- /dev/null +++ b/src/qs22j.h @@ -0,0 +1,7 @@ +#ifndef __QS22J__ +#define __QS22J__ + +void qs22j(void *base, size_t nmemb, size_t size, + int (*compar)(const void *, const void *)); + +#endif diff --git a/src/r_bsp.c b/src/r_bsp.c index bacda6819..d2ac631e9 100644 --- a/src/r_bsp.c +++ b/src/r_bsp.c @@ -25,6 +25,8 @@ #include "k_terrain.h" +#include "qs22j.h" + seg_t *curline; side_t *sidedef; line_t *linedef; @@ -759,7 +761,7 @@ void R_SortPolyObjects(subsector_t *sub) // 03/10/06: only bother if there are actually polys to sort if (numpolys >= 2) { - qsort(po_ptrs, numpolys, sizeof(polyobj_t *), + qs22j(po_ptrs, numpolys, sizeof(polyobj_t *), R_PolyobjCompare); } } @@ -862,7 +864,7 @@ static void R_AddPolyObjects(subsector_t *sub) // render polyobjects for (i = 0; i < numpolys; ++i) { - qsort(po_ptrs[i]->segs, po_ptrs[i]->segCount, sizeof(seg_t *), R_PolysegCompare); + qs22j(po_ptrs[i]->segs, po_ptrs[i]->segCount, sizeof(seg_t *), R_PolysegCompare); for (j = 0; j < po_ptrs[i]->segCount; ++j) R_AddLine(po_ptrs[i]->segs[j]); }