From dc83777ab9e925e66c172d16621956bd9af17019 Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Mon, 12 Sep 2011 01:11:56 -0700 Subject: [PATCH 1/3] Minor tweaks, update relfunc behavior --- libraries/libmdb/mdb.c | 19 ++++++++-------- libraries/libmdb/mdb.h | 51 ++++++++++++++++++++++++++++++++++-------- 2 files changed, 52 insertions(+), 18 deletions(-) diff --git a/libraries/libmdb/mdb.c b/libraries/libmdb/mdb.c index 8769ebb4aa..94a650d73e 100644 --- a/libraries/libmdb/mdb.c +++ b/libraries/libmdb/mdb.c @@ -662,6 +662,7 @@ typedef struct MDB_dbx { MDB_cmp_func *md_cmp; /**< function for comparing keys */ MDB_cmp_func *md_dcmp; /**< function for comparing data items */ MDB_rel_func *md_rel; /**< user relocate function */ + void *md_relctx; /**< user-provided context for md_rel */ MDB_dbi md_parent; /**< parent DB of a sub-DB */ unsigned int md_dirty; /**< TRUE if DB was written in this txn */ } MDB_dbx; @@ -923,15 +924,6 @@ mdb_cmp(MDB_txn *txn, MDB_dbi dbi, const MDB_val *a, const MDB_val *b) return txn->mt_dbxs[dbi].md_cmp(a, b); } -/** Compare two data items according to a particular database. - * This returns a comparison as if the two items were data items of - * a sorted duplicates #MDB_DUPSORT database. - * @param[in] txn A transaction handle returned by #mdb_txn_begin() - * @param[in] dbi A database handle returned by #mdb_open() - * @param[in] a The first item to compare - * @param[in] b The second item to compare - * @return < 0 if a < b, 0 if a == b, > 0 if a > b - */ int mdb_dcmp(MDB_txn *txn, MDB_dbi dbi, const MDB_val *a, const MDB_val *b) { @@ -4731,4 +4723,13 @@ int mdb_set_relfunc(MDB_txn *txn, MDB_dbi dbi, MDB_rel_func *rel) return MDB_SUCCESS; } +int mdb_set_relctx(MDB_txn *txn, MDB_dbi dbi, void *ctx) +{ + if (txn == NULL || !dbi || dbi >= txn->mt_numdbs) + return EINVAL; + + txn->mt_dbxs[dbi].md_relctx = ctx; + return MDB_SUCCESS; +} + /** @} */ diff --git a/libraries/libmdb/mdb.h b/libraries/libmdb/mdb.h index ce3137ce1f..b99ece7c95 100644 --- a/libraries/libmdb/mdb.h +++ b/libraries/libmdb/mdb.h @@ -125,13 +125,18 @@ typedef struct MDB_val { typedef int (MDB_cmp_func)(const MDB_val *a, const MDB_val *b); /** A callback function used to relocate a position-dependent data item - * in a fixed-address database. The \b newptr gives the item's current address in - * the memory map, and \b oldptr gives its previous address. This callback is - * expected to walk through the fields of the record in newptr and modify any + * in a fixed-address database. The \b newptr gives the item's desired address in + * the memory map, and \b oldptr gives its previous address. The item's actual + * data resides at the address in \b item. This callback is expected to walk + * through the fields of the record in \b item and modify any * values based at the \b oldptr address to be relative to the \b newptr address. + * @param[in,out] item The item that is to be relocated. + * @param[in] oldptr The previous address. + * @param[in] newptr The new address to relocate to. + * @param[in] relctx An application-provided context, set by #mdb_set_relctx(). * @todo This feature is currently unimplemented. */ -typedef void (MDB_rel_func)(void *newptr, void *oldptr, size_t size); +typedef void (MDB_rel_func)(MDB_val *item, void *oldptr, void *newptr, void *relctx); /** @defgroup mdb_env Environment Flags * @{ @@ -178,7 +183,10 @@ typedef void (MDB_rel_func)(void *newptr, void *oldptr, size_t size); #define MDB_CURRENT 0x40 /* @} */ -/** Cursor operations */ +/** Cursor Get operations + * This is the set of all operations for retrieving data + * using a cursor. + */ typedef enum MDB_cursor_op { MDB_FIRST, /**< Position at first key/data item */ MDB_FIRST_DUP, /**< Position at first data item of current key. @@ -596,8 +604,8 @@ void mdb_close(MDB_txn *txn, MDB_dbi dbi); * with #mdb_open(), the keys are compared lexically, with shorter keys collating * before longer keys. * @warning This function must be called before any data access functions are used, - * otherwise data corruption may occur. The same function must be used by every - * process accessing the database, every time the database is used. + * otherwise data corruption may occur. The same comparison function must be used by every + * program accessing the database, every time the database is used. * @param[in] txn A transaction handle returned by #mdb_txn_begin() * @param[in] dbi A database handle returned by #mdb_open() * @param[in] cmp A #MDB_cmp_func function @@ -618,8 +626,8 @@ int mdb_set_compare(MDB_txn *txn, MDB_dbi dbi, MDB_cmp_func *cmp); * with #mdb_open(), the data items are compared lexically, with shorter items collating * before longer items. * @warning This function must be called before any data access functions are used, - * otherwise data corruption may occur. The same function must be used by every - * process accessing the database, every time the database is used. + * otherwise data corruption may occur. The same comparison function must be used by every + * program accessing the database, every time the database is used. * @param[in] txn A transaction handle returned by #mdb_txn_begin() * @param[in] dbi A database handle returned by #mdb_open() * @param[in] cmp A #MDB_cmp_func function @@ -650,6 +658,21 @@ int mdb_set_dupsort(MDB_txn *txn, MDB_dbi dbi, MDB_cmp_func *cmp); */ int mdb_set_relfunc(MDB_txn *txn, MDB_dbi dbi, MDB_rel_func *rel); + /** Set a context pointer for a #MDB_FIXEDMAP database's relocation function. + * See #mdb_set_relfunc and #MDB_rel_func for more details. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_open() + * @param[in] ctx An arbitrary pointer for whatever the application needs. + * It will be passed to the callback function set by #mdb_set_relfunc + * as its \b relctx parameter whenever the callback is invoked. + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * + */ +int mdb_set_relctx(MDB_txn *txn, MDB_dbi dbi, void *ctx); + /** Get items from a database. * This function retrieves key/data pairs from the database. The address * and length of the data associated with the specified \b key are returned @@ -852,5 +875,15 @@ int mdb_cursor_count(MDB_cursor *cursor, size_t *countp); */ int mdb_cmp(MDB_txn *txn, MDB_dbi dbi, const MDB_val *a, const MDB_val *b); + /** Compare two data items according to a particular database. + * This returns a comparison as if the two items were data items of + * a sorted duplicates #MDB_DUPSORT database. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_open() + * @param[in] a The first item to compare + * @param[in] b The second item to compare + * @return < 0 if a < b, 0 if a == b, > 0 if a > b + */ +int mdb_dcmp(MDB_txn *txn, MDB_dbi dbi, const MDB_val *a, const MDB_val *b); /** @} */ #endif /* _MDB_H_ */ From 70c79df1dfd70e90a4c18d3d9710a3b01be011b2 Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Tue, 13 Sep 2011 11:25:17 -0700 Subject: [PATCH 2/3] cursor_set/search_node tweaks, use intcmp when possible --- libraries/libmdb/mdb.c | 83 +++++++++++++++++++++++++----------------- 1 file changed, 50 insertions(+), 33 deletions(-) diff --git a/libraries/libmdb/mdb.c b/libraries/libmdb/mdb.c index 94a650d73e..498d87833f 100644 --- a/libraries/libmdb/mdb.c +++ b/libraries/libmdb/mdb.c @@ -850,7 +850,7 @@ static size_t mdb_branch_size(MDB_env *env, MDB_val *key); static void mdb_default_cmp(MDB_txn *txn, MDB_dbi dbi); /** @cond */ -static MDB_cmp_func memncmp, memnrcmp, intcmp, cintcmp; +static MDB_cmp_func memncmp, memnrcmp, intcmp, cintcmp, longcmp; /** @endcond */ #ifdef _WIN32 @@ -2291,22 +2291,20 @@ mdb_env_close(MDB_env *env) free(env); } +/* only for aligned size_t's */ +static int +longcmp(const MDB_val *a, const MDB_val *b) +{ + return (*(size_t *)a->mv_data < *(size_t *)b->mv_data) ? -1 : + *(size_t *)a->mv_data > *(size_t *)b->mv_data; +} + /* only for aligned ints */ static int intcmp(const MDB_val *a, const MDB_val *b) { - if (a->mv_size == sizeof(long)) - { - unsigned long *la, *lb; - la = a->mv_data; - lb = b->mv_data; - return *la - *lb; - } else { - unsigned int *ia, *ib; - ia = a->mv_data; - ib = b->mv_data; - return *ia - *ib; - } + return (*(unsigned int *)a->mv_data < *(unsigned int *)b->mv_data) ? -1 : + *(unsigned int *)a->mv_data > *(unsigned int *)b->mv_data; } /* ints must always be the same size */ @@ -2401,6 +2399,17 @@ mdb_search_node(MDB_cursor *mc, MDB_val *key, int *exactp) low = IS_LEAF(mp) ? 0 : 1; high = nkeys - 1; cmp = mc->mc_dbx->md_cmp; + + /* Branch pages have no data, so if using integer keys, + * alignment is guaranteed. Use faster intcmp. + */ + if (cmp == cintcmp && IS_BRANCH(mp)) { + if (NODEPTR(mp, 1)->mn_ksize == sizeof(size_t)) + cmp = longcmp; + else + cmp = intcmp; + } + if (IS_LEAF2(mp)) { nodekey.mv_size = mc->mc_db->md_pad; node = NODEPTR(mp, 0); /* fake */ @@ -2867,6 +2876,7 @@ mdb_cursor_set(MDB_cursor *mc, MDB_val *key, MDB_val *data, MDB_cursor_op op, int *exactp) { int rc; + MDB_page *mp; MDB_node *leaf; DKBUF; @@ -2878,11 +2888,12 @@ mdb_cursor_set(MDB_cursor *mc, MDB_val *key, MDB_val *data, if (mc->mc_flags & C_INITIALIZED) { MDB_val nodekey; - if (mc->mc_pg[mc->mc_top]->mp_flags & P_LEAF2) { + mp = mc->mc_pg[mc->mc_top]; + if (mp->mp_flags & P_LEAF2) { nodekey.mv_size = mc->mc_db->md_pad; - nodekey.mv_data = LEAF2KEY(mc->mc_pg[mc->mc_top], 0, nodekey.mv_size); + nodekey.mv_data = LEAF2KEY(mp, 0, nodekey.mv_size); } else { - leaf = NODEPTR(mc->mc_pg[mc->mc_top], 0); + leaf = NODEPTR(mp, 0); MDB_SET_KEY(leaf, &nodekey); } rc = mc->mc_dbx->md_cmp(key, &nodekey); @@ -2891,26 +2902,29 @@ mdb_cursor_set(MDB_cursor *mc, MDB_val *key, MDB_val *data, * was the one we wanted. */ mc->mc_ki[mc->mc_top] = 0; -set1: + leaf = NODEPTR(mp, 0); if (exactp) *exactp = 1; - leaf = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]); - goto set3; + goto set1; } if (rc > 0) { unsigned int i; - if (NUMKEYS(mc->mc_pg[mc->mc_top]) > 1) { - if (mc->mc_pg[mc->mc_top]->mp_flags & P_LEAF2) { - nodekey.mv_data = LEAF2KEY(mc->mc_pg[mc->mc_top], - NUMKEYS(mc->mc_pg[mc->mc_top])-1, nodekey.mv_size); + unsigned int nkeys = NUMKEYS(mp); + if (nkeys > 1) { + if (mp->mp_flags & P_LEAF2) { + nodekey.mv_data = LEAF2KEY(mp, + nkeys-1, nodekey.mv_size); } else { - leaf = NODEPTR(mc->mc_pg[mc->mc_top], NUMKEYS(mc->mc_pg[mc->mc_top])-1); + leaf = NODEPTR(mp, nkeys-1); MDB_SET_KEY(leaf, &nodekey); } rc = mc->mc_dbx->md_cmp(key, &nodekey); if (rc == 0) { /* last node was the one we wanted */ - mc->mc_ki[mc->mc_top] = NUMKEYS(mc->mc_pg[mc->mc_top])-1; + mc->mc_ki[mc->mc_top] = nkeys-1; + leaf = NODEPTR(mp, nkeys-1); + if (exactp) + *exactp = 1; goto set1; } if (rc < 0) { @@ -2928,7 +2942,7 @@ set1: break; if (i == mc->mc_top) { /* There are no other pages */ - mc->mc_ki[mc->mc_top] = NUMKEYS(mc->mc_pg[mc->mc_top]); + mc->mc_ki[mc->mc_top] = nkeys; return MDB_NOTFOUND; } } @@ -2938,7 +2952,8 @@ set1: if (rc != MDB_SUCCESS) return rc; - assert(IS_LEAF(mc->mc_pg[mc->mc_top])); + mp = mc->mc_pg[mc->mc_top]; + assert(IS_LEAF(mp)); set2: leaf = mdb_search_node(mc, key, exactp); @@ -2951,18 +2966,18 @@ set2: DPUTS("===> inexact leaf not found, goto sibling"); if ((rc = mdb_sibling(mc, 1)) != MDB_SUCCESS) return rc; /* no entries matched */ - mc->mc_ki[mc->mc_top] = 0; - assert(IS_LEAF(mc->mc_pg[mc->mc_top])); - leaf = NODEPTR(mc->mc_pg[mc->mc_top], 0); + mp = mc->mc_pg[mc->mc_top]; + assert(IS_LEAF(mp)); + leaf = NODEPTR(mp, 0); } -set3: +set1: mc->mc_flags |= C_INITIALIZED; mc->mc_flags &= ~C_EOF; - if (IS_LEAF2(mc->mc_pg[mc->mc_top])) { + if (IS_LEAF2(mp)) { key->mv_size = mc->mc_db->md_pad; - key->mv_data = LEAF2KEY(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top], key->mv_size); + key->mv_data = LEAF2KEY(mp, mc->mc_ki[mc->mc_top], key->mv_size); return MDB_SUCCESS; } @@ -3731,6 +3746,8 @@ mdb_xcursor_init1(MDB_cursor *mc, MDB_node *node) mx->mx_dbx.md_name.mv_size = node->mn_ksize; mx->mx_cursor.mc_snum = 0; mx->mx_cursor.mc_flags = 0; + if (mx->mx_dbx.md_cmp == intcmp && mx->mx_db.md_pad == sizeof(size_t)) + mx->mx_dbx.md_cmp = longcmp; } static void From 56fe0d4f1a90f0a8bac5f6096ddfdc6d670fea95 Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Tue, 13 Sep 2011 13:34:22 -0700 Subject: [PATCH 3/3] Tweak search_node inner loop to avoid LEAF2 checks --- libraries/libmdb/mdb.c | 82 ++++++++++++++++++++++++------------------ 1 file changed, 47 insertions(+), 35 deletions(-) diff --git a/libraries/libmdb/mdb.c b/libraries/libmdb/mdb.c index 498d87833f..0f09121e76 100644 --- a/libraries/libmdb/mdb.c +++ b/libraries/libmdb/mdb.c @@ -2413,36 +2413,43 @@ mdb_search_node(MDB_cursor *mc, MDB_val *key, int *exactp) if (IS_LEAF2(mp)) { nodekey.mv_size = mc->mc_db->md_pad; node = NODEPTR(mp, 0); /* fake */ - } - while (low <= high) { - i = (low + high) >> 1; - - if (IS_LEAF2(mp)) { + while (low <= high) { + i = (low + high) >> 1; nodekey.mv_data = LEAF2KEY(mp, i, nodekey.mv_size); - } else { - node = NODEPTR(mp, i); - - nodekey.mv_size = node->mn_ksize; - nodekey.mv_data = NODEKEY(node); - } - - rc = cmp(key, &nodekey); - -#if DEBUG - if (IS_LEAF(mp)) + rc = cmp(key, &nodekey); DPRINTF("found leaf index %u [%s], rc = %i", i, DKEY(&nodekey), rc); - else - DPRINTF("found branch index %u [%s -> %zu], rc = %i", - i, DKEY(&nodekey), NODEPGNO(node), rc); -#endif + if (rc == 0) + break; + if (rc > 0) + low = i + 1; + else + high = i - 1; + } + } else { + while (low <= high) { + i = (low + high) >> 1; - if (rc == 0) - break; - if (rc > 0) - low = i + 1; - else - high = i - 1; + node = NODEPTR(mp, i); + nodekey.mv_size = NODEKSZ(node); + nodekey.mv_data = NODEKEY(node); + + rc = cmp(key, &nodekey); +#if DEBUG + if (IS_LEAF(mp)) + DPRINTF("found leaf index %u [%s], rc = %i", + i, DKEY(&nodekey), rc); + else + DPRINTF("found branch index %u [%s -> %zu], rc = %i", + i, DKEY(&nodekey), NODEPGNO(node), rc); +#endif + if (rc == 0) + break; + if (rc > 0) + low = i + 1; + else + high = i - 1; + } } if (rc > 0) { /* Found entry is less than the key. */ @@ -2530,36 +2537,41 @@ mdb_search_page_root(MDB_cursor *mc, MDB_val *key, int modify) while (IS_BRANCH(mp)) { MDB_node *node; + indx_t i; DPRINTF("branch page %zu has %u keys", mp->mp_pgno, NUMKEYS(mp)); assert(NUMKEYS(mp) > 1); DPRINTF("found index 0 to page %zu", NODEPGNO(NODEPTR(mp, 0))); if (key == NULL) /* Initialize cursor to first page. */ - mc->mc_ki[mc->mc_top] = 0; + i = 0; else if (key->mv_size > MAXKEYSIZE && key->mv_data == NULL) { /* cursor to last page */ - mc->mc_ki[mc->mc_top] = NUMKEYS(mp)-1; + i = NUMKEYS(mp)-1; } else { int exact; node = mdb_search_node(mc, key, &exact); if (node == NULL) - mc->mc_ki[mc->mc_top] = NUMKEYS(mp) - 1; - else if (!exact) { - assert(mc->mc_ki[mc->mc_top] > 0); - mc->mc_ki[mc->mc_top]--; + i = NUMKEYS(mp) - 1; + else { + i = mc->mc_ki[mc->mc_top]; + if (!exact) { + assert(i > 0); + i--; + } } } if (key) DPRINTF("following index %u for key [%s]", - mc->mc_ki[mc->mc_top], DKEY(key)); - assert(mc->mc_ki[mc->mc_top] < NUMKEYS(mp)); - node = NODEPTR(mp, mc->mc_ki[mc->mc_top]); + i, DKEY(key)); + assert(i < NUMKEYS(mp)); + node = NODEPTR(mp, i); if ((rc = mdb_get_page(mc->mc_txn, NODEPGNO(node), &mp))) return rc; + mc->mc_ki[mc->mc_top] = i; if ((rc = cursor_push_page(mc, mp))) return rc;