diff options
Diffstat (limited to 'src/sort.c')
-rw-r--r-- | src/sort.c | 481 |
1 files changed, 337 insertions, 144 deletions
diff --git a/src/sort.c b/src/sort.c index 5f7a1ee2f53..527d5550342 100644 --- a/src/sort.c +++ b/src/sort.c @@ -34,6 +34,90 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ #include "lisp.h" +/* Reverse a slice of a vector in place, from lo up to (exclusive) hi. */ +static void +reverse_slice(Lisp_Object *lo, Lisp_Object *hi) +{ + eassert (lo && hi); + + --hi; + while (lo < hi) { + Lisp_Object t = *lo; + *lo = *hi; + *hi = t; + ++lo; + --hi; + } +} + +/* A sortslice contains a pointer to an array of keys and a pointer to + an array of corresponding values. In other words, keys[i] + corresponds with values[i]. If values == NULL, then the keys are + also the values. + + Several convenience routines are provided here, so that keys and + values are always moved in sync. */ + +typedef struct { + Lisp_Object *keys; + Lisp_Object *values; +} sortslice; + +/* FIXME: Instead of values=NULL, can we set values=keys, so that they + are both moved in lockstep and we avoid a lot of branches? + We may do some useless work but it might be cheaper overall. */ + +static inline void +sortslice_copy (sortslice *s1, ptrdiff_t i, sortslice *s2, ptrdiff_t j) +{ + s1->keys[i] = s2->keys[j]; + if (s1->values != NULL) + s1->values[i] = s2->values[j]; +} + +static inline void +sortslice_copy_incr (sortslice *dst, sortslice *src) +{ + *dst->keys++ = *src->keys++; + if (dst->values != NULL) + *dst->values++ = *src->values++; +} + +static inline void +sortslice_copy_decr (sortslice *dst, sortslice *src) +{ + *dst->keys-- = *src->keys--; + if (dst->values != NULL) + *dst->values-- = *src->values--; +} + + +static inline void +sortslice_memcpy (sortslice *s1, ptrdiff_t i, sortslice *s2, ptrdiff_t j, + ptrdiff_t n) +{ + memcpy (&s1->keys[i], &s2->keys[j], sizeof s1->keys[0] * n); + if (s1->values != NULL) + memcpy (&s1->values[i], &s2->values[j], sizeof s1->values[0] * n); +} + +static inline void +sortslice_memmove (sortslice *s1, ptrdiff_t i, sortslice *s2, ptrdiff_t j, + ptrdiff_t n) +{ + memmove (&s1->keys[i], &s2->keys[j], sizeof s1->keys[0] * n); + if (s1->values != NULL) + memmove (&s1->values[i], &s2->values[j], sizeof s1->values[0] * n); +} + +static inline void +sortslice_advance (sortslice *slice, ptrdiff_t n) +{ + slice->keys += n; + if (slice->values != NULL) + slice->values += n; +} + /* MAX_MERGE_PENDING is the maximum number of entries in merge_state's pending-stretch stack. For a list with n elements, this needs at most floor(log2(n)) + 1 entries even if we didn't force runs to a @@ -54,23 +138,24 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ struct stretch { - Lisp_Object *base; + sortslice base; ptrdiff_t len; int power; }; struct reloc { - Lisp_Object **src; - Lisp_Object **dst; + sortslice *src; + sortslice *dst; ptrdiff_t *size; int order; /* -1 while in merge_lo; +1 while in merg_hi; 0 otherwise. */ }; -typedef struct +typedef struct merge_state { - Lisp_Object *listbase; + Lisp_Object *basekeys; + Lisp_Object *allocated_keys; /* heap-alloc'ed key array or NULL */ ptrdiff_t listlen; /* PENDING is a stack of N pending stretches yet to be merged. @@ -91,7 +176,7 @@ typedef struct with merges. 'A' initially points to TEMPARRAY, and subsequently to newly allocated memory if needed. */ - Lisp_Object *a; + sortslice a; ptrdiff_t alloced; specpdl_ref count; Lisp_Object temparray[MERGESTATE_TEMP_SIZE]; @@ -102,20 +187,32 @@ typedef struct struct reloc reloc; - /* PREDICATE is the lisp comparison predicate for the sort. */ + /* The C ordering (less-than) predicate. */ + bool (*pred_fun) (struct merge_state *ms, Lisp_Object a, Lisp_Object b); + /* The Lisp ordering predicate; Qnil means value<. */ Lisp_Object predicate; } merge_state; -/* Return true iff (PREDICATE A B) is non-nil. */ +static bool +order_pred_lisp (merge_state *ms, Lisp_Object a, Lisp_Object b) +{ + return !NILP (call2 (ms->predicate, a, b)); +} -static inline bool -inorder (const Lisp_Object predicate, const Lisp_Object a, const Lisp_Object b) +static bool +order_pred_valuelt (merge_state *ms, Lisp_Object a, Lisp_Object b) { - return !NILP (call2 (predicate, a, b)); + return !NILP (Fvaluelt (a, b)); } +/* Return true iff A < B according to the order predicate. */ +static inline bool +inorder (merge_state *ms, Lisp_Object a, Lisp_Object b) +{ + return ms->pred_fun (ms, a, b); +} /* Sort the list starting at LO and ending at HI using a stable binary insertion sort algorithm. On entry the sublist [LO, START) (with @@ -124,24 +221,22 @@ inorder (const Lisp_Object predicate, const Lisp_Object a, const Lisp_Object b) permutation of the input (nothing is lost or duplicated). */ static void -binarysort (merge_state *ms, Lisp_Object *lo, const Lisp_Object *hi, +binarysort (merge_state *ms, sortslice lo, const Lisp_Object *hi, Lisp_Object *start) { - Lisp_Object pred = ms->predicate; - - eassume (lo <= start && start <= hi); - if (lo == start) + eassume (lo.keys <= start && start <= hi); + if (lo.keys == start) ++start; for (; start < hi; ++start) { - Lisp_Object *l = lo; + Lisp_Object *l = lo.keys; Lisp_Object *r = start; Lisp_Object pivot = *r; eassume (l < r); do { Lisp_Object *p = l + ((r - l) >> 1); - if (inorder (pred, pivot, *p)) + if (inorder (ms, pivot, *p)) r = p; else l = p + 1; @@ -150,6 +245,17 @@ binarysort (merge_state *ms, Lisp_Object *lo, const Lisp_Object *hi, for (Lisp_Object *p = start; p > l; --p) p[0] = p[-1]; *l = pivot; + + if (lo.values != NULL) + { + ptrdiff_t offset = lo.values - lo.keys; + Lisp_Object *p = start + offset; + pivot = *p; + l += offset; + for (Lisp_Object *p = start + offset; p > l; --p) + p[0] = p[-1]; + *l = pivot; + } } } @@ -167,8 +273,6 @@ static ptrdiff_t count_run (merge_state *ms, Lisp_Object *lo, const Lisp_Object *hi, bool *descending) { - Lisp_Object pred = ms->predicate; - eassume (lo < hi); *descending = 0; ++lo; @@ -177,12 +281,12 @@ count_run (merge_state *ms, Lisp_Object *lo, const Lisp_Object *hi, return n; n = 2; - if (inorder (pred, lo[0], lo[-1])) + if (inorder (ms, lo[0], lo[-1])) { *descending = 1; for (lo = lo + 1; lo < hi; ++lo, ++n) { - if (!inorder (pred, lo[0], lo[-1])) + if (!inorder (ms, lo[0], lo[-1])) break; } } @@ -190,7 +294,7 @@ count_run (merge_state *ms, Lisp_Object *lo, const Lisp_Object *hi, { for (lo = lo + 1; lo < hi; ++lo, ++n) { - if (inorder (pred, lo[0], lo[-1])) + if (inorder (ms, lo[0], lo[-1])) break; } } @@ -223,21 +327,19 @@ static ptrdiff_t gallop_left (merge_state *ms, const Lisp_Object key, Lisp_Object *a, const ptrdiff_t n, const ptrdiff_t hint) { - Lisp_Object pred = ms->predicate; - eassume (a && n > 0 && hint >= 0 && hint < n); a += hint; ptrdiff_t lastofs = 0; ptrdiff_t ofs = 1; - if (inorder (pred, *a, key)) + if (inorder (ms, *a, key)) { /* When a[hint] < key, gallop right until a[hint + lastofs] < key <= a[hint + ofs]. */ const ptrdiff_t maxofs = n - hint; /* This is one after the end of a. */ while (ofs < maxofs) { - if (inorder (pred, a[ofs], key)) + if (inorder (ms, a[ofs], key)) { lastofs = ofs; eassume (ofs <= (PTRDIFF_MAX - 1) / 2); @@ -259,7 +361,7 @@ gallop_left (merge_state *ms, const Lisp_Object key, Lisp_Object *a, const ptrdiff_t maxofs = hint + 1; /* Here &a[0] is lowest. */ while (ofs < maxofs) { - if (inorder (pred, a[-ofs], key)) + if (inorder (ms, a[-ofs], key)) break; /* Here key <= a[hint - ofs]. */ lastofs = ofs; @@ -284,7 +386,7 @@ gallop_left (merge_state *ms, const Lisp_Object key, Lisp_Object *a, { ptrdiff_t m = lastofs + ((ofs - lastofs) >> 1); - if (inorder (pred, a[m], key)) + if (inorder (ms, a[m], key)) lastofs = m + 1; /* Here a[m] < key. */ else ofs = m; /* Here key <= a[m]. */ @@ -307,21 +409,19 @@ static ptrdiff_t gallop_right (merge_state *ms, const Lisp_Object key, Lisp_Object *a, const ptrdiff_t n, const ptrdiff_t hint) { - Lisp_Object pred = ms->predicate; - eassume (a && n > 0 && hint >= 0 && hint < n); a += hint; ptrdiff_t lastofs = 0; ptrdiff_t ofs = 1; - if (inorder (pred, key, *a)) + if (inorder (ms, key, *a)) { /* When key < a[hint], gallop left until a[hint - ofs] <= key < a[hint - lastofs]. */ const ptrdiff_t maxofs = hint + 1; /* Here &a[0] is lowest. */ while (ofs < maxofs) { - if (inorder (pred, key, a[-ofs])) + if (inorder (ms, key, a[-ofs])) { lastofs = ofs; eassume (ofs <= (PTRDIFF_MAX - 1) / 2); @@ -344,7 +444,7 @@ gallop_right (merge_state *ms, const Lisp_Object key, Lisp_Object *a, const ptrdiff_t maxofs = n - hint; /* Here &a[n-1] is highest. */ while (ofs < maxofs) { - if (inorder (pred, key, a[ofs])) + if (inorder (ms, key, a[ofs])) break; /* Here a[hint + ofs] <= key. */ lastofs = ofs; @@ -368,7 +468,7 @@ gallop_right (merge_state *ms, const Lisp_Object key, Lisp_Object *a, { ptrdiff_t m = lastofs + ((ofs - lastofs) >> 1); - if (inorder (pred, key, a[m])) + if (inorder (ms, key, a[m])) ofs = m; /* Here key < a[m]. */ else lastofs = m + 1; /* Here a[m] <= key. */ @@ -378,21 +478,47 @@ gallop_right (merge_state *ms, const Lisp_Object key, Lisp_Object *a, } +static void merge_register_cleanup (merge_state *ms); + static void -merge_init (merge_state *ms, const ptrdiff_t list_size, Lisp_Object *lo, - const Lisp_Object predicate) +merge_init (merge_state *ms, const ptrdiff_t list_size, + Lisp_Object *allocated_keys, sortslice *lo, Lisp_Object predicate) { eassume (ms != NULL); - ms->a = ms->temparray; - ms->alloced = MERGESTATE_TEMP_SIZE; + if (lo->values != NULL) + { + /* The temporary space for merging will need at most half the list + size rounded up. Use the minimum possible space so we can use the + rest of temparray for other things. In particular, if there is + enough extra space, if will be used to store the keys. */ + ms->alloced = (list_size + 1) / 2; + + /* ms->alloced describes how many keys will be stored at + ms->temparray, but we also need to store the values. Hence, + ms->alloced is capped at half of MERGESTATE_TEMP_SIZE. */ + if (MERGESTATE_TEMP_SIZE / 2 < ms->alloced) + ms->alloced = MERGESTATE_TEMP_SIZE / 2; + ms->a.values = &ms->temparray[ms->alloced]; + } + else + { + ms->alloced = MERGESTATE_TEMP_SIZE; + ms->a.values = NULL; + } + ms->a.keys = ms->temparray; ms->n = 0; ms->min_gallop = GALLOP_WIN_MIN; ms->listlen = list_size; - ms->listbase = lo; + ms->basekeys = lo->keys; + ms->allocated_keys = allocated_keys; + ms->pred_fun = NILP (predicate) ? order_pred_valuelt : order_pred_lisp; ms->predicate = predicate; ms->reloc = (struct reloc){NULL, NULL, NULL, 0}; + ms->count = make_invalid_specpdl_ref (); + if (allocated_keys != NULL) + merge_register_cleanup (ms); } @@ -408,8 +534,10 @@ merge_markmem (void *arg) if (ms->reloc.size != NULL && *ms->reloc.size > 0) { - eassume (ms->reloc.src != NULL); - mark_objects (*ms->reloc.src, *ms->reloc.size); + Lisp_Object *src = (ms->reloc.src->values + ? ms->reloc.src->values : ms->reloc.src->keys); + eassume (src != NULL); + mark_objects (src, *ms->reloc.size); } } @@ -432,16 +560,37 @@ cleanup_mem (void *arg) if (ms->reloc.order != 0 && *ms->reloc.size > 0) { - eassume (*ms->reloc.src != NULL && *ms->reloc.dst != NULL); + Lisp_Object *src = (ms->reloc.src->values + ? ms->reloc.src->values : ms->reloc.src->keys); + Lisp_Object *dst = (ms->reloc.dst->values + ? ms->reloc.dst->values : ms->reloc.dst->keys); + eassume (src != NULL && dst != NULL); ptrdiff_t n = *ms->reloc.size; ptrdiff_t shift = ms->reloc.order == -1 ? 0 : n - 1; - memcpy (*ms->reloc.dst - shift, *ms->reloc.src, n * word_size); + memcpy (dst - shift, src, n * word_size); } /* Free any remaining temp storage. */ - xfree (ms->a); + if (ms->a.keys != ms->temparray) + { + xfree (ms->a.keys); + ms->a.keys = NULL; + } + + if (ms->allocated_keys != NULL) + { + xfree (ms->allocated_keys); + ms->allocated_keys = NULL; + } } +static void +merge_register_cleanup (merge_state *ms) +{ + specpdl_ref count = SPECPDL_INDEX (); + record_unwind_protect_ptr_mark (cleanup_mem, ms, merge_markmem); + ms->count = count; +} /* Allocate enough temp memory for NEED array slots. Any previously allocated memory is first freed, and a cleanup routine is @@ -453,13 +602,12 @@ merge_getmem (merge_state *ms, const ptrdiff_t need) { eassume (ms != NULL); - if (ms->a == ms->temparray) + if (ms->a.keys == ms->temparray) { /* We only get here if alloc is needed and this is the first time, so we set up the unwind protection. */ - specpdl_ref count = SPECPDL_INDEX (); - record_unwind_protect_ptr_mark (cleanup_mem, ms, merge_markmem); - ms->count = count; + if (!specpdl_ref_valid_p (ms->count)) + merge_register_cleanup (ms); } else { @@ -467,10 +615,13 @@ merge_getmem (merge_state *ms, const ptrdiff_t need) what's in the block we don't use realloc which would waste cycles copying the old data. We just free and alloc again. */ - xfree (ms->a); + xfree (ms->a.keys); } - ms->a = xmalloc (need * word_size); + ptrdiff_t bytes = (need * word_size) << (ms->a.values != NULL ? 1 : 0); + ms->a.keys = xmalloc (bytes); ms->alloced = need; + if (ms->a.values != NULL) + ms->a.values = &ms->a.keys[need]; } @@ -488,21 +639,19 @@ needmem (merge_state *ms, ptrdiff_t na) NB. */ static void -merge_lo (merge_state *ms, Lisp_Object *ssa, ptrdiff_t na, Lisp_Object *ssb, - ptrdiff_t nb) +merge_lo (merge_state *ms, sortslice ssa, ptrdiff_t na, + sortslice ssb, ptrdiff_t nb) { - Lisp_Object pred = ms->predicate; - - eassume (ms && ssa && ssb && na > 0 && nb > 0); - eassume (ssa + na == ssb); + eassume (ms && ssa.keys && ssb.keys && na > 0 && nb > 0); + eassume (ssa.keys + na == ssb.keys); needmem (ms, na); - memcpy (ms->a, ssa, na * word_size); - Lisp_Object *dest = ssa; + sortslice_memcpy (&ms->a, 0, &ssa, 0, na); + sortslice dest = ssa; ssa = ms->a; ms->reloc = (struct reloc){&ssa, &dest, &na, -1}; - *dest++ = *ssb++; + sortslice_copy_incr (&dest, &ssb); --nb; if (nb == 0) goto Succeed; @@ -519,9 +668,9 @@ merge_lo (merge_state *ms, Lisp_Object *ssa, ptrdiff_t na, Lisp_Object *ssb, for (;;) { eassume (na > 1 && nb > 0); - if (inorder (pred, *ssb, *ssa)) + if (inorder (ms, ssb.keys[0], ssa.keys[0])) { - *dest++ = *ssb++ ; + sortslice_copy_incr (&dest, &ssb); ++bcount; acount = 0; --nb; @@ -532,7 +681,7 @@ merge_lo (merge_state *ms, Lisp_Object *ssa, ptrdiff_t na, Lisp_Object *ssb, } else { - *dest++ = *ssa++; + sortslice_copy_incr (&dest, &ssa); ++acount; bcount = 0; --na; @@ -552,13 +701,13 @@ merge_lo (merge_state *ms, Lisp_Object *ssa, ptrdiff_t na, Lisp_Object *ssb, eassume (na > 1 && nb > 0); min_gallop -= min_gallop > 1; ms->min_gallop = min_gallop; - ptrdiff_t k = gallop_right (ms, ssb[0], ssa, na, 0); + ptrdiff_t k = gallop_right (ms, ssb.keys[0], ssa.keys, na, 0); acount = k; if (k) { - memcpy (dest, ssa, k * word_size); - dest += k; - ssa += k; + sortslice_memcpy (&dest, 0, &ssa, 0, k); + sortslice_advance (&dest, k); + sortslice_advance (&ssa, k); na -= k; if (na == 1) goto CopyB; @@ -567,23 +716,23 @@ merge_lo (merge_state *ms, Lisp_Object *ssa, ptrdiff_t na, Lisp_Object *ssb, if (na == 0) goto Succeed; } - *dest++ = *ssb++ ; + sortslice_copy_incr (&dest, &ssb); --nb; if (nb == 0) goto Succeed; - k = gallop_left (ms, ssa[0], ssb, nb, 0); + k = gallop_left (ms, ssa.keys[0], ssb.keys, nb, 0); bcount = k; if (k) { - memmove (dest, ssb, k * word_size); - dest += k; - ssb += k; + sortslice_memmove (&dest, 0, &ssb, 0, k); + sortslice_advance (&dest, k); + sortslice_advance (&ssb, k); nb -= k; if (nb == 0) goto Succeed; } - *dest++ = *ssa++; + sortslice_copy_incr (&dest, &ssa); --na; if (na == 1) goto CopyB; @@ -595,15 +744,15 @@ merge_lo (merge_state *ms, Lisp_Object *ssa, ptrdiff_t na, Lisp_Object *ssb, ms->reloc = (struct reloc){NULL, NULL, NULL, 0}; if (na) - memcpy (dest, ssa, na * word_size); + sortslice_memcpy(&dest, 0, &ssa, 0, na); return; CopyB: eassume (na == 1 && nb > 0); ms->reloc = (struct reloc){NULL, NULL, NULL, 0}; /* The last element of ssa belongs at the end of the merge. */ - memmove (dest, ssb, nb * word_size); - dest[nb] = ssa[0]; + sortslice_memmove (&dest, 0, &ssb, 0, nb); + sortslice_copy (&dest, nb, &ssa, 0); } @@ -613,25 +762,25 @@ merge_lo (merge_state *ms, Lisp_Object *ssa, ptrdiff_t na, Lisp_Object *ssb, NB. */ static void -merge_hi (merge_state *ms, Lisp_Object *ssa, ptrdiff_t na, - Lisp_Object *ssb, ptrdiff_t nb) +merge_hi (merge_state *ms, sortslice ssa, ptrdiff_t na, + sortslice ssb, ptrdiff_t nb) { - Lisp_Object pred = ms->predicate; - - eassume (ms && ssa && ssb && na > 0 && nb > 0); - eassume (ssa + na == ssb); + eassume (ms && ssa.keys && ssb.keys && na > 0 && nb > 0); + eassume (ssa.keys + na == ssb.keys); needmem (ms, nb); - Lisp_Object *dest = ssb; - dest += nb - 1; - memcpy(ms->a, ssb, nb * word_size); - Lisp_Object *basea = ssa; - Lisp_Object *baseb = ms->a; - ssb = ms->a + nb - 1; - ssa += na - 1; + sortslice dest = ssb; + sortslice_advance (&dest, nb-1); + sortslice_memcpy (&ms->a, 0, &ssb, 0, nb); + sortslice basea = ssa; + sortslice baseb = ms->a; + ssb.keys = ms->a.keys + nb - 1; + if (ssb.values != NULL) + ssb.values = ms->a.values + nb - 1; + sortslice_advance (&ssa, na - 1); ms->reloc = (struct reloc){&baseb, &dest, &nb, 1}; - *dest-- = *ssa--; + sortslice_copy_decr (&dest, &ssa); --na; if (na == 0) goto Succeed; @@ -645,9 +794,9 @@ merge_hi (merge_state *ms, Lisp_Object *ssa, ptrdiff_t na, for (;;) { eassume (na > 0 && nb > 1); - if (inorder (pred, *ssb, *ssa)) + if (inorder (ms, ssb.keys[0], ssa.keys[0])) { - *dest-- = *ssa--; + sortslice_copy_decr (&dest, &ssa); ++acount; bcount = 0; --na; @@ -658,7 +807,7 @@ merge_hi (merge_state *ms, Lisp_Object *ssa, ptrdiff_t na, } else { - *dest-- = *ssb--; + sortslice_copy_decr (&dest, &ssb); ++bcount; acount = 0; --nb; @@ -677,31 +826,31 @@ merge_hi (merge_state *ms, Lisp_Object *ssa, ptrdiff_t na, eassume (na > 0 && nb > 1); min_gallop -= min_gallop > 1; ms->min_gallop = min_gallop; - ptrdiff_t k = gallop_right (ms, ssb[0], basea, na, na - 1); + ptrdiff_t k = gallop_right (ms, ssb.keys[0], basea.keys, na, na - 1); k = na - k; acount = k; if (k) { - dest += -k; - ssa += -k; - memmove(dest + 1, ssa + 1, k * word_size); + sortslice_advance (&dest, -k); + sortslice_advance (&ssa, -k); + sortslice_memmove (&dest, 1, &ssa, 1, k); na -= k; if (na == 0) goto Succeed; } - *dest-- = *ssb--; + sortslice_copy_decr(&dest, &ssb); --nb; if (nb == 1) goto CopyA; - k = gallop_left (ms, ssa[0], baseb, nb, nb - 1); + k = gallop_left (ms, ssa.keys[0], baseb.keys, nb, nb - 1); k = nb - k; bcount = k; if (k) { - dest += -k; - ssb += -k; - memcpy(dest + 1, ssb + 1, k * word_size); + sortslice_advance (&dest, -k); + sortslice_advance (&ssb, -k); + sortslice_memcpy (&dest, 1, &ssb, 1, k); nb -= k; if (nb == 1) goto CopyA; @@ -710,7 +859,7 @@ merge_hi (merge_state *ms, Lisp_Object *ssa, ptrdiff_t na, if (nb == 0) goto Succeed; } - *dest-- = *ssa--; + sortslice_copy_decr (&dest, &ssa); --na; if (na == 0) goto Succeed; @@ -721,16 +870,16 @@ merge_hi (merge_state *ms, Lisp_Object *ssa, ptrdiff_t na, Succeed: ms->reloc = (struct reloc){NULL, NULL, NULL, 0}; if (nb) - memcpy (dest - nb + 1, baseb, nb * word_size); + sortslice_memcpy (&dest, -(nb-1), &baseb, 0, nb); return; CopyA: eassume (nb == 1 && na > 0); ms->reloc = (struct reloc){NULL, NULL, NULL, 0}; /* The first element of ssb belongs at the front of the merge. */ - memmove (dest + 1 - na, ssa + 1 - na, na * word_size); - dest += -na; - ssa += -na; - dest[0] = ssb[0]; + sortslice_memmove (&dest, 1-na, &ssa, 1-na, na); + sortslice_advance (&dest, -na); + sortslice_advance (&ssa, -na); + sortslice_copy (&dest, 0, &ssb, 0); } @@ -744,12 +893,12 @@ merge_at (merge_state *ms, const ptrdiff_t i) eassume (i >= 0); eassume (i == ms->n - 2 || i == ms->n - 3); - Lisp_Object *ssa = ms->pending[i].base; + sortslice ssa = ms->pending[i].base; ptrdiff_t na = ms->pending[i].len; - Lisp_Object *ssb = ms->pending[i + 1].base; + sortslice ssb = ms->pending[i + 1].base; ptrdiff_t nb = ms->pending[i + 1].len; eassume (na > 0 && nb > 0); - eassume (ssa + na == ssb); + eassume (ssa.keys + na == ssb.keys); /* Record the length of the combined runs. The current run i+1 goes away after the merge. If i is the 3rd-last run now, slide the @@ -761,16 +910,16 @@ merge_at (merge_state *ms, const ptrdiff_t i) /* Where does b start in a? Elements in a before that can be ignored (they are already in place). */ - ptrdiff_t k = gallop_right (ms, *ssb, ssa, na, 0); + ptrdiff_t k = gallop_right (ms, *ssb.keys, ssa.keys, na, 0); eassume (k >= 0); - ssa += k; + sortslice_advance (&ssa, k); na -= k; if (na == 0) return; /* Where does a end in b? Elements in b after that can be ignored (they are already in place). */ - nb = gallop_left (ms, ssa[na - 1], ssb, nb, nb - 1); + nb = gallop_left (ms, ssa.keys[na - 1], ssb.keys, nb, nb - 1); if (nb == 0) return; eassume (nb > 0); @@ -841,7 +990,7 @@ found_new_run (merge_state *ms, const ptrdiff_t n2) { eassume (ms->n > 0); struct stretch *p = ms->pending; - ptrdiff_t s1 = p[ms->n - 1].base - ms->listbase; + ptrdiff_t s1 = p[ms->n - 1].base.keys - ms->basekeys; ptrdiff_t n1 = p[ms->n - 1].len; int power = powerloop (s1, n1, n2, ms->listlen); while (ms->n > 1 && p[ms->n - 2].power > power) @@ -898,39 +1047,80 @@ merge_compute_minrun (ptrdiff_t n) static void -reverse_vector (Lisp_Object *s, const ptrdiff_t n) +reverse_sortslice (sortslice *s, const ptrdiff_t n) { - for (ptrdiff_t i = 0; i < n >> 1; i++) - { - Lisp_Object tem = s[i]; - s[i] = s[n - i - 1]; - s[n - i - 1] = tem; - } + reverse_slice(s->keys, &s->keys[n]); + if (s->values != NULL) + reverse_slice(s->values, &s->values[n]); } -/* Sort the array SEQ with LENGTH elements in the order determined by - PREDICATE. */ - -void -tim_sort (Lisp_Object predicate, Lisp_Object *seq, const ptrdiff_t length) +static Lisp_Object +resolve_fun (Lisp_Object fun) { - if (SYMBOLP (predicate)) + if (SYMBOLP (fun)) { /* Attempt to resolve the function as far as possible ahead of time, to avoid having to do it for each call. */ - Lisp_Object fun = XSYMBOL (predicate)->u.s.function; - if (SYMBOLP (fun)) + Lisp_Object f = XSYMBOL (fun)->u.s.function; + if (SYMBOLP (f)) /* Function was an alias; use slow-path resolution. */ - fun = indirect_function (fun); + f = indirect_function (f); /* Don't resolve to an autoload spec; that would be very slow. */ - if (!NILP (fun) && !(CONSP (fun) && EQ (XCAR (fun), Qautoload))) - predicate = fun; + if (!NILP (f) && !(CONSP (f) && EQ (XCAR (f), Qautoload))) + fun = f; } + return fun; +} +/* Sort the array SEQ with LENGTH elements in the order determined by + PREDICATE (where Qnil means value<) and KEYFUNC (where Qnil means identity), + optionally reversed. */ +void +tim_sort (Lisp_Object predicate, Lisp_Object keyfunc, + Lisp_Object *seq, const ptrdiff_t length, bool reverse) +{ + /* FIXME: hoist this to the caller? */ + if (EQ (predicate, Qvaluelt)) + predicate = Qnil; + if (!NILP (predicate)) + predicate = resolve_fun (predicate); + if (EQ (keyfunc, Qidentity)) + keyfunc = Qnil; + + sortslice lo; + Lisp_Object *keys; + Lisp_Object *allocated_keys = NULL; merge_state ms; - Lisp_Object *lo = seq; - merge_init (&ms, length, lo, predicate); + if (reverse) + reverse_slice (seq, seq + length); /* preserve stability */ + + if (NILP (keyfunc)) + { + keys = NULL; + lo.keys = seq; + lo.values = NULL; + } + else + { + keyfunc = resolve_fun (keyfunc); + if (length < MERGESTATE_TEMP_SIZE / 2) + keys = &ms.temparray[length + 1]; + else + keys = allocated_keys = xmalloc (length * word_size); + + for (ptrdiff_t i = 0; i < length; i++) + keys[i] = call1 (keyfunc, seq[i]); + + lo.keys = keys; + lo.values = seq; + } + + /* FIXME: This is where we would check the keys for interesting + properties for more optimised comparison (such as all being fixnums + etc). */ + + merge_init (&ms, length, allocated_keys, &lo, predicate); /* March over the array once, left to right, finding natural runs, and extending short natural runs to minrun elements. */ @@ -940,19 +1130,19 @@ tim_sort (Lisp_Object predicate, Lisp_Object *seq, const ptrdiff_t length) bool descending; /* Identify the next run. */ - ptrdiff_t n = count_run (&ms, lo, lo + nremaining, &descending); + ptrdiff_t n = count_run (&ms, lo.keys, lo.keys + nremaining, &descending); if (descending) - reverse_vector (lo, n); + reverse_sortslice (&lo, n); /* If the run is short, extend it to min(minrun, nremaining). */ if (n < minrun) { - const ptrdiff_t force = nremaining <= minrun ? - nremaining : minrun; - binarysort (&ms, lo, lo + force, lo + n); + const ptrdiff_t force = min (nremaining, minrun); + binarysort (&ms, lo, lo.keys + force, lo.keys + n); n = force; } - eassume (ms.n == 0 || ms.pending[ms.n - 1].base + - ms.pending[ms.n - 1].len == lo); + eassume (ms.n == 0 + || (ms.pending[ms.n - 1].base.keys + ms.pending[ms.n - 1].len + == lo.keys)); found_new_run (&ms, n); /* Push the new run on to the stack. */ eassume (ms.n < MAX_MERGE_PENDING); @@ -960,7 +1150,7 @@ tim_sort (Lisp_Object predicate, Lisp_Object *seq, const ptrdiff_t length) ms.pending[ms.n].len = n; ++ms.n; /* Advance to find the next run. */ - lo += n; + sortslice_advance(&lo, n); nremaining -= n; } while (nremaining); @@ -969,6 +1159,9 @@ tim_sort (Lisp_Object predicate, Lisp_Object *seq, const ptrdiff_t length) eassume (ms.pending[0].len == length); lo = ms.pending[0].base; - if (ms.a != ms.temparray) + if (reverse) + reverse_slice (seq, seq + length); + + if (ms.a.keys != ms.temparray || allocated_keys != NULL) unbind_to (ms.count, Qnil); } |