Path: blob/main/core/posix-wasm/src/lib/bsd/merge.c
1067 views
/*-1* SPDX-License-Identifier: BSD-3-Clause2*3* Copyright (c) 1992, 19934* The Regents of the University of California. All rights reserved.5*6* This code is derived from software contributed to Berkeley by7* Peter McIlroy.8*9* Redistribution and use in source and binary forms, with or without10* modification, are permitted provided that the following conditions11* are met:12* 1. Redistributions of source code must retain the above copyright13* notice, this list of conditions and the following disclaimer.14* 2. Redistributions in binary form must reproduce the above copyright15* notice, this list of conditions and the following disclaimer in the16* documentation and/or other materials provided with the distribution.17* 3. Neither the name of the University nor the names of its contributors18* may be used to endorse or promote products derived from this software19* without specific prior written permission.20*21* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND22* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE23* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE24* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE25* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL26* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS27* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)28* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT29* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY30* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF31* SUCH DAMAGE.32*/3334/*35* Hybrid exponential search/linear search merge sort with hybrid36* natural/pairwise first pass. Requires about .3% more comparisons37* for random data than LSMS with pairwise first pass alone.38* It works for objects as small as two bytes.39*/4041#define NATURAL42#define THRESHOLD 16 /* Best choice for natural merge cut-off. */4344#define roundup2(x, y) (((x)+((y)-1))&(~((y)-1))) /* if y is powers of two */4546/* #define NATURAL to get hybrid natural merge.47* (The default is pairwise merging.)48*/4950#include <sys/types.h>51#include <sys/param.h>5253#include <errno.h>54#include <stdlib.h>55#include <string.h>56#include <stdint.h>5758#ifdef I_AM_MERGESORT_B59#include "block_abi.h"60#define DECLARE_CMP DECLARE_BLOCK(int, cmp, const void *, const void *)61typedef DECLARE_BLOCK(int, cmp_t, const void *, const void *);62#define CMP(x, y) CALL_BLOCK(cmp, x, y)63#else64typedef int (*cmp_t)(const void *, const void *);65#define CMP(x, y) cmp(x, y)66#endif6768static void setup(u_char *, u_char *, size_t, size_t, cmp_t);69static void insertionsort(u_char *, size_t, size_t, cmp_t);7071#define ISIZE sizeof(int)72#define PSIZE sizeof(u_char *)73#define ICOPY_LIST(src, dst, last) \74do \75*(int*)dst = *(int*)src, src += ISIZE, dst += ISIZE; \76while(src < last)77#define ICOPY_ELT(src, dst, i) \78do \79*(int*) dst = *(int*) src, src += ISIZE, dst += ISIZE; \80while (i -= ISIZE)8182#define CCOPY_LIST(src, dst, last) \83do \84*dst++ = *src++; \85while (src < last)86#define CCOPY_ELT(src, dst, i) \87do \88*dst++ = *src++; \89while (i -= 1)9091/*92* Find the next possible pointer head. (Trickery for forcing an array93* to do double duty as a linked list when objects do not align with word94* boundaries.95*/96/* Assumption: PSIZE is a power of 2. */97#define EVAL(p) (u_char **)roundup2((uintptr_t)p, PSIZE)9899#ifdef I_AM_MERGESORT_B100int mergesort_b(void *, size_t, size_t, cmp_t);101#else102int mergesort(void *, size_t, size_t, cmp_t);103#endif104105/*106* Arguments are as for qsort.107*/108int109#ifdef I_AM_MERGESORT_B110mergesort_b(void *base, size_t nmemb, size_t size, cmp_t cmp)111#else112mergesort(void *base, size_t nmemb, size_t size, cmp_t cmp)113#endif114{115size_t i;116int sense;117int big, iflag;118u_char *f1, *f2, *t, *b, *tp2, *q, *l1, *l2;119u_char *list2, *list1, *p2, *p, *last, **p1;120121if (size < PSIZE / 2) { /* Pointers must fit into 2 * size. */122errno = EINVAL;123return (-1);124}125126if (nmemb == 0)127return (0);128129/*130* XXX131* Stupid subtraction for the Cray.132*/133iflag = 0;134if (!(size % ISIZE) && !(((char *)base - (char *)0) % ISIZE))135iflag = 1;136137if ((list2 = malloc(nmemb * size + PSIZE)) == NULL)138return (-1);139140list1 = base;141setup(list1, list2, nmemb, size, cmp);142last = list2 + nmemb * size;143i = big = 0;144while (*EVAL(list2) != last) {145l2 = list1;146p1 = EVAL(list1);147for (tp2 = p2 = list2; p2 != last; p1 = EVAL(l2)) {148p2 = *EVAL(p2);149f1 = l2;150f2 = l1 = list1 + (p2 - list2);151if (p2 != last)152p2 = *EVAL(p2);153l2 = list1 + (p2 - list2);154while (f1 < l1 && f2 < l2) {155if (CMP(f1, f2) <= 0) {156q = f2;157b = f1, t = l1;158sense = -1;159} else {160q = f1;161b = f2, t = l2;162sense = 0;163}164if (!big) { /* here i = 0 */165while ((b += size) < t && CMP(q, b) >sense)166if (++i == 6) {167big = 1;168goto EXPONENTIAL;169}170} else {171EXPONENTIAL: for (i = size; ; i <<= 1)172if ((p = (b + i)) >= t) {173if ((p = t - size) > b &&174CMP(q, p) <= sense)175t = p;176else177b = p;178break;179} else if (CMP(q, p) <= sense) {180t = p;181if (i == size)182big = 0;183goto FASTCASE;184} else185b = p;186while (t > b+size) {187i = (((t - b) / size) >> 1) * size;188if (CMP(q, p = b + i) <= sense)189t = p;190else191b = p;192}193goto COPY;194FASTCASE: while (i > size)195if (CMP(q,196p = b + (i >>= 1)) <= sense)197t = p;198else199b = p;200COPY: b = t;201}202i = size;203if (q == f1) {204if (iflag) {205ICOPY_LIST(f2, tp2, b);206ICOPY_ELT(f1, tp2, i);207} else {208CCOPY_LIST(f2, tp2, b);209CCOPY_ELT(f1, tp2, i);210}211} else {212if (iflag) {213ICOPY_LIST(f1, tp2, b);214ICOPY_ELT(f2, tp2, i);215} else {216CCOPY_LIST(f1, tp2, b);217CCOPY_ELT(f2, tp2, i);218}219}220}221if (f2 < l2) {222if (iflag)223ICOPY_LIST(f2, tp2, l2);224else225CCOPY_LIST(f2, tp2, l2);226} else if (f1 < l1) {227if (iflag)228ICOPY_LIST(f1, tp2, l1);229else230CCOPY_LIST(f1, tp2, l1);231}232*p1 = l2;233}234tp2 = list1; /* swap list1, list2 */235list1 = list2;236list2 = tp2;237last = list2 + nmemb*size;238}239if (base == list2) {240memmove(list2, list1, nmemb*size);241list2 = list1;242}243free(list2);244return (0);245}246247#define swap(a, b) { \248s = b; \249i = size; \250do { \251tmp = *a; *a++ = *s; *s++ = tmp; \252} while (--i); \253a -= size; \254}255#define reverse(bot, top) { \256s = top; \257do { \258i = size; \259do { \260tmp = *bot; *bot++ = *s; *s++ = tmp; \261} while (--i); \262s -= size2; \263} while(bot < s); \264}265266/*267* Optional hybrid natural/pairwise first pass. Eats up list1 in runs of268* increasing order, list2 in a corresponding linked list. Checks for runs269* when THRESHOLD/2 pairs compare with same sense. (Only used when NATURAL270* is defined. Otherwise simple pairwise merging is used.)271*/272void273setup(u_char *list1, u_char *list2, size_t n, size_t size, cmp_t cmp)274{275int i, length, size2, tmp, sense;276u_char *f1, *f2, *s, *l2, *last, *p2;277278size2 = size*2;279if (n <= 5) {280insertionsort(list1, n, size, cmp);281*EVAL(list2) = (u_char*) list2 + n*size;282return;283}284/*285* Avoid running pointers out of bounds; limit n to evens286* for simplicity.287*/288i = 4 + (n & 1);289insertionsort(list1 + (n - i) * size, i, size, cmp);290last = list1 + size * (n - i);291*EVAL(list2 + (last - list1)) = list2 + n * size;292293#ifdef NATURAL294p2 = list2;295f1 = list1;296sense = (CMP(f1, f1 + size) > 0);297for (; f1 < last; sense = !sense) {298length = 2;299/* Find pairs with same sense. */300for (f2 = f1 + size2; f2 < last; f2 += size2) {301if ((CMP(f2, f2+ size) > 0) != sense)302break;303length += 2;304}305if (length < THRESHOLD) { /* Pairwise merge */306do {307p2 = *EVAL(p2) = f1 + size2 - list1 + list2;308if (sense > 0)309swap (f1, f1 + size);310} while ((f1 += size2) < f2);311} else { /* Natural merge */312l2 = f2;313for (f2 = f1 + size2; f2 < l2; f2 += size2) {314if ((CMP(f2-size, f2) > 0) != sense) {315p2 = *EVAL(p2) = f2 - list1 + list2;316if (sense > 0)317reverse(f1, f2-size);318f1 = f2;319}320}321if (sense > 0)322reverse (f1, f2-size);323f1 = f2;324if (f2 < last || CMP(f2 - size, f2) > 0)325p2 = *EVAL(p2) = f2 - list1 + list2;326else327p2 = *EVAL(p2) = list2 + n*size;328}329}330#else /* pairwise merge only. */331for (f1 = list1, p2 = list2; f1 < last; f1 += size2) {332p2 = *EVAL(p2) = p2 + size2;333if (CMP (f1, f1 + size) > 0)334swap(f1, f1 + size);335}336#endif /* NATURAL */337}338339/*340* This is to avoid out-of-bounds addresses in sorting the341* last 4 elements.342*/343static void344insertionsort(u_char *a, size_t n, size_t size, cmp_t cmp)345{346u_char *ai, *s, *t, *u, tmp;347int i;348349for (ai = a+size; --n >= 1; ai += size)350for (t = ai; t > a; t -= size) {351u = t - size;352if (CMP(u, t) <= 0)353break;354swap(u, t);355}356}357358359