summaryrefslogtreecommitdiff
path: root/src/db/db_sort_multiple.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/db/db_sort_multiple.c')
-rw-r--r--src/db/db_sort_multiple.c327
1 files changed, 327 insertions, 0 deletions
diff --git a/src/db/db_sort_multiple.c b/src/db/db_sort_multiple.c
new file mode 100644
index 00000000..c5e2e941
--- /dev/null
+++ b/src/db/db_sort_multiple.c
@@ -0,0 +1,327 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1996, 2012 Oracle and/or its affiliates. All rights reserved.
+ */
+
+#include "db_config.h"
+
+#include "db_int.h"
+#include "dbinc/db_page.h"
+#include "dbinc/btree.h"
+
+static int __db_quicksort __P((DB *, DBT *, DBT *, u_int32_t *, u_int32_t *,
+ u_int32_t *, u_int32_t *, u_int32_t));
+
+/*
+ * __db_compare_both --
+ * Use the comparison functions from db to compare akey and bkey, and if
+ * DB_DUPSORT adata and bdata.
+ *
+ * PUBLIC: int __db_compare_both __P((DB *, const DBT *, const DBT *,
+ * PUBLIC: const DBT *, const DBT *));
+ */
+int
+__db_compare_both(db, akey, adata, bkey, bdata)
+ DB *db;
+ const DBT *akey;
+ const DBT *adata;
+ const DBT *bkey;
+ const DBT *bdata;
+{
+ BTREE *t;
+ int cmp;
+
+ t = (BTREE *)db->bt_internal;
+
+ cmp = t->bt_compare(db, akey, bkey);
+ if (cmp != 0) return cmp;
+ if (!F_ISSET(db, DB_AM_DUPSORT))
+ return (0);
+
+ if (adata == 0) return bdata == 0 ? 0 : -1;
+ if (bdata == 0) return 1;
+
+#ifdef HAVE_COMPRESSION
+ if (DB_IS_COMPRESSED(db))
+ return t->compress_dup_compare(db, adata, bdata);
+#endif
+ return db->dup_compare(db, adata, bdata);
+}
+
+#define DB_SORT_SWAP(a, ad, b, bd) \
+do { \
+ tmp = (a)[0]; (a)[0] = (b)[0]; (b)[0] = tmp; \
+ tmp = (a)[-1]; (a)[-1] = (b)[-1]; (b)[-1] = tmp; \
+ if (data != NULL) { \
+ tmp = (ad)[0]; (ad)[0] = (bd)[0]; (bd)[0] = tmp; \
+ tmp = (ad)[-1]; (ad)[-1] = (bd)[-1]; (bd)[-1] = tmp; \
+ } \
+} while (0)
+
+#define DB_SORT_LOAD_DBT(a, ad, aptr, adptr) \
+do { \
+ (a).data = (u_int8_t*)key->data + (aptr)[0]; \
+ (a).size = (aptr)[-1]; \
+ if (data != NULL) { \
+ (ad).data = (u_int8_t*)data->data + (adptr)[0]; \
+ (ad).size = (adptr)[-1]; \
+ } \
+} while (0)
+
+#define DB_SORT_COMPARE(a, ad, b, bd) (data != NULL ? \
+ __db_compare_both(db, &(a), &(ad), &(b), &(bd)) : \
+ __db_compare_both(db, &(a), 0, &(b), 0))
+
+#define DB_SORT_STACKSIZE 32
+
+/*
+ * __db_quicksort --
+ * The quicksort implementation for __db_sort_multiple() and
+ * __db_sort_multiple_key().
+ */
+static int
+__db_quicksort(db, key, data, kstart, kend, dstart, dend, size)
+ DB *db;
+ DBT *key, *data;
+ u_int32_t *kstart, *kend, *dstart, *dend;
+ u_int32_t size;
+{
+ int ret, cmp;
+ u_int32_t tmp, len;
+ u_int32_t *kptr, *dptr, *kl, *dl, *kr, *dr;
+ DBT a, ad, b, bd, m, md;
+ ENV *env;
+
+ struct DB_SORT_quicksort_stack {
+ u_int32_t *kstart;
+ u_int32_t *kend;
+ u_int32_t *dstart;
+ u_int32_t *dend;
+ } stackbuf[DB_SORT_STACKSIZE], *stack;
+ u_int32_t soff, slen;
+
+ ret = 0;
+ env = db->env;
+
+ memset(&a, 0, sizeof(DBT));
+ memset(&ad, 0, sizeof(DBT));
+ memset(&b, 0, sizeof(DBT));
+ memset(&bd, 0, sizeof(DBT));
+ memset(&m, 0, sizeof(DBT));
+ memset(&md, 0, sizeof(DBT));
+
+ /* NB end is smaller than start */
+
+ stack = stackbuf;
+ soff = 0;
+ slen = DB_SORT_STACKSIZE;
+
+ start:
+ if (kend >= kstart) goto pop;
+
+ /* If there's only one value, it's already sorted */
+ len = (u_int32_t)(kstart - kend) / size;
+ if (len == 1) goto pop;
+
+ DB_SORT_LOAD_DBT(a, ad, kstart, dstart);
+ DB_SORT_LOAD_DBT(b, bd, kend + size, dend + size);
+
+ if (len == 2) {
+ /* Special case the sorting of two value sequences */
+ if (DB_SORT_COMPARE(a, ad, b, bd) > 0) {
+ DB_SORT_SWAP(kstart, dstart, kend + size,
+ dend + size);
+ }
+ goto pop;
+ }
+
+ kptr = kstart - (len / 2) * size;
+ dptr = dstart - (len / 2) * size;
+ DB_SORT_LOAD_DBT(m, md, kptr, dptr);
+
+ /* Find the median of three */
+ if (DB_SORT_COMPARE(a, ad, b, bd) < 0) {
+ if (DB_SORT_COMPARE(m, md, a, ad) < 0) {
+ /* m < a < b */
+ if (len == 3) {
+ DB_SORT_SWAP(kstart, dstart, kptr, dptr);
+ goto pop;
+ }
+ DB_SORT_SWAP(kstart, dstart, kend + size, dend + size);
+ } else if (DB_SORT_COMPARE(m, md, b, bd) < 0) {
+ /* a <= m < b */
+ if (len == 3) {
+ goto pop;
+ }
+ DB_SORT_SWAP(kptr, dptr, kend + size, dend + size);
+ } else {
+ /* a < b <= m */
+ if (len == 3) {
+ DB_SORT_SWAP(kptr, dptr, kend + size,
+ dend + size);
+ goto pop;
+ }
+ /* Do nothing */
+ }
+ } else {
+ if (DB_SORT_COMPARE(a, ad, m, md) < 0) {
+ /* b <= a < m */
+ DB_SORT_SWAP(kstart, dstart, kend + size,
+ dend + size);
+ if (len == 3) {
+ DB_SORT_SWAP(kptr, dptr, kend + size,
+ dend + size);
+ goto pop;
+ }
+ } else if (DB_SORT_COMPARE(b, bd, m, md) < 0) {
+ /* b < m <= a */
+ if (len == 3) {
+ DB_SORT_SWAP(kstart, dstart, kend + size,
+ dend + size);
+ goto pop;
+ }
+ DB_SORT_SWAP(kptr, dptr, kend + size, dend + size);
+ } else {
+ /* m <= b <= a */
+ if (len == 3) {
+ DB_SORT_SWAP(kstart, dstart, kptr, dptr);
+ DB_SORT_SWAP(kptr, dptr, kend + size,
+ dend + size);
+ goto pop;
+ }
+ /* Do nothing */
+ }
+ }
+
+ /* partition */
+ DB_SORT_LOAD_DBT(b, bd, kend + size, dend + size);
+ kl = kstart;
+ dl = dstart;
+ kr = kend + size;
+ dr = dend + size;
+ kptr = kstart;
+ dptr = dstart;
+ while (kptr >= kr) {
+ DB_SORT_LOAD_DBT(a, ad, kptr, dptr);
+ cmp = DB_SORT_COMPARE(a, ad, b, bd);
+ if (cmp < 0) {
+ DB_SORT_SWAP(kl, dl, kptr, dptr);
+ kl -= size;
+ dl -= size;
+ kptr -= size;
+ dptr -= size;
+ } else if (cmp > 0) {
+ DB_SORT_SWAP(kr, dr, kptr, dptr);
+ kr += size;
+ dr += size;
+ } else {
+ kptr -= size;
+ dptr -= size;
+ }
+ }
+
+ if (soff == slen) {
+ /* Grow the stack */
+ slen = slen * 2;
+ if (stack == stackbuf) {
+ ret = __os_malloc(env, slen *
+ sizeof(struct DB_SORT_quicksort_stack), &stack);
+ if (ret != 0) goto error;
+ memcpy(stack, stackbuf, soff *
+ sizeof(struct DB_SORT_quicksort_stack));
+ } else {
+ ret = __os_realloc(env, slen *
+ sizeof(struct DB_SORT_quicksort_stack), &stack);
+ if (ret != 0) goto error;
+ }
+ }
+
+ /* divide and conquer */
+ stack[soff].kstart = kr - size;
+ stack[soff].kend = kend;
+ stack[soff].dstart = dr - size;
+ stack[soff].dend = dend;
+ ++soff;
+
+ kend = kl;
+ dend = dl;
+
+ goto start;
+
+ pop:
+ if (soff != 0) {
+ --soff;
+ kstart = stack[soff].kstart;
+ kend = stack[soff].kend;
+ dstart = stack[soff].dstart;
+ dend = stack[soff].dend;
+ goto start;
+ }
+
+ error:
+ if (stack != stackbuf)
+ __os_free(env, stack);
+
+ return (ret);
+}
+
+#undef DB_SORT_SWAP
+#undef DB_SORT_LOAD_DBT
+
+/*
+ * __db_sort_multiple --
+ * If flags == DB_MULTIPLE_KEY, sorts a DB_MULTIPLE_KEY format DBT using
+ * the BTree comparison function and duplicate comparison function.
+ *
+ * If flags == DB_MULTIPLE, sorts one or two DB_MULTIPLE format DBTs using
+ * the BTree comparison function and duplicate comparison function. Will
+ * assume key and data specifies pairs of key/data to sort together. If
+ * data is NULL, will just sort key according to the btree comparison
+ * function.
+ *
+ * Uses an in-place quicksort algorithm, with median of three for the pivot
+ * point.
+ *
+ * PUBLIC: int __db_sort_multiple __P((DB *, DBT *, DBT *, u_int32_t));
+ */
+int
+__db_sort_multiple(db, key, data, flags)
+ DB *db;
+ DBT *key, *data;
+ u_int32_t flags;
+{
+ u_int32_t *kstart, *kend, *dstart, *dend;
+
+ /* TODO: sanity checks on the DBTs */
+ /* DB_ILLEGAL_METHOD(db, DB_OK_BTREE); */
+
+ kstart = (u_int32_t*)((u_int8_t *)key->data + key->ulen) - 1;
+
+ switch (flags) {
+ case DB_MULTIPLE:
+ if (data != NULL)
+ dstart = (u_int32_t*)((u_int8_t *)data->data +
+ data->ulen) - 1;
+ else
+ dstart = kstart;
+
+ /* Find the end */
+ for (kend = kstart, dend = dstart;
+ *kend != (u_int32_t)-1 && *dend != (u_int32_t)-1;
+ kend -= 2, dend -= 2)
+ ;
+
+ return (__db_quicksort(db, key, data, kstart, kend, dstart,
+ dend, 2));
+ case DB_MULTIPLE_KEY:
+ /* Find the end */
+ for (kend = kstart; *kend != (u_int32_t)-1; kend -= 4)
+ ;
+
+ return (__db_quicksort(db, key, key, kstart, kend, kstart - 2,
+ kend - 2, 4));
+ default:
+ return (__db_ferr(db->env, "DB->sort_multiple", 0));
+ }
+}