diff --git a/GNUmakefile.in b/GNUmakefile.in index 746a1f9..0c75368 100644 --- a/GNUmakefile.in +++ b/GNUmakefile.in @@ -88,7 +88,7 @@ stamp-h: config.h.in config.status echo > stamp-h clean: - rm -f mtd mtclient mttest test_string test_atomics *.o libjson.a + rm -f mtd mtclient mttest scantest test_string test_atomics *.o libjson.a rm -rf .deps DEPFILES := $(wildcard $(DEPSDIR)/*.d) diff --git a/masstree.hh b/masstree.hh index ea7d5c0..b08fafc 100644 --- a/masstree.hh +++ b/masstree.hh @@ -60,6 +60,7 @@ class basic_table { typedef typename P::threadinfo_type threadinfo; typedef unlocked_tcursor

unlocked_cursor_type; typedef tcursor

cursor_type; + typedef std::pair itvalue_type; inline basic_table(); @@ -76,6 +77,11 @@ class basic_table { template int rscan(Str firstkey, bool matchfirst, F& scanner, threadinfo& ti) const; + class iterator; + iterator begin(threadinfo& ti); + iterator iterate_from(Str firstkey, threadinfo& ti); + iterator end(threadinfo& ti); + template inline int modify(Str key, F& f, threadinfo& ti); template diff --git a/masstree_iterator.hh b/masstree_iterator.hh new file mode 100644 index 0000000..68a8351 --- /dev/null +++ b/masstree_iterator.hh @@ -0,0 +1,195 @@ +/* Masstree + * Eddie Kohler, Yandong Mao, Robert Morris + * Copyright (c) 2012-2014 President and Fellows of Harvard College + * Copyright (c) 2012-2014 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, subject to the conditions + * listed in the Masstree LICENSE file. These conditions include: you must + * preserve this copyright notice, and you cannot mention the copyright + * holders in advertising related to the Software without their permission. + * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This + * notice is a summary of the Masstree LICENSE file; the license in that file + * is legally binding. + */ +#ifndef MASSTREE_ITERATOR_HH +#define MASSTREE_ITERATOR_HH +#include "masstree_scan.hh" + +namespace Masstree { +template +class basic_table

::iterator + : std::iterator { + typedef typename P::ikey_type ikey_type; + typedef typename node_type::key_type key_type; + typedef typename node_type::leaf_type::leafvalue_type leafvalue_type; + typedef typename node_type::nodeversion_type nodeversion_type; + typedef typename leaf_type::permuter_type permuter_type; + typedef typename leaf_type::bound_type bound_type; + + public: + iterator(basic_table

* table, threadinfo* ti, Str firstkey = ""); + static iterator make_end(basic_table

* table, threadinfo *ti); + + itvalue_type& operator*() { return pair_; }; + itvalue_type* operator->() { return &pair_; }; + bool operator==(iterator& rhs) { return (end_ == rhs.end_) && (end_ || ka_.compare(rhs.ka_) == 0); }; + bool operator!=(iterator& rhs) { return !(*this == rhs); }; + bool operator<(iterator& rhs) { return (!end_ && rhs.end_) || ka_.compare(rhs.ka_) < 0; }; + bool operator<=(iterator& rhs) { return *this < rhs || *this == rhs; }; + iterator operator++() { advance(); return *this; }; + iterator operator++(int) { iterator it = *this; advance(); return it; }; + + private: + basic_table

* table_; + threadinfo* ti_; + key_type ka_; + itvalue_type pair_; + bool emit_equal_; + bool end_; + union { + ikey_type x[(MASSTREE_MAXKEYLEN + sizeof(ikey_type) - 1)/sizeof(ikey_type)]; + char s[MASSTREE_MAXKEYLEN]; + } keybuf_; + + void advance(bool emit_equal = false); + + + // Debugging support. + int id_; + static int count_; + + void dprintf(const char *format, ...) { + va_list args; + va_start(args, format); + fprintf(stderr, "it%d: ", id_); + vfprintf(stderr, format, args); + va_end(args); + } +}; + +template int basic_table

::iterator::count_ = 0; + +template +basic_table

::iterator::iterator(basic_table

* table, threadinfo* ti, Str firstkey) + : table_(table), ti_(ti), emit_equal_(true), end_(false), id_(count_++) { + masstree_precondition(firstkey.len <= (int) sizeof(keybuf_)); + memcpy(keybuf_.s, firstkey.s, firstkey.len); + ka_ = key_type(keybuf_.s, firstkey.len); + + advance(true); +}; + +template +typename basic_table

::iterator +basic_table

::iterator::make_end(basic_table

* table, threadinfo *ti) { + iterator it = iterator(table, ti); + it.end_ = true; + return it; +} + +template +void +basic_table

::iterator::advance(bool emit_equal) { + int ki; + bool try_next_key = true; + bool try_next_index = true; + leaf_type* n; + nodeversion_type v; + node_type* root; + permuter_type perm; + Str suffix; + char suffixbuf[MASSTREE_MAXKEYLEN]; + + retry_root: + ka_.unshift_all(); + root = table_->root(); + n = root->reach_leaf(ka_, v, *ti_); + perm = n->permutation(); + ki = bound_type::lower(ka_, *n).i; + + retry: + if (v.deleted()) + goto retry_root; + + int kp = (unsigned(ki) < unsigned(perm.size())) ? perm[ki] : -1; + if (kp < 0) { + n = n->safe_next(); + if (!n) { + if (!try_next_key) { + end_ = true; + return; + } + + if (ka_.is_shifted()) + ka_.unshift(); + while (ka_.increment() && ka_.is_shifted()) + ka_.unshift(); + ka_.assign_store_ikey(ka_.ikey()); + try_next_key = false; + goto retry_root; + } + perm = n->permutation(); + v = n->stable(); + ki = bound_type::lower(ka_, *n).i; + goto retry; + } + + int keylenx = n->keylenx_[kp]; + ikey_type ikey = n->ikey0_[kp]; + leafvalue_type entry = n->lv_[kp]; + if (n->keylenx_has_ksuf(keylenx)) { + suffix = n->ksuf(kp); + memcpy(suffixbuf, suffix.s, suffix.len); + suffix.s = suffixbuf; + } + + if (n->has_changed(v)) + goto retry_root; + + ka_.assign_store_ikey(ikey); + if (n->keylenx_is_layer(keylenx)) { + ka_.shift(); + root = entry.layer(); + n = root->reach_leaf(ka_, v, *ti_); + perm = n->permutation(); + ki = bound_type::lower(ka_, *n).i; + goto retry; + } + + // XXX This condition is suspect. + if (!emit_equal && try_next_index) { + try_next_index = false; + ki++; + goto retry; + } + + int keylen = keylenx; + if (n->keylenx_has_ksuf(keylenx)) { + keylen = ka_.assign_store_suffix(suffix); + } + ka_.assign_store_length(keylen); + ka_.unshift_all(); + pair_ = itvalue_type(ka_, entry.value()); +} + +template +typename basic_table

::iterator +basic_table

::begin(threadinfo& ti) { + return iterator(this, &ti); +} + +template +typename basic_table

::iterator +basic_table

::end(threadinfo& ti) { + return iterator::make_end(this, &ti); +} + +template +typename basic_table

::iterator +basic_table

::iterate_from(Str firstkey, threadinfo& ti) { + return iterator(this, &ti, firstkey); +} +} +#endif diff --git a/masstree_key.hh b/masstree_key.hh index 4d1e8a7..f003348 100644 --- a/masstree_key.hh +++ b/masstree_key.hh @@ -185,7 +185,6 @@ class key { // Return true iff wrapped. if (has_suffix()) { ++ikey0_; - len_ = 1; return unlikely(!ikey0_); } else { ++len_; diff --git a/query_masstree.cc b/query_masstree.cc index 5c88402..4e3a18a 100644 --- a/query_masstree.cc +++ b/query_masstree.cc @@ -19,6 +19,7 @@ #include "masstree_tcursor.hh" #include "masstree_get.hh" #include "masstree_insert.hh" +#include "masstree_iterator.hh" #include "masstree_split.hh" #include "masstree_remove.hh" #include "masstree_scan.hh" @@ -413,6 +414,45 @@ void query_table

::test(threadinfo& ti) { // XXX destroy tree } +template +void query_table

::iterator_test(threadinfo& ti) { + typedef typename basic_table

::iterator iterator; + + query_table

t; + t.initialize(ti); + query q; + + const char * const values[] = { + "", "0", "1", "10", "100000000", // 0-4 + "1000000001", "1000000002", "2", "20", "200000000", // 5-9 + "aaaaaaaaaaaaaaaaaaaaaaaaaa", // 10 + "aaaaaaaaaaaaaaabbbb", "aaaaaaaaaaaaaaabbbc", "aaaaaaaaaxaaaaabbbc", "b", "c", "d", "e", "f", "g", "h", "i", "j", + "kkkkkkkk\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" "a", + "kkkkkkkk\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" "b", + "xxxxxxxxy" + }; + const char *values_copy[arraysize(values)]; + memcpy(values_copy, values, sizeof(values)); + + for (int i = arraysize(values); i > 0; --i) { + int x = rand() % i; + q.run_replace(t.table(), Str(values_copy[x]), Str(values_copy[x]), ti); + values_copy[x] = values_copy[i - 1]; + } + + const char * const * pos = values; + iterator itend = t.table_.end(ti); + for (iterator it = t.table_.begin(ti); it != itend; it++) { + Str key = it->first; + if ((int) strlen(*pos) != key.len || memcmp(*pos, key.s, key.len) != 0) { + fprintf(stderr, "scan encountered \"%.*s\", expected \"%s\"\n", key.len, key.s, *pos); + assert((int) strlen(*pos) == key.len && memcmp(*pos, key.s, key.len) == 0); + } + fprintf(stderr, "scan %.*s\n", key.len, key.s); + pos++; + } +} + template void query_table

::print(FILE *f, int indent) const { table_.print(f, indent); diff --git a/query_masstree.hh b/query_masstree.hh index eb99899..98434e6 100644 --- a/query_masstree.hh +++ b/query_masstree.hh @@ -61,6 +61,7 @@ class query_table { void print(FILE* f, int indent) const; static void test(threadinfo& ti); + static void iterator_test(threadinfo& ti); static const char* name() { return "mb"; diff --git a/scantest.cc b/scantest.cc index 7a30549..04b3afe 100644 --- a/scantest.cc +++ b/scantest.cc @@ -10,9 +10,21 @@ kvtimestamp_t initial_timestamp; int main(int argc, char *argv[]) { - (void) argc; - (void) argv; + if (argc != 2) { + fprintf(stderr, "usage: %s \n", argv[0]); + return 1; + } threadinfo* ti = threadinfo::make(threadinfo::TI_MAIN, -1); - default_table::test(*ti); + + if (strcmp(argv[1], "callback") == 0) + default_table::test(*ti); + else if (strcmp(argv[1], "iterator") == 0) + default_table::iterator_test(*ti); + else { + fprintf(stderr, "fatal: unknown test\n"); + return 1; + } + + return 0; }