Commit 68acf666 authored by Vincenzo Liberatore's avatar Vincenzo Liberatore

Closes #855 Merged back into trunk

git-svn-id: file:///svn/tokudb@4308 c7de825b-a66e-492c-adef-691d508d4ae1
parent b12decff
...@@ -13,8 +13,6 @@ ...@@ -13,8 +13,6 @@
#include "kv-pair.h" #include "kv-pair.h"
#include "leafentry.h" #include "leafentry.h"
typedef void* OMTVALUE;
#include "omt.h" #include "omt.h"
#ifndef BRT_FANOUT #ifndef BRT_FANOUT
......
...@@ -91,7 +91,7 @@ int toku_testsetup_insert_to_leaf (BRT brt, DISKOFF diskoff, char *key, int keyl ...@@ -91,7 +91,7 @@ int toku_testsetup_insert_to_leaf (BRT brt, DISKOFF diskoff, char *key, int keyl
BRT_CMD_S cmd = {BRT_INSERT, 0, .u.id={toku_fill_dbt(&keydbt, key, keylen), BRT_CMD_S cmd = {BRT_INSERT, 0, .u.id={toku_fill_dbt(&keydbt, key, keylen),
toku_fill_dbt(&valdbt, val, vallen)}}; toku_fill_dbt(&valdbt, val, vallen)}};
struct cmd_leafval_bessel_extra be = {brt, &cmd, node->flags & TOKU_DB_DUPSORT}; struct cmd_leafval_bessel_extra be = {brt, &cmd, node->flags & TOKU_DB_DUPSORT};
r = toku_omt_find_zero(node->u.l.buffer, toku_cmd_leafval_bessel, &be, &storeddatav, &idx); r = toku_omt_find_zero(node->u.l.buffer, toku_cmd_leafval_bessel, &be, &storeddatav, &idx, NULL);
if (r==0) { if (r==0) {
......
...@@ -469,7 +469,7 @@ static int brtleaf_split (TOKULOGGER logger, FILENUM filenum, BRT t, BRTNODE nod ...@@ -469,7 +469,7 @@ static int brtleaf_split (TOKULOGGER logger, FILENUM filenum, BRT t, BRTNODE nod
if (splitk) { if (splitk) {
memset(splitk, 0, sizeof *splitk); memset(splitk, 0, sizeof *splitk);
OMTVALUE lev; OMTVALUE lev;
r=toku_omt_fetch(node->u.l.buffer, toku_omt_size(node->u.l.buffer)-1, &lev); r=toku_omt_fetch(node->u.l.buffer, toku_omt_size(node->u.l.buffer)-1, &lev, NULL);
assert(r==0); // that fetch should have worked. assert(r==0); // that fetch should have worked.
LEAFENTRY le=lev; LEAFENTRY le=lev;
if (node->flags&TOKU_DB_DUPSORT) { if (node->flags&TOKU_DB_DUPSORT) {
...@@ -1461,7 +1461,7 @@ static int brt_leaf_put_cmd (BRT t, BRTNODE node, BRT_CMD cmd, ...@@ -1461,7 +1461,7 @@ static int brt_leaf_put_cmd (BRT t, BRTNODE node, BRT_CMD cmd,
FILENUM filenum = toku_cachefile_filenum(t->cf); FILENUM filenum = toku_cachefile_filenum(t->cf);
LEAFENTRY storeddata; LEAFENTRY storeddata;
OMTVALUE storeddatav; OMTVALUE storeddatav=NULL; // TODO BBB This is not entirely safe. Verify initialization needed.
u_int32_t idx; u_int32_t idx;
int r; int r;
...@@ -1476,7 +1476,7 @@ static int brt_leaf_put_cmd (BRT t, BRTNODE node, BRT_CMD cmd, ...@@ -1476,7 +1476,7 @@ static int brt_leaf_put_cmd (BRT t, BRTNODE node, BRT_CMD cmd,
case BRT_INSERT: case BRT_INSERT:
r = toku_omt_find_zero(node->u.l.buffer, toku_cmd_leafval_bessel, &be, r = toku_omt_find_zero(node->u.l.buffer, toku_cmd_leafval_bessel, &be,
&storeddatav, &idx); &storeddatav, &idx, NULL);
if (r==DB_NOTFOUND) { if (r==DB_NOTFOUND) {
storeddata = 0; storeddata = 0;
} else if (r!=0) { } else if (r!=0) {
...@@ -1494,7 +1494,7 @@ static int brt_leaf_put_cmd (BRT t, BRTNODE node, BRT_CMD cmd, ...@@ -1494,7 +1494,7 @@ static int brt_leaf_put_cmd (BRT t, BRTNODE node, BRT_CMD cmd,
// Delete the one item // Delete the one item
r = toku_omt_find_zero(node->u.l.buffer, toku_cmd_leafval_bessel, &be, r = toku_omt_find_zero(node->u.l.buffer, toku_cmd_leafval_bessel, &be,
&storeddatav, &idx); &storeddatav, &idx, NULL);
if (r == DB_NOTFOUND) break; if (r == DB_NOTFOUND) break;
if (r != 0) return r; if (r != 0) return r;
storeddata=storeddatav; storeddata=storeddatav;
...@@ -1515,7 +1515,7 @@ static int brt_leaf_put_cmd (BRT t, BRTNODE node, BRT_CMD cmd, ...@@ -1515,7 +1515,7 @@ static int brt_leaf_put_cmd (BRT t, BRTNODE node, BRT_CMD cmd,
// Delete all the matches // Delete all the matches
r = toku_omt_find_zero(node->u.l.buffer, toku_cmd_leafval_bessel, &be, r = toku_omt_find_zero(node->u.l.buffer, toku_cmd_leafval_bessel, &be,
&storeddatav, &idx); &storeddatav, &idx, NULL);
if (r == DB_NOTFOUND) break; if (r == DB_NOTFOUND) break;
if (r != 0) return r; if (r != 0) return r;
storeddata=storeddatav; storeddata=storeddatav;
...@@ -1532,7 +1532,7 @@ static int brt_leaf_put_cmd (BRT t, BRTNODE node, BRT_CMD cmd, ...@@ -1532,7 +1532,7 @@ static int brt_leaf_put_cmd (BRT t, BRTNODE node, BRT_CMD cmd,
BRT_CMD_S ncmd = { cmd->type, cmd->xid, .u.id={cmd->u.id.key, toku_fill_dbt(&valdbt, save_val, vallen)}}; BRT_CMD_S ncmd = { cmd->type, cmd->xid, .u.id={cmd->u.id.key, toku_fill_dbt(&valdbt, save_val, vallen)}};
struct cmd_leafval_bessel_extra nbe = {t, &ncmd, 1}; struct cmd_leafval_bessel_extra nbe = {t, &ncmd, 1};
r = toku_omt_find(node->u.l.buffer, toku_cmd_leafval_bessel, &nbe, +1, r = toku_omt_find(node->u.l.buffer, toku_cmd_leafval_bessel, &nbe, +1,
&storeddatav, &idx); &storeddatav, &idx, NULL);
toku_free(save_val); toku_free(save_val);
if (r!=0) break; if (r!=0) break;
...@@ -2708,7 +2708,7 @@ static int brt_search_leaf_node(BRT brt, BRTNODE node, brt_search_t *search, DBT ...@@ -2708,7 +2708,7 @@ static int brt_search_leaf_node(BRT brt, BRTNODE node, brt_search_t *search, DBT
bessel_from_search_t, bessel_from_search_t,
search, search,
direction, direction,
&datav, &idx); &datav, &idx, NULL);
if (r!=0) return r; if (r!=0) return r;
LEAFENTRY le = datav; LEAFENTRY le = datav;
...@@ -2727,7 +2727,7 @@ static int brt_search_leaf_node(BRT brt, BRTNODE node, brt_search_t *search, DBT ...@@ -2727,7 +2727,7 @@ static int brt_search_leaf_node(BRT brt, BRTNODE node, brt_search_t *search, DBT
break; break;
} }
if (idx>=toku_omt_size(node->u.l.buffer)) continue; if (idx>=toku_omt_size(node->u.l.buffer)) continue;
r = toku_omt_fetch(node->u.l.buffer, idx, &datav); r = toku_omt_fetch(node->u.l.buffer, idx, &datav, NULL);
assert(r==0); // we just validated the index assert(r==0); // we just validated the index
le = datav; le = datav;
if (!le_is_provdel(le)) goto got_a_good_value; if (!le_is_provdel(le)) goto got_a_good_value;
...@@ -3246,12 +3246,14 @@ static void toku_brt_keyrange_internal (BRT brt, CACHEKEY nodename, DBT *key, u_ ...@@ -3246,12 +3246,14 @@ static void toku_brt_keyrange_internal (BRT brt, CACHEKEY nodename, DBT *key, u_
BRT_CMD_S cmd = { BRT_INSERT, 0, .u.id={key,0}}; BRT_CMD_S cmd = { BRT_INSERT, 0, .u.id={key,0}};
struct cmd_leafval_bessel_extra be = {brt, &cmd, 0}; struct cmd_leafval_bessel_extra be = {brt, &cmd, 0};
u_int32_t idx; u_int32_t idx;
int r = toku_omt_find_zero(node->u.l.buffer, toku_cmd_leafval_bessel, &be, 0, &idx); int r = toku_omt_find_zero(node->u.l.buffer, toku_cmd_leafval_bessel, &be, 0, &idx, NULL);
// TODO: Check for r==ENOMEM if the last argument (cursor) is not NULL
// (history: we changed find_zero to support cursor, and now find_zero can fail if the cursor cannot grow)
*less += idx; *less += idx;
if (r==0 && (brt->flags & TOKU_DB_DUP)) { if (r==0 && (brt->flags & TOKU_DB_DUP)) {
// There is something, and so we now want to find the rightmost extent. // There is something, and so we now want to find the rightmost extent.
u_int32_t idx2; u_int32_t idx2;
r = toku_omt_find(node->u.l.buffer, toku_cmd_leafval_bessel, &be, +1, 0, &idx2); r = toku_omt_find(node->u.l.buffer, toku_cmd_leafval_bessel, &be, +1, 0, &idx2, NULL);
if (r==0) { if (r==0) {
*greater += toku_omt_size(node->u.l.buffer)-idx2; *greater += toku_omt_size(node->u.l.buffer)-idx2;
*equal += idx2-idx; *equal += idx2-idx;
......
...@@ -1022,7 +1022,7 @@ static int find_filenum (OMTVALUE v, void *brtv) { ...@@ -1022,7 +1022,7 @@ static int find_filenum (OMTVALUE v, void *brtv) {
int toku_txn_note_brt (TOKUTXN txn, BRT brt) { int toku_txn_note_brt (TOKUTXN txn, BRT brt) {
OMTVALUE txnv; OMTVALUE txnv;
u_int32_t index; u_int32_t index;
int r = toku_omt_find_zero(brt->txns, find_ptr, txn, &txnv, &index); int r = toku_omt_find_zero(brt->txns, find_ptr, txn, &txnv, &index, NULL);
if (r==0) { if (r==0) {
// It's already there. // It's already there.
assert((TOKUTXN)txnv==txn); assert((TOKUTXN)txnv==txn);
...@@ -1039,9 +1039,9 @@ int toku_txn_note_brt (TOKUTXN txn, BRT brt) { ...@@ -1039,9 +1039,9 @@ int toku_txn_note_brt (TOKUTXN txn, BRT brt) {
static int remove_brt (OMTVALUE txnv, u_int32_t UU(idx), void *brtv) { static int remove_brt (OMTVALUE txnv, u_int32_t UU(idx), void *brtv) {
TOKUTXN txn = txnv; TOKUTXN txn = txnv;
BRT brt = brtv; BRT brt = brtv;
OMTVALUE brtv_again; OMTVALUE brtv_again=0; // TODO BBB This is not entirely safe. Verify initialization needed.
u_int32_t index; u_int32_t index;
int r = toku_omt_find_zero(txn->open_brts, find_filenum, brt, &brtv_again, &index); int r = toku_omt_find_zero(txn->open_brts, find_filenum, brt, &brtv_again, &index, NULL);
assert(r==0); assert(r==0);
assert((void*)brtv_again==brtv); assert((void*)brtv_again==brtv);
r = toku_omt_delete_at(txn->open_brts, index); r = toku_omt_delete_at(txn->open_brts, index);
...@@ -1058,9 +1058,9 @@ int toku_txn_note_close_brt (BRT brt) { ...@@ -1058,9 +1058,9 @@ int toku_txn_note_close_brt (BRT brt) {
static int remove_txn (OMTVALUE brtv, u_int32_t UU(idx), void *txnv) { static int remove_txn (OMTVALUE brtv, u_int32_t UU(idx), void *txnv) {
BRT brt = brtv; BRT brt = brtv;
TOKUTXN txn = txnv; TOKUTXN txn = txnv;
OMTVALUE txnv_again; OMTVALUE txnv_again=0; // TODO BBB This is not entirely safe. Verify initialization needed.
u_int32_t index; u_int32_t index;
int r = toku_omt_find_zero(brt->txns, find_ptr, txn, &txnv_again, &index); int r = toku_omt_find_zero(brt->txns, find_ptr, txn, &txnv_again, &index, NULL);
assert(r==0); assert(r==0);
assert((void*)txnv_again==txnv); assert((void*)txnv_again==txnv);
r = toku_omt_delete_at(brt->txns, index); r = toku_omt_delete_at(brt->txns, index);
......
/**
\brief OMT implementation header
*/
#if !defined(OMTI_H)
#define OMTI_H
#ident "Copyright (c) 2008 Tokutek Inc. All rights reserved."
#include <stdint.h>
/** Type for the node index */
typedef u_int32_t node_idx;
/** Define a NULL index in the node array */
#define NODE_NULL UINT32_MAX
/** OMT node */
typedef struct omt_node *OMT_NODE;
struct omt_node {
u_int32_t weight; /* Size of subtree rooted at this node
(including this one). */
node_idx left; /* Index of left subtree. */
node_idx right; /* Index of right subtree. */
OMTVALUE value; /* The value stored in the node. */
};
/** Order Maintenance Tree */
struct omt {
node_idx root;
u_int32_t node_capacity;
OMT_NODE nodes;
node_idx free_idx;
u_int32_t tmparray_size;
node_idx* tmparray;
};
//Initial max size of root-to-leaf path
#define TOKU_OMTCURSOR_INITIAL_SIZE 64
// Cursor for order maintenance tree
struct omtcursor {
u_int32_t max_pathlen; //Max (root to leaf) path length;
u_int32_t pathlen; //Length of current path
node_idx *path;
OMT omt; //Associated OMT
};
#endif /* #ifndef OMTI_H */
...@@ -2,36 +2,14 @@ ...@@ -2,36 +2,14 @@
#include <errno.h> #include <errno.h>
#include <sys/types.h> #include <sys/types.h>
#include <stdint.h>
typedef void *OMTVALUE;
#include "omt.h" #include "omt.h"
#include "omt-internal.h"
#include "../newbrt/memory.h" #include "../newbrt/memory.h"
#include "../newbrt/toku_assert.h" #include "../newbrt/toku_assert.h"
#include "../include/db.h" #include "../include/db.h"
#include "../newbrt/brttypes.h" #include "../newbrt/brttypes.h"
typedef u_int32_t node_idx;
static const node_idx NODE_NULL = UINT32_MAX;
typedef struct omt_node *OMT_NODE;
struct omt_node {
u_int32_t weight; /* Size of subtree rooted at this node (including this one). */
node_idx left; /* Index of left subtree. */
node_idx right; /* Index of right subtree. */
OMTVALUE value; /* The value stored in the node. */
};
struct omt {
node_idx root;
u_int32_t node_capacity;
OMT_NODE nodes;
node_idx free_idx;
u_int32_t tmparray_size;
node_idx* tmparray;
};
static int omt_create_internal(OMT *omtp, u_int32_t num_starting_nodes) { static int omt_create_internal(OMT *omtp, u_int32_t num_starting_nodes) {
if (num_starting_nodes < 2) num_starting_nodes = 2; if (num_starting_nodes < 2) num_starting_nodes = 2;
...@@ -339,21 +317,60 @@ int toku_omt_delete_at(OMT omt, u_int32_t index) { ...@@ -339,21 +317,60 @@ int toku_omt_delete_at(OMT omt, u_int32_t index) {
return 0; return 0;
} }
static inline void fetch_internal(OMT V, node_idx idx, u_int32_t i, OMTVALUE *v) { static int omtcursor_stack_push(OMTCURSOR c, node_idx idx) {
if (c->max_pathlen-1<=c->pathlen) {
//Increase max_pathlen
u_int32_t new_max = c->max_pathlen*2;
node_idx *tmp_path = toku_realloc(c->path, new_max*sizeof(*c->path));
if (tmp_path==NULL) return errno;
c->path = tmp_path;
c->max_pathlen = new_max;
}
c->path[c->pathlen++] = idx;
return 0;
}
static node_idx omtcursor_stack_peek(OMTCURSOR c) {
return c->path[c->pathlen-1];
}
static node_idx omtcursor_stack_pop(OMTCURSOR c) {
assert(c->pathlen);
node_idx value = omtcursor_stack_peek(c);;
c->pathlen--;
return value;
}
static void omtcursor_associate(OMTCURSOR c, OMT omt) {
c->omt = omt;
c->pathlen = 0;
}
static inline int fetch_internal(OMT V, node_idx idx, u_int32_t i, OMTVALUE *v, OMTCURSOR c) {
int r;
// Add the current index to the cursor path
if (c!=NULL && (r=omtcursor_stack_push(c, idx))) return r;
/* Find the node corresponding to index idx */
OMT_NODE n = V->nodes+idx; OMT_NODE n = V->nodes+idx;
/* Visit recursively the appropriate sub-tree */
if (i < nweight(V, n->left)) { if (i < nweight(V, n->left)) {
fetch_internal(V, n->left, i, v); return fetch_internal(V, n->left, i, v, c);
} else if (i == nweight(V, n->left)) { } else if (i == nweight(V, n->left)) {
*v = n->value; *v = n->value;
} else { } else {
fetch_internal(V, n->right, i-nweight(V, n->left)-1, v); return fetch_internal(V, n->right, i-nweight(V, n->left)-1, v, c);
} }
return 0;
} }
int toku_omt_fetch(OMT V, u_int32_t i, OMTVALUE *v) { int toku_omt_fetch(OMT V, u_int32_t i, OMTVALUE *v, OMTCURSOR c) {
if (i>=nweight(V, V->root)) return ERANGE; if (i>=nweight(V, V->root)) return ERANGE;
fetch_internal(V, V->root, i, v); if (c!=NULL) omtcursor_associate(c, V);
return 0; int r = fetch_internal(V, V->root, i, v, c);
if (c!=NULL && r!=0) toku_omt_cursor_invalidate(c);
return r;
} }
static inline int iterate_internal(OMT omt, u_int32_t left, u_int32_t right, static inline int iterate_internal(OMT omt, u_int32_t left, u_int32_t right,
...@@ -381,7 +398,7 @@ int toku_omt_insert(OMT omt, OMTVALUE value, int(*h)(OMTVALUE, void*v), void *v, ...@@ -381,7 +398,7 @@ int toku_omt_insert(OMT omt, OMTVALUE value, int(*h)(OMTVALUE, void*v), void *v,
int r; int r;
u_int32_t idx; u_int32_t idx;
r = toku_omt_find_zero(omt, h, v, NULL, &idx); r = toku_omt_find_zero(omt, h, v, NULL, &idx, NULL);
if (r==0) { if (r==0) {
if (index) *index = idx; if (index) *index = idx;
return DB_KEYEXIST; return DB_KEYEXIST;
...@@ -394,80 +411,131 @@ int toku_omt_insert(OMT omt, OMTVALUE value, int(*h)(OMTVALUE, void*v), void *v, ...@@ -394,80 +411,131 @@ int toku_omt_insert(OMT omt, OMTVALUE value, int(*h)(OMTVALUE, void*v), void *v,
return 0; return 0;
} }
static inline int find_internal_zero(OMT omt, node_idx n_idx, int (*h)(OMTVALUE, void*extra), void*extra, OMTVALUE *value, u_int32_t *index) { static inline int find_internal_zero(OMT omt, node_idx n_idx, int (*h)(OMTVALUE, void*extra), void*extra, OMTVALUE *value, u_int32_t *index, OMTCURSOR c) {
int r;
if (n_idx==NODE_NULL) { if (n_idx==NODE_NULL) {
if (index!=NULL) (*index)=0; *index=0;
return DB_NOTFOUND; return DB_NOTFOUND;
} }
// Add the current index to the cursor path
if (c!=NULL && (r=omtcursor_stack_push(c, n_idx))) return r;
OMT_NODE n = omt->nodes+n_idx; OMT_NODE n = omt->nodes+n_idx;
int hv = h(n->value, extra); int hv = h(n->value, extra);
if (hv<0) { if (hv<0) {
int r = find_internal_zero(omt, n->right, h, extra, value, index); r = find_internal_zero(omt, n->right, h, extra, value, index, c);
if (index!=NULL) (*index) += nweight(omt, n->left)+1; *index += nweight(omt, n->left)+1;
return r; return r;
} else if (hv>0) { } else if (hv>0) {
return find_internal_zero(omt, n->left, h, extra, value, index); r = find_internal_zero(omt, n->left, h, extra, value, index, c);
if (c!=NULL && r==DB_NOTFOUND && *index==nweight(omt, n->left)) {
//Truncate the saved cursor path at n_idx.
while (omtcursor_stack_peek(c)!=n_idx) omtcursor_stack_pop(c);
}
return r;
} else { } else {
int r = find_internal_zero(omt, n->left, h, extra, value, index); r = find_internal_zero(omt, n->left, h, extra, value, index, c);
if (r==DB_NOTFOUND) { if (r==DB_NOTFOUND) {
if (index!=NULL) *index = nweight(omt, n->left); *index = nweight(omt, n->left);
if (value!=NULL) *value = n->value; *value = n->value;
if (c!=NULL) {
//Truncate the saved cursor path at n_idx.
while (omtcursor_stack_peek(c)!=n_idx) omtcursor_stack_pop(c);
}
r = 0; r = 0;
} }
return r; return r;
} }
} }
int toku_omt_find_zero(OMT V, int (*h)(OMTVALUE, void*extra), void*extra, OMTVALUE *value, u_int32_t *index) { int toku_omt_find_zero(OMT V, int (*h)(OMTVALUE, void*extra), void*extra, OMTVALUE *value, u_int32_t *index, OMTCURSOR c) {
return find_internal_zero(V, V->root, h, extra, value, index); if (c!=NULL) omtcursor_associate(c, V);
u_int32_t idx_tmp;
OMTVALUE val_tmp;
int r = find_internal_zero(V, V->root, h, extra, &val_tmp, &idx_tmp, c);
if (c!=NULL && ( (r!=0 && r!=DB_NOTFOUND) ||
idx_tmp==nweight(V, V->root))) {
toku_omt_cursor_invalidate(c);
}
if (c==NULL || r==0 || r==DB_NOTFOUND) {
if (index!=NULL) *index = idx_tmp;
if (value!=NULL && r==0) *value = val_tmp;
}
return r;
} }
// If direction <0 then find the largest i such that h(V_i,extra)<0. // If direction <0 then find the largest i such that h(V_i,extra)<0.
static inline int find_internal_minus(OMT omt, node_idx n_idx, int (*h)(OMTVALUE, void*extra), void*extra, OMTVALUE *value, u_int32_t *index) { static inline int find_internal_minus(OMT omt, node_idx n_idx, int (*h)(OMTVALUE, void*extra), void*extra, OMTVALUE *value, u_int32_t *index, OMTCURSOR c) {
int r;
if (n_idx==NODE_NULL) return DB_NOTFOUND; if (n_idx==NODE_NULL) return DB_NOTFOUND;
// Add the current index to the cursor path
if (c!=NULL && (r=omtcursor_stack_push(c, n_idx))) return r;
OMT_NODE n = omt->nodes+n_idx; OMT_NODE n = omt->nodes+n_idx;
int hv = h(n->value, extra); int hv = h(n->value, extra);
if (hv<0) { if (hv<0) {
int r = find_internal_minus(omt, n->right, h, extra, value, index); r = find_internal_minus(omt, n->right, h, extra, value, index, c);
if (r==0 && index!=NULL) (*index) += nweight(omt, n->left)+1; if (r==0) (*index) += nweight(omt, n->left)+1;
else if (r==DB_NOTFOUND) { else if (r==DB_NOTFOUND) {
if (index!=NULL) *index = nweight(omt, n->left); *index = nweight(omt, n->left);
if (value!=NULL) *value = n->value; *value = n->value;
if (c!=NULL) {
//Truncate the saved cursor path at n_idx.
while (omtcursor_stack_peek(c)!=n_idx) omtcursor_stack_pop(c);
}
r = 0; r = 0;
} }
return r; return r;
} else { } else {
return find_internal_minus(omt, n->left, h, extra, value, index); return find_internal_minus(omt, n->left, h, extra, value, index, c);
} }
} }
// If direction >0 then find the smallest i such that h(V_i,extra)>0. // If direction >0 then find the smallest i such that h(V_i,extra)>0.
static inline int find_internal_plus(OMT omt, node_idx n_idx, int (*h)(OMTVALUE, void*extra), void*extra, OMTVALUE *value, u_int32_t *index) { static inline int find_internal_plus(OMT omt, node_idx n_idx, int (*h)(OMTVALUE, void*extra), void*extra, OMTVALUE *value, u_int32_t *index, OMTCURSOR c) {
int r;
if (n_idx==NODE_NULL) return DB_NOTFOUND; if (n_idx==NODE_NULL) return DB_NOTFOUND;
// Add the current index to the cursor path
if (c!=NULL && (r=omtcursor_stack_push(c, n_idx))) return r;
OMT_NODE n = omt->nodes+n_idx; OMT_NODE n = omt->nodes+n_idx;
int hv = h(n->value, extra); int hv = h(n->value, extra);
if (hv>0) { if (hv>0) {
int r = find_internal_plus(omt, n->left, h, extra, value, index); r = find_internal_plus(omt, n->left, h, extra, value, index, c);
if (r==DB_NOTFOUND) { if (r==DB_NOTFOUND) {
if (index!=NULL) *index = nweight(omt, n->left); *index = nweight(omt, n->left);
if (value!=NULL) *value = n->value; *value = n->value;
if (c!=NULL) {
//Truncate the saved cursor path at n_idx.
while (omtcursor_stack_peek(c)!=n_idx) omtcursor_stack_pop(c);
}
r = 0; r = 0;
} }
return r; return r;
} else { } else {
int r = find_internal_plus(omt, n->right, h, extra, value, index); r = find_internal_plus(omt, n->right, h, extra, value, index, c);
if (r==0 && index!=NULL) (*index) += nweight(omt, n->left)+1; if (r==0) (*index) += nweight(omt, n->left)+1;
return r; return r;
} }
} }
int toku_omt_find(OMT V, int (*h)(OMTVALUE, void*extra), void*extra, int direction, OMTVALUE *value, u_int32_t *index) { int toku_omt_find(OMT V, int (*h)(OMTVALUE, void*extra), void*extra, int direction, OMTVALUE *value, u_int32_t *index, OMTCURSOR c) {
if (direction==0) { if (direction==0) {
abort(); abort();
} else if (direction<0) { }
return find_internal_minus(V, V->root, h, extra, value, index); else {
} else { int r;
return find_internal_plus( V, V->root, h, extra, value, index); u_int32_t idx_tmp;
OMTVALUE val_tmp;
if (c!=NULL) omtcursor_associate(c, V);
if (direction<0) {
r = find_internal_minus(V, V->root, h, extra, &val_tmp, &idx_tmp, c);
} else {
r = find_internal_plus( V, V->root, h, extra, &val_tmp, &idx_tmp, c);
}
if (c!=NULL && r!=0) toku_omt_cursor_invalidate(c);
if (r==0) {
if (index!=NULL) *index = idx_tmp;
if (value!=NULL) *value = val_tmp;
}
return r;
} }
} }
...@@ -524,6 +592,114 @@ void toku_omt_clear(OMT omt) { ...@@ -524,6 +592,114 @@ void toku_omt_clear(OMT omt) {
omt->root = NODE_NULL; omt->root = NODE_NULL;
} }
unsigned long toku_omt_memory_size (OMT omt) { int toku_omt_cursor_create(OMTCURSOR *p) {
OMTCURSOR MALLOC(result);
if (result==NULL) return errno;
result->max_pathlen = TOKU_OMTCURSOR_INITIAL_SIZE;
result->pathlen = 0;
MALLOC_N(result->max_pathlen, result->path);
if (result->path==NULL) {
toku_free(result);
return errno;
}
result->omt = NULL;
*p = result;
return 0;
}
void toku_omt_cursor_destroy(OMTCURSOR *p) {
OMTCURSOR c=*p;
toku_free(c->path);
toku_free(c);
*p = NULL;
}
int toku_omt_cursor_is_valid(OMTCURSOR c) {
return c->pathlen>0 && c->omt!=NULL;
}
void toku_omt_cursor_invalidate(OMTCURSOR c) {
c->pathlen = 0;
c->omt=NULL;
}
static void omtcursor_current_internal(OMTCURSOR c, OMTVALUE *v) {
*v = c->omt->nodes[omtcursor_stack_peek(c)].value;
}
int toku_omt_cursor_current(OMTCURSOR c, OMTVALUE *v) {
if (!toku_omt_cursor_is_valid(c)) return EINVAL;
omtcursor_current_internal(c, v);
return 0;
}
static int omtcursor_next_internal(OMTCURSOR c) {
if (!toku_omt_cursor_is_valid(c)) return EINVAL;
OMT_NODE current = c->omt->nodes+omtcursor_stack_peek(c);
if (current->right!=NODE_NULL) {
//Enter into subtree
if (omtcursor_stack_push(c, current->right)) goto invalidate;
current = c->omt->nodes+current->right;
while (current->left!=NODE_NULL) {
if (omtcursor_stack_push(c, current->left)) goto invalidate;
current = c->omt->nodes+current->left;
}
return 0;
}
else {
//Pop the stack till we remove a left child.
while (c->pathlen>=2) {
node_idx child_idx = omtcursor_stack_pop(c);
node_idx parent_idx = omtcursor_stack_peek(c);
if (c->omt->nodes[parent_idx].left==child_idx) return 0;
}
goto invalidate;
}
invalidate:
toku_omt_cursor_invalidate(c);
return EINVAL;
}
int toku_omt_cursor_next(OMTCURSOR c, OMTVALUE *v) {
if (omtcursor_next_internal(c)) return EINVAL;
omtcursor_current_internal(c, v);
return 0;
}
static int omtcursor_prev_internal(OMTCURSOR c) {
if (!toku_omt_cursor_is_valid(c)) return EINVAL;
OMT_NODE current = c->omt->nodes+omtcursor_stack_peek(c);
if (current->left!=NODE_NULL) {
//Enter into subtree
if (omtcursor_stack_push(c, current->left)) goto invalidate;
current = c->omt->nodes+current->left;
while (current->right!=NODE_NULL) {
if (omtcursor_stack_push(c, current->right)) goto invalidate;
current = c->omt->nodes+current->right;
}
return 0;
}
else {
//Pop the stack till we remove a right child.
while (c->pathlen>=2) {
node_idx child_idx = omtcursor_stack_pop(c);
node_idx parent_idx = omtcursor_stack_peek(c);
if (c->omt->nodes[parent_idx].right==child_idx) return 0;
}
goto invalidate;
}
invalidate:
toku_omt_cursor_invalidate(c);
return EINVAL;
}
int toku_omt_cursor_prev(OMTCURSOR c, OMTVALUE *v) {
if (omtcursor_prev_internal(c)) return EINVAL;
omtcursor_current_internal(c, v);
return 0;
}
size_t toku_omt_memory_size (OMT omt) {
return sizeof(*omt)+omt->node_capacity*sizeof(omt->nodes[0]) + omt->tmparray_size*sizeof(omt->tmparray[0]); return sizeof(*omt)+omt->node_capacity*sizeof(omt->nodes[0]) + omt->tmparray_size*sizeof(omt->tmparray[0]);
} }
...@@ -49,11 +49,79 @@ ...@@ -49,11 +49,79 @@
// Insertion and deletion should run with $O(\log |V|)$ time and $O(\log |V|)$ calls to the Heaviside function. // Insertion and deletion should run with $O(\log |V|)$ time and $O(\log |V|)$ calls to the Heaviside function.
// The memory required is O(|V|). // The memory required is O(|V|).
// //
//**********************************************************************
//* OMT Cursors
//**********************************************************************
// OMTs also support cursors. An OMTCURSOR is a mutable
// An OMTCURSOR is a mutable object that, at any moment in time, is
// either associated with a single OMT or is not associated with any
// OMT. Many different OMTCURSORs can be associated with a single OMT.
// We say that an OMTCURSOR is *valid* if it is currently
// associated with an OMT and has an abstract offset assigned to it.
// An OMTCURSOR that is not valid is said to be invalid.
// Abstractly, an OMTCURSOR simply contains an integer offset of a
// particular OMTVALUE. We call this abstract integer the *offset*.
// Note, however, that the implementation may use a more
// complex representation in order to obtain higher performance.
// (Note: A first implementation might use the integer.)
// Given a valid OMTCURSOR, one
// * obtain the OMTVALUE at which the integer points in O(1) time,
// * increment or decrement the abstract integer (usually quickly.)
// The requirements are that the cursor is initialized to a
// randomly chosen valid integer, then the integer can be
// incremented in O(1) expected time.
// The OMTCURSOR may become invalidated under several conditions:
// * Incrementing or decrementing the abstract integer out of its
// valid range invalidates the OMTCURSOR.
// * If the OMT is modified, it may invalidate the cursor.
// * The user of the OMTCURSOR may explicitly invalidate the cursor.
// * The OMT is destroyed (in which case the OMTCURSOR is
// invalidated, but not destroyed.)
// Implementation Hint: One way to implement the OMTCURSOR is with an
// integer. The problem is that obtaining the value at which the integer
// points takes O(\log n) time, which is not fast enough to meet the
// specification. However, this implementation is probably much
// faster than our current implementation because it is O(\log n)
// integer comparisons instead of O(\log n) key comparisons. This
// simple implementation may be the right thing for a first cut.
//
// To actually achieve the performance requirements, here's a better
// implementation: The OMTCURSOR contains a path from root to leaf.
// Fetching the current value is O(1) time since the leaf is
// immediately accessible. Modifying the path to find the next or
// previous item has O(1) expected time at a randomly chosen valid
// point
//
// The path can be implemented as an array. It probably makes sense
// for the array to by dynamically resized as needed. Since the
// array's size is O(log n), it is not necessary to ever shrink the
// array. Also, from the perspective of testing, it's probably best
// if the array is initialized to a short length (e.g., length 4) so
// that the doubling code is actually exercised.
// One way to implement invalidation is for each OMT to maintain a
// doubly linked list of OMTCURSORs. When destroying an OMT or
// changing the OMT's shape, one can simply step through the list
// invalidating all the OMTCURSORs.
// The list of OMTCURSORs should use the list.h abstraction. If it's
// not clear how to use it, Rich can explain it.
// The programming API: // The programming API:
//typedef struct value *OMTVALUE; // A slight improvement over using void*. typedef void *OMTVALUE;
typedef struct omt *OMT; typedef struct omt *OMT;
typedef struct omtcursor *OMTCURSOR;
int toku_omt_create (OMT *omtp); int toku_omt_create (OMT *omtp);
// Effect: Create an empty OMT. Stores it in *omtp. // Effect: Create an empty OMT. Stores it in *omtp.
// Requires: omtp != NULL // Requires: omtp != NULL
...@@ -175,83 +243,107 @@ int toku_omt_delete_at(OMT omt, u_int32_t index); ...@@ -175,83 +243,107 @@ int toku_omt_delete_at(OMT omt, u_int32_t index);
// Rationale: To delete an item, first find its index using toku_omt_find, then delete it. // Rationale: To delete an item, first find its index using toku_omt_find, then delete it.
// Performance: time=O(\log N) amortized. // Performance: time=O(\log N) amortized.
int toku_omt_fetch (OMT V, u_int32_t i, OMTVALUE *v, OMTCURSOR c);
int toku_omt_fetch (OMT V, u_int32_t i, OMTVALUE *v);
// Effect: Set *v=V_i // Effect: Set *v=V_i
// If c != NULL then set c's abstract offset to i.
// Requires: v != NULL // Requires: v != NULL
// Returns // Returns
// 0 success // 0 success
// ERANGE if index>=toku_omt_size(omt) // ERANGE if index>=toku_omt_size(omt)
// On nonzero return, *v is unchanged. // ENOMEM if c!=NULL and we run out of memory
// On nonzero return, *v is unchanged, and c (if nonnull) is either
// invalidated or unchanged.
// Performance: time=O(\log N) // Performance: time=O(\log N)
// Notes: It is possible that c was previously valid and was
// associated with a different OMT. If c is changed by this
// function, the function must remove c's association with the old
// OMT, and associate it with the new OMT.
int toku_omt_find_zero(OMT V, int (*h)(OMTVALUE, void*extra), void*extra, OMTVALUE *value, u_int32_t *index); int toku_omt_find_zero(OMT V, int (*h)(OMTVALUE, void*extra), void*extra, OMTVALUE *value, u_int32_t *index, OMTCURSOR c);
// Effect: Find the smallest i such that h(V_i, extra)>=0 // Effect: Find the smallest i such that h(V_i, extra)>=0
// If c != NULL and there is such an i then set c's abstract offset to i.
// If there is such an i and h(V_i,extra)==0 then set *index=i and return 0. // If there is such an i and h(V_i,extra)==0 then set *index=i and return 0.
// If there is such an i and h(V_i,extra)>0 then set *index=i and return DB_NOTFOUND. // If there is such an i and h(V_i,extra)>0 then set *index=i and return DB_NOTFOUND.
// If there is no such i then set *index=toku_omt_size(V) and return DB_NOTFOUND. // If there is no such i then set *index=toku_omt_size(V), invalidate the cursor (if not NULL), and return DB_NOTFOUND.
// Requires: index!=NULL // Requires: index!=NULL
// Returns
// 0 success
// ENOMEM if c!=NULL and we run out of memory
// Performance: time=O(\log N) (calls to h)
// Notes: It is possible that c was previously valid and was
// associated with a different OMT. If c is changed by this
// function, the function must remove c's association with the old
// OMT, and associate it with the new OMT.
// Future directions: the current implementation can be improved, in some cases, by supporting tail recursion.
// This would require an additional parameter that represents the current value of the index where the function is recursing,
// so that it becomes similar to the way fetch works.
int toku_omt_find(OMT V, int (*h)(OMTVALUE, void*extra), void*extra, int direction, OMTVALUE *value, u_int32_t *index); int toku_omt_find(OMT V, int (*h)(OMTVALUE, void*extra), void*extra, int direction, OMTVALUE *value, u_int32_t *index, OMTCURSOR c);
/* Effect: // Effect:
If direction >0 then find the smallest i such that h(V_i,extra)>0. // If direction >0 then find the smallest i such that h(V_i,extra)>0.
If direction <0 then find the largest i such that h(V_i,extra)<0. // If direction <0 then find the largest i such that h(V_i,extra)<0.
(Direction may not be equal to zero.) // (Direction may not be equal to zero.)
If value!=NULL then store V_i in *value // If value!=NULL then store V_i in *value
If index!=NULL then store i in *index. // If index!=NULL then store i in *index.
Requires: The signum of h is monotically increasing. // If c != NULL and there is such an i then set c's abstract offset to i.
Returns // Requires: The signum of h is monotically increasing.
0 success // Performance: time=O(\log N) (calls to h)
DB_NOTFOUND no such value is found. // Returns
On nonzero return, *value and *index are unchanged. // 0 success
Performance: time=O(\log N) // DB_NOTFOUND no such value is found.
Rationale: // ENOMEM if c!= NULL and we run out of memory
Here's how to use the find function to find various things // On nonzero return, *value and *index are unchanged, and c (if nonnull) is either
Cases for find: // invalidated or unchanged.
find first value: ( h(v)=+1, direction=+1 ) // Notes: It is possible that c was previously valid and was
find last value ( h(v)=-1, direction=-1 ) // associated with a different OMT. If c is changed by this
find first X ( h(v)=(v< x) ? -1 : 1 direction=+1 ) // function, the function must remove c's association with the old
find last X ( h(v)=(v<=x) ? -1 : 1 direction=-1 ) // OMT, and associate it with the new OMT.
find X or successor to X ( same as find first X. ) // Rationale:
// Here's how to use the find function to find various things
Rationale: To help understand heaviside functions and behavor of find: // Cases for find:
There are 7 kinds of heaviside functions. // find first value: ( h(v)=+1, direction=+1 )
The signus of the h must be monotonically increasing. // find last value ( h(v)=-1, direction=-1 )
Given a function of the following form, A is the element // find first X ( h(v)=(v< x) ? -1 : 1 direction=+1 )
returned for direction>0, B is the element returned // find last X ( h(v)=(v<=x) ? -1 : 1 direction=-1 )
for direction<0, C is the element returned for // find X or successor to X ( same as find first X. )
direction==0 (see find_zero) (with a return of 0), and D is the element //
returned for direction==0 (see find_zero) with a return of DB_NOTFOUND. // Rationale: To help understand heaviside functions and behavor of find:
If any of A, B, or C are not found, then asking for the // There are 7 kinds of heaviside functions.
associated direction will return DB_NOTFOUND. // The signum of the h must be monotonically increasing.
See find_zero for more information. // Given a function of the following form, A is the element
// returned for direction>0, B is the element returned
Let the following represent the signus of the heaviside function. // for direction<0, C is the element returned for
// direction==0 (see find_zero) (with a return of 0), and D is the element
-...- // returned for direction==0 (see find_zero) with a return of DB_NOTFOUND.
A // If any of A, B, or C are not found, then asking for the
D // associated direction will return DB_NOTFOUND.
// See find_zero for more information.
+...+ //
B // Let the following represent the signum of the heaviside function.
D //
// -...-
0...0 // A
C // D
//
-...-0...0 // +...+
AC // B
// D
0...0+...+ //
C B // 0...0
// C
-...-+...+ //
AB // -...-0...0
D // AC
//
-...-0...0+...+ // 0...0+...+
AC B // C B
*/ //
// -...-+...+
// AB
// D
//
// -...-0...0+...+
// AC B
int toku_omt_split_at(OMT omt, OMT *newomt, u_int32_t index); int toku_omt_split_at(OMT omt, OMT *newomt, u_int32_t index);
...@@ -282,7 +374,92 @@ void toku_omt_clear(OMT omt); ...@@ -282,7 +374,92 @@ void toku_omt_clear(OMT omt);
// Note: Will not resize the array, since void precludes allowing a malloc. // Note: Will not resize the array, since void precludes allowing a malloc.
// Performance: time=O(1) // Performance: time=O(1)
unsigned long toku_omt_memory_size (OMT omt); int toku_omt_cursor_create (OMTCURSOR *p);
// Effect: Create an OMTCURSOR. Stores it in *p. The OMTCURSOR is
// initially invalid.
// Requires: p != NULL
// Returns:
// 0 success
// ENOMEM out of memory (and doesn't modify *omtp)
// Performance: constant time.
void toku_omt_cursor_destroy (OMTCURSOR *p);
// Effect: Invalidates *p (if it is valid) and frees any memory
// associated with *p.
// Also sets *p=NULL.
// Requires: *p != NULL
// Rationale: The usage is to do something like
// toku_omt_cursor_destroy(&c);
// and now c will have a NULL pointer instead of a dangling freed pointer.
// Rationale: Returns no values since free() cannot fail.
// Performance: time=O(1) x #calls to free
int toku_omt_cursor_is_valid (OMTCURSOR c);
// Effect: returns 0 iff c is invalid.
// Performance: time=O(1)
int toku_omt_cursor_next (OMTCURSOR c, OMTVALUE *v);
// Effect: Increment c's abstract offset, and store the corresponding value in v.
// Requires: v != NULL
// Returns
// 0 success
// EINVAL if the offset goes out of range or c is invalid.
// On nonzero return, *v is unchanged and c is invalidated.
// Performance: time=O(log N) worst case, expected time=O(1) for a randomly
// chosen initial position.
int toku_omt_cursor_current (OMTCURSOR c, OMTVALUE *v);
// Effect: Store in v the value pointed by c's abstract offset
// Requires: v != NULL
// Returns
// 0 success
// EINVAL if c is invalid
// On non-zero return, *v is unchanged
// Performance: O(1) time
int toku_omt_cursor_prev (OMTCURSOR c, OMTVALUE *v);
// Effect: Decrement c's abstract offset, and store the corresponding value in v.
// Requires: v != NULL
// Returns
// 0 success
// EINVAL if the offset goes out of range or c is invalid.
// On nonzero return, *v is unchanged and c is invalidated.
// Performance: time=O(log N) worst case, expected time=O(1) for a randomly
// chosen initial position.
void toku_omt_cursor_invalidate (OMTCURSOR c);
// Effect: Invalidate c. (This does not mean that c is destroyed or
// that its memory is freed.)
// Usage Hint: The OMTCURSOR is designed to be used inside the
// BRTcursor. A BRTcursor includes a pointer to an OMTCURSOR, which
// is created when the BRTcursor is created.
//
// The brt cursor implements its search by first finding a leaf node,
// containing an OMT. The BRT then passes its OMTCURSOR into the lookup
// method (i.e., one of toku_ebdomt_fetch, toku_omt_find_zero,
// toku_omt_find). The lookup method, if successful, sets the
// OMTCURSOR to refer to that element.
//
// As long as the OMTCURSOR remains valid, a BRTCURSOR next or prev
// operation can be implemented using next or prev on the OMTCURSOR.
//
// If the OMTCURSOR becomes invalidated, then the BRT must search
// again from the root of the tree. The only error that an OMTCURSOR
// next operation can raise is that it is invalid.
//
// If an element is inserted into the BRT, it may cause an OMTCURSOR
// to become invalid. This is especially true if the element will end
// up in the OMT associated with the cursor. A simple implementation
// is to invalidate all OMTCURSORS any time anything is inserted into
// into the BRT. Since the BRT already contains a list of BRT cursors
// associated with it, it is straightforward to go through that list
// and invalidate all the cursors.
//
// When the BRT closes a cursor, it destroys the OMTCURSOR.
size_t toku_omt_memory_size (OMT omt);
// Effect: Return the size (in bytes) of the omt, as it resides in main memory. Don't include any of the OMTVALUES. // Effect: Return the size (in bytes) of the omt, as it resides in main memory. Don't include any of the OMTVALUES.
#endif /* #ifndef OMT_H */ #endif /* #ifndef OMT_H */
......
...@@ -607,7 +607,7 @@ void toku_recover_deleteleafentry (LSN lsn, FILENUM filenum, DISKOFF diskoff, u_ ...@@ -607,7 +607,7 @@ void toku_recover_deleteleafentry (LSN lsn, FILENUM filenum, DISKOFF diskoff, u_
node->log_lsn = lsn; node->log_lsn = lsn;
{ {
OMTVALUE data = 0; OMTVALUE data = 0;
r=toku_omt_fetch(node->u.l.buffer, idx, &data); r=toku_omt_fetch(node->u.l.buffer, idx, &data, NULL);
assert(r==0); assert(r==0);
LEAFENTRY oldleafentry=data; LEAFENTRY oldleafentry=data;
u_int32_t len = leafentry_memsize(oldleafentry); u_int32_t len = leafentry_memsize(oldleafentry);
......
...@@ -55,8 +55,8 @@ static int do_insertion (enum brt_cmd_type type, TXNID xid, FILENUM filenum, BYT ...@@ -55,8 +55,8 @@ static int do_insertion (enum brt_cmd_type type, TXNID xid, FILENUM filenum, BYT
data data
? toku_fill_dbt(&data_dbt, data->data, data->len) ? toku_fill_dbt(&data_dbt, data->data, data->len)
: toku_init_dbt(&data_dbt) }}; : toku_init_dbt(&data_dbt) }};
OMTVALUE brtv; OMTVALUE brtv=NULL; // TODO BBB This is not entirely safe. Verify initialization needed.
r = toku_omt_find_zero(txn->open_brts, find_brt_from_filenum, &filenum, &brtv, NULL); r = toku_omt_find_zero(txn->open_brts, find_brt_from_filenum, &filenum, &brtv, NULL, NULL);
if (r==DB_NOTFOUND) { if (r==DB_NOTFOUND) {
r = toku_cachefile_root_put_cmd(cf, &brtcmd, toku_txn_logger(txn)); r = toku_cachefile_root_put_cmd(cf, &brtcmd, toku_txn_logger(txn));
......
#ident "Copyright (c) 2007 Tokutek Inc. All rights reserved." #ident "Copyright (c) 2007 Tokutek Inc. All rights reserved."
#include <errno.h> #include <errno.h>
#include <sys/types.h> #include <sys/types.h>
typedef struct value *OMTVALUE; // typedef struct value *TESTVALUE;
#include "omt.h" typedef struct value *TESTVALUE;
#include "../newbrt/memory.h" #include "omt.h"
#include "../newbrt/toku_assert.h" #include "../newbrt/memory.h"
#include "../include/db.h" #include "../newbrt/toku_assert.h"
#include "../newbrt/brttypes.h" #include "../include/db.h"
#include <stdlib.h> #include "../newbrt/brttypes.h"
#include <stdint.h> #include <stdlib.h>
#include <stdint.h>
/* Things that would go in a omt-tests.h if we split to multiple files later. */
int verbose=0; /* Things that would go in a omt-tests.h if we split to multiple files later. */
int verbose=0;
#define CKERR(r) ({ if (r!=0) fprintf(stderr, "%s:%d error %d %s\n", __FILE__, __LINE__, r, strerror(r)); assert(r==0); })
#define CKERR2(r,r2) ({ if (r!=r2) fprintf(stderr, "%s:%d error %d %s, expected %d\n", __FILE__, __LINE__, r, strerror(r), r2); assert(r==r2); }) #define CKERR(r) ({ if (r!=0) fprintf(stderr, "%s:%d error %d %s\n", __FILE__, __LINE__, r, strerror(r)); assert(r==0); })
#define CKERR2s(r,r2,r3) ({ if (r!=r2 && r!=r3) fprintf(stderr, "%s:%d error %d %s, expected %d or %d\n", __FILE__, __LINE__, r, strerror(r), r2,r3); assert(r==r2||r==r3); }) #define CKERR2(r,r2) ({ if (r!=r2) fprintf(stderr, "%s:%d error %d %s, expected %d\n", __FILE__, __LINE__, r, strerror(r), r2); assert(r==r2); })
#define CKERR2s(r,r2,r3) ({ if (r!=r2 && r!=r3) fprintf(stderr, "%s:%d error %d %s, expected %d or %d\n", __FILE__, __LINE__, r, strerror(r), r2,r3); assert(r==r2||r==r3); })
#include <string.h>
void parse_args (int argc, const char *argv[]) { #include <string.h>
const char *argv0=argv[0]; void parse_args (int argc, const char *argv[]) {
while (argc>1) { const char *argv0=argv[0];
int resultcode=0; while (argc>1) {
if (strcmp(argv[1], "-v")==0) { int resultcode=0;
verbose++; if (strcmp(argv[1], "-v")==0) {
} else if (strcmp(argv[1], "-q")==0) { verbose++;
verbose = 0; } else if (strcmp(argv[1], "-q")==0) {
} else if (strcmp(argv[1], "-h")==0) { verbose = 0;
do_usage: } else if (strcmp(argv[1], "-h")==0) {
fprintf(stderr, "Usage:\n%s [-v|-h]\n", argv0); do_usage:
exit(resultcode); fprintf(stderr, "Usage:\n%s [-v|-h]\n", argv0);
} else { exit(resultcode);
resultcode=1; } else {
goto do_usage; resultcode=1;
} goto do_usage;
argc--; }
argv++; argc--;
} argv++;
} }
/* End ".h like" stuff. */ }
/* End ".h like" stuff. */
struct value {
u_int32_t number; struct value {
}; u_int32_t number;
};
enum rand_type {
TEST_RANDOM, enum rand_type {
TEST_SORTED, TEST_RANDOM,
TEST_IDENTITY TEST_SORTED,
}; TEST_IDENTITY
enum close_when_done { };
CLOSE_WHEN_DONE, enum close_when_done {
KEEP_WHEN_DONE CLOSE_WHEN_DONE,
}; KEEP_WHEN_DONE
enum create_type { };
BATCH_INSERT, enum create_type {
INSERT_AT, BATCH_INSERT,
INSERT_AT_ALMOST_RANDOM, INSERT_AT,
}; INSERT_AT_ALMOST_RANDOM,
};
/* Globals */
OMT omt; /* Globals */
OMTVALUE* values = NULL; OMT omt;
struct value* nums = NULL; TESTVALUE* values = NULL;
u_int32_t length; struct value* nums = NULL;
u_int32_t length;
void cleanup_globals(void) {
assert(values); void cleanup_globals(void) {
toku_free(values); assert(values);
values = NULL; toku_free(values);
assert(nums); values = NULL;
toku_free(nums); assert(nums);
nums = NULL; toku_free(nums);
} nums = NULL;
}
const unsigned int random_seed = 0xFEADACBA;
const unsigned int random_seed = 0xFEADACBA;
void init_init_values(unsigned int seed, u_int32_t num_elements) {
srandom(seed); void init_init_values(unsigned int seed, u_int32_t num_elements) {
srandom(seed);
cleanup_globals();
cleanup_globals();
MALLOC_N(num_elements, values);
assert(values); MALLOC_N(num_elements, values);
MALLOC_N(num_elements, nums); assert(values);
assert(nums); MALLOC_N(num_elements, nums);
length = num_elements; assert(nums);
} length = num_elements;
}
void init_identity_values(unsigned int seed, u_int32_t num_elements) {
u_int32_t i; void init_identity_values(unsigned int seed, u_int32_t num_elements) {
u_int32_t i;
init_init_values(seed, num_elements);
init_init_values(seed, num_elements);
for (i = 0; i < length; i++) {
nums[i].number = i; for (i = 0; i < length; i++) {
values[i] = (OMTVALUE)&nums[i]; nums[i].number = i;
} values[i] = (TESTVALUE)&nums[i];
} }
}
void init_distinct_sorted_values(unsigned int seed, u_int32_t num_elements) {
u_int32_t i; void init_distinct_sorted_values(unsigned int seed, u_int32_t num_elements) {
u_int32_t i;
init_init_values(seed, num_elements);
init_init_values(seed, num_elements);
u_int32_t number = 0;
u_int32_t number = 0;
for (i = 0; i < length; i++) {
number += (u_int32_t)(random() % 32) + 1; for (i = 0; i < length; i++) {
nums[i].number = number; number += (u_int32_t)(random() % 32) + 1;
values[i] = (OMTVALUE)&nums[i]; nums[i].number = number;
} values[i] = (TESTVALUE)&nums[i];
} }
}
void init_distinct_random_values(unsigned int seed, u_int32_t num_elements) {
init_distinct_sorted_values(seed, num_elements); void init_distinct_random_values(unsigned int seed, u_int32_t num_elements) {
init_distinct_sorted_values(seed, num_elements);
u_int32_t i;
u_int32_t choice; u_int32_t i;
u_int32_t choices; u_int32_t choice;
struct value temp; u_int32_t choices;
for (i = 0; i < length - 1; i++) { struct value temp;
choices = length - i; for (i = 0; i < length - 1; i++) {
choice = random() % choices; choices = length - i;
if (choice != i) { choice = random() % choices;
temp = nums[i]; if (choice != i) {
nums[i] = nums[choice]; temp = nums[i];
nums[choice] = temp; nums[i] = nums[choice];
} nums[choice] = temp;
} }
} }
}
void init_globals(void) {
MALLOC_N(1, values); void init_globals(void) {
assert(values); MALLOC_N(1, values);
MALLOC_N(1, nums); assert(values);
assert(nums); MALLOC_N(1, nums);
length = 1; assert(nums);
} length = 1;
}
void test_close(enum close_when_done close) {
if (close == KEEP_WHEN_DONE) return; void test_close(enum close_when_done close) {
assert(close == CLOSE_WHEN_DONE); if (close == KEEP_WHEN_DONE) return;
toku_omt_destroy(&omt); assert(close == CLOSE_WHEN_DONE);
assert(omt==NULL); toku_omt_destroy(&omt);
} assert(omt==NULL);
}
void test_create(enum close_when_done close) {
int r; void test_create(enum close_when_done close) {
omt = NULL; int r;
omt = NULL;
r = toku_omt_create(&omt);
CKERR(r); r = toku_omt_create(&omt);
assert(omt!=NULL); CKERR(r);
test_close(close); assert(omt!=NULL);
} test_close(close);
}
void test_create_size(enum close_when_done close) {
test_create(KEEP_WHEN_DONE); void test_create_size(enum close_when_done close) {
assert(toku_omt_size(omt) == 0); test_create(KEEP_WHEN_DONE);
test_close(close); assert(toku_omt_size(omt) == 0);
} test_close(close);
}
void test_create_insert_at_almost_random(enum close_when_done close) {
u_int32_t i; void test_create_insert_at_almost_random(enum close_when_done close) {
int r; u_int32_t i;
u_int32_t size = 0; int r;
u_int32_t size = 0;
test_create(KEEP_WHEN_DONE);
r = toku_omt_insert_at(omt, values[0], toku_omt_size(omt)+1); test_create(KEEP_WHEN_DONE);
CKERR2(r, ERANGE); r = toku_omt_insert_at(omt, values[0], toku_omt_size(omt)+1);
r = toku_omt_insert_at(omt, values[0], toku_omt_size(omt)+2); CKERR2(r, ERANGE);
CKERR2(r, ERANGE); r = toku_omt_insert_at(omt, values[0], toku_omt_size(omt)+2);
for (i = 0; i < length/2; i++) { CKERR2(r, ERANGE);
assert(size==toku_omt_size(omt)); for (i = 0; i < length/2; i++) {
r = toku_omt_insert_at(omt, values[i], i); assert(size==toku_omt_size(omt));
CKERR(r); r = toku_omt_insert_at(omt, values[i], i);
assert(++size==toku_omt_size(omt)); CKERR(r);
r = toku_omt_insert_at(omt, values[length-1-i], i+1); assert(++size==toku_omt_size(omt));
CKERR(r); r = toku_omt_insert_at(omt, values[length-1-i], i+1);
assert(++size==toku_omt_size(omt)); CKERR(r);
} assert(++size==toku_omt_size(omt));
r = toku_omt_insert_at(omt, values[0], toku_omt_size(omt)+1); }
CKERR2(r, ERANGE); r = toku_omt_insert_at(omt, values[0], toku_omt_size(omt)+1);
r = toku_omt_insert_at(omt, values[0], toku_omt_size(omt)+2); CKERR2(r, ERANGE);
CKERR2(r, ERANGE); r = toku_omt_insert_at(omt, values[0], toku_omt_size(omt)+2);
assert(size==toku_omt_size(omt)); CKERR2(r, ERANGE);
test_close(close); assert(size==toku_omt_size(omt));
} test_close(close);
}
void test_create_insert_at_sequential(enum close_when_done close) {
u_int32_t i; void test_create_insert_at_sequential(enum close_when_done close) {
int r; u_int32_t i;
u_int32_t size = 0; int r;
u_int32_t size = 0;
test_create(KEEP_WHEN_DONE);
r = toku_omt_insert_at(omt, values[0], toku_omt_size(omt)+1); test_create(KEEP_WHEN_DONE);
CKERR2(r, ERANGE); r = toku_omt_insert_at(omt, values[0], toku_omt_size(omt)+1);
r = toku_omt_insert_at(omt, values[0], toku_omt_size(omt)+2); CKERR2(r, ERANGE);
CKERR2(r, ERANGE); r = toku_omt_insert_at(omt, values[0], toku_omt_size(omt)+2);
for (i = 0; i < length; i++) { CKERR2(r, ERANGE);
assert(size==toku_omt_size(omt)); for (i = 0; i < length; i++) {
r = toku_omt_insert_at(omt, values[i], i); assert(size==toku_omt_size(omt));
CKERR(r); r = toku_omt_insert_at(omt, values[i], i);
assert(++size==toku_omt_size(omt)); CKERR(r);
} assert(++size==toku_omt_size(omt));
r = toku_omt_insert_at(omt, values[0], toku_omt_size(omt)+1); }
CKERR2(r, ERANGE); r = toku_omt_insert_at(omt, values[0], toku_omt_size(omt)+1);
r = toku_omt_insert_at(omt, values[0], toku_omt_size(omt)+2); CKERR2(r, ERANGE);
CKERR2(r, ERANGE); r = toku_omt_insert_at(omt, values[0], toku_omt_size(omt)+2);
assert(size==toku_omt_size(omt)); CKERR2(r, ERANGE);
test_close(close); assert(size==toku_omt_size(omt));
} test_close(close);
}
void test_create_from_sorted_array(enum create_type create_choice, enum close_when_done close) {
int r; void test_create_from_sorted_array(enum create_type create_choice, enum close_when_done close) {
omt = NULL; int r;
omt = NULL;
if (create_choice == BATCH_INSERT) {
r = toku_omt_create_from_sorted_array(&omt, values, length); if (create_choice == BATCH_INSERT) {
CKERR(r); r = toku_omt_create_from_sorted_array(&omt, (OMTVALUE) values, length);
} CKERR(r);
else if (create_choice == INSERT_AT) { }
test_create_insert_at_sequential(KEEP_WHEN_DONE); else if (create_choice == INSERT_AT) {
} test_create_insert_at_sequential(KEEP_WHEN_DONE);
else if (create_choice == INSERT_AT_ALMOST_RANDOM) { }
test_create_insert_at_almost_random(KEEP_WHEN_DONE); else if (create_choice == INSERT_AT_ALMOST_RANDOM) {
} test_create_insert_at_almost_random(KEEP_WHEN_DONE);
else assert(FALSE); }
else assert(FALSE);
assert(omt!=NULL);
test_close(close); assert(omt!=NULL);
} test_close(close);
}
void test_create_from_sorted_array_size(enum create_type create_choice, enum close_when_done close) {
test_create_from_sorted_array(create_choice, KEEP_WHEN_DONE); void test_create_from_sorted_array_size(enum create_type create_choice, enum close_when_done close) {
assert(toku_omt_size(omt)==length); test_create_from_sorted_array(create_choice, KEEP_WHEN_DONE);
test_close(close); assert(toku_omt_size(omt)==length);
} test_close(close);
}
void test_fetch_verify (OMT omtree, OMTVALUE* val, u_int32_t len ) {
u_int32_t i; void test_fetch_verify (OMT omtree, TESTVALUE* val, u_int32_t len ) {
int r; u_int32_t i;
OMTVALUE v = (OMTVALUE)&i; int j;
OMTVALUE oldv = v; int r;
assert(len == toku_omt_size(omtree)); TESTVALUE v = (TESTVALUE)&i;
for (i = 0; i < len; i++) { TESTVALUE oldv = v;
assert(oldv!=val[i]);
v = NULL; OMTCURSOR c;
r = toku_omt_fetch(omtree, i, &v); r = toku_omt_cursor_create(&c);
CKERR(r); CKERR(r);
assert(v != NULL);
assert(v != oldv); assert(len == toku_omt_size(omtree));
assert(v == val[i]); for (i = 0; i < len; i++) {
assert(v->number == val[i]->number); assert(oldv!=val[i]);
v = NULL;
v = oldv; r = toku_omt_fetch(omtree, i, (OMTVALUE) &v, NULL);
r = toku_omt_fetch(omtree, i, &v); CKERR(r);
CKERR(r); assert(v != NULL);
assert(v != NULL); assert(v != oldv);
assert(v != oldv); assert(v == val[i]);
assert(v == val[i]); assert(v->number == val[i]->number);
assert(v->number == val[i]->number);
} v = oldv;
for (i = len; i < len*2; i++) { r = toku_omt_fetch(omtree, i, (OMTVALUE) &v, c);
v = oldv; CKERR(r);
r = toku_omt_fetch(omtree, i, &v); assert(v != NULL);
CKERR2(r, ERANGE); assert(v != oldv);
assert(v == oldv); assert(v == val[i]);
v = NULL; assert(v->number == val[i]->number);
r = toku_omt_fetch(omtree, i, &v); assert(toku_omt_cursor_is_valid(c));
CKERR2(r, ERANGE);
assert(v == NULL); v = oldv;
} r = toku_omt_cursor_current(c, (OMTVALUE) &v);
} CKERR(r);
assert(v != NULL);
void test_create_fetch_verify(enum create_type create_choice, enum close_when_done close) { assert(v != oldv);
test_create_from_sorted_array(create_choice, KEEP_WHEN_DONE); assert(v == val[i]);
test_fetch_verify(omt, values, length); assert(v->number == val[i]->number);
test_close(close); assert(toku_omt_cursor_is_valid(c));
}
v = oldv;
static int iterate_helper_error_return = 1; j = i + 1;
while ((r = toku_omt_cursor_next(c, (OMTVALUE) &v)) == 0) {
int iterate_helper(OMTVALUE v, u_int32_t idx, void* extra) { assert(toku_omt_cursor_is_valid(c));
if (extra == NULL) return iterate_helper_error_return; assert(v != NULL);
OMTVALUE* vals = (OMTVALUE *)extra; assert(v != oldv);
assert(v != NULL); assert(v == val[j]);
assert(v == vals[idx]); assert(v->number == val[j]->number);
assert(v->number == vals[idx]->number); j++;
return 0; v = oldv;
} }
CKERR2(r, EINVAL);
void test_iterate_verify(OMT omtree, OMTVALUE* vals, u_int32_t len) { assert(j == (int) len);
int r;
iterate_helper_error_return = 0; assert(oldv!=val[i]);
r = toku_omt_iterate(omtree, iterate_helper, (void*)vals); v = NULL;
CKERR(r); r = toku_omt_fetch(omtree, i, (OMTVALUE) &v, c);
iterate_helper_error_return = 0xFEEDABBA; CKERR(r);
r = toku_omt_iterate(omtree, iterate_helper, NULL); assert(v != NULL);
if (!len) { assert(v != oldv);
CKERR2(r, 0); assert(v == val[i]);
} assert(v->number == val[i]->number);
else {
CKERR2(r, iterate_helper_error_return); v = oldv;
} j = i - 1;
} while ((r = toku_omt_cursor_prev(c, (OMTVALUE) &v)) == 0) {
assert(toku_omt_cursor_is_valid(c));
void test_create_iterate_verify(enum create_type create_choice, enum close_when_done close) { assert(v != NULL);
test_create_from_sorted_array(create_choice, KEEP_WHEN_DONE); assert(v != oldv);
test_iterate_verify(omt, values, length); assert(v == val[j]);
test_close(close); assert(v->number == val[j]->number);
} j--;
v = oldv;
}
void permute_array(u_int32_t* arr, u_int32_t len) { CKERR2(r, EINVAL);
// assert(j == -1);
// create a permutation of 0...size-1
// }
u_int32_t i = 0;
for (i = 0; i < len; i++) { for (i = len; i < len*2; i++) {
arr[i] = i; v = oldv;
} r = toku_omt_fetch(omtree, i, (OMTVALUE) &v, NULL);
for (i = 0; i < len - 1; i++) { CKERR2(r, ERANGE);
u_int32_t choices = len - i; assert(v == oldv);
u_int32_t choice = random() % choices; v = NULL;
if (choice != i) { r = toku_omt_fetch(omtree, i, (OMTVALUE) &v, c);
u_int32_t temp = arr[i]; CKERR2(r, ERANGE);
arr[i] = arr[choice]; assert(v == NULL);
arr[choice] = temp; }
}
} toku_omt_cursor_destroy(&c);
} }
void test_create_set_at(enum create_type create_choice, enum close_when_done close) { void test_create_fetch_verify(enum create_type create_choice, enum close_when_done close) {
u_int32_t i = 0; test_create_from_sorted_array(create_choice, KEEP_WHEN_DONE);
test_fetch_verify(omt, values, length);
struct value* old_nums = NULL; test_close(close);
MALLOC_N(length, old_nums); }
assert(nums);
static int iterate_helper_error_return = 1;
u_int32_t* perm = NULL;
MALLOC_N(length, perm); int iterate_helper(TESTVALUE v, u_int32_t idx, void* extra) {
assert(perm); if (extra == NULL) return iterate_helper_error_return;
TESTVALUE* vals = (TESTVALUE *)extra;
OMTVALUE* old_values = NULL; assert(v != NULL);
MALLOC_N(length, old_values); assert(v == vals[idx]);
assert(old_values); assert(v->number == vals[idx]->number);
return 0;
permute_array(perm, length); }
// void test_iterate_verify(OMT omtree, TESTVALUE* vals, u_int32_t len) {
// These are going to be the new values int r;
// iterate_helper_error_return = 0;
for (i = 0; i < length; i++) { r = toku_omt_iterate(omtree, (OMTVALUE) iterate_helper, (void*)vals);
old_nums[i] = nums[i]; CKERR(r);
old_values[i] = &old_nums[i]; iterate_helper_error_return = 0xFEEDABBA;
values[i] = &old_nums[i]; r = toku_omt_iterate(omtree, (OMTVALUE) iterate_helper, NULL);
} if (!len) {
test_create_from_sorted_array(create_choice, KEEP_WHEN_DONE); CKERR2(r, 0);
int r; }
r = toku_omt_set_at (omt, values[0], length); else {
CKERR2(r,ERANGE); CKERR2(r, iterate_helper_error_return);
r = toku_omt_set_at (omt, values[0], length+1); }
CKERR2(r,ERANGE); }
for (i = 0; i < length; i++) {
u_int32_t choice = perm[i]; void test_create_iterate_verify(enum create_type create_choice, enum close_when_done close) {
values[choice] = &nums[choice]; test_create_from_sorted_array(create_choice, KEEP_WHEN_DONE);
nums[choice].number = (u_int32_t)random(); test_iterate_verify(omt, values, length);
r = toku_omt_set_at (omt, values[choice], choice); test_close(close);
CKERR(r); }
test_iterate_verify(omt, values, length);
test_fetch_verify(omt, values, length);
} void permute_array(u_int32_t* arr, u_int32_t len) {
r = toku_omt_set_at (omt, values[0], length); //
CKERR2(r,ERANGE); // create a permutation of 0...size-1
r = toku_omt_set_at (omt, values[0], length+1); //
CKERR2(r,ERANGE); u_int32_t i = 0;
for (i = 0; i < len; i++) {
toku_free(perm); arr[i] = i;
toku_free(old_values); }
toku_free(old_nums); for (i = 0; i < len - 1; i++) {
u_int32_t choices = len - i;
test_close(close); u_int32_t choice = random() % choices;
} if (choice != i) {
u_int32_t temp = arr[i];
int insert_helper(OMTVALUE value, void* extra_insert) { arr[i] = arr[choice];
OMTVALUE to_insert = (OMTVALUE)extra_insert; arr[choice] = temp;
assert(to_insert); }
}
if (value->number < to_insert->number) return -1; }
if (value->number > to_insert->number) return +1;
return 0; void test_create_set_at(enum create_type create_choice, enum close_when_done close) {
} u_int32_t i = 0;
void test_create_insert(enum close_when_done close) { struct value* old_nums = NULL;
u_int32_t i = 0; MALLOC_N(length, old_nums);
assert(nums);
u_int32_t* perm = NULL;
MALLOC_N(length, perm); u_int32_t* perm = NULL;
assert(perm); MALLOC_N(length, perm);
assert(perm);
permute_array(perm, length);
TESTVALUE* old_values = NULL;
test_create(KEEP_WHEN_DONE); MALLOC_N(length, old_values);
int r; assert(old_values);
u_int32_t size = length;
length = 0; permute_array(perm, length);
while (length < size) {
u_int32_t choice = perm[length]; //
OMTVALUE to_insert = &nums[choice]; // These are going to be the new values
u_int32_t idx = UINT32_MAX; //
for (i = 0; i < length; i++) {
assert(length==toku_omt_size(omt)); old_nums[i] = nums[i];
r = toku_omt_insert(omt, to_insert, insert_helper, to_insert, &idx); old_values[i] = &old_nums[i];
CKERR(r); values[i] = &old_nums[i];
assert(idx <= length); }
if (idx > 0) { test_create_from_sorted_array(create_choice, KEEP_WHEN_DONE);
assert(to_insert->number > values[idx-1]->number); int r;
} r = toku_omt_set_at (omt, values[0], length);
if (idx < length) { CKERR2(r,ERANGE);
assert(to_insert->number < values[idx]->number); r = toku_omt_set_at (omt, values[0], length+1);
} CKERR2(r,ERANGE);
length++; for (i = 0; i < length; i++) {
assert(length==toku_omt_size(omt)); u_int32_t choice = perm[i];
/* Make room */ values[choice] = &nums[choice];
for (i = length-1; i > idx; i--) { nums[choice].number = (u_int32_t)random();
values[i] = values[i-1]; r = toku_omt_set_at (omt, values[choice], choice);
} CKERR(r);
values[idx] = to_insert; test_iterate_verify(omt, values, length);
test_fetch_verify(omt, values, length); test_fetch_verify(omt, values, length);
test_iterate_verify(omt, values, length); }
r = toku_omt_set_at (omt, values[0], length);
idx = UINT32_MAX; CKERR2(r,ERANGE);
r = toku_omt_insert(omt, to_insert, insert_helper, to_insert, &idx); r = toku_omt_set_at (omt, values[0], length+1);
CKERR2(r, DB_KEYEXIST); CKERR2(r,ERANGE);
assert(idx < length);
assert(values[idx]->number == to_insert->number); toku_free(perm);
assert(length==toku_omt_size(omt)); toku_free(old_values);
toku_free(old_nums);
test_iterate_verify(omt, values, length);
test_fetch_verify(omt, values, length); test_close(close);
} }
toku_free(perm); int insert_helper(TESTVALUE value, void* extra_insert) {
TESTVALUE to_insert = (OMTVALUE)extra_insert;
test_close(close); assert(to_insert);
}
if (value->number < to_insert->number) return -1;
void test_create_delete_at(enum create_type create_choice, enum close_when_done close) { if (value->number > to_insert->number) return +1;
u_int32_t i = 0; return 0;
int r = ENOSYS; }
test_create_from_sorted_array(create_choice, KEEP_WHEN_DONE);
void test_create_insert(enum close_when_done close) {
assert(length == toku_omt_size(omt)); u_int32_t i = 0;
r = toku_omt_delete_at(omt, length);
CKERR2(r,ERANGE); u_int32_t* perm = NULL;
assert(length == toku_omt_size(omt)); MALLOC_N(length, perm);
r = toku_omt_delete_at(omt, length+1); assert(perm);
CKERR2(r,ERANGE);
while (length > 0) { permute_array(perm, length);
assert(length == toku_omt_size(omt));
u_int32_t index_to_delete = random()%length; test_create(KEEP_WHEN_DONE);
r = toku_omt_delete_at(omt, index_to_delete); int r;
CKERR(r); u_int32_t size = length;
for (i = index_to_delete+1; i < length; i++) { length = 0;
values[i-1] = values[i]; while (length < size) {
} u_int32_t choice = perm[length];
length--; TESTVALUE to_insert = &nums[choice];
test_fetch_verify(omt, values, length); u_int32_t idx = UINT32_MAX;
test_iterate_verify(omt, values, length);
} assert(length==toku_omt_size(omt));
assert(length == 0); r = toku_omt_insert(omt, to_insert, (OMTVALUE) insert_helper, to_insert, &idx);
assert(length == toku_omt_size(omt)); CKERR(r);
r = toku_omt_delete_at(omt, length); assert(idx <= length);
CKERR2(r, ERANGE); if (idx > 0) {
assert(length == toku_omt_size(omt)); assert(to_insert->number > values[idx-1]->number);
r = toku_omt_delete_at(omt, length+1); }
CKERR2(r, ERANGE); if (idx < length) {
test_close(close); assert(to_insert->number < values[idx]->number);
} }
length++;
void test_split_merge(enum create_type create_choice, enum close_when_done close) { assert(length==toku_omt_size(omt));
int r = ENOSYS; /* Make room */
u_int32_t i = 0; for (i = length-1; i > idx; i--) {
OMT left_split = NULL; values[i] = values[i-1];
OMT right_split = NULL; }
test_create_from_sorted_array(create_choice, KEEP_WHEN_DONE); values[idx] = to_insert;
test_fetch_verify(omt, values, length);
for (i = 0; i <= length; i++) { test_iterate_verify(omt, values, length);
r = toku_omt_split_at(omt, &right_split, length+1);
CKERR2(r,ERANGE); idx = UINT32_MAX;
r = toku_omt_split_at(omt, &right_split, length+2); r = toku_omt_insert(omt, to_insert, (OMTVALUE) insert_helper, to_insert, &idx);
CKERR2(r,ERANGE); CKERR2(r, DB_KEYEXIST);
assert(idx < length);
// assert(values[idx]->number == to_insert->number);
// test successful split assert(length==toku_omt_size(omt));
//
r = toku_omt_split_at(omt, &right_split, i); test_iterate_verify(omt, values, length);
CKERR(r); test_fetch_verify(omt, values, length);
left_split = omt; }
omt = NULL;
assert(toku_omt_size(left_split) == i); toku_free(perm);
assert(toku_omt_size(right_split) == length - i);
test_fetch_verify(left_split, values, i); test_close(close);
test_iterate_verify(left_split, values, i); }
test_fetch_verify(right_split, &values[i], length - i);
test_iterate_verify(right_split, &values[i], length - i); void test_create_delete_at(enum create_type create_choice, enum close_when_done close) {
// u_int32_t i = 0;
// verify that new OMT's cannot do bad splits int r = ENOSYS;
// test_create_from_sorted_array(create_choice, KEEP_WHEN_DONE);
r = toku_omt_split_at(left_split, &omt, i+1);
CKERR2(r,ERANGE); assert(length == toku_omt_size(omt));
assert(toku_omt_size(left_split) == i); r = toku_omt_delete_at(omt, length);
assert(toku_omt_size(right_split) == length - i); CKERR2(r,ERANGE);
r = toku_omt_split_at(left_split, &omt, i+2); assert(length == toku_omt_size(omt));
CKERR2(r,ERANGE); r = toku_omt_delete_at(omt, length+1);
assert(toku_omt_size(left_split) == i); CKERR2(r,ERANGE);
assert(toku_omt_size(right_split) == length - i); while (length > 0) {
r = toku_omt_split_at(right_split, &omt, length - i + 1); assert(length == toku_omt_size(omt));
CKERR2(r,ERANGE); u_int32_t index_to_delete = random()%length;
assert(toku_omt_size(left_split) == i); r = toku_omt_delete_at(omt, index_to_delete);
assert(toku_omt_size(right_split) == length - i); CKERR(r);
r = toku_omt_split_at(right_split, &omt, length - i + 1); for (i = index_to_delete+1; i < length; i++) {
CKERR2(r,ERANGE); values[i-1] = values[i];
assert(toku_omt_size(left_split) == i); }
assert(toku_omt_size(right_split) == length - i); length--;
test_fetch_verify(omt, values, length);
// test_iterate_verify(omt, values, length);
// test merge }
// assert(length == 0);
r = toku_omt_merge(left_split,right_split,&omt); assert(length == toku_omt_size(omt));
CKERR(r); r = toku_omt_delete_at(omt, length);
left_split = NULL; CKERR2(r, ERANGE);
right_split = NULL; assert(length == toku_omt_size(omt));
assert(toku_omt_size(omt) == length); r = toku_omt_delete_at(omt, length+1);
test_fetch_verify(omt, values, length); CKERR2(r, ERANGE);
test_iterate_verify(omt, values, length); test_close(close);
} }
test_close(close);
} void test_split_merge(enum create_type create_choice, enum close_when_done close) {
int r = ENOSYS;
u_int32_t i = 0;
void init_values(enum rand_type rand_choice) { OMT left_split = NULL;
const u_int32_t test_size = 100; OMT right_split = NULL;
if (rand_choice == TEST_RANDOM) { test_create_from_sorted_array(create_choice, KEEP_WHEN_DONE);
init_distinct_random_values(random_seed, test_size);
} for (i = 0; i <= length; i++) {
else if (rand_choice == TEST_SORTED) { r = toku_omt_split_at(omt, &right_split, length+1);
init_distinct_sorted_values(random_seed, test_size); CKERR2(r,ERANGE);
} r = toku_omt_split_at(omt, &right_split, length+2);
else if (rand_choice == TEST_IDENTITY) { CKERR2(r,ERANGE);
init_identity_values( random_seed, test_size);
} //
else assert(FALSE); // test successful split
} //
r = toku_omt_split_at(omt, &right_split, i);
void test_create_array(enum create_type create_choice, enum rand_type rand_choice) { CKERR(r);
/* ********************************************************************** */ left_split = omt;
init_values(rand_choice); omt = NULL;
test_create_from_sorted_array( create_choice, CLOSE_WHEN_DONE); assert(toku_omt_size(left_split) == i);
test_create_from_sorted_array_size(create_choice, CLOSE_WHEN_DONE); assert(toku_omt_size(right_split) == length - i);
/* ********************************************************************** */ test_fetch_verify(left_split, values, i);
init_values(rand_choice); test_iterate_verify(left_split, values, i);
test_create_fetch_verify( create_choice, CLOSE_WHEN_DONE); test_fetch_verify(right_split, &values[i], length - i);
/* ********************************************************************** */ test_iterate_verify(right_split, &values[i], length - i);
init_values(rand_choice); //
test_create_iterate_verify( create_choice, CLOSE_WHEN_DONE); // verify that new OMT's cannot do bad splits
/* ********************************************************************** */ //
init_values(rand_choice); r = toku_omt_split_at(left_split, &omt, i+1);
test_create_set_at( create_choice, CLOSE_WHEN_DONE); CKERR2(r,ERANGE);
/* ********************************************************************** */ assert(toku_omt_size(left_split) == i);
init_values(rand_choice); assert(toku_omt_size(right_split) == length - i);
test_create_delete_at( create_choice, CLOSE_WHEN_DONE); r = toku_omt_split_at(left_split, &omt, i+2);
/* ********************************************************************** */ CKERR2(r,ERANGE);
init_values(rand_choice); assert(toku_omt_size(left_split) == i);
test_create_insert( CLOSE_WHEN_DONE); assert(toku_omt_size(right_split) == length - i);
/* ********************************************************************** */ r = toku_omt_split_at(right_split, &omt, length - i + 1);
init_values(rand_choice); CKERR2(r,ERANGE);
test_split_merge( create_choice, CLOSE_WHEN_DONE); assert(toku_omt_size(left_split) == i);
} assert(toku_omt_size(right_split) == length - i);
r = toku_omt_split_at(right_split, &omt, length - i + 1);
typedef struct { CKERR2(r,ERANGE);
u_int32_t first_zero; assert(toku_omt_size(left_split) == i);
u_int32_t first_pos; assert(toku_omt_size(right_split) == length - i);
} h_extra;
//
int test_heaviside(OMTVALUE v, void* x) { // test merge
h_extra* extra = (h_extra*)x; //
assert(v && x); r = toku_omt_merge(left_split,right_split,&omt);
assert(extra->first_zero <= extra->first_pos); CKERR(r);
left_split = NULL;
u_int32_t value = v->number; right_split = NULL;
if (value < extra->first_zero) return -1; assert(toku_omt_size(omt) == length);
if (value < extra->first_pos) return 0; test_fetch_verify(omt, values, length);
return 1; test_iterate_verify(omt, values, length);
} }
test_close(close);
void heavy_extra(h_extra* extra, u_int32_t first_zero, u_int32_t first_pos) { }
extra->first_zero = first_zero;
extra->first_pos = first_pos;
} void init_values(enum rand_type rand_choice) {
const u_int32_t test_size = 100;
void test_find_dir(int dir, void* extra, int (*h)(OMTVALUE, void*), if (rand_choice == TEST_RANDOM) {
int r_expect, BOOL idx_will_change, u_int32_t idx_expect, init_distinct_random_values(random_seed, test_size);
u_int32_t number_expect) { }
u_int32_t idx = UINT32_MAX; else if (rand_choice == TEST_SORTED) {
u_int32_t old_idx = idx; init_distinct_sorted_values(random_seed, test_size);
OMTVALUE omt_val; }
int r; else if (rand_choice == TEST_IDENTITY) {
init_identity_values( random_seed, test_size);
omt_val = NULL; }
if (dir == 0) { else assert(FALSE);
r = toku_omt_find_zero(omt, h, extra, &omt_val, &idx); }
}
else { void test_create_array(enum create_type create_choice, enum rand_type rand_choice) {
r = toku_omt_find( omt, h, extra, dir, &omt_val, &idx); /* ********************************************************************** */
} init_values(rand_choice);
CKERR2(r, r_expect); test_create_from_sorted_array( create_choice, CLOSE_WHEN_DONE);
if (idx_will_change) { test_create_from_sorted_array_size(create_choice, CLOSE_WHEN_DONE);
assert(idx == idx_expect); /* ********************************************************************** */
} init_values(rand_choice);
else { test_create_fetch_verify( create_choice, CLOSE_WHEN_DONE);
assert(idx == old_idx); /* ********************************************************************** */
} init_values(rand_choice);
if (r == DB_NOTFOUND) { test_create_iterate_verify( create_choice, CLOSE_WHEN_DONE);
assert(omt_val == NULL); /* ********************************************************************** */
} init_values(rand_choice);
else { test_create_set_at( create_choice, CLOSE_WHEN_DONE);
assert(omt_val->number == number_expect); /* ********************************************************************** */
} init_values(rand_choice);
test_create_delete_at( create_choice, CLOSE_WHEN_DONE);
/* Verify we can pass NULL value. */ /* ********************************************************************** */
omt_val = NULL; init_values(rand_choice);
idx = old_idx; test_create_insert( CLOSE_WHEN_DONE);
if (dir == 0) { /* ********************************************************************** */
r = toku_omt_find_zero(omt, h, extra, NULL, &idx); init_values(rand_choice);
} test_split_merge( create_choice, CLOSE_WHEN_DONE);
else { }
r = toku_omt_find( omt, h, extra, dir, NULL, &idx);
} typedef struct {
CKERR2(r, r_expect); u_int32_t first_zero;
if (idx_will_change) { u_int32_t first_pos;
assert(idx == idx_expect); } h_extra;
}
else { int test_heaviside(OMTVALUE v_omt, void* x) {
assert(idx == old_idx); TESTVALUE v = (OMTVALUE) v_omt;
} h_extra* extra = (h_extra*)x;
assert(omt_val == NULL); assert(v && x);
assert(extra->first_zero <= extra->first_pos);
/* Verify we can pass NULL idx. */
omt_val = NULL; u_int32_t value = v->number;
idx = old_idx; if (value < extra->first_zero) return -1;
if (dir == 0) { if (value < extra->first_pos) return 0;
r = toku_omt_find_zero(omt, h, extra, &omt_val, NULL); return 1;
} }
else {
r = toku_omt_find( omt, h, extra, dir, &omt_val, NULL); void heavy_extra(h_extra* extra, u_int32_t first_zero, u_int32_t first_pos) {
} extra->first_zero = first_zero;
CKERR2(r, r_expect); extra->first_pos = first_pos;
assert(idx == old_idx); }
if (r == DB_NOTFOUND) {
assert(omt_val == NULL); void test_find_dir(int dir, void* extra, int (*h)(OMTVALUE, void*),
} int r_expect, BOOL idx_will_change, u_int32_t idx_expect,
else { u_int32_t number_expect, BOOL cursor_valid) {
assert(omt_val->number == number_expect); u_int32_t idx = UINT32_MAX;
} u_int32_t old_idx = idx;
TESTVALUE omt_val, omt_val_curs;
/* Verify we can pass NULL both. */ OMTCURSOR c;
omt_val = NULL; int r;
idx = old_idx; BOOL found;
if (dir == 0) {
r = toku_omt_find_zero(omt, h, extra, NULL, NULL); r = toku_omt_cursor_create(&c);
} CKERR(r);
else {
r = toku_omt_find( omt, h, extra, dir, NULL, NULL); omt_val = NULL;
} if (dir == 0) {
CKERR2(r, r_expect); r = toku_omt_find_zero(omt, h, extra, (OMTVALUE) &omt_val, &idx, c);
assert(idx == old_idx); }
assert(omt_val == NULL); else {
} r = toku_omt_find( omt, h, extra, dir, (OMTVALUE) &omt_val, &idx, c);
}
void test_find(enum create_type create_choice, enum close_when_done close) { CKERR2(r, r_expect);
h_extra extra; if (idx_will_change) {
init_identity_values(random_seed, 100); assert(idx == idx_expect);
test_create_from_sorted_array(create_choice, KEEP_WHEN_DONE); }
else {
/* assert(idx == old_idx);
-...- }
A if (r == DB_NOTFOUND) {
*/ assert(omt_val == NULL);
heavy_extra(&extra, length, length); found = FALSE;
test_find_dir(-1, &extra, test_heaviside, 0, TRUE, length-1, length-1); }
test_find_dir(+1, &extra, test_heaviside, DB_NOTFOUND, FALSE, 0, 0); else {
test_find_dir(0, &extra, test_heaviside, DB_NOTFOUND, TRUE, length, length); assert(omt_val->number == number_expect);
found = TRUE;
}
/*
+...+ assert(!cursor_valid == !toku_omt_cursor_is_valid(c));
B if (cursor_valid) {
*/ TESTVALUE tmp;
heavy_extra(&extra, 0, 0); assert(idx_will_change);
test_find_dir(-1, &extra, test_heaviside, DB_NOTFOUND, FALSE, 0, 0); omt_val_curs = NULL;
test_find_dir(+1, &extra, test_heaviside, 0, TRUE, 0, 0); r = toku_omt_cursor_current(c, (OMTVALUE) &omt_val_curs);
test_find_dir(0, &extra, test_heaviside, DB_NOTFOUND, TRUE, 0, 0); CKERR(r);
assert(toku_omt_cursor_is_valid(c));
/* r = toku_omt_fetch(omt, idx, (OMTVALUE) &tmp, NULL);
0...0 CKERR(r);
C if (found) assert(tmp==omt_val);
*/ assert(omt_val_curs != NULL);
heavy_extra(&extra, 0, length); assert(omt_val_curs == tmp);
test_find_dir(-1, &extra, test_heaviside, DB_NOTFOUND, FALSE, 0, 0); assert(omt_val_curs->number == tmp->number);
test_find_dir(+1, &extra, test_heaviside, DB_NOTFOUND, FALSE, 0, 0); if (found) assert(omt_val_curs->number==number_expect);
test_find_dir(0, &extra, test_heaviside, 0, TRUE, 0, 0); }
/* toku_omt_cursor_invalidate(c);
-...-0...0 assert(!toku_omt_cursor_is_valid(c));
AC toku_omt_cursor_destroy(&c);
*/
heavy_extra(&extra, length/2, length); /* Verify we can pass NULL value. */
test_find_dir(-1, &extra, test_heaviside, 0, TRUE, length/2-1, length/2-1); omt_val = NULL;
test_find_dir(+1, &extra, test_heaviside, DB_NOTFOUND, FALSE, 0, 0); idx = old_idx;
test_find_dir(0, &extra, test_heaviside, 0, TRUE, length/2, length/2); if (dir == 0) {
r = toku_omt_find_zero(omt, h, extra, NULL, &idx, NULL);
/* }
0...0+...+ else {
C B r = toku_omt_find( omt, h, extra, dir, NULL, &idx, NULL);
*/ }
heavy_extra(&extra, 0, length/2); CKERR2(r, r_expect);
test_find_dir(-1, &extra, test_heaviside, DB_NOTFOUND, FALSE, 0, 0); if (idx_will_change) {
test_find_dir(+1, &extra, test_heaviside, 0, TRUE, length/2, length/2); assert(idx == idx_expect);
test_find_dir(0, &extra, test_heaviside, 0, TRUE, 0, 0); }
else {
/* assert(idx == old_idx);
-...-+...+ }
AB assert(omt_val == NULL);
*/
heavy_extra(&extra, length/2, length/2); /* Verify we can pass NULL idx. */
test_find_dir(-1, &extra, test_heaviside, 0, TRUE, length/2-1, length/2-1); omt_val = NULL;
test_find_dir(+1, &extra, test_heaviside, 0, TRUE, length/2, length/2); idx = old_idx;
test_find_dir(0, &extra, test_heaviside, DB_NOTFOUND, TRUE, length/2, length/2); if (dir == 0) {
r = toku_omt_find_zero(omt, h, extra, (OMTVALUE) &omt_val, 0, NULL);
/* }
-...-0...0+...+ else {
AC B r = toku_omt_find( omt, h, extra, dir, (OMTVALUE) &omt_val, 0, NULL);
*/ }
heavy_extra(&extra, length/3, 2*length/3); CKERR2(r, r_expect);
test_find_dir(-1, &extra, test_heaviside, 0, TRUE, length/3-1, length/3-1); assert(idx == old_idx);
test_find_dir(+1, &extra, test_heaviside, 0, TRUE, 2*length/3, 2*length/3); if (r == DB_NOTFOUND) {
test_find_dir(0, &extra, test_heaviside, 0, TRUE, length/3, length/3); assert(omt_val == NULL);
}
/* Cleanup */ else {
test_close(close); assert(omt_val->number == number_expect);
} }
void runtests_create_choice(enum create_type create_choice) { /* Verify we can pass NULL both. */
test_create_array(create_choice, TEST_SORTED); omt_val = NULL;
test_create_array(create_choice, TEST_RANDOM); idx = old_idx;
test_create_array(create_choice, TEST_IDENTITY); if (dir == 0) {
test_find( create_choice, CLOSE_WHEN_DONE); r = toku_omt_find_zero(omt, h, extra, NULL, 0, NULL);
} }
else {
int main(int argc, const char *argv[]) { r = toku_omt_find( omt, h, extra, dir, NULL, 0, NULL);
parse_args(argc, argv); }
init_globals(); CKERR2(r, r_expect);
test_create( CLOSE_WHEN_DONE); assert(idx == old_idx);
test_create_size( CLOSE_WHEN_DONE); assert(omt_val == NULL);
runtests_create_choice(BATCH_INSERT); }
runtests_create_choice(INSERT_AT);
runtests_create_choice(INSERT_AT_ALMOST_RANDOM); void test_find(enum create_type create_choice, enum close_when_done close) {
cleanup_globals(); h_extra extra;
return 0; init_identity_values(random_seed, 100);
} test_create_from_sorted_array(create_choice, KEEP_WHEN_DONE);
/*
-...-
A
*/
heavy_extra(&extra, length, length);
test_find_dir(-1, &extra, test_heaviside, 0, TRUE, length-1, length-1, TRUE);
test_find_dir(+1, &extra, test_heaviside, DB_NOTFOUND, FALSE, 0, 0, FALSE);
test_find_dir(0, &extra, test_heaviside, DB_NOTFOUND, TRUE, length, length, FALSE);
/*
+...+
B
*/
heavy_extra(&extra, 0, 0);
test_find_dir(-1, &extra, test_heaviside, DB_NOTFOUND, FALSE, 0, 0, FALSE);
test_find_dir(+1, &extra, test_heaviside, 0, TRUE, 0, 0, TRUE);
test_find_dir(0, &extra, test_heaviside, DB_NOTFOUND, TRUE, 0, 0, TRUE);
/*
0...0
C
*/
heavy_extra(&extra, 0, length);
test_find_dir(-1, &extra, test_heaviside, DB_NOTFOUND, FALSE, 0, 0, FALSE);
test_find_dir(+1, &extra, test_heaviside, DB_NOTFOUND, FALSE, 0, 0, FALSE);
test_find_dir(0, &extra, test_heaviside, 0, TRUE, 0, 0, TRUE);
/*
-...-0...0
AC
*/
heavy_extra(&extra, length/2, length);
test_find_dir(-1, &extra, test_heaviside, 0, TRUE, length/2-1, length/2-1, TRUE);
test_find_dir(+1, &extra, test_heaviside, DB_NOTFOUND, FALSE, 0, 0, FALSE);
test_find_dir(0, &extra, test_heaviside, 0, TRUE, length/2, length/2, TRUE);
/*
0...0+...+
C B
*/
heavy_extra(&extra, 0, length/2);
test_find_dir(-1, &extra, test_heaviside, DB_NOTFOUND, FALSE, 0, 0, FALSE);
test_find_dir(+1, &extra, test_heaviside, 0, TRUE, length/2, length/2, TRUE);
test_find_dir(0, &extra, test_heaviside, 0, TRUE, 0, 0, TRUE);
/*
-...-+...+
AB
*/
heavy_extra(&extra, length/2, length/2);
test_find_dir(-1, &extra, test_heaviside, 0, TRUE, length/2-1, length/2-1, TRUE);
test_find_dir(+1, &extra, test_heaviside, 0, TRUE, length/2, length/2, TRUE);
test_find_dir(0, &extra, test_heaviside, DB_NOTFOUND, TRUE, length/2, length/2, TRUE);
/*
-...-0...0+...+
AC B
*/
heavy_extra(&extra, length/3, 2*length/3);
test_find_dir(-1, &extra, test_heaviside, 0, TRUE, length/3-1, length/3-1, TRUE);
test_find_dir(+1, &extra, test_heaviside, 0, TRUE, 2*length/3, 2*length/3, TRUE);
test_find_dir(0, &extra, test_heaviside, 0, TRUE, length/3, length/3, TRUE);
/* Cleanup */
test_close(close);
}
void runtests_create_choice(enum create_type create_choice) {
test_create_array(create_choice, TEST_SORTED);
test_create_array(create_choice, TEST_RANDOM);
test_create_array(create_choice, TEST_IDENTITY);
test_find( create_choice, CLOSE_WHEN_DONE);
}
int main(int argc, const char *argv[]) {
parse_args(argc, argv);
init_globals();
test_create( CLOSE_WHEN_DONE);
test_create_size( CLOSE_WHEN_DONE);
runtests_create_choice(BATCH_INSERT);
runtests_create_choice(INSERT_AT);
runtests_create_choice(INSERT_AT_ALMOST_RANDOM);
cleanup_globals();
return 0;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment