Files
openGauss-third_party/dependency/masstree/0001-Masstree-v1.0.1-MOT.patch

25862 lines
847 KiB
Diff

From c4f1e38566d811d5a4de1c6ac30ed2175cc343cf Mon Sep 17 00:00:00 2001
From: zhengshaoyu <zhengshaoyu@huawei.com>
Date: Tue, 5 Sep 2023 19:33:27 +0800
Subject: [PATCH] masstree-mot
---
.gitignore | 54 -
GNUmakefile.in | 104 -
Makefile | 37 +
bootstrap.sh | 5 -
checkpoint.cc | 30 -
checkpoint.hh | 56 -
compiler.cc | 51 -
compiler.hh | 138 +-
configure.ac | 448 -----
doc/.gitignore | 18 -
doc/GNUmakefile | 80 -
doc/elements.mp | 520 -----
doc/elemfig.sty | 88 -
doc/examples.mp | 53 -
doc/insert1.mp | 179 --
doc/masstree.mp | 200 --
doc/patches.mp | 44 -
doc/remove1.mp | 327 ----
doc/remove2.mp | 408 ----
doc/spec.tex | 1397 --------------
file.cc | 89 -
file.hh | 83 -
json.cc | 1231 ------------
json.hh | 3196 -------------------------------
jsontest.cc | 609 ------
kpermuter.hh | 2 +
ksearch.hh | 23 +
kvio.cc | 112 --
kvio.hh | 67 -
kvproto.hh | 57 -
kvrandom.cc | 38 -
kvrandom.hh | 150 --
kvrow.hh | 349 ----
kvstats.hh | 53 -
kvtest.hh | 1876 ------------------
kvthread.cc | 254 ---
value_string.cc => kvthread.cpp | 28 +-
kvthread.hh | 239 ++-
log.cc | 867 ---------
log.hh | 233 ---
masstree.hh | 37 +-
masstree_config.h | 66 +
masstree_get.hh | 12 +
masstree_insert.hh | 95 +-
masstree_key.hh | 9 +
masstree_print.hh | 186 --
masstree_remove.hh | 38 +-
masstree_scan.hh | 89 +-
masstree_split.hh | 122 +-
masstree_stats.hh | 130 --
masstree_struct.hh | 129 +-
masstree_tcursor.hh | 33 +-
memdebug.cc | 79 -
misc.cc | 40 -
misc.hh | 111 --
msgpack.cc | 240 ---
msgpack.hh | 692 -------
msgpacktest.cc | 209 --
mtclient.cc | 1724 -----------------
mtclient.hh | 219 ---
mtd.cc | 1715 -----------------
mttest.cc | 1430 --------------
nodeversion.hh | 4 +
perfstat.cc | 216 ---
perfstat.hh | 38 -
query_masstree.cc | 342 ----
query_masstree.hh | 83 -
scantest.cc | 18 -
str.cc | 53 -
str.hh | 4 +-
straccum.cc => straccum.cpp | 12 +-
straccum.hh | 8 +-
string.cc => string.cpp | 18 +-
string_base.hh | 8 +
string_slice.cc | 16 -
test_atomics.cc | 468 -----
test_string.cc | 89 -
testrunner.cc | 54 -
testrunner.hh | 60 -
unit-mt.cc | 190 --
value_array.cc | 66 -
value_array.hh | 156 --
value_bag.hh | 285 ---
value_string.hh | 193 --
value_versioned_array.cc | 97 -
value_versioned_array.hh | 205 --
86 files changed, 900 insertions(+), 22981 deletions(-)
delete mode 100644 .gitignore
delete mode 100644 GNUmakefile.in
create mode 100644 Makefile
delete mode 100755 bootstrap.sh
delete mode 100644 checkpoint.cc
delete mode 100644 checkpoint.hh
delete mode 100644 compiler.cc
delete mode 100644 configure.ac
delete mode 100644 doc/.gitignore
delete mode 100644 doc/GNUmakefile
delete mode 100644 doc/elements.mp
delete mode 100644 doc/elemfig.sty
delete mode 100644 doc/examples.mp
delete mode 100644 doc/insert1.mp
delete mode 100644 doc/masstree.mp
delete mode 100644 doc/patches.mp
delete mode 100644 doc/remove1.mp
delete mode 100644 doc/remove2.mp
delete mode 100644 doc/spec.tex
delete mode 100644 file.cc
delete mode 100644 file.hh
delete mode 100644 json.cc
delete mode 100644 json.hh
delete mode 100644 jsontest.cc
delete mode 100644 kvio.cc
delete mode 100644 kvio.hh
delete mode 100644 kvproto.hh
delete mode 100644 kvrandom.cc
delete mode 100644 kvrandom.hh
delete mode 100644 kvrow.hh
delete mode 100644 kvstats.hh
delete mode 100644 kvtest.hh
delete mode 100644 kvthread.cc
rename value_string.cc => kvthread.cpp (60%)
delete mode 100644 log.cc
delete mode 100644 log.hh
create mode 100644 masstree_config.h
delete mode 100644 masstree_print.hh
delete mode 100644 masstree_stats.hh
delete mode 100644 memdebug.cc
delete mode 100644 misc.cc
delete mode 100644 misc.hh
delete mode 100644 msgpack.cc
delete mode 100644 msgpack.hh
delete mode 100644 msgpacktest.cc
delete mode 100644 mtclient.cc
delete mode 100644 mtclient.hh
delete mode 100644 mtd.cc
delete mode 100644 mttest.cc
delete mode 100644 perfstat.cc
delete mode 100644 perfstat.hh
delete mode 100644 query_masstree.cc
delete mode 100644 query_masstree.hh
delete mode 100644 scantest.cc
delete mode 100644 str.cc
rename straccum.cc => straccum.cpp (97%)
rename string.cc => string.cpp (99%)
delete mode 100644 string_slice.cc
delete mode 100644 test_atomics.cc
delete mode 100644 test_string.cc
delete mode 100644 testrunner.cc
delete mode 100644 testrunner.hh
delete mode 100644 unit-mt.cc
delete mode 100644 value_array.cc
delete mode 100644 value_array.hh
delete mode 100644 value_bag.hh
delete mode 100644 value_string.hh
delete mode 100644 value_versioned_array.cc
delete mode 100644 value_versioned_array.hh
diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index 9e6bc0c..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,54 +0,0 @@
-*.o
-*.d
-*.a
-*.swp
-*~
-/.deps
-/autom4te.cache
-/config.h
-/config.h.in
-/config.log
-/config.status
-/configure
-/msgpacktest
-/mtclient
-/mtd
-/mttest
-kvd-log-*
-kvd-ckp-*
-notebook-kvtest.json
-stamp-h
-/jsontest
-/scantest
-/test_atomics
-/test_binsearch
-/test_string
-bench/GNUmakefile
-bench/client
-bench/load/YCSB
-bench/load/wl_gen
-bench/drivers/bin
-bench/drivers/mongo-cxx-driver-v2.0
-bench/drivers/mongodb-linux-x86_64-v2.0-latest.tgz
-bench/drivers/voltdb-client-cpp-linux-x86_64-1.3.6.1
-debugoutput
-plannerlog.txt
-obj
-volt2.jar
-ycsb.jar
-.oprofile
-voltdbroot
-memcached-1.4.8
-mongodb-linux-x86_64-2.0.0
-voltdb-2.0
-voltdb-ent-2.0
-deploy
-root
-bench/server/volt2mp/go.sh
-bench/server/voltdb-kv-benchmark/kv.jar
-bench/server/voltdb-kv-benchmark/kvbench-log.txt
-bench/server/voltdb-kv-benchmark/bin
-GNUmakefile
-log
-tags
-results
diff --git a/GNUmakefile.in b/GNUmakefile.in
deleted file mode 100644
index 1a089fa..0000000
--- a/GNUmakefile.in
+++ /dev/null
@@ -1,104 +0,0 @@
-AR = ar
-CC = @CC@
-CXX = @CXX@
-CPPFLAGS = @CPPFLAGS@
-CXXFLAGS = @CXXFLAGS@
-RANLIB = @RANLIB@
-DEPSDIR := .deps
-DEPCFLAGS = -MD -MF $(DEPSDIR)/$*.d -MP
-ifeq ($(strip $(MEMMGR)), )
- MEMMGR = @MALLOC_LIBS@
-endif
-ifneq ($(strip $(KEYSWAP)), )
- CPPFLAGS += -DKEYSWAP
-endif
-ifneq ($(strip $(NOPREFETCH)), )
- CPPFLAGS += -DNOPREFETCH
-endif
-ifneq ($(strip $(NOSUPERPAGE)), )
- CPPFLAGS += -DNOSUPERPAGE
-endif
-LIBS = @LIBS@ -lpthread -lm
-LDFLAGS = @LDFLAGS@
-
-all: test_atomics mtd mtclient mttest
-
-%.o: %.c config.h $(DEPSDIR)/stamp
- $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(DEPCFLAGS) -include config.h -c -o $@ $<
-
-%.o: %.cc config.h $(DEPSDIR)/stamp
- $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(DEPCFLAGS) -include config.h -c -o $@ $<
-
-%.S: %.o
- objdump -S $< > $@
-
-libjson.a: json.o string.o straccum.o str.o msgpack.o \
- clp.o kvrandom.o compiler.o memdebug.o kvthread.o
- @rm -f $@
- $(AR) cr $@ $^
- $(RANLIB) $@
-
-KVTREES = query_masstree.o \
- value_string.o value_array.o value_versioned_array.o \
- string_slice.o
-
-mtd: mtd.o log.o checkpoint.o file.o misc.o $(KVTREES) \
- kvio.o libjson.a
- $(CXX) $(CXXFLAGS) -o $@ $^ $(MEMMGR) $(LDFLAGS) $(LIBS)
-
-mtclient: mtclient.o misc.o testrunner.o kvio.o libjson.a
- $(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS)
-
-mttest: mttest.o misc.o checkpoint.o $(KVTREES) testrunner.o \
- kvio.o libjson.a
- $(CXX) $(CXXFLAGS) -o $@ $^ $(MEMMGR) $(LDFLAGS) $(LIBS)
-
-test_string: test_string.o string.o straccum.o compiler.o
- $(CXX) $(CXXFLAGS) -o $@ $^ $(MEMMGR) $(LDFLAGS) $(LIBS)
-
-test_atomics: test_atomics.o string.o straccum.o kvrandom.o \
- json.o compiler.o kvio.o
- $(CXX) $(CXXFLAGS) -o $@ $^ $(MEMMGR) $(LDFLAGS) $(LIBS)
-
-jsontest: jsontest.o string.o straccum.o json.o compiler.o
- $(CXX) $(CXXFLAGS) -o $@ $^ $(MEMMGR) $(LDFLAGS) $(LIBS)
-
-msgpacktest: msgpacktest.o string.o straccum.o json.o compiler.o msgpack.o
- $(CXX) $(CXXFLAGS) -o $@ $^ $(MEMMGR) $(LDFLAGS) $(LIBS)
-
-scantest: scantest.o compiler.o misc.o $(KVTREES) libjson.a
- $(CXX) $(CXXFLAGS) -o $@ $^ $(MEMMGR) $(LDFLAGS) $(LIBS)
-
-unit-mt: unit-mt.o compiler.o misc.o libjson.a
- $(CXX) $(CXXFLAGS) -o $@ $^ $(MEMMGR) $(LDFLAGS) $(LIBS)
-
-config.h: stamp-h
-
-GNUmakefile: GNUmakefile.in config.status
- CONFIG_FILES=$@ CONFIG_HEADERS= $(SHELL) ./config.status
-
-configure config.h.in: configure.ac
- autoreconf -i
- touch config.h.in
-
-config.status: configure
- ./configure @ac_configure_args@
-
-$(DEPSDIR)/stamp:
- mkdir -p $(DEPSDIR)
- touch $@
-
-stamp-h: config.h.in config.status
- CONFIG_FILES= $(SHELL) ./config.status
- echo > stamp-h
-
-clean:
- rm -f mtd mtclient mttest test_string test_atomics *.o libjson.a
- rm -rf .deps
-
-DEPFILES := $(wildcard $(DEPSDIR)/*.d)
-ifneq ($(DEPFILES),)
-include $(DEPFILES)
-endif
-
-.PHONY: clean all
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..c1dcb19
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,37 @@
+
+ifeq ($(strip $(MEMMGR)), )
+ MEMMGR = -ljemalloc
+endif
+ifneq ($(strip $(KEYSWAP)), )
+ CPPFLAGS += -DKEYSWAP
+endif
+ifneq ($(strip $(NOPREFETCH)), )
+ CPPFLAGS += -DNOPREFETCH
+endif
+ifneq ($(strip $(NOSUPERPAGE)), )
+ CPPFLAGS += -DNOSUPERPAGE
+endif
+LIBS = -lnuma -lpthread -lm
+LDFLAGS = -fstack-protector-all -z,now
+
+CXXFLAGS = -g -W -O3 -std=gnu++11 -Wextra -D_GLIBCXX_USE_CXX11_ABI=0 -fPIC -g -Werror -DNDEBUG -D_FORTIFY_SOURCE=2 -Wall -Werror -Wno-unused-parameter -Wno-unused-but-set-variable -Wno-unused-variable -Wno-unused-function -Wno-strict-aliasing -faligned-new -Wwrite-strings -Wcast-align -Wreturn-type -Wpointer-arith -Wlogical-op -Waddress -Wsizeof-pointer-memaccess -Winit-self -fno-exceptions -fno-rtti -Wnon-virtual-dtor -Wno-missing-field-initializers
+
+ifeq ($(shell uname -p),aarch64)
+ CXXFLAGS += -march=armv8-a+crc
+else
+ CXXFLAGS += -mcx16
+endif
+
+all: libmasstree.so
+
+%.o: %.cpp
+ $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(MEMMGR) $(LDFLAGS) $(LIBS) -c -o $@ $<
+
+libmasstree.so: straccum.o string.o kvthread.o
+ @rm -f $@
+ $(CXX) -fstack-protector-all -Wl,-z,relro,-z,now -shared $^ -o $@
+
+clean:
+ rm -f *.o libmasstree.so
+
+.PHONY: clean all
diff --git a/bootstrap.sh b/bootstrap.sh
deleted file mode 100755
index a8510df..0000000
--- a/bootstrap.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#! /bin/sh -e
-
-autoreconf -i
-
-echo "Now, run ./configure."
diff --git a/checkpoint.cc b/checkpoint.cc
deleted file mode 100644
index 0b43a88..0000000
--- a/checkpoint.cc
+++ /dev/null
@@ -1,30 +0,0 @@
-/* Masstree
- * Eddie Kohler, Yandong Mao, Robert Morris
- * Copyright (c) 2012-2013 President and Fellows of Harvard College
- * Copyright (c) 2012-2013 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.
- */
-#include "checkpoint.hh"
-
-// add one key/value to a checkpoint.
-// called by checkpoint_tree() for each node.
-bool ckstate::visit_value(Str key, const row_type* value, threadinfo&) {
- if (endkey && key >= endkey)
- return false;
- if (!row_is_marker(value)) {
- msgpack::unparser<kvout> up(*vals);
- up.write(key).write_wide(value->timestamp());
- value->checkpoint_write(up);
- ++count;
- }
- return true;
-}
diff --git a/checkpoint.hh b/checkpoint.hh
deleted file mode 100644
index 7783a96..0000000
--- a/checkpoint.hh
+++ /dev/null
@@ -1,56 +0,0 @@
-/* Masstree
- * Eddie Kohler, Yandong Mao, Robert Morris
- * Copyright (c) 2012-2013 President and Fellows of Harvard College
- * Copyright (c) 2012-2013 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_CHECKPOINT_HH
-#define MASSTREE_CHECKPOINT_HH
-#include "kvrow.hh"
-#include "kvio.hh"
-#include "msgpack.hh"
-
-struct ckstate {
- kvout *vals; // key, val, timestamp in msgpack
- uint64_t count; // total nodes written
- uint64_t bytes;
- pthread_cond_t state_cond;
- volatile int state;
- threadinfo *ti;
- Str startkey;
- Str endkey;
-
- template <typename SS, typename K>
- void visit_leaf(const SS&, const K&, threadinfo&) {
- }
- bool visit_value(Str key, const row_type* value, threadinfo& ti);
-
- template <typename T>
- static void insert(T& table, msgpack::parser& par, threadinfo& ti);
-};
-
-template <typename T>
-void ckstate::insert(T& table, msgpack::parser& par, threadinfo& ti) {
- Str key;
- kvtimestamp_t ts{};
- par >> key >> ts;
- row_type* row = row_type::checkpoint_read(par, ts, ti);
-
- typename T::cursor_type lp(table, key);
- bool found = lp.find_insert(ti);
- masstree_invariant(!found); (void) found;
- ti.observe_phantoms(lp.node());
- lp.value() = row;
- lp.finish(1, ti);
-}
-
-#endif
diff --git a/compiler.cc b/compiler.cc
deleted file mode 100644
index fbb1251..0000000
--- a/compiler.cc
+++ /dev/null
@@ -1,51 +0,0 @@
-/* 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.
- */
-#include "compiler.hh"
-#include <stdio.h>
-#include <stdlib.h>
-
-void fail_always_assert(const char* file, int line,
- const char* assertion, const char* message) {
- if (message)
- fprintf(stderr, "assertion \"%s\" [%s] failed: file \"%s\", line %d\n",
- message, assertion, file, line);
- else
- fprintf(stderr, "assertion \"%s\" failed: file \"%s\", line %d\n",
- assertion, file, line);
- abort();
-}
-
-void fail_masstree_invariant(const char* file, int line,
- const char* assertion, const char* message) {
- if (message)
- fprintf(stderr, "invariant \"%s\" [%s] failed: file \"%s\", line %d\n",
- message, assertion, file, line);
- else
- fprintf(stderr, "invariant \"%s\" failed: file \"%s\", line %d\n",
- assertion, file, line);
- abort();
-}
-
-void fail_masstree_precondition(const char* file, int line,
- const char* assertion, const char* message) {
- if (message)
- fprintf(stderr, "precondition \"%s\" [%s] failed: file \"%s\", line %d\n",
- message, assertion, file, line);
- else
- fprintf(stderr, "precondition \"%s\" failed: file \"%s\", line %d\n",
- assertion, file, line);
- abort();
-}
diff --git a/compiler.hh b/compiler.hh
index 7f085ce..10fbd86 100644
--- a/compiler.hh
+++ b/compiler.hh
@@ -15,9 +15,9 @@
*/
#ifndef MASSTREE_COMPILER_HH
#define MASSTREE_COMPILER_HH 1
+
+#include "masstree_config.h"
#include <stdint.h>
-#define __STDC_FORMAT_MACROS
-#include <inttypes.h>
#include <arpa/inet.h>
#if HAVE_TYPE_TRAITS
#include <type_traits>
@@ -25,8 +25,12 @@
#define arraysize(a) (sizeof(a) / sizeof((a)[0]))
+#ifndef likely
#define likely(x) __builtin_expect(!!(x), 1)
+#endif
+#ifndef unlikely
#define unlikely(x) __builtin_expect(!!(x), 0)
+#endif
#if HAVE_OFF_T_IS_LONG_LONG
#define PRIdOFF_T "lld"
@@ -78,29 +82,49 @@ inline int ffs_msb(unsigned long long x) {
* Prevents reordering of loads and stores by the compiler. Not intended to
* synchronize the processor's caches. */
inline void fence() {
+#if defined(__x86_64__) || defined(__x86__)
asm volatile("" : : : "memory");
+#else
+ __sync_synchronize();
+#endif
}
/** @brief Acquire fence. */
inline void acquire_fence() {
+#if defined(__x86_64__) || defined(__x86__)
asm volatile("" : : : "memory");
+#else
+ __sync_synchronize();
+#endif
}
/** @brief Release fence. */
inline void release_fence() {
+#if defined(__x86_64__) || defined(__x86__)
asm volatile("" : : : "memory");
+#else
+ __sync_synchronize();
+#endif
}
/** @brief Compiler fence that relaxes the processor.
Use this in spinloops, for example. */
inline void relax_fence() {
+#if defined(__x86_64__) || defined(__x86__)
asm volatile("pause" : : : "memory"); // equivalent to "rep; nop"
+#else
+ asm volatile("" : : : "memory"); // equivalent to "rep; nop"
+#endif
}
/** @brief Full memory fence. */
inline void memory_fence() {
+#if defined(__x86_64__) || defined(__x86__)
asm volatile("mfence" : : : "memory");
+#else
+ __sync_synchronize();
+#endif
}
/** @brief Do-nothing function object. */
@@ -149,13 +173,17 @@ template <int SIZE, typename BARRIER> struct sized_compiler_operations;
template <typename B> struct sized_compiler_operations<1, B> {
typedef char type;
static inline type xchg(type* object, type new_value) {
+#if defined(__x86_64__) || defined(__x86__)
asm volatile("xchgb %0,%1"
: "+q" (new_value), "+m" (*object));
B()();
return new_value;
+#else
+ return __sync_lock_test_and_set(object, new_value);
+#endif
}
static inline type val_cmpxchg(type* object, type expected, type desired) {
-#if __x86__ && (PREFER_X86 || !HAVE___SYNC_VAL_COMPARE_AND_SWAP)
+#if (defined(__x86_64__) || defined(__x86__)) && (PREFER_X86 || !HAVE___SYNC_VAL_COMPARE_AND_SWAP)
asm volatile("lock; cmpxchgb %2,%1"
: "+a" (expected), "+m" (*object)
: "r" (desired) : "cc");
@@ -178,7 +206,7 @@ template <typename B> struct sized_compiler_operations<1, B> {
#endif
}
static inline type fetch_and_add(type *object, type addend) {
-#if __x86__ && (PREFER_X86 || !HAVE___SYNC_FETCH_AND_ADD)
+#if (defined(__x86_64__) || defined(__x86__)) && (PREFER_X86 || !HAVE___SYNC_FETCH_AND_ADD)
asm volatile("lock; xaddb %0,%1"
: "+q" (addend), "+m" (*object) : : "cc");
B()();
@@ -188,7 +216,7 @@ template <typename B> struct sized_compiler_operations<1, B> {
#endif
}
static inline void atomic_or(type* object, type addend) {
-#if __x86__
+#if defined(__x86_64__) || defined(__x86__)
asm volatile("lock; orb %0,%1"
: "=r" (addend), "+m" (*object) : : "cc");
B()();
@@ -205,13 +233,17 @@ template <typename B> struct sized_compiler_operations<2, B> {
typedef int16_t type;
#endif
static inline type xchg(type* object, type new_value) {
+#if defined(__x86_64__) || defined(__x86__)
asm volatile("xchgw %0,%1"
: "+r" (new_value), "+m" (*object));
B()();
return new_value;
+#else
+ return __sync_lock_test_and_set(object, new_value);
+#endif
}
static inline type val_cmpxchg(type* object, type expected, type desired) {
-#if __x86__ && (PREFER_X86 || !HAVE___SYNC_VAL_COMPARE_AND_SWAP)
+#if (defined(__x86_64__) || defined(__x86__)) && (PREFER_X86 || !HAVE___SYNC_VAL_COMPARE_AND_SWAP)
asm volatile("lock; cmpxchgw %2,%1"
: "+a" (expected), "+m" (*object)
: "r" (desired) : "cc");
@@ -234,7 +266,7 @@ template <typename B> struct sized_compiler_operations<2, B> {
#endif
}
static inline type fetch_and_add(type* object, type addend) {
-#if __x86__ && (PREFER_X86 || !HAVE___SYNC_FETCH_AND_ADD)
+#if (defined(__x86_64__) || defined(__x86__)) && (PREFER_X86 || !HAVE___SYNC_FETCH_AND_ADD)
asm volatile("lock; xaddw %0,%1"
: "+r" (addend), "+m" (*object) : : "cc");
B()();
@@ -244,7 +276,7 @@ template <typename B> struct sized_compiler_operations<2, B> {
#endif
}
static inline void atomic_or(type* object, type addend) {
-#if __x86__
+#if (defined(__x86_64__) || defined(__x86__))
asm volatile("lock; orw %0,%1"
: "=r" (addend), "+m" (*object) : : "cc");
B()();
@@ -261,13 +293,17 @@ template <typename B> struct sized_compiler_operations<4, B> {
typedef int32_t type;
#endif
static inline type xchg(type* object, type new_value) {
+#if (defined(__x86_64__) || defined(__x86__))
asm volatile("xchgl %0,%1"
: "+r" (new_value), "+m" (*object));
B()();
return new_value;
+#else
+ return __sync_lock_test_and_set(object, new_value);
+#endif
}
static inline type val_cmpxchg(type* object, type expected, type desired) {
-#if __x86__ && (PREFER_X86 || !HAVE___SYNC_VAL_COMPARE_AND_SWAP)
+#if (defined(__x86_64__) || defined(__x86__)) && (PREFER_X86 || !HAVE___SYNC_VAL_COMPARE_AND_SWAP)
asm volatile("lock; cmpxchgl %2,%1"
: "+a" (expected), "+m" (*object)
: "r" (desired) : "cc");
@@ -290,7 +326,7 @@ template <typename B> struct sized_compiler_operations<4, B> {
#endif
}
static inline type fetch_and_add(type *object, type addend) {
-#if __x86__ && (PREFER_X86 || !HAVE___SYNC_FETCH_AND_ADD)
+#if (defined(__x86_64__) || defined(__x86__)) && (PREFER_X86 || !HAVE___SYNC_FETCH_AND_ADD)
asm volatile("lock; xaddl %0,%1"
: "+r" (addend), "+m" (*object) : : "cc");
B()();
@@ -300,7 +336,7 @@ template <typename B> struct sized_compiler_operations<4, B> {
#endif
}
static inline void atomic_or(type* object, type addend) {
-#if __x86__
+#if (defined(__x86_64__) || defined(__x86__))
asm volatile("lock; orl %0,%1"
: "=r" (addend), "+m" (*object) : : "cc");
B()();
@@ -318,14 +354,16 @@ template <typename B> struct sized_compiler_operations<8, B> {
#else
typedef int64_t type;
#endif
-#if __x86_64__
static inline type xchg(type* object, type new_value) {
+#if (defined(__x86_64__) || defined(__x86__))
asm volatile("xchgq %0,%1"
: "+r" (new_value), "+m" (*object));
B()();
return new_value;
- }
+#else
+ return __sync_lock_test_and_set(object, new_value);
#endif
+ }
static inline type val_cmpxchg(type* object, type expected, type desired) {
#if __x86_64__ && (PREFER_X86 || !HAVE___SYNC_VAL_COMPARE_AND_SWAP_8)
asm volatile("lock; cmpxchgq %2,%1"
@@ -574,8 +612,12 @@ inline void prefetch(const void *ptr) {
#ifdef NOPREFETCH
(void) ptr;
#else
+#if (defined(__x86_64__) || defined(__x86__))
typedef struct { char x[CACHE_LINE_SIZE]; } cacheline_t;
asm volatile("prefetcht0 %0" : : "m" (*(const cacheline_t *)ptr));
+#else
+ __builtin_prefetch(ptr);
+#endif
#endif
}
#endif
@@ -584,8 +626,12 @@ inline void prefetchnta(const void *ptr) {
#ifdef NOPREFETCH
(void) ptr;
#else
+#if (defined(__x86_64__) || defined(__x86__))
typedef struct { char x[CACHE_LINE_SIZE]; } cacheline_t;
asm volatile("prefetchnta %0" : : "m" (*(const cacheline_t *)ptr));
+#else
+ __builtin_prefetch(ptr,0,0);
+#endif
#endif
}
@@ -618,9 +664,11 @@ inline uint64_t ntohq(uint64_t val) {
asm("bswapl %0; bswapl %1; xchgl %0,%1"
: "+r" (v.s.a), "+r" (v.s.b));
return v.u;
-#else /* __i386__ */
+#elif __x86_64__
asm("bswapq %0" : "+r" (val));
return val;
+#else
+ return __builtin_bswap64(val);
#endif
}
@@ -966,20 +1014,6 @@ inline T read_in_net_order(const uint8_t* s) {
return read_in_net_order<T>(reinterpret_cast<const char*>(s));
}
-
-inline uint64_t read_pmc(uint32_t ecx) {
- uint32_t a, d;
- __asm __volatile("rdpmc" : "=a"(a), "=d"(d) : "c"(ecx));
- return ((uint64_t)a) | (((uint64_t)d) << 32);
-}
-
-inline uint64_t read_tsc(void)
-{
- uint32_t low, high;
- asm volatile("rdtsc" : "=a" (low), "=d" (high));
- return ((uint64_t)low) | (((uint64_t)high) << 32);
-}
-
template <typename T>
inline int compare(T a, T b) {
if (a == b)
@@ -996,24 +1030,6 @@ template <typename T> struct type_synonym {
typedef T type;
};
-
-#if HAVE_CXX_TEMPLATE_ALIAS && HAVE_TYPE_TRAITS
-template <typename T, T V>
-using integral_constant = std::integral_constant<T, V>;
-typedef std::true_type true_type;
-typedef std::false_type false_type;
-#else
-template <typename T, T V>
-struct integral_constant {
- typedef integral_constant<T, V> type;
- typedef T value_type;
- static constexpr T value = V;
-};
-template <typename T, T V> constexpr T integral_constant<T, V>::value;
-typedef integral_constant<bool, true> true_type;
-typedef integral_constant<bool, false> false_type;
-#endif
-
#if HAVE_CXX_TEMPLATE_ALIAS && HAVE_TYPE_TRAITS
template <bool B, typename T, typename F>
using conditional = std::conditional<B, T, F>;
@@ -1158,36 +1174,6 @@ template <typename T> constexpr bool fast_argument<T, false>::is_reference;
}
-
-template <typename T>
-struct has_fast_int_multiply : public mass::false_type {
- // enum { check_t_integral = mass::integer_traits<T>::is_signed };
-};
-
-#if defined(__i386__) || defined(__x86_64__)
-inline void int_multiply(unsigned a, unsigned b, unsigned &xlow, unsigned &xhigh)
-{
- __asm__("mul %2" : "=a" (xlow), "=d" (xhigh) : "r" (a), "a" (b) : "cc");
-}
-template <> struct has_fast_int_multiply<unsigned> : public mass::true_type {};
-
-# if SIZEOF_LONG == 4 || (defined(__x86_64__) && SIZEOF_LONG == 8)
-inline void int_multiply(unsigned long a, unsigned long b, unsigned long &xlow, unsigned long &xhigh)
-{
- __asm__("mul %2" : "=a" (xlow), "=d" (xhigh) : "r" (a), "a" (b) : "cc");
-}
-template <> struct has_fast_int_multiply<unsigned long> : public mass::true_type {};
-# endif
-
-# if defined(__x86_64__) && SIZEOF_LONG_LONG == 8
-inline void int_multiply(unsigned long long a, unsigned long long b, unsigned long long &xlow, unsigned long long &xhigh)
-{
- __asm__("mul %2" : "=a" (xlow), "=d" (xhigh) : "r" (a), "a" (b) : "cc");
-}
-template <> struct has_fast_int_multiply<unsigned long long> : public mass::true_type {};
-# endif
-#endif
-
struct uninitialized_type {};
#endif
diff --git a/configure.ac b/configure.ac
deleted file mode 100644
index 01afa01..0000000
--- a/configure.ac
+++ /dev/null
@@ -1,448 +0,0 @@
-dnl Process this file with autoconf to produce a configure script.
-
-AC_INIT([masstree-beta], [0.1])
-AC_PREREQ(2.60)
-AC_CONFIG_HEADERS([config.h])
-AC_CONFIG_FILES([GNUmakefile])
-AC_SUBST(ac_configure_args)
-
-ac_user_cxx=${CXX+y}
-if test "${CXXFLAGS+y}" != y; then CXXFLAGS="-g -W -Wall -O3"; fi
-
-AC_PROG_CC
-AC_PROG_CXX
-AC_LANG_CPLUSPLUS
-AC_PROG_RANLIB
-
-AC_DEFINE([WORDS_BIGENDIAN_SET], [1], [Define if WORDS_BIGENDIAN has been set.])
-AC_C_BIGENDIAN()
-
-AC_CHECK_HEADERS([sys/epoll.h numa.h])
-
-AC_SEARCH_LIBS([numa_available], [numa], [AC_DEFINE([HAVE_LIBNUMA], [1], [Define if you have libnuma.])])
-
-
-dnl Builtins
-
-AC_DEFUN([KVDB_CHECK_BUILTIN], [
- AC_CACHE_CHECK([for $1 builtin], [ac_cv_have_$1],
- [AC_LINK_IFELSE([AC_LANG_PROGRAM([$2], [])],
- [ac_cv_have_$1=yes], [ac_cv_have_$1=no])])
- if test $ac_cv_have_$1 = yes; then
- AC_DEFINE(AS_TR_CPP([HAVE_$1]), [1], [Define if you have the $1 builtin.])
- fi
-])
-
-KVDB_CHECK_BUILTIN([__builtin_clz],
- [[unsigned f(unsigned x) { return __builtin_clz(x); }]])
-
-KVDB_CHECK_BUILTIN([__builtin_clzl],
- [[unsigned long f(unsigned long x) { return __builtin_clzl(x); }]])
-
-KVDB_CHECK_BUILTIN([__builtin_clzll],
- [[unsigned long long f(unsigned long long x) { return __builtin_clzll(x); }]])
-
-KVDB_CHECK_BUILTIN([__builtin_ctz],
- [[unsigned f(unsigned x) { return __builtin_ctz(x); }]])
-
-KVDB_CHECK_BUILTIN([__builtin_ctzl],
- [[unsigned long f(unsigned long x) { return __builtin_ctzl(x); }]])
-
-KVDB_CHECK_BUILTIN([__builtin_ctzll],
- [[unsigned long long f(unsigned long long x) { return __builtin_ctzll(x); }]])
-
-KVDB_CHECK_BUILTIN([__sync_synchronize], [[long x = 11;
- void f(long i) { long* y = &x; __sync_synchronize(); *y = i; }]])
-
-KVDB_CHECK_BUILTIN([__sync_fetch_and_add],
- [[long f(long* x) { return __sync_fetch_and_add(x, 2L); }]])
-
-KVDB_CHECK_BUILTIN([__sync_add_and_fetch],
- [[long f(long* x) { return __sync_add_and_fetch(x, 2L); }]])
-
-KVDB_CHECK_BUILTIN([__sync_fetch_and_add_8],
- [[#include <stdint.h>
- int64_t f(int64_t* x) { return __sync_fetch_and_add(x, (int64_t) 2); }]])
-
-KVDB_CHECK_BUILTIN([__sync_add_and_fetch_8],
- [[#include <stdint.h>
- int64_t f(int64_t* x) { return __sync_add_and_fetch(x, (int64_t) 2); }]])
-
-KVDB_CHECK_BUILTIN([__sync_fetch_and_or],
- [[long f(long* x) { return __sync_fetch_and_or(x, 2L); }]])
-
-KVDB_CHECK_BUILTIN([__sync_or_and_fetch],
- [[long f(long* x) { return __sync_or_and_fetch(x, 2L); }]])
-
-KVDB_CHECK_BUILTIN([__sync_fetch_and_or_8],
- [[#include <stdint.h>
- int64_t f(int64_t* x) { return __sync_fetch_and_or(x, (int64_t) 2); }]])
-
-KVDB_CHECK_BUILTIN([__sync_or_and_fetch_8],
- [[#include <stdint.h>
- int64_t f(int64_t* x) { return __sync_or_and_fetch(x, (int64_t) 2); }]])
-
-KVDB_CHECK_BUILTIN([__sync_bool_compare_and_swap],
- [[bool f(long* x, long y, long z) { return __sync_bool_compare_and_swap(x, y, z); }]])
-
-KVDB_CHECK_BUILTIN([__sync_bool_compare_and_swap_8],
- [[#include <stdint.h>
- bool f(int64_t* x, int64_t y, int64_t z) { return __sync_bool_compare_and_swap(x, y, z); }]])
-
-KVDB_CHECK_BUILTIN([__sync_val_compare_and_swap],
- [[long f(long* x, long y, long z) { return __sync_val_compare_and_swap(x, y, z); }]])
-
-KVDB_CHECK_BUILTIN([__sync_val_compare_and_swap_8],
- [[#include <stdint.h>
- int64_t f(int64_t* x, int64_t y, int64_t z) { return __sync_val_compare_and_swap(x, y, z); }]])
-
-KVDB_CHECK_BUILTIN([__sync_lock_test_and_set],
- [[long f(long* x) { return __sync_lock_test_and_set(x, 1); }]])
-
-KVDB_CHECK_BUILTIN([__sync_lock_test_and_set_val],
- [[long f(long* x, long y) { return __sync_lock_test_and_set(x, y); }]])
-
-KVDB_CHECK_BUILTIN([__sync_lock_release_set],
- [[void f(long* x) { __sync_lock_release(x); }]])
-
-
-dnl C++ features
-
-dnl Mimics AC_CACHE_CHECK(MESSAGE, CACHE-ID, COMMANDS), but retries COMMANDS
-dnl with CXX='${CXX} -std=gnu++1y' if the user has not specified CXX.
-AC_DEFUN([KVDB_CHECK_C11_FEATURE], [
- AC_CACHE_CHECK([$1], [$2], [$3])
-
- if test "$$2" != yes -a -z "$ac_user_cxx"; then
- CXX="${CXX} -std=gnu++1y"
- AC_MSG_CHECKING([$1 with -std=gnu++1y])
- $3
- AC_MSG_RESULT([$$2])
- fi
-])
-
-KVDB_CHECK_C11_FEATURE([whether the C++ compiler understands 'auto'], [ac_cv_cxx_auto], [
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[struct s { int a; }; int f(s x) { auto &y = x; return y.a; }]], [[]])],
- [ac_cv_cxx_auto=yes], [ac_cv_cxx_auto=no])])
-
-KVDB_CHECK_C11_FEATURE([whether the C++ compiler understands constexpr], [ac_cv_cxx_constexpr], [
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[constexpr int f(int x) { return x + 1; }]], [[]])],
- [ac_cv_cxx_constexpr=yes], [ac_cv_cxx_constexpr=no])])
-
-KVDB_CHECK_C11_FEATURE([whether the C++ compiler understands static_assert], [ac_cv_cxx_static_assert], [
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[const int f = 2;]], [[static_assert(f == 2, "f should be 2");]])],
- [ac_cv_cxx_static_assert=yes], [ac_cv_cxx_static_assert=no])])
-
-KVDB_CHECK_C11_FEATURE([whether the C++ compiler understands rvalue references], [ac_cv_cxx_rvalue_references], [
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[int f(int &) { return 1; } int f(int &&) { return 0; }]], [[return f(int());]])],
- [ac_cv_cxx_rvalue_references=yes], [ac_cv_cxx_rvalue_references=no])])
-
-KVDB_CHECK_C11_FEATURE([whether the C++ compiler understands template alias], [ac_cv_cxx_template_alias], [
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[template <typename T> struct X { typedef T type; }; template <typename T> using Y = X<T>; int f(int x) { return x; }]], [[return f(Y<int>::type());]])],
- [ac_cv_cxx_template_alias=yes], [ac_cv_cxx_template_alias=no])])
-if test "$ac_cv_cxx_template_alias" = yes; then
- AC_DEFINE([HAVE_CXX_TEMPLATE_ALIAS], [1], [Define if the C++ compiler understands template alias.])
-fi
-
-if test "$ac_cv_cxx_auto" != yes -o "$ac_cv_cxx_static_assert" != yes \
- -o "$ac_cv_cxx_rvalue_references" != yes -o "$ac_cv_cxx_constexpr" != yes; then
- AC_MSG_ERROR([
-
-The C++ compiler does not appear to understand C++14.
-To fix this problem, try supplying a "CXX" argument to ./configure,
-such as "./configure CXX='c++ -std=gnu++1y'".
-
-========================================================])
-fi
-
-AC_CHECK_HEADERS([type_traits])
-
-KVDB_CHECK_C11_FEATURE([for std::hash], [ac_cv_have_std_hash], [
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include <functional>
-#include <stddef.h>],
- [[std::hash<int> h; size_t x = h(1); return x == 0;]])],
- [ac_cv_have_std_hash=yes], [ac_cv_have_std_hash=no])])
-if test $ac_cv_have_std_hash = yes; then
- AC_DEFINE([HAVE_STD_HASH], [1], [Define if you have std::hash.])
-fi
-
-KVDB_CHECK_C11_FEATURE([for __has_trivial_copy], [ac_cv_have___has_trivial_copy], [
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [[long x = 1; if (__has_trivial_copy(long)) x = 0;]])], [ac_cv_have___has_trivial_copy=yes], [ac_cv_have___has_trivial_copy=no])])
-if test $ac_cv_have___has_trivial_copy = yes; then
- AC_DEFINE([HAVE___HAS_TRIVIAL_COPY], [1], [Define if you have the __has_trivial_copy compiler intrinsic.])
-fi
-
-KVDB_CHECK_C11_FEATURE([for __has_trivial_destructor], [ac_cv_have___has_trivial_destructor], [
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [[long x = 1; if (__has_trivial_destructor(long)) x = 0;]])], [ac_cv_have___has_trivial_destructor=yes], [ac_cv_have___has_trivial_destructor=no])])
-if test $ac_cv_have___has_trivial_destructor = yes; then
- AC_DEFINE([HAVE___HAS_TRIVIAL_DESTRUCTOR], [1], [Define if you have the __has_trivial_destructor compiler intrinsic.])
-fi
-
-if test "$ac_cv_cxx_rvalue_references" = yes; then
- KVDB_CHECK_C11_FEATURE([for std::move], [ac_cv_std_move], [
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include <utility>], [[long x = 0; long &&y = std::move(x);]])], [ac_cv_std_move=yes], [ac_cv_std_move=no])])
- if test "$ac_cv_std_move" != yes; then
- AC_MSG_ERROR([
-
-The C++ compiler understands C++11, but does not have std::move.
-If you are using clang on Mac, ensure the -stdlib=libc++ option.
-
-========================================================])
- fi
-fi
-
-KVDB_CHECK_C11_FEATURE([for std::is_trivially_copyable], [ac_cv_have_std_is_trivially_copyable], [
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include <type_traits>], [[return std::is_trivially_copyable<int>::value;]])], [ac_cv_have_std_is_trivially_copyable=yes], [ac_cv_have_std_is_trivially_copyable=no])])
-if test $ac_cv_have_std_is_trivially_copyable = yes; then
- AC_DEFINE([HAVE_STD_IS_TRIVIALLY_COPYABLE], [1], [Define if you have the std::is_trivially_copyable template.])
-fi
-
-KVDB_CHECK_C11_FEATURE([for std::is_trivially_destructible], [ac_cv_have_std_is_trivially_destructible], [
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include <type_traits>], [[return std::is_trivially_destructible<int>::value;]])], [ac_cv_have_std_is_trivially_destructible=yes], [ac_cv_have_std_is_trivially_destructible=no])])
-if test $ac_cv_have_std_is_trivially_destructible = yes; then
- AC_DEFINE([HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE], [1], [Define if you have the std::is_trivially_destructible template.])
-fi
-
-KVDB_CHECK_C11_FEATURE([for std::is_rvalue_reference], [ac_cv_have_std_is_rvalue_reference], [
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include <type_traits>], [[return std::is_rvalue_reference<int>::value;]])], [ac_cv_have_std_is_rvalue_reference=yes], [ac_cv_have_std_is_rvalue_reference=no])])
-if test $ac_cv_have_std_is_rvalue_reference = yes; then
- AC_DEFINE([HAVE_STD_IS_RVALUE_REFERENCE], [1], [Define if you have the std::is_rvalue_reference template.])
-fi
-
-
-dnl Memory allocator
-
-AC_CHECK_LIB([flow], [malloc], [have_flow=true], [have_flow=])
-AC_CHECK_LIB([jemalloc], [mallctl], [have_jemalloc=true], [have_jemalloc=])
-AC_CHECK_LIB([tcmalloc_minimal], [tc_malloc], [have_tcmalloc_minimal=true], [have_tcmalloc_minimal=])
-AC_CHECK_LIB([hoard], [_Z16getMainHoardHeapv], [have_hoard=true], [have_hoard=])
-
-AC_ARG_WITH([malloc],
- [AS_HELP_STRING([--with-malloc=TYPE],
- [memory allocator (malloc|jemalloc|tcmalloc|hoard|flow)])],
- [ac_mtd_malloc=$withval], [ac_mtd_malloc=yes])
-
-if test \( "$ac_mtd_malloc" = tcmalloc -a -z "$have_tcmalloc_minimal" \) \
- -o \( "$ac_mtd_malloc" = jemalloc -a -z "$have_jemalloc" \) \
- -o \( "$ac_mtd_malloc" = flow -a -z "$have_flow" \) \
- -o \( "$ac_mtd_malloc" = hoard -a -z "$have_hoard" \) ; then
- AC_MSG_ERROR([$ac_mtd_malloc not found])
-elif test "$ac_mtd_malloc" = tcmalloc -o "$ac_mtd_malloc" = jemalloc -o "$ac_mtd_malloc" = flow -o "$ac_mtd_malloc" = hoard; then
- :
-elif test "$ac_mtd_malloc" = yes -o "$ac_mtd_malloc" = default; then
- AC_MSG_CHECKING([for malloc library])
- if test -n "$have_flow"; then ac_mtd_malloc=flow;
- elif test -n "$have_jemalloc"; then ac_mtd_malloc=jemalloc;
- elif test -n "$have_tcmalloc_minimal"; then ac_mtd_malloc=tcmalloc;
- else ac_mtd_malloc=malloc; fi
- AC_MSG_RESULT([$ac_mtd_malloc])
-elif test "$ac_mtd_malloc" = no -o "$ac_mtd_malloc" = malloc -o -z "$ac_mtd_malloc"; then
- ac_mtd_malloc=malloc
-else
- AC_MSG_ERROR([Unknown malloc type $ac_mtd_malloc])
-fi
-
-AC_MSG_CHECKING([for malloc library])
-if test "$ac_mtd_malloc" = tcmalloc; then
- MALLOC_LIBS="-ltcmalloc_minimal"
- AC_DEFINE([HAVE_TCMALLOC], [1], [Define if you are using libtcmalloc for malloc.])
- AC_MSG_RESULT([-ltcmalloc_minimal])
-elif test "$ac_mtd_malloc" = jemalloc; then
- MALLOC_LIBS="-ljemalloc"
- AC_DEFINE([HAVE_JEMALLOC], [1], [Define if you are using libjemalloc for malloc.])
- AC_MSG_RESULT([-ljemalloc])
-elif test "$ac_mtd_malloc" = flow; then
- MALLOC_LIBS="-lflow"
- AC_DEFINE([HAVE_FLOW_MALLOC], [1], [Define if you are using libflow for malloc.])
- AC_MSG_RESULT([-lflow])
-elif test "$ac_mtd_malloc" = hoard; then
- MALLOC_LIBS="-lhoard"
- AC_DEFINE([HAVE_HOARD_MALLOC], [1], [Define if you are using libhoard for malloc.])
- AC_MSG_RESULT([-lhoard])
-else
- MALLOC_LIBS=
- AC_MSG_RESULT([default])
-fi
-AC_SUBST(MALLOC_LIBS)
-
-
-dnl Types
-
-AC_DEFUN([KVDB_CHECK_SAME_TYPE], [
- pushdef([KVDB_CST_VAR], [AS_TR_SH([ac_cv_have_same_type_$1_is_$2])])
- AC_CACHE_CHECK([whether $1 and $2 are the same type], KVDB_CST_VAR,
- [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([$3
-int f($1) {return 0;} int f($2) {return 0;}], [])],
- [KVDB_CST_VAR=no], [KVDB_CST_VAR=yes])])
- if test $KVDB_CST_VAR = yes; then
- AC_DEFINE(AS_TR_CPP([HAVE_$1_IS_$2]), [1], [Define if $1 and $2 are the same type.])
- fi
- popdef([KVDB_CST_VAR])
-])
-
-KVDB_CHECK_SAME_TYPE([off_t], [long], [#include <stdio.h>])
-KVDB_CHECK_SAME_TYPE([off_t], [long long], [#include <stdio.h>])
-KVDB_CHECK_SAME_TYPE([int64_t], [long], [#include <stdint.h>])
-KVDB_CHECK_SAME_TYPE([int64_t], [long long], [#include <stdint.h>])
-KVDB_CHECK_SAME_TYPE([size_t], [unsigned], [#include <stdio.h>])
-KVDB_CHECK_SAME_TYPE([size_t], [unsigned long], [#include <stdio.h>])
-KVDB_CHECK_SAME_TYPE([size_t], [unsigned long long], [#include <stdio.h>])
-
-AC_CHECK_TYPES([long long])
-AC_CHECK_SIZEOF([short])
-AC_CHECK_SIZEOF([int])
-AC_CHECK_SIZEOF([long])
-AC_CHECK_SIZEOF([long long])
-AC_CHECK_SIZEOF([void *])
-
-AC_CHECK_DECLS([getline])
-
-AC_CHECK_HEADERS([time.h execinfo.h])
-AC_CHECK_DECLS([clock_gettime], [], [], [#if HAVE_TIME_H
-# include <time.h>
-#endif])
-AC_SEARCH_LIBS([clock_gettime], [rt])
-AC_CHECK_FUNCS([clock_gettime])
-AC_SEARCH_LIBS([backtrace], [execinfo])
-
-
-AC_ARG_ENABLE([row-type],
- [AS_HELP_STRING([--enable-row-type=ARG],
- [row type: bag array array_ver str, default bag])],
- [ac_cv_row_type=$enableval], [ac_cv_row_type=bag])
-if test "$ac_cv_row_type" = array; then
- AC_DEFINE_UNQUOTED([MASSTREE_ROW_TYPE_ARRAY], [1], [Define if the default row type is value_timed_array.])
-elif test "$ac_cv_row_type" = array_ver; then
- AC_DEFINE_UNQUOTED([MASSTREE_ROW_TYPE_ARRAY_VER], [1], [Define if the default row type is value_timed_array_ver.])
-elif test "$ac_cv_row_type" = bag; then
- AC_DEFINE_UNQUOTED([MASSTREE_ROW_TYPE_BAG], [1], [Define if the default row type is value_timed_bag.])
-elif test "$ac_cv_row_type" = str; then
- AC_DEFINE_UNQUOTED([MASSTREE_ROW_TYPE_STR], [1], [Define if the default row type is value_timed_str.])
-else
- AC_MSG_ERROR([$ac_cv_row_type: Unknown row type])
-fi
-
-AC_ARG_ENABLE([max-key-len],
- [AS_HELP_STRING([--enable-max-key-len=ARG],
- [maximum length of a key in bytes, default 255])],
- [ac_cv_max_key_len=$enableval], [ac_cv_max_key_len=255])
-AC_DEFINE_UNQUOTED([MASSTREE_MAXKEYLEN], [$ac_cv_max_key_len], [Maximum key length])
-
-AC_MSG_CHECKING([whether MADV_HUGEPAGE is supported])
-AC_PREPROC_IFELSE([AC_LANG_PROGRAM([[#include <sys/mman.h>
-#ifndef MADV_HUGEPAGE
-#error "no"
-#endif]], [])],
- [have_madv_hugepage=yes], [have_madv_hugepage=no])
-AC_MSG_RESULT([$have_madv_hugepage])
-if test $have_madv_hugepage = yes; then
- AC_DEFINE([HAVE_MADV_HUGEPAGE], [1], [Define if MADV_HUGEPAGE is supported.])
-fi
-
-AC_MSG_CHECKING([whether MAP_HUGETLB is supported])
-AC_PREPROC_IFELSE([AC_LANG_PROGRAM([[#include <sys/mman.h>
-#ifndef MAP_HUGETLB
-#error "no"
-#endif]], [])],
- [have_map_hugetlb=yes], [have_map_hugetlb=no])
-AC_MSG_RESULT([$have_map_hugetlb])
-if test $have_map_hugetlb = yes; then
- AC_DEFINE([HAVE_MAP_HUGETLB], [1], [Define if MAP_HUGETLB is supported.])
-fi
-
-AC_ARG_ENABLE([superpage],
- [AS_HELP_STRING([--disable-superpage],
- [disable superpage support])],
- [], [enable_superpage=maybe])
-if test "$enable_superpage $have_madv_hugepage $have_map_hugetlb" = "yes no no"; then
- AC_MSG_ERROR([
-Error: superpages are not supported on this machine.
-Try again without --enable-superpage.
-])
-elif test "$enable_superpage $have_madv_hugepage $have_map_hugetlb" != "maybe no no" -a "$enable_superpage" != no; then
- AC_DEFINE_UNQUOTED([HAVE_SUPERPAGE], [1], [Define if superpage support is enabled.])
-fi
-
-AC_ARG_ENABLE([memdebug],
- [AS_HELP_STRING([--enable-memdebug],
- [enable memory debugging])])
-if test "$enable_memdebug" = yes; then
- AC_DEFINE_UNQUOTED([HAVE_MEMDEBUG], [1], [Define if memory debugging support is enabled.])
-fi
-
-AC_ARG_ENABLE([assert],
- [],
- [AC_MSG_WARN([Use --disable-assertions instead of --disable-assert.])])
-AC_ARG_ENABLE([assertions],
- [AS_HELP_STRING([--disable-assertions],
- [disable debugging assertions])])
-if test "$enable_assertions" != no -o "(" -z "$enable_assertions" -a "$enable_assert" != no ")"; then
- AC_DEFINE_UNQUOTED([ENABLE_ASSERTIONS], [1], [Define to enable debugging assertions.])
-fi
-
-AC_ARG_ENABLE([preconditions],
- [AS_HELP_STRING([--disable-preconditions],
- [disable precondition assertions])])
-if test "$enable_preconditions" = no; then
- AC_DEFINE_UNQUOTED([ENABLE_PRECONDITIONS], [0], [Define to enable precondition assertions.])
-elif test -n "$enable_preconditions"; then
- AC_DEFINE_UNQUOTED([ENABLE_PRECONDITIONS], [1], [Define to enable precondition assertions.])
-fi
-
-AC_ARG_ENABLE([invariants],
- [AS_HELP_STRING([--disable-invariants],
- [disable invariant assertions])])
-if test "$enable_invariants" = no; then
- AC_DEFINE_UNQUOTED([ENABLE_INVARIANTS], [0], [Define to enable invariant assertions.])
-elif test -n "$enable_preconditions"; then
- AC_DEFINE_UNQUOTED([ENABLE_INVARIANTS], [1], [Define to enable invariant assertions.])
-fi
-
-AC_DEFINE_UNQUOTED([CACHE_LINE_SIZE], [64], [Assumed size of a cache line.])
-
-AH_TOP([#ifndef MASSTREE_CONFIG_H_INCLUDED
-#define MASSTREE_CONFIG_H_INCLUDED 1])
-
-AH_BOTTOM([#if !FORCE_ENABLE_ASSERTIONS && !ENABLE_ASSERTIONS
-# define NDEBUG 1
-#endif
-
-/** @brief Assert macro that always runs. */
-extern void fail_always_assert(const char* file, int line, const char* assertion, const char* message = 0) __attribute__((noreturn));
-#define always_assert(x, ...) do { if (!(x)) fail_always_assert(__FILE__, __LINE__, #x, ## __VA_ARGS__); } while (0)
-#define mandatory_assert always_assert
-
-/** @brief Assert macro for invariants.
-
- masstree_invariant(x) is executed if --enable-invariants or
- --enable-assertions. */
-extern void fail_masstree_invariant(const char* file, int line, const char* assertion, const char* message = 0) __attribute__((noreturn));
-#if FORCE_ENABLE_ASSERTIONS || (!defined(ENABLE_INVARIANTS) && ENABLE_ASSERTIONS) || ENABLE_INVARIANTS
-#define masstree_invariant(x, ...) do { if (!(x)) fail_masstree_invariant(__FILE__, __LINE__, #x, ## __VA_ARGS__); } while (0)
-#else
-#define masstree_invariant(x, ...) do { } while (0)
-#endif
-
-/** @brief Assert macro for preconditions.
-
- masstree_precondition(x) is executed if --enable-preconditions or
- --enable-assertions. */
-extern void fail_masstree_precondition(const char* file, int line, const char* assertion, const char* message = 0) __attribute__((noreturn));
-#if FORCE_ENABLE_ASSERTIONS || (!defined(ENABLE_PRECONDITIONS) && ENABLE_ASSERTIONS) || ENABLE_PRECONDITIONS
-#define masstree_precondition(x, ...) do { if (!(x)) fail_masstree_precondition(__FILE__, __LINE__, #x, ## __VA_ARGS__); } while (0)
-#else
-#define masstree_precondition(x, ...) do { } while (0)
-#endif
-
-#ifndef invariant
-#define invariant masstree_invariant
-#endif
-#ifndef precondition
-#define precondition masstree_precondition
-#endif
-
-#endif])
-
-AC_DEFINE_UNQUOTED([HAVE_UNALIGNED_ACCESS], [1], [Define if unaligned accesses are OK.])
-
-AC_OUTPUT
diff --git a/doc/.gitignore b/doc/.gitignore
deleted file mode 100644
index c309924..0000000
--- a/doc/.gitignore
+++ /dev/null
@@ -1,18 +0,0 @@
-*.aux
-*.bbl
-*.blg
-*.dvi
-*.log
-*.mpx
-*_[0-9].eps
-*_[0-9].pdf
-*-[0-9].pdf
-*_[0-9][0-9].eps
-*_[0-9][0-9].pdf
-*-[0-9][0-9].pdf
-*.[0-9]
-*.[0-9][0-9]
-*.[0-9][0-9][0-9]
-!GNUmakefile
-mpxerr.tex
-spec.pdf
diff --git a/doc/GNUmakefile b/doc/GNUmakefile
deleted file mode 100644
index 41d63e0..0000000
--- a/doc/GNUmakefile
+++ /dev/null
@@ -1,80 +0,0 @@
-MAIN = spec.tex
-
-BASE := $(basename $(MAIN))
-
-BIBFILES := $(wildcard *.bib)
-STYFILES := $(wildcard *.sty)
-
-GS_FONTPATH := $(shell x=`kpsewhich cmr10.pfb`; echo $$GS_FONTPATH:`dirname $$x`)
-export GS_FONTPATH
-
-#LATEX = pdflatex \\nonstopmode\\input
-USEPDFLATEX = 1
-PDFLATEX = pdflatex
-LATEX = latex
-BIBTEX = bibtex -min-crossrefs=1000
-METAPOST = mpost
-DVIPS := dvips -P pdf -j0 -G0 -t letter
-PS2PDF := ps2pdf
-PS2PDF_LETTER := GS_OPTIONS=-sPAPERSIZE=letter $(PS2PDF) -sPAPERSIZE=letter
-PDF2PS := $(shell (which acroread >/dev/null 2>&1 && echo acroread -toPostScript) || echo pdf2ps)
-
-PDFFIGS += examples-1.pdf \
- remove1-1.pdf remove1-2.pdf remove1-3.pdf remove1-31.pdf remove1-4.pdf remove1-5.pdf \
- remove1-6.pdf remove1-7.pdf remove1-8.pdf remove1-9.pdf \
- remove2-1.pdf remove2-2.pdf remove2-3.pdf remove2-4.pdf remove2-5.pdf \
- remove2-6.pdf remove2-7.pdf remove2-8.pdf remove2-9.pdf \
- remove2-11.pdf remove2-12.pdf remove2-15.pdf remove2-16.pdf \
- insert1-1.pdf insert1-2.pdf insert1-3.pdf insert1-4.pdf insert1-5.pdf
-
-RERUN = egrep -q '(^LaTeX Warning:|\(natbib\)).* Rerun'
-UNDEFINED = egrep -q '^(LaTeX|Package natbib) Warning:.* undefined'
-
-RERUNLATEX = if test ! -f $(2).bbl || $(RERUN) $(2).log || $(UNDEFINED) $(2).log; then \
- set -x; $(BIBTEX) $(2); $(1); \
- ! $(RERUN) $(2).log || $(1); \
- ! $(RERUN) $(2).log || $(1); \
- fi
-
-%.dvi: %.ltx
- test ! -s $*.aux || $(BIBTEX) $* || rm -f $*.aux $*.bbl
- $(LATEX) $<
- @$(call RERUNLATEX,$(LATEX) $<,$*)
-
-%.ps: %.dvi
- $(DVIPS) $< -o $@
-
-%.pdf: %.ps
- $(PS2PDF_LETTER) $<
-
-ifeq ($(USEPDFLATEX),1)
-%.pdf: %.tex
- test ! -s $*.aux || $(BIBTEX) $* || rm -f $*.aux $*.bbl
- $(PDFLATEX) $<
- @$(call RERUNLATEX,$(PDFLATEX) $<,$*)
-endif
-
-all: $(BASE).pdf
-
-$(BASE).dvi $(BASE).pdf: $(TEXFILES) $(STYFILES) $(BIBFILES) $(PDFFIGS)
-
-bib:
- $(LATEX) $(MAIN)
- rm -f $(BASE).dvi
- $(MAKE) $(BASE).dvi
-
-%-1.pdf %-2.pdf %-3.pdf %-4.pdf %-5.pdf %-6.pdf %-7.pdf %-8.pdf %-9.pdf \
-%-10.pdf %-11.pdf %-12.pdf %-13.pdf %-14.pdf %-15.pdf %-16.pdf %-17.pdf %-18.pdf %-19.pdf \
-%-20.pdf %-21.pdf %-22.pdf %-23.pdf %-24.pdf %-25.pdf %-26.pdf %-27.pdf %-28.pdf %-29.pdf \
-%-30.pdf %-31.pdf %-32.pdf %-33.pdf %-34.pdf %-35.pdf %-36.pdf %-37.pdf %-38.pdf %-39.pdf \
-: %.mp elements.mp elemfig.sty patches.mp masstree.mp
- rm -f $*.[0-9] $*.[0-9][0-9] $*.[0-9][0-9][0-9]
- TEX=$(LATEX) $(METAPOST) $<
- mptopdf `ls $*.* | grep -e '\.[0-9][0-9]*$$'`
-
-clean:
- rm -f $(EPSFIGS) $(PDFFIGS) $(EPSGRAPHS) $(PDFGRAPHS)
- rm -f *.ps $(BASE).pdf *.aux
- rm -f *.dvi *.aux *.log *.bbl *.blg *.lof *.lot *.toc *.bak *.[1-9] *.[1-3][0-9] *.mpout *~
-
-.PHONY: clean bib all
diff --git a/doc/elements.mp b/doc/elements.mp
deleted file mode 100644
index 1161c01..0000000
--- a/doc/elements.mp
+++ /dev/null
@@ -1,520 +0,0 @@
-% elements.mp -- MetaPost macros for drawing Click configuration graphs
-% Eddie Kohler
-%
-% Copyright (c) 1999-2001 Massachusetts Institute of Technology
-% Copyright (c) 2001-2003 International Computer Science Institute
-% Copyright (c) 2006 Regents of the University of California
-%
-% 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 Click 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 Click LICENSE file; the license in that file is
-% legally binding.
-
-input rboxes;
-prologues := 1;
-string defaultelementfont;
-defaultscale := 1;
-linejoin := mitered;
-
-pair element_offset;
-element_offset = (7.5, 4.5);
-min_element_height = 19;
-element_height_increment = 4;
-
-defaultelementborderscale = 1;
-defaultelementportscale = 1;
-
-port_length = 6;
-port_sep = 3;
-port_offset = 4;
-input_length = 7;
-input_width = 4.5;
-output_length = 6;
-output_width = 3.8;
-agnostic_sep = 1;
-
-push = 0;
-pull = 1;
-agnostic = 2;
-agnostic_push = 3;
-agnostic_pull = 4;
-push_to_pull = 5;
-pull_to_push = 6;
-
-pen elementpen.border, elementpen.port, connectionpen;
-elementpen.border = pencircle scaled 0.9;
-elementpen.port = pencircle scaled 0.35;
-connectionpen = pencircle scaled 0.45;
-
-color personalitycolor[], agnosticcolor[];
-personalitycolor[push] = black;
-personalitycolor[agnostic_push] = personalitycolor[agnostic] = white;
-personalitycolor[pull] = personalitycolor[agnostic_pull] = white;
-agnosticcolor[agnostic_push] = black;
-agnosticcolor[agnostic_pull] = white;
-agnosticcolor[agnostic] = 0.6white;
-
-path _agnostic_output, _agnostic_input, _normal_output, _normal_input;
-_agnostic_output := ((-.5,0.5output_length-agnostic_sep)
- -- (-output_width+agnostic_sep,0.5output_length-agnostic_sep)
- -- (-output_width+agnostic_sep,-0.5output_length+agnostic_sep)
- -- (-.5,-0.5output_length+agnostic_sep) -- cycle);
-_agnostic_input := ((.5,0.5input_length-1.414agnostic_sep)
- -- (input_width-1.414agnostic_sep,0)
- -- (.5,-0.5input_length+1.414agnostic_sep) -- cycle);
-_normal_input := ((.5,0.5input_length) -- (input_width,0)
- -- (.5,-0.5input_length) -- cycle);
-_normal_output := ((-.5,0.5output_length) -- (-output_width,0.5output_length)
- -- (-output_width,-0.5output_length) -- (-.5,-0.5output_length) -- cycle);
-
-
-%% redefine 'drawboxes' to allow extra text
-
-vardef drawboxes(text t) text rest = % Draw boundary path for each box
- forsuffixes s=t: draw bpath.s rest; endfor
-enddef;
-
-
-%%
-
-vardef _make_element_ports(suffix $, port, side)(expr n, length, isout) =
- save _i_, _sc; pair _sc.adj;
- _sc.sep = (length - 2*port_offset - n*port_length + port_sep) / n;
- _sc.delta = port_length + _sc.sep;
- _sc = length/2 - port_offset - (_sc.sep - port_sep)/2 - 0.5port_length;
- _sc.adj = if isout: 1/2$.flowvector else: -1/2$.flowvector fi;
- for _i_ = 0 upto n-1:
- $.port[_i_] = $.side + $.sidevector * (_sc - _sc.delta*_i_) + _sc.adj;
- endfor;
-enddef;
-
-vardef make_element_inputs(suffix $)(expr xlen, ylen) =
- if $.down:
- _make_element_ports($, in, if $.rev: s else: n fi, $.nin, xlen-6, false);
- else:
- _make_element_ports($, in, if $.rev: e else: w fi, $.nin, ylen, false);
- fi;
-enddef;
-
-vardef make_element_outputs(suffix $)(expr xlen, ylen) =
- if $.down:
- _make_element_ports($, out, if $.rev: n else: s fi, $.nout, xlen-6, true);
- else:
- _make_element_ports($, out, if $.rev: w else: e fi, $.nout, ylen, true);
- fi;
-enddef;
-
-vardef clearelement_(suffix $) =
- _n_ := str $;
- generic_redeclare(numeric) _n.down, _n.rev, _n.sidevector, _n.flowvector, _n.width, _n.height, _n.nin, _n.nout, _n.borderscale, _n.portscale, _n.drawports;
- _n_ := str $ & ".in0";
- generic_redeclare(numeric) _n;
- _n_ := str $ & ".out0";
- generic_redeclare(numeric) _n;
- _n_ := str $ & ".inpers0";
- generic_redeclare(numeric) _n;
- _n_ := str $ & ".outpers0";
- generic_redeclare(numeric) _n;
- _n_ := "elemdraw_." & str $;
- generic_redeclare(numeric) _n;
-enddef;
-
-vardef _elementit@#(expr label_str, ninputs, noutputs, personality, down_var, rev_var) =
- picture _label_; numeric _x_, _y_;
-
- if picture label_str: _label_ = label_str
- elseif label_str = "": _label_ = nullpicture
- else: _label_ = label_str infont defaultelementfont scaled defaultscale
- fi;
-
- boxit.@#(_label_);
- _n_ := str @#;
- generic_declare(boolean) _n.down, _n.rev, _n.drawports;
- generic_declare(pair) _n.sidevector, _n.flowvector;
- generic_declare(numeric) _n.width, _n.height, _n.nin, _n.nout, _n.borderscale, _n.portscale;
- _n_ := str @# & ".in0";
- generic_declare(pair) _n;
- _n_ := str @# & ".out0";
- generic_declare(pair) _n;
- _n_ := "elemdraw_." & str @#;
- generic_declare(string) _n;
-
- @#.down = down_var;
- if down_var: @#.sidevector = (-1, 0); else: @#.sidevector = (0, 1); fi;
- if down_var: @#.flowvector = (0, -1); else: @#.flowvector = (1, 0); fi;
- @#.rev = rev_var;
- if rev_var: @#.flowvector := -@#.flowvector; @#.sidevector := -@#.sidevector; fi;
- @#.drawports = true;
-
- @#.width = xpart(@#.e - @#.w);
- @#.height = ypart(@#.n - @#.s);
-
- @#.nin = ninputs;
- @#.nout = noutputs;
- if ninputs > 0: make_element_inputs(@#, @#.width, @#.height); fi;
- if noutputs > 0: make_element_outputs(@#, @#.width, @#.height); fi;
-
- _x_ := personality;
- if _x_ = push_to_pull: _x_ := push;
- elseif _x_ = pull_to_push: _x_ := pull; fi;
- for _y_ = 0 upto ninputs-1: @#.inpers[_y_] = _x_; endfor;
-
- _x_ := personality;
- if _x_ = push_to_pull: _x_ := pull;
- elseif _x_ = pull_to_push: _x_ := push; fi;
- for _y_ = 0 upto noutputs-1: @#.outpers[_y_] = _x_; endfor;
-
- elemdraw_@# = "drawboxes";
- sproc_@# := "sizeelement_";
-
- expandafter def expandafter clearboxes expandafter =
- clearboxes clearelement_(@#);
- enddef
-enddef;
-
-vardef elementit@#(expr s, ninputs, noutputs, personality_var) =
- _elementit.@#(s, ninputs, noutputs, personality_var, false, false);
-enddef;
-vardef relementit@#(expr s, ninputs, noutputs, personality_var) =
- _elementit.@#(s, ninputs, noutputs, personality_var, false, true);
-enddef;
-vardef velementit@#(expr s, ninputs, noutputs, personality_var) =
- _elementit.@#(s, ninputs, noutputs, personality_var, true, false);
-enddef;
-vardef rvelementit@#(expr s, ninputs, noutputs, personality_var) =
- _elementit.@#(s, ninputs, noutputs, personality_var, true, true);
-enddef;
-
-
-%% change
-
-vardef killinput(suffix $)(expr p) =
- if (p >= 0) and (p < $.nin): save _i_;
- for _i_ = p upto $.nin-2:
- $.in[_i_] := $.in[_i_+1];
- $.inpers[_i_] := $.inpers[_i_+1];
- endfor;
- $.nin := $.nin - 1
- fi
-enddef;
-
-vardef killoutput(suffix $)(expr p) =
- if (p >= 0) and (p < $.nout): save _i_;
- for _i_ = p upto $.nout-2:
- $.out[_i_] := $.out[_i_+1];
- $.outpers[_i_] := $.outpers[_i_+1];
- endfor;
- $.nout := $.nout - 1
- fi
-enddef;
-
-vardef portinteriorin(suffix $)(expr i) =
- path _p_;
- _p_ := if $.inpers[i] >= agnostic: _agnostic_input else: _normal_input fi
- scaled $.portscale;
- if $.down: _p_ := _p_ rotated -90 fi;
- if $.rev: _p_ := _p_ rotated 180 fi;
- _p_ := _p_ shifted $.in[i];
- if $.down and $.rev: .5[ulcorner _p_,urcorner _p_]
- elseif $.down: .5[llcorner _p_,lrcorner _p_]
- elseif $.rev: .5[ulcorner _p_,llcorner _p_]
- else: .5[urcorner _p_,lrcorner _p_] fi
-enddef;
-
-vardef portinteriorout(suffix $)(expr i) =
- path _p_;
- _p_ := if $.outpers[i] >= agnostic: _agnostic_output else: _normal_output fi
- scaled $.portscale;
- if $.down: _p_ := _p_ rotated -90 fi;
- if $.rev: _p_ := _p_ rotated 180 fi;
- _p_ := _p_ shifted $.out[i];
- if $.down and $.rev: .5[llcorner _p_,lrcorner _p_]
- elseif $.down: .5[ulcorner _p_,urcorner _p_]
- elseif $.rev: .5[urcorner _p_,lrcorner _p_]
- else: .5[ulcorner _p_,llcorner _p_] fi
-enddef;
-
-
-%% fix
-
-vardef set_element_dx(suffix $) =
- if $.down: save x;
- x.maxport = max($.nin, $.nout);
- x.len = x.maxport*port_length + (x.maxport-1)*port_sep + 2port_offset;
- x.w = xpart(urcorner pic_$ - llcorner pic_$);
- x.ww = x.w + 2xpart(element_offset);
- if x.len > x.ww: $.dx = (x.len - x.w) / 2;
- else: $.dx = xpart element_offset; fi;
- else:
- $.dx = xpart element_offset;
- fi;
-enddef;
-
-vardef set_element_dy(suffix $) =
- save y;
- y.h = ypart(urcorner pic_$ - llcorner pic_$);
- y.hh = y.h + 2ypart(element_offset);
- if $.down: y := y.hh;
- else:
- y.maxport = max($.nin, $.nout);
- y.len = y.maxport*port_length + (y.maxport-1)*port_sep + 2port_offset;
- y := max(y.hh, y.len);
- fi;
-
- y'' := min_element_height;
- forever:
- exitif y'' >= y;
- y'' := y'' + element_height_increment;
- endfor;
-
- $.dy = (y'' - y.h)/2;
-enddef;
-
-def sizeelement_(suffix $) =
- if unknown $.dx: set_element_dx($); fi
- if unknown $.dy: set_element_dy($); fi
- if unknown $.borderscale: $.borderscale = defaultelementborderscale; fi
- if unknown $.portscale: $.portscale = defaultelementportscale; fi
-enddef;
-
-vardef fixelementsizeleft(text t) =
- forsuffixes $=t:
- fixsize($);
- if $.dx > xpart element_offset: $.off := $.off - ($.dx - xpart element_offset, 0); fi;
- endfor;
-enddef;
-
-vardef fixelement(text elements) =
- fixsize(elements);
- fixpos(elements);
-enddef;
-
-vardef elementleftjustify(text elements) =
- fixsize(elements);
- forsuffixes $=elements:
- if $.dx > xpart element_offset: $.off := $.off - ($.dx - xpart element_offset, 0); fi;
- endfor;
-enddef;
-
-vardef fixrelations(suffix efirst)(text elements) =
- forsuffixes $=elements:
- if unknown xpart(efirst.off - $.off): xpart $.off = xpart efirst.off fi;
- if unknown ypart(efirst.off - $.off): ypart $.off = ypart efirst.off fi;
- endfor;
-enddef;
-
-vardef elementbbox(suffix efirst)(text elements) =
- fixsize(efirst,elements);
- fixrelations(efirst,elements);
- save __t,__l,__r,__b,__p; picture __p;
- __t = ypart(efirst.n - efirst.off);
- __l = xpart(efirst.w - efirst.off);
- __r = xpart(efirst.e - efirst.off);
- __b = ypart(efirst.s - efirst.off);
- forsuffixes $=elements:
- if ypart($.n - efirst.off) > __t: __t := ypart($.n - efirst.off) fi;
- if xpart($.w - efirst.off) < __l: __l := xpart($.w - efirst.off) fi;
- if xpart($.e - efirst.off) > __r: __r := xpart($.e - efirst.off) fi;
- if ypart($.s - efirst.off) < __b: __b := ypart($.s - efirst.off) fi;
- endfor;
- __p = nullpicture;
- setbounds __p to ((__l,__t) -- (__r,__t) -- (__r,__b) -- (__l,__b) -- cycle)
- if known efirst.off: shifted efirst.off fi;
- __p
-enddef;
-
-vardef compoundelementlink@#(suffix efirst)(text elements) =
- fixsize(efirst,elements);
- fixrelations(efirst,elements);
- save __t,__l,__r,__b,__p; picture __p;
- __t = ypart(efirst.n - efirst.off);
- __l = xpart(efirst.w - efirst.off);
- __r = xpart(efirst.e - efirst.off);
- __b = ypart(efirst.s - efirst.off);
- forsuffixes $=elements:
- if ypart($.n - efirst.off) > __t: __t := ypart($.n - efirst.off) fi;
- if xpart($.w - efirst.off) < __l: __l := xpart($.w - efirst.off) fi;
- if xpart($.e - efirst.off) > __r: __r := xpart($.e - efirst.off) fi;
- if ypart($.s - efirst.off) < __b: __b := ypart($.s - efirst.off) fi;
- endfor;
- @#.c = efirst.off + .5[(__l,__t), (__r,__b)]
-enddef;
-
-
-%% draw
-
-vardef draw_element_inputs(suffix $) =
- path _p_, _ag_;
- _p_ := _normal_input scaled $.portscale;
- _ag_ := _agnostic_input scaled $.portscale;
- if $.down: _p_ := _p_ rotated -90; _ag_ := _ag_ rotated -90; fi;
- if $.rev: _p_ := _p_ rotated 180; _ag_ := _ag_ rotated 180; fi;
- for _i_ = 0 upto $.nin - 1:
- if $.inpers[_i_] >= 0:
- fill _p_ shifted $.in[_i_] withcolor personalitycolor[$.inpers[_i_]];
- draw _p_ shifted $.in[_i_];
- if $.inpers[_i_] >= agnostic:
- fill _ag_ shifted $.in[_i_] withcolor agnosticcolor[$.inpers[_i_]];
- draw _ag_ shifted $.in[_i_]; fi
- fi;
- endfor
-enddef;
-
-vardef draw_element_outputs(suffix $) =
- path _p_, _ag_;
- _p_ := _normal_output scaled $.portscale;
- _ag_ := _agnostic_output scaled $.portscale;
- if $.down: _p_ := _p_ rotated -90; _ag_ := _ag_ rotated -90; fi;
- if $.rev: _p_ := _p_ rotated 180; _ag_ := _ag_ rotated 180; fi;
- for _i_ = 0 upto $.nout - 1:
- if $.outpers[_i_] >= 0:
- fill _p_ shifted $.out[_i_] withcolor personalitycolor[$.outpers[_i_]];
- draw _p_ shifted $.out[_i_];
- if $.outpers[_i_] >= agnostic:
- fill _ag_ shifted $.out[_i_] withcolor agnosticcolor[$.outpers[_i_]];
- draw _ag_ shifted $.out[_i_]; fi
- fi;
- endfor
-enddef;
-
-vardef drawelement(text elements) text rest =
- drawelementbox(elements) rest;
- drawunboxed(elements);
-enddef;
-
-vardef drawelementbox(text elements) text rest =
- save $, oldpen; oldpen := savepen;
- interim linejoin := mitered;
- fixsize(elements);
- fixpos(elements);
- forsuffixes $ = elements:
- if $.drawports:
- pickup elementpen.port scaled $.portscale;
- if $.nin > 0: draw_element_inputs($); fi;
- if $.nout > 0: draw_element_outputs($); fi;
- fi;
- if $.borderscale > 0:
- pickup elementpen.border scaled $.borderscale;
- scantokens elemdraw_$($) rest;
- fi;
- endfor;
- pickup oldpen;
-enddef;
-
-vardef fillelement(text elements)(text color) =
- fixsize(elements);
- fixpos(elements);
- forsuffixes $=elements:
- fill bpath.$ withcolor color;
- endfor;
-enddef;
-
-
-%% queues
-
-vardef _drawqueued(expr p,delta,rot,lim,pp) text rest =
- save i; interim linecap := squared; i := delta;
- forever:
- draw (p) shifted ((i,0) rotated rot) withpen currentpen scaled 0.25 rest;
- i := i + delta; exitunless i < lim;
- endfor;
- draw (pp) rest;
-enddef;
-def drawqueued(suffix $) =
- _drawqueued($.ne -- $.se, 6, 180, .9*$.width, $.nw -- $.ne -- $.se -- $.sw)
-enddef;
-def drawrqueued(suffix $) =
- _drawqueued($.nw -- $.sw, 6, 0, .9*$.width, $.ne -- $.nw -- $.sw -- $.se)
-enddef;
-def drawvqueued(suffix $) =
- _drawqueued($.se -- $.sw, 5, 90, .9*$.height, $.nw -- $.sw -- $.se -- $.ne)
-enddef;
-def drawrvqueued(suffix $) =
- _drawqueued($.ne -- $.nw, 5, 270, .9*$.height, $.sw -- $.nw -- $.ne -- $.se)
-enddef;
-
-vardef queueit@#(expr s) =
- _elementit.@#(s, 1, 1, push_to_pull, false, false);
- elemdraw_@# := "drawqueued";
-enddef;
-vardef rqueueit@#(expr s) =
- _elementit.@#(s, 1, 1, push_to_pull, false, true);
- elemdraw_@# := "drawrqueued";
-enddef;
-vardef vqueueit@#(expr s) =
- _elementit.@#(s, 1, 1, push_to_pull, true, false);
- elemdraw_@# := "drawvqueued";
-enddef;
-vardef rvqueueit@#(expr s) =
- _elementit.@#(s, 1, 1, push_to_pull, true, true);
- elemdraw_@# := "drawrvqueued";
-enddef;
-
-
-%% connections
-
-picture _cutarrpic;
-
-vardef arrowhead expr p =
- save q,h,e,f; path q,h; pair e,f;
- e = point length p of p;
- q = gobble(p shifted -e cutafter makepath(pencircle scaled 2ahlength))
- cuttings;
- h = gobble(p shifted -e cutafter makepath(pencircle scaled 1.5ahlength))
- cuttings;
- f = point 0 of h;
- (q rotated .5ahangle & reverse q rotated -.5ahangle -- f -- cycle) shifted e
-enddef;
-def _cutarr(expr b,e) text t =
- _cutarrpic := image(draw (0,0) -- (1,0) -- cycle t);
- _cutarramt := (2ypart urcorner _cutarrpic / sind .5ahangle) - 0.75;
- if _cutarramt > 0:
- _apth := subpath (xpart(_apth intersectiontimes makepath(pencircle
- scaled (b*_cutarramt)) shifted (point 0 of _apth)),
- xpart(_apth intersectiontimes makepath(pencircle scaled (e*_cutarramt))
- shifted (point length _apth of _apth))) of _apth;
- fi
-enddef;
-def _finarr text t =
- _cutarr(0,1) t;
- draw (subpath (0, xpart(_apth intersectiontimes makepath(pencircle scaled 1.2ahlength) shifted (point length _apth of _apth))) of _apth) t;
- fill arrowhead _apth t
-enddef;
-def _findarr text t =
- _cutarr(1,1) t;
- draw (subpath
- (xpart(_apth intersectiontimes makepath(pencircle scaled 1.2ahlength) shifted (point 0 of _apth)),
- xpart(_apth intersectiontimes makepath(pencircle scaled 1.2ahlength) shifted (point length _apth of _apth))) of _apth) t;
- fill arrowhead _apth t;
- fill arrowhead reverse _apth t
-enddef;
-
-def connectpath(suffix $,#,##,$$) =
- $.out[#]{$.flowvector} .. {$$.flowvector}$$.in[##]
-enddef;
-vardef drawconnectj(suffix $,#,##,$$)(text t) text rest =
- interim linejoin := mitered;
- drawarrow $.out[#]{$.flowvector} t {$$.flowvector}$$.in[##] withpen connectionpen rest
-enddef;
-def drawconnect(suffix $,#,##,$$) =
- drawconnectj($,#,##,$$)(..)
-enddef;
-vardef drawconnectna(suffix $,#,##,$$) text rest =
- interim linejoin := mitered;
- draw $.out[#]{$.flowvector} .. {$$.flowvector}$$.in[##] withpen connectionpen rest
-enddef;
-
-def drawconnarrow expr p =
- _apth:=p; _finarr withpen connectionpen
-enddef;
-def drawconnarrowna expr p =
- draw p withpen connectionpen
-enddef;
-def drawdblconnarrow expr p =
- _apth:=p; _findarr withpen connectionpen
-enddef;
diff --git a/doc/elemfig.sty b/doc/elemfig.sty
deleted file mode 100644
index 48bec23..0000000
--- a/doc/elemfig.sty
+++ /dev/null
@@ -1,88 +0,0 @@
-% elemfig.sty -- LaTeX macros for Click configuration graphs in MetaPost
-% Eddie Kohler
-%
-% Copyright (c) 1999-2001 Massachusetts Institute of Technology
-% Copyright (c) 2001-2003 International Computer Science Institute
-%
-% 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 Click 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 Click LICENSE file; the license in that file is
-% legally binding.
-
-\makeatletter
-\usepackage{times,mathptmx}
-\usepackage[T1]{fontenc}
-%\input{../notation}
-
-\IfFileExists{./pfonts.sty}{\input{./pfonts.sty}}{%
- \IfFileExists{../pfonts.sty}{\input{../pfonts.sty}}{%
- \input{times.sty}%
- }%
-}%
-\@ifundefined{elementdefault}{}{\long\global\edef\rmdefault{\elementdefault}}
-
-% for MetaPost
-
-\renewcommand{\v}[1]{\ifmmode\mathit{#1\strut}\else\textit{#1}\fi}
-
-\let\elementshape\itshape
-\newdimen\@elementQdp\setbox0\hbox{\elementshape Q}\@elementQdp=\dp0
-\def\element#1{{\setbox0\hbox{\elementshape #1\vphantom{Q}}%
- \dimen0=\dp0\advance\dimen0 by-0.5\@elementQdp\dp0=\dimen0\box0}}%
-\def\belement#1{\textbf{\element{#1}}}%
-\newcommand{\elementlabel}[1]{\element{#1}}
-
-\newcommand{\melementlabel}[1]{{%
- \elementshape
- \begin{tabular}{@{}c@{}}%
- #1
- \end{tabular}%
-}}%
-\newcommand{\lmelementlabel}[1]{{%
- \elementshape
- \begin{tabular}{@{}l@{}}%
- #1
- \end{tabular}%
-}}%
-\newcommand{\rmelementlabel}[1]{{%
- \elementshape
- \begin{tabular}{@{}r@{}}%
- #1
- \end{tabular}%
-}}%
-\let\lelementlabel\lmelementlabel
-\let\relementlabel\rmelementlabel
-\newcommand{\mlabel}[1]{{%
- \begin{tabular}{@{}c@{}}%
- #1
- \end{tabular}%
-}}%
-\newcommand{\lmlabel}[1]{{%
- \begin{tabular}{@{}l@{}}%
- #1
- \end{tabular}%
-}}%
-\newcommand{\rmlabel}[1]{{%
- \begin{tabular}{@{}r@{}}%
- #1
- \end{tabular}%
-}}%
-\let\llabel\lmlabel
-\let\rlabel\rmlabel
-\newcommand{\portlabel}[1]{\hbox{%
- \tiny\sffamily\baselineskip6pt\begin{tabular}{@{}c@{}}#1\end{tabular}%
-}}%
-\newcommand{\lportlabel}[1]{\hbox{%
- \tiny\sffamily\baselineskip6pt\begin{tabular}{@{}l@{}}#1\end{tabular}%
-}}%
-\newcommand{\rportlabel}[1]{\hbox{%
- \tiny\sffamily\baselineskip6pt\begin{tabular}{@{}r@{}}#1\end{tabular}%
-}}%
-
-\makeatother
-\endinput
diff --git a/doc/examples.mp b/doc/examples.mp
deleted file mode 100644
index 103e15c..0000000
--- a/doc/examples.mp
+++ /dev/null
@@ -1,53 +0,0 @@
-input masstree;
-verbatimtex %&latex
-\documentclass[12pt]{article}
-\usepackage{elemfig,amsmath}
-\begin{document}
-\newcommand{\xlab}[1]{\mlabel{\textsc{#1}}}
-etex;
-
-picture data[];
-data[0] = btex \xlab{a} etex; data[1] = btex \xlab{b} etex; data[2] = btex \xlab{c} etex;
-data[3] = btex \xlab{d} etex; data[4] = btex \xlab{e} etex; data[5] = btex \xlab{f} etex;
-data[6] = btex \xlab{g} etex; data[7] = btex \xlab{h} etex; data[8] = btex \xlab{i} etex;
-data[9] = btex \xlab{j} etex; data[10] = btex \xlab{k} etex; data[11] = btex \xlab{l} etex;
-data[12] = btex \xlab{m} etex; data[13] = btex \xlab{n} etex; data[14] = btex \xlab{o} etex;
-
-pair min_reasonable_cell; min_reasonable_cell := (0,0);
-for _i_ = 0 upto 14:
- min_reasonable_cell :=
- (max(xpart min_reasonable_cell, xpart (urcorner data[_i_] - llcorner data[_i_])),
- max(ypart min_reasonable_cell, ypart (urcorner data[_i_] - llcorner data[_i_])));
-endfor;
-color deletedelement; deletedelement = .5white;
-
-picture emptyqueue; emptyqueue := btex \phantom{\elementlabel{Queue}} etex;
-picture vemptyqueue; vemptyqueue := emptyqueue rotated 90 xscaled .4;
-hardborderscale = 3;
-
-beginfig(1);
- % initial tree
- boxjoin(b.w - a.e = (20,0));
- leafit.a(data[0], data[1], data[2]);
- leafit.d(data[3], data[4], data[5]);
- leafit.g(data[6], data[7], data[8]);
- leafit.j(data[9], data[10], data[11]);
- leafit.m(data[12], data[13], data[14]);
- boxjoin();
- internalnodeit.ina(data[3], data[6], nullpicture);
- internalnodeit.inj(data[12], nullpicture, nullpicture);
- internalnodeit.root(data[9], nullpicture, nullpicture);
- ina.s = .5[a.nw,g.ne] + (0,24);
- inj.s = .5[j.nw,m.ne] + (0,24);
- root.s = .5[ina.nw,inj.ne] + (0,24);
- fixelement(a,d,g,j,m);
- fixelement(root,ina,inj);
- leafconnect(a,d,g,j,m);
- drawelement(a,d,g,j,m,ina,inj,root);
- internalnodeconnect(ina,a,d,g);
- internalnodeconnect(inj,j,m);
- internalnodeconnect(root,ina,inj);
- label.lft(btex \small{(1)~~} etex, inj.w);
-endfig;
-
-end
diff --git a/doc/insert1.mp b/doc/insert1.mp
deleted file mode 100644
index 5c2b9aa..0000000
--- a/doc/insert1.mp
+++ /dev/null
@@ -1,179 +0,0 @@
-input masstree;
-verbatimtex %&latex
-\documentclass[12pt]{article}
-\usepackage{elemfig,amsmath}
-\begin{document}
-\newcommand{\xlab}[1]{\mlabel{\textsc{#1}}}
-etex;
-
-picture data[];
-data[0] = btex \xlab{a} etex; data[1] = btex \xlab{b} etex; data[2] = btex \xlab{c} etex;
-data[3] = btex \xlab{d} etex; data[4] = btex \xlab{e} etex; data[5] = btex \xlab{f} etex;
-data[6] = btex \xlab{g} etex; data[7] = btex \xlab{h} etex; data[8] = btex \xlab{i} etex;
-data[9] = btex \xlab{j} etex; data[10] = btex \xlab{k} etex; data[11] = btex \xlab{l} etex;
-data[12] = btex \xlab{m} etex; data[13] = btex \xlab{n} etex; data[14] = btex \xlab{o} etex;
-picture dotdata;
-dotdata = btex \xlab{\dots\dots} etex;
-
-pair min_reasonable_cell; min_reasonable_cell := (0,0);
-for _i_ = 0 upto 14:
- min_reasonable_cell :=
- (max(xpart min_reasonable_cell, xpart (urcorner data[_i_] - llcorner data[_i_])),
- max(ypart min_reasonable_cell, ypart (urcorner data[_i_] - llcorner data[_i_])));
-endfor;
-color deletedelement; deletedelement = .5white;
-color upperlayer, upperlayerfill; upperlayer = (0,0,.5); upperlayerfill = (.9,.9,1);
-
-picture emptyqueue; emptyqueue := btex \phantom{\elementlabel{Queue}} etex;
-picture vemptyqueue; vemptyqueue := emptyqueue rotated 90 xscaled .4;
-hardborderscale = 3;
-
-beginfig(1);
- % tree with MNO removed, gclayer begin
- boxjoin(b.w - a.e = (20,0));
- leafit.a(data[0], data[1], data[2]);
- leafit.d(data[3], data[4], data[5]);
- leafit.g(data[6], data[7], data[8]);
- leafit.j(data[9], data[10], data[11]);
- boxjoin();
- leafit.x(dotdata, dotdata, dotdata, dotdata);
- x.nextpath = x.prevpath = emptypath;
- internalnodeit.ina(data[3], data[6], data[9]);
- ina.s = .5[a.nw,j.ne] + (0,24);
- .5[x.sw,x.se] = ina.n + (0,24);
- fixelement(a,d,g,j,ina,x);
- leafconnect(a,d,g,j);
- drawelement(a,d,g,j,ina);
- fillelement(x)(upperlayerfill); drawelement(x) withcolor upperlayer;
- begingroup
- interim linecap := butt;
- draw (x.sw - (40,10)) -- (x.se + (40,-10)) withpen pencircle scaled 5 dashed evenly scaled 3 withcolor upperlayerfill;
- endgroup;
- drawconnarrow x.value[1] {down} .. tension 2 .. {down} ina.n withpen connectionpen scaled 2 withcolor upperlayer;
- internalnodeconnect(ina,a,d,g,j);
-endfig;
-
-beginfig(2);
- % tree with MNO removed, gclayer begin
- boxjoin(b.w - a.e = (20,0));
- leafit.a(data[0], data[1], data[2]);
- leafit.d(data[3], data[4], data[5]);
- leafit.g(data[6], data[7], data[8]);
- leafit.j(data[9], data[10], data[11]);
- leafit.m(data[12], nullpicture, nullpicture);
- j.locked = m.locked = true;
- boxjoin();
- leafit.x(dotdata, dotdata, dotdata, dotdata);
- x.nextpath = x.prevpath = emptypath;
- internalnodeit.ina(data[3], data[6], data[9]);
- ina.s = .5[a.nw,j.ne] + (0,24);
- .5[x.sw,x.se] = ina.n + (0,24);
- fixelement(a,d,g,j,m,ina,x);
- leafconnect(a,d,g,j,m);
- drawelement(a,d,g,j,m,ina);
- fillelement(x)(upperlayerfill); drawelement(x) withcolor upperlayer;
- begingroup
- interim linecap := butt;
- draw (x.sw - (40,10)) -- (x.se + (40,-10)) withpen pencircle scaled 5 dashed evenly scaled 3 withcolor upperlayerfill;
- endgroup;
- drawconnarrow x.value[1] {down} .. tension 2 .. {down} ina.n withpen connectionpen scaled 2 withcolor upperlayer;
- internalnodeconnect(ina,a,d,g,j);
-endfig;
-
-beginfig(3);
- % tree with MNO removed, gclayer begin
- boxjoin(b.w - a.e = (20,0));
- leafit.a(data[0], data[1], data[2]);
- leafit.d(data[3], data[4], data[5]);
- leafit.g(data[6], data[7], data[8]);
- leafit.j(data[9], data[10], data[11]);
- leafit.m(data[12], nullpicture, nullpicture);
- boxjoin();
- leafit.x(dotdata, dotdata, dotdata, dotdata);
- x.nextpath = x.prevpath = emptypath;
- internalnodeit.ina(data[3], data[6], nullpicture);
- internalnodeit.inj(data[12], nullpicture, nullpicture);
- ina.locked = inj.locked = true;
- ina.s = .5[a.nw,g.ne] + (0,24);
- inj.s = .5[j.nw,m.ne] + (0,24);
- .5[x.sw,x.se] = ina.n + (0,24);
- fixelement(a,d,g,j,m,ina,inj,x);
- leafconnect(a,d,g,j,m);
- drawelement(a,d,g,j,m,ina,inj);
- fillelement(x)(upperlayerfill); drawelement(x) withcolor upperlayer;
- begingroup
- interim linecap := butt;
- draw (x.sw - (40,10)) -- (x.se + (40,-10)) withpen pencircle scaled 5 dashed evenly scaled 3 withcolor upperlayerfill;
- endgroup;
- drawconnarrow x.value[1] {down} .. tension 2 .. {down} ina.n withpen connectionpen scaled 2 withcolor upperlayer;
- internalnodeconnect(ina,a,d,g);
- internalnodeconnect(inj,j,m);
-endfig;
-
-beginfig(4);
- % tree with MNO removed, gclayer begin
- boxjoin(b.w - a.e = (20,0));
- leafit.a(data[0], data[1], data[2]);
- leafit.d(data[3], data[4], data[5]);
- leafit.g(data[6], data[7], data[8]);
- leafit.j(data[9], data[10], data[11]);
- leafit.m(data[12], nullpicture, nullpicture);
- boxjoin();
- leafit.x(dotdata, dotdata, dotdata, dotdata);
- x.nextpath = x.prevpath = emptypath;
- internalnodeit.ina(data[3], data[6], nullpicture);
- internalnodeit.inj(data[12], nullpicture, nullpicture);
- internalnodeit.root(data[9], nullpicture, nullpicture);
- ina.s = .5[a.nw,g.ne] + (0,24);
- inj.s = .5[j.nw,m.ne] + (0,24);
- root.sw = .3[ina.n,inj.n] + (0,24);
- .8[x.sw,x.se] = root.n + (0,24);
- fixelement(a,d,g,j,m,root,ina,inj,x);
- leafconnect(a,d,g,j,m);
- drawelement(a,d,g,j,m,ina,inj,root);
- fillelement(x)(upperlayerfill); drawelement(x) withcolor upperlayer;
- begingroup
- interim linecap := butt;
- draw (x.sw - (40,10)) -- (x.se + (40,-10)) withpen pencircle scaled 5 dashed evenly scaled 3 withcolor upperlayerfill;
- endgroup;
- drawconnarrow x.value[1] {down} .. tension 2 .. {down} ina.n withpen connectionpen scaled 2 withcolor upperlayer;
- internalnodeconnect(ina,a,d,g);
- internalnodeconnect(inj,j,m);
- ina.connectin := .6[ina.nw,ina.ne];
- internalnodeconnect(root,ina,inj);
-endfig;
-
-beginfig(5);
- % tree with MNO removed, gclayer begin
- boxjoin(b.w - a.e = (20,0));
- leafit.a(data[0], data[1], data[2]);
- leafit.d(data[3], data[4], data[5]);
- leafit.g(data[6], data[7], data[8]);
- leafit.j(data[9], data[10], data[11]);
- leafit.m(data[12], nullpicture, nullpicture);
- boxjoin();
- leafit.x(dotdata, dotdata, dotdata, dotdata);
- x.locked = true;
- x.nextpath = x.prevpath = emptypath;
- internalnodeit.ina(data[3], data[6], nullpicture);
- internalnodeit.inj(data[12], nullpicture, nullpicture);
- internalnodeit.root(data[9], nullpicture, nullpicture);
- ina.s = .5[a.nw,g.ne] + (0,24);
- inj.s = .5[j.nw,m.ne] + (0,24);
- root.sw = .3[ina.n,inj.n] + (0,24);
- .5[x.sw,x.se] = root.n + (0,24);
- fixelement(a,d,g,j,m,root,ina,inj,x);
- leafconnect(a,d,g,j,m);
- drawelement(a,d,g,j,m,ina,inj,root);
- fillelement(x)(upperlayerfill); drawelement(x) withcolor upperlayer;
- begingroup
- interim linecap := butt;
- draw (x.sw - (40,10)) -- (x.se + (40,-10)) withpen pencircle scaled 5 dashed evenly scaled 3 withcolor upperlayerfill;
- endgroup;
- drawconnarrow x.value[1] {down} .. tension 2 .. {down} root.n withpen connectionpen scaled 2 withcolor upperlayer;
- internalnodeconnect(ina,a,d,g);
- internalnodeconnect(inj,j,m);
- internalnodeconnect(root,ina,inj);
-endfig;
-
-end
diff --git a/doc/masstree.mp b/doc/masstree.mp
deleted file mode 100644
index d813505..0000000
--- a/doc/masstree.mp
+++ /dev/null
@@ -1,200 +0,0 @@
-input patches;
-
-def emptypath =
- subpath (0,0) of (0,0) -- (0,0)
-enddef;
-
-vardef clearnode_(suffix $) =
- if $.isleaf:
- _n_ := str $;
- generic_redeclare(numeric) _n.prev, _n.previn, _n.next, _n.nextin, _n.nextpath, _n.prevpath,
- _n.nextbpath, _n.prevbpath;
- _n_ := str $ & ".value0";
- generic_redeclare(numeric) _n;
- else:
- _n_ := str $ & ".child0";
- generic_redeclare(numeric) _n;
- fi
- _n_ := str $;
- generic_redeclare(numeric) _n.linkwidth, _n.nkeys, _n.deleted, _n.isleaf, _n.locked, _n.keywidth, _n.connectin;
- _n_ := str $ & ".keypic0";
- generic_redeclare(numeric) _n;
-enddef;
-
-vardef drawnode_(suffix $) text rest =
- save _i_, _w_, _pos_, _ll_; numeric _i_, _w_; pair _pos_, _ll_;
- _ll_ = $.sw + ($.linkwidth,0); _w_ = $.keywidth;
- for _i_ = 0 upto $.nkeys-1:
- if xpart (llcorner $.keypic[_i_] - urcorner $.keypic[_i_]) = 0:
- fill ((0,0) -- (_w_,0) -- (_w_,$.height) -- (0,$.height) -- cycle) shifted (_ll_ + (_w_*_i_,0)) withcolor .9white;
- fi
- endfor
- if $.locked:
- begingroup interim linecap := butt;
- draw ((0,-3) {left} .. (-3,0) {up} .. (0,3) {right} .. {down} (3,0)) shifted $.nw withpen pencircle scaled 2 rest;
- endgroup;
- fi
- drawboxes($) rest;
- if $.isleaf:
- draw ($.nw + ($.linkwidth,0)) -- ($.sw + ($.linkwidth,0)) withpen connectionpen rest;
- if known $.nextpath:
- drawarrow $.nextpath dashed withdots scaled 0.5 rest;
- else:
- draw $.ne -- ($.se - ($.linkwidth,0)) withpen connectionpen scaled 0.2 rest;
- fi
- draw ($.ne - ($.linkwidth,0)) -- ($.se - ($.linkwidth,0)) withpen connectionpen rest;
- if known $.prevpath:
- drawarrow $.prevpath dashed withdots scaled 0.5 rest;
- else:
- draw ($.nw + ($.linkwidth,0)) -- $.sw withpen connectionpen scaled 0.5 rest;
- fi
- fi
- for _i_ = 0 upto $.nkeys-1:
- _pos_ := .5[llcorner $.keypic[_i_],urcorner $.keypic[_i_]];
- draw $.keypic[_i_] shifted (_ll_ + (_w_*(_i_+0.5),.5*$.height) - _pos_) rest;
- if _i_ > 0:
- draw (_ll_ + (_w_*_i_,0)) -- (_ll_ + (_w_*_i_,.15*$.height)) withpen connectionpen scaled 2 rest;
- draw (_ll_ + (_w_*_i_,$.height)) -- (_ll_ + (_w_*_i_,.85*$.height)) withpen connectionpen scaled 2 rest;
- fi
- endfor
- if $.deleted:
- draw ($.nw + .1*($.se-$.nw)) -- ($.se - .1*($.se-$.nw))
- withpen penrazor scaled 5 rotated 60 withcolor white;
- draw ($.sw - .1*($.sw-$.ne)) -- ($.ne + .1*($.sw-$.ne))
- withpen penrazor scaled 5 rotated -60 withcolor white;
- fi
-enddef;
-
-vardef sizenode_(suffix $) =
- if unknown $.linkwidth: $.linkwidth = if $.isleaf: 5 else: 0 fi; fi
- if unknown $.deleted: $.deleted = false; fi
- if unknown $.locked: $.locked = false; fi
- if unknown $.dx or unknown $.dy:
- save _a_, _i_; pair _a_; _a_ := min_reasonable_cell;
- for _i_ = 0 upto $.nkeys-1:
- _a_ := (max(xpart _a_,xpart (urcorner $.keypic[_i_] - llcorner $.keypic[_i_])),
- max(ypart _a_,ypart (urcorner $.keypic[_i_] - llcorner $.keypic[_i_])));
- endfor
- if unknown $.dx: $.dx = ($.linkwidth*2 + (2*xpart element_offset + xpart _a_)*$.nkeys)/2; fi
- if unknown $.dy: $.dy = (2*ypart element_offset + ypart _a_)/2; fi
- fi
- sizeelement_($)
-enddef;
-
-vardef leafit@#(text components) =
- _elementit.@#(nullpicture, 0, 0, push, false, false);
- _n_ := str @#;
- generic_declare(numeric) _n.linkwidth, _n.nkeys, _n.keywidth;
- generic_declare(boolean) _n.deleted, _n.isleaf, _n.locked;
- generic_declare(pair) _n.prev, _n.previn, _n.next, _n.nextin, _n.connectin;
- generic_declare(path) _n.prevpath, _n.nextpath, _n.nextbpath, _n.prevbpath;
- _n_ := str @# & ".keypic0";
- generic_declare(picture) _n;
- _n_ := str @# & ".value0";
- generic_declare(pair) _n;
- @#.isleaf = true;
- save _i_, _nk_; picture _i_; _nk_ := 0;
- for _i_ = components:
- @#.keypic[_nk_] = _i_;
- @#.value[_nk_] = @#.sw + (@#.linkwidth + @#.keywidth*(_nk_+0.5),.15*@#.height);
- _nk_ := _nk_ + 1;
- endfor
- elemdraw_@# := "drawnode_";
- sproc_@# := "sizenode_";
- @#.prev = @#.w + (@#.linkwidth/2,-@#.height/10);
- @#.previn = @#.e + (0,-@#.height/10);
- @#.next = @#.e + (-@#.linkwidth/2,@#.height/10);
- @#.nextin = @#.w + (0,@#.height/10);
- @#.nkeys = _nk_;
- @#.keywidth = (@#.width - 2 * @#.linkwidth) / @#.nkeys;
- expandafter def expandafter clearboxes expandafter =
- clearboxes clearnode_(@#);
- enddef
-enddef;
-
-vardef leafconnectnext(suffix $,$$)(text connection) =
- $.nextpath = $.next connection $$.nextin;
-enddef;
-vardef leafconnectprev(suffix $,$$)(text connection) =
- $.prevpath = $.prev connection $$.previn;
-enddef;
-vardef leafconnect(text suffixes) =
- save _fart_; string _fart_;
- forsuffixes $=suffixes:
- if known _fart_:
- leafconnectnext(scantokens _fart_,$)(--);
- leafconnectprev($,scantokens _fart_)(--);
- fi
- _fart_ := str $;
- endfor
-enddef;
-def leafnextbpath(suffix $) =
- $.ne -- ($.ne - ($.linkwidth,0)) -- ($.se - ($.linkwidth,0)) -- $.se -- cycle
-enddef;
-def leafprevbpath(suffix $) =
- $.nw -- ($.nw + ($.linkwidth,0)) -- ($.sw + ($.linkwidth,0)) -- $.sw -- cycle
-enddef;
-def nodecellsbpath(suffix $)(expr i,j) =
- (((0,0) -- ((j-i+1)*$.keywidth,0) -- ((j-i+1)*$.keywidth,$.height) -- (0,$.height) -- cycle)
- shifted ($.sw + ($.linkwidth + i*$.keywidth,0)))
-enddef;
-def nodecellbpath(suffix $)(expr i) =
- nodecellsbpath($,i,i)
-enddef;
-
-def internalnodeconnectone(suffix $,$$)(expr i) =
- if known $$.connectin:
- drawconnarrow $.child[i] {down} .. tension 4 .. {down} $$.connectin
- else:
- drawconnarrow $.child[i] {down} .. tension 4 .. {down} $$.n
- fi
-enddef;
-vardef internalnodeconnect(text suffixes) =
- save _fart_, _i_; string _fart_; _i_ = 0;
- forsuffixes $=suffixes:
- if unknown _fart_:
- _fart_ := str $;
- elseif _i_ <= $.nkeys:
- forsuffixes $$=scantokens _fart_:
- internalnodeconnectone($$,$,_i_);
- endfor
- _i_ := _i_ + 1;
- fi
- endfor
-enddef;
-
-vardef internalnodeit@#(text components) =
- _elementit.@#(nullpicture, 0, 0, push, false, false);
- _n_ := str @#;
- generic_declare(numeric) _n.linkwidth, _n.nkeys;
- generic_declare(boolean) _n.deleted, _n.isleaf, _n.locked;
- generic_declare(pair) _n.connectin;
- _n_ := str @# & ".keypic0";
- generic_declare(picture) _n;
- _n_ := str @# & ".child0";
- generic_declare(pair) _n;
- @#.isleaf = false;
- save _i_, _nk_; picture _i_; _nk_ := 0;
- for _i_ = components:
- @#.keypic[_nk_] = _i_;
- @#.child[_nk_] = @#.sw + (@#.linkwidth + _nk_*@#.keywidth,0);
- _nk_ := _nk_ + 1;
- endfor
- @#.child[_nk_] = @#.sw + (@#.linkwidth + _nk_*@#.keywidth,0);
- elemdraw_@# := "drawnode_";
- sproc_@# := "sizenode_";
- @#.nkeys = _nk_;
- @#.keywidth = (@#.width - 2*@#.linkwidth) / @#.nkeys;
- expandafter def expandafter clearboxes expandafter =
- clearboxes clearnode_(@#);
- enddef
-enddef;
-
-vardef drawterminationarrow(expr p) text rest =
- draw p withpen connectionpen rest;
- save endpt, enddir; pair endpt;
- endpt = point length p of p; enddir = angle(direction length p of p);
- draw ((-4,0) -- (4,0)) shifted endpt rotated (enddir + 90) withpen connectionpen rest;
- draw ((-2.25,-1.5) -- (2.25,-1.5)) shifted endpt rotated (enddir + 90) withpen connectionpen rest;
- draw ((-.5,-3) -- (.5,-3)) shifted endpt rotated (enddir + 90) withpen connectionpen rest;
-enddef;
diff --git a/doc/patches.mp b/doc/patches.mp
deleted file mode 100644
index b2a1af1..0000000
--- a/doc/patches.mp
+++ /dev/null
@@ -1,44 +0,0 @@
-input elements;
-
-def thediamond_(suffix $) =
- $.e -- $.n -- $.w -- $s -- cycle
-enddef;
-
-vardef sizediamond_(suffix $) =
- save a_, b_;
- (a_,b_) = .5*(urcorner pic_$ - llcorner pic_$);
- if (unknown $dy or unknown $dx) and unknown $squatness: $squatness = 1; fi
- if unknown $dy: $dy = $squatness * ($dx + a_) - b_; fi
- if unknown $dy:
- $dy = b_ + circmargin * sqrt($squatness+1) + $squatness * a_;
- fi
- sizeelement_($);
-enddef;
-
-vardef cleardiamond_(suffix $) =
- _n_ := str $;
- generic_redeclare(numeric) _n.squatness;
-enddef;
-
-vardef patchit@#(expr s, type) =
- _elementit.@#(s, 0, 0, push, false, false);
- if type = 0:
- pproc_.@# := "thecirc_";
- % sproc_.@# := "sizecirc_";
- elseif type = 2:
- _n_ := str @#;
- generic_declare(numeric) _n.squatness, _n.dx, _n.dy;
- pproc_.@# := "thediamond_";
- sproc_.@# := "sizediamond_";
- expandafter def expandafter clearboxes expandafter =
- clearboxes cleardiamond_(@#);
- enddef
- elseif type = 3:
- pproc_.@# := "rboxpath_";
- fi;
-enddef;
-
-vardef drawconnpatch(suffix $,$$)(text conn) text rest =
- interim linejoin := mitered;
- drawarrow ($.c conn $$.c) cutbefore bpath.$ cutafter bpath.$$ withpen connectionpen rest
-enddef;
diff --git a/doc/remove1.mp b/doc/remove1.mp
deleted file mode 100644
index 48568b9..0000000
--- a/doc/remove1.mp
+++ /dev/null
@@ -1,327 +0,0 @@
-input masstree;
-verbatimtex %&latex
-\documentclass[12pt]{article}
-\usepackage{elemfig,amsmath}
-\begin{document}
-\newcommand{\xlab}[1]{\mlabel{\textsc{#1}}}
-etex;
-
-picture data[];
-data[0] = btex \xlab{a} etex; data[1] = btex \xlab{b} etex; data[2] = btex \xlab{c} etex;
-data[3] = btex \xlab{d} etex; data[4] = btex \xlab{e} etex; data[5] = btex \xlab{f} etex;
-data[6] = btex \xlab{g} etex; data[7] = btex \xlab{h} etex; data[8] = btex \xlab{i} etex;
-data[9] = btex \xlab{j} etex; data[10] = btex \xlab{k} etex; data[11] = btex \xlab{l} etex;
-data[12] = btex \xlab{m} etex; data[13] = btex \xlab{n} etex; data[14] = btex \xlab{o} etex;
-
-pair min_reasonable_cell; min_reasonable_cell := (0,0);
-for _i_ = 0 upto 14:
- min_reasonable_cell :=
- (max(xpart min_reasonable_cell, xpart (urcorner data[_i_] - llcorner data[_i_])),
- max(ypart min_reasonable_cell, ypart (urcorner data[_i_] - llcorner data[_i_])));
-endfor;
-color deletedelement; deletedelement = .5white;
-
-picture emptyqueue; emptyqueue := btex \phantom{\elementlabel{Queue}} etex;
-picture vemptyqueue; vemptyqueue := emptyqueue rotated 90 xscaled .4;
-hardborderscale = 3;
-
-beginfig(1);
- % initial tree
- boxjoin(b.w - a.e = (20,0));
- leafit.a(data[0], data[1], data[2]);
- leafit.d(data[3], data[4], data[5]);
- leafit.g(data[6], data[7], data[8]);
- leafit.j(data[9], data[10], data[11]);
- leafit.m(data[12], data[13], data[14]);
- boxjoin();
- internalnodeit.ina(data[3], data[6], nullpicture);
- internalnodeit.inj(data[12], nullpicture, nullpicture);
- internalnodeit.root(data[9], nullpicture, nullpicture);
- ina.s = .5[a.nw,g.ne] + (0,24);
- inj.s = .5[j.nw,m.ne] + (0,24);
- root.s = .5[ina.nw,inj.ne] + (0,24);
- fixelement(a,d,g,j,m);
- fixelement(root,ina,inj);
- leafconnect(a,d,g,j,m);
- drawelement(a,d,g,j,m,ina,inj,root);
- internalnodeconnect(ina,a,d,g);
- internalnodeconnect(inj,j,m);
- internalnodeconnect(root,ina,inj);
-endfig;
-
-beginfig(2);
- % delete element E
- boxjoin(b.w - a.e = (20,0));
- leafit.a(data[0], data[1], data[2]);
- leafit.d(data[3], data[4], data[5]);
- d.locked = true;
- leafit.g(data[6], data[7], data[8]);
- leafit.j(data[9], data[10], data[11]);
- leafit.m(data[12], data[13], data[14]);
- boxjoin();
- internalnodeit.ina(data[3], data[6], nullpicture);
- internalnodeit.inj(data[12], nullpicture, nullpicture);
- internalnodeit.root(data[9], nullpicture, nullpicture);
- ina.s = .5[a.nw,g.ne] + (0,24);
- inj.s = .5[j.nw,m.ne] + (0,24);
- root.s = .5[ina.nw,inj.ne] + (0,24);
- fixelement(a,d,g,j,m);
- fixelement(root,ina,inj);
- fill nodecellbpath(d,1) withcolor deletedelement;
- leafconnect(a,d,g,j,m);
- drawelement(a,d,g,j,m,ina,inj,root);
- internalnodeconnect(ina,a,d,g);
- internalnodeconnect(inj,j,m);
- internalnodeconnect(root,ina,inj);
-endfig;
-
-beginfig(3);
- % delete node DEF: poison next pointer
- boxjoin(b.w - a.e = (20,0));
- leafit.a(data[0], data[1], data[2]);
- leafit.d(data[3], data[4], data[5]);
- d.locked = d.deleted = true;
- leafit.g(data[6], data[7], data[8]);
- leafit.j(data[9], data[10], data[11]);
- leafit.m(data[12], data[13], data[14]);
- boxjoin();
- internalnodeit.ina(data[3], data[6], nullpicture);
- internalnodeit.inj(data[12], nullpicture, nullpicture);
- internalnodeit.root(data[9], nullpicture, nullpicture);
- ina.s = .5[a.nw,g.ne] + (0,24);
- inj.s = .5[j.nw,m.ne] + (0,24);
- root.s = .5[ina.nw,inj.ne] + (0,24);
- fixelement(a,d,g,j,m);
- fixelement(root,ina,inj);
- fill nodecellsbpath(d,0,2) withcolor deletedelement;
- fill leafnextbpath(d) withcolor black;
- leafconnect(a,d,g,j,m);
- drawelement(a,d,g,j,m,ina,inj,root);
- internalnodeconnect(ina,a,d,g);
- internalnodeconnect(inj,j,m);
- internalnodeconnect(root,ina,inj);
-endfig;
-
-beginfig(31);
- % delete node DEF: poison ABC's next pointer
- boxjoin(b.w - a.e = (20,0));
- leafit.a(data[0], data[1], data[2]);
- leafit.d(data[3], data[4], data[5]);
- d.locked = d.deleted = true;
- leafit.g(data[6], data[7], data[8]);
- leafit.j(data[9], data[10], data[11]);
- leafit.m(data[12], data[13], data[14]);
- boxjoin();
- internalnodeit.ina(data[3], data[6], nullpicture);
- internalnodeit.inj(data[12], nullpicture, nullpicture);
- internalnodeit.root(data[9], nullpicture, nullpicture);
- ina.s = .5[a.nw,g.ne] + (0,24);
- inj.s = .5[j.nw,m.ne] + (0,24);
- root.s = .5[ina.nw,inj.ne] + (0,24);
- fixelement(a,d,g,j,m);
- fixelement(root,ina,inj);
- fill nodecellsbpath(d,0,2) withcolor deletedelement;
- fill leafnextbpath(d) withcolor black;
- fill leafnextbpath(a) withcolor black;
- leafconnect(a,d,g,j,m);
- drawelement(a,d,g,j,m,ina,inj,root);
- internalnodeconnect(ina,a,d,g);
- internalnodeconnect(inj,j,m);
- internalnodeconnect(root,ina,inj);
-endfig;
-
-beginfig(4);
- % delete node DEF: adjust GHI.prev
- boxjoin(b.w - a.e = (20,0));
- leafit.a(data[0], data[1], data[2]);
- leafit.d(data[3], data[4], data[5]);
- d.locked = d.deleted = true;
- leafit.g(data[6], data[7], data[8]);
- leafit.j(data[9], data[10], data[11]);
- leafit.m(data[12], data[13], data[14]);
- boxjoin();
- internalnodeit.ina(data[3], data[6], nullpicture);
- internalnodeit.inj(data[12], nullpicture, nullpicture);
- internalnodeit.root(data[9], nullpicture, nullpicture);
- ina.s = .5[a.nw,g.ne] + (0,24);
- inj.s = .5[j.nw,m.ne] + (0,24);
- root.s = .5[ina.nw,inj.ne] + (0,24);
- fixelement(a,d,g,j,m);
- fixelement(root,ina,inj);
- fill nodecellsbpath(d,0,2) withcolor deletedelement;
- fill leafnextbpath(d) withcolor black;
- fill leafnextbpath(a) withcolor black;
- leafconnect(a,d);
- leafconnect(g,j,m);
- g.prevpath = g.prev .. (d.se - (0,3)) .. tension 1.5 .. (d.sw - (0,3)) .. (a.previn - (0,1));
- d.nextpath = d.next -- g.nextin;
- drawelement(a,d,g,j,m,ina,inj,root);
- internalnodeconnect(ina,a,d,g);
- internalnodeconnect(inj,j,m);
- internalnodeconnect(root,ina,inj);
-endfig;
-
-beginfig(5);
- % delete node DEF: adjust ABC.next
- boxjoin(b.w - a.e = (20,0));
- leafit.a(data[0], data[1], data[2]);
- leafit.d(data[3], data[4], data[5]);
- d.locked = d.deleted = true;
- leafit.g(data[6], data[7], data[8]);
- leafit.j(data[9], data[10], data[11]);
- leafit.m(data[12], data[13], data[14]);
- boxjoin();
- internalnodeit.ina(data[3], data[6], nullpicture);
- internalnodeit.inj(data[12], nullpicture, nullpicture);
- internalnodeit.root(data[9], nullpicture, nullpicture);
- ina.s = .5[a.nw,g.ne] + (0,24);
- inj.s = .5[j.nw,m.ne] + (0,24);
- root.s = .5[ina.nw,inj.ne] + (0,24);
- fixelement(a,d,g,j,m);
- fixelement(root,ina,inj);
- fill nodecellsbpath(d,0,2) withcolor deletedelement;
- fill leafnextbpath(d) withcolor black;
- leafconnect(g,j,m);
- a.nextpath = a.next .. (d.nw + (0,3)) .. tension 1.5 .. (d.ne + (0,3)) .. (g.nextin + (0,1));
- d.prevpath = d.prev -- a.previn;
- d.nextpath = d.next -- g.nextin;
- g.prevpath = g.prev .. (d.se - (0,3)) .. tension 1.5 .. (d.sw - (0,3)) .. (a.previn - (0,1));
- drawelement(a,d,g,j,m,ina,inj,root);
- internalnodeconnect(ina,a,d,g);
- internalnodeconnect(inj,j,m);
- internalnodeconnect(root,ina,inj);
-endfig;
-
-beginfig(6);
- % delete node DEF: adjust internal node
- boxjoin(b.w - a.e = (20,0));
- leafit.a(data[0], data[1], data[2]);
- leafit.d(data[3], data[4], data[5]);
- d.deleted = true;
- leafit.g(data[6], data[7], data[8]);
- leafit.j(data[9], data[10], data[11]);
- leafit.m(data[12], data[13], data[14]);
- boxjoin();
- internalnodeit.ina(data[6], nullpicture, nullpicture);
- ina.locked = true;
- internalnodeit.inj(data[12], nullpicture, nullpicture);
- internalnodeit.root(data[9], nullpicture, nullpicture);
- ina.s = .5[a.nw,g.ne] + (0,24);
- inj.s = .5[j.nw,m.ne] + (0,24);
- root.s = .5[ina.nw,inj.ne] + (0,24);
- fixelement(a,d,g,j,m);
- fixelement(root,ina,inj);
- fill nodecellsbpath(d,0,2) withcolor deletedelement;
- fill leafnextbpath(d) withcolor black;
- leafconnect(g,j,m);
- a.nextpath = a.next .. (d.nw + (0,3)) .. tension 1.5 .. (d.ne + (0,3)) .. (g.nextin + (0,1));
- d.prevpath = d.prev -- a.previn;
- d.nextpath = d.next -- g.nextin;
- g.prevpath = g.prev .. (d.se - (0,3)) .. tension 1.5 .. (d.sw - (0,3)) .. (a.previn - (0,1));
- drawelement(a,d,g,j,m,ina,inj,root);
- internalnodeconnect(ina,a,g);
- internalnodeconnect(inj,j,m);
- internalnodeconnect(root,ina,inj);
-endfig;
-
-beginfig(7);
- % delete node GHI: after leaf unlink
- boxjoin(b.w - a.e = (20,0));
- leafit.a(data[0], data[1], data[2]);
- leafit.d(data[3], data[4], data[5]);
- d.deleted = true;
- leafit.g(data[6], data[7], data[8]);
- g.locked = g.deleted = true;
- leafit.j(data[9], data[10], data[11]);
- leafit.m(data[12], data[13], data[14]);
- boxjoin();
- internalnodeit.ina(data[6], nullpicture, nullpicture);
- internalnodeit.inj(data[12], nullpicture, nullpicture);
- internalnodeit.root(data[9], nullpicture, nullpicture);
- ina.s = .5[a.nw,g.ne] + (0,24);
- inj.s = .5[j.nw,m.ne] + (0,24);
- root.s = .5[ina.nw,inj.ne] + (0,24);
- fixelement(a,d,g,j,m);
- fixelement(root,ina,inj);
- fill nodecellsbpath(g,0,2) withcolor deletedelement;
- fill leafnextbpath(g) withcolor black;
- leafconnect(j,m);
- a.nextpath = a.next .. (g.nw + (0,3)) .. tension 1.5 .. (g.ne + (0,3)) .. (j.nextin + (0,1));
- g.prevpath = g.prev -- a.previn;
- g.nextpath = g.next -- j.nextin;
- j.prevpath = j.prev .. (g.se - (0,3)) .. tension 1.5 .. (g.sw - (0,3)) .. (a.previn - (0,2));
- drawelement(a,g,j,m,ina,inj,root);
- internalnodeconnect(ina,a,g);
- internalnodeconnect(inj,j,m);
- internalnodeconnect(root,ina,inj);
-endfig;
-
-beginfig(8);
- % delete node GHI: after internal node
- boxjoin(b.w - a.e = (20,0));
- leafit.a(data[0], data[1], data[2]);
- leafit.d(data[3], data[4], data[5]);
- d.deleted = true;
- leafit.g(data[6], data[7], data[8]);
- g.deleted = true;
- leafit.j(data[9], data[10], data[11]);
- leafit.m(data[12], data[13], data[14]);
- boxjoin();
- internalnodeit.ina(nullpicture, nullpicture, nullpicture);
- ina.locked = true;
- internalnodeit.inj(data[12], nullpicture, nullpicture);
- internalnodeit.root(data[9], nullpicture, nullpicture);
- ina.s = .5[a.nw,g.ne] + (0,24);
- inj.s = .5[j.nw,m.ne] + (0,24);
- root.s = .5[ina.nw,inj.ne] + (0,24);
- fixelement(a,d,g,j,m);
- fixelement(root,ina,inj);
- fill nodecellsbpath(g,0,2) withcolor deletedelement;
- fill leafnextbpath(g) withcolor black;
- leafconnect(j,m);
- a.nextpath = a.next .. (g.nw + (0,3)) .. tension 1.5 .. (g.ne + (0,3)) .. (j.nextin + (0,1));
- g.prevpath = g.prev -- a.previn;
- g.nextpath = g.next -- j.nextin;
- j.prevpath = j.prev .. (g.se - (0,3)) .. tension 1.5 .. (g.sw - (0,3)) .. (a.previn - (0,2));
- drawelement(a,g,j,m,ina,inj,root);
- internalnodeconnect(ina,a);
- internalnodeconnect(inj,j,m);
- internalnodeconnect(root,ina,inj);
-endfig;
-
-beginfig(9);
- % delete node GHI: after collapse
- boxjoin(b.w - a.e = (20,0));
- leafit.a(data[0], data[1], data[2]);
- leafit.d(data[3], data[4], data[5]);
- d.deleted = true;
- leafit.g(data[6], data[7], data[8]);
- g.deleted = true;
- leafit.j(data[9], data[10], data[11]);
- leafit.m(data[12], data[13], data[14]);
- boxjoin();
- internalnodeit.ina(nullpicture, nullpicture, nullpicture);
- ina.deleted = true;
- internalnodeit.inj(data[12], nullpicture, nullpicture);
- internalnodeit.root(data[9], nullpicture, nullpicture);
- root.locked = true;
- ina.s = .5[a.nw,g.ne] + (0,24);
- inj.s = .5[j.nw,m.ne] + (0,24);
- root.s = .5[ina.nw,inj.ne] + (0,24);
- fixelement(a,d,g,j,m);
- fixelement(root,ina,inj);
- fill nodecellsbpath(g,0,2) withcolor deletedelement;
- fill leafnextbpath(g) withcolor black;
- leafconnect(j,m);
- a.nextpath = a.next .. (g.nw + (0,3)) .. tension 1.5 .. (g.ne + (0,3)) .. (j.nextin + (0,1));
- g.prevpath = g.prev -- a.previn;
- g.nextpath = g.next -- j.nextin;
- j.prevpath = j.prev .. (g.se - (0,3)) .. tension 1.5 .. (g.sw - (0,3)) .. (a.previn - (0,2));
- drawelement(a,g,j,m,ina,inj,root);
- internalnodeconnect(inj,j,m);
- drawconnarrow ina.child[0] {down} .. tension 4 .. {down} (a.n + (3,0));
- drawconnarrow root.child[0] {down} .. tension 2 and 1 .. (ina.nw + (0,4)) .. {down} a.n;
- internalnodeconnectone(root,inj,1);
-endfig;
-
-end
diff --git a/doc/remove2.mp b/doc/remove2.mp
deleted file mode 100644
index 1f9db91..0000000
--- a/doc/remove2.mp
+++ /dev/null
@@ -1,408 +0,0 @@
-input masstree;
-verbatimtex %&latex
-\documentclass[12pt]{article}
-\usepackage{elemfig,amsmath}
-\begin{document}
-\newcommand{\xlab}[1]{\mlabel{\textsc{#1}}}
-etex;
-
-picture data[];
-data[0] = btex \xlab{a} etex; data[1] = btex \xlab{b} etex; data[2] = btex \xlab{c} etex;
-data[3] = btex \xlab{d} etex; data[4] = btex \xlab{e} etex; data[5] = btex \xlab{f} etex;
-data[6] = btex \xlab{g} etex; data[7] = btex \xlab{h} etex; data[8] = btex \xlab{i} etex;
-data[9] = btex \xlab{j} etex; data[10] = btex \xlab{k} etex; data[11] = btex \xlab{l} etex;
-data[12] = btex \xlab{m} etex; data[13] = btex \xlab{n} etex; data[14] = btex \xlab{o} etex;
-picture dotdata;
-dotdata = btex \xlab{\dots\dots} etex;
-
-pair min_reasonable_cell; min_reasonable_cell := (0,0);
-for _i_ = 0 upto 14:
- min_reasonable_cell :=
- (max(xpart min_reasonable_cell, xpart (urcorner data[_i_] - llcorner data[_i_])),
- max(ypart min_reasonable_cell, ypart (urcorner data[_i_] - llcorner data[_i_])));
-endfor;
-color deletedelement; deletedelement = .5white;
-color upperlayer, upperlayerfill; upperlayer = (0,0,.5); upperlayerfill = (.9,.9,1);
-
-picture emptyqueue; emptyqueue := btex \phantom{\elementlabel{Queue}} etex;
-picture vemptyqueue; vemptyqueue := emptyqueue rotated 90 xscaled .4;
-hardborderscale = 3;
-
-beginfig(1);
- % tree with JKL removed
- boxjoin(b.w - a.e = (20,0));
- leafit.a(data[0], data[1], data[2]);
- leafit.d(data[3], data[4], data[5]);
- leafit.g(data[6], data[7], data[8]);
- leafit.j(data[9], data[10], data[11]);
- j.locked = j.deleted = true;
- leafit.m(data[12], data[13], data[14]);
- boxjoin();
- internalnodeit.ina(data[3], data[6], nullpicture);
- internalnodeit.inj(data[12], nullpicture, nullpicture);
- internalnodeit.root(data[9], nullpicture, nullpicture);
- ina.s = .5[a.nw,g.ne] + (0,24);
- inj.s = .5[j.nw,m.ne] + (0,24);
- root.s = .5[ina.nw,inj.ne] + (0,24);
- fixelement(a,d,g,j,m);
- fixelement(root,ina,inj);
- leafconnect(a,d,g);
- fill nodecellsbpath(j,0,2) withcolor deletedelement;
- fill leafnextbpath(j) withcolor black;
- g.nextpath = g.next .. (j.nw + (0,3)) .. tension 1.5 .. (j.ne + (0,3)) .. (m.nextin + (0,1));
- j.prevpath = j.prev -- g.previn;
- j.nextpath = j.next -- m.nextin;
- m.prevpath = m.prev .. (j.se - (0,3)) .. tension 1.5 .. (j.sw - (0,3)) .. (g.previn - (0,1));
- drawelement(a,d,g,j,m,ina,inj,root);
- internalnodeconnect(ina,a,d,g);
- internalnodeconnect(inj,j,m);
- internalnodeconnect(root,ina,inj);
-endfig;
-
-beginfig(2);
- % bad idea: tree with M in internal node removed
- boxjoin(b.w - a.e = (20,0));
- leafit.a(data[0], data[1], data[2]);
- leafit.d(data[3], data[4], data[5]);
- leafit.g(data[6], data[7], data[8]);
- leafit.j(data[9], data[10], data[11]);
- j.deleted = true;
- leafit.m(data[12], data[13], data[14]);
- boxjoin();
- internalnodeit.ina(data[3], data[6], nullpicture);
- internalnodeit.inj(nullpicture, nullpicture, nullpicture);
- inj.locked = true;
- internalnodeit.root(data[9], nullpicture, nullpicture);
- ina.s = .5[a.nw,g.ne] + (0,24);
- inj.s = .5[j.nw,m.ne] + (0,24);
- root.s = .5[ina.nw,inj.ne] + (0,24);
- fixelement(a,d,g,j,m);
- fixelement(root,ina,inj);
- leafconnect(a,d,g);
- fill nodecellsbpath(j,0,2) withcolor deletedelement;
- fill leafnextbpath(j) withcolor black;
- g.nextpath = g.next .. (j.nw + (0,3)) .. tension 1.5 .. (j.ne + (0,3)) .. (m.nextin + (0,1));
- j.prevpath = j.prev -- g.previn;
- j.nextpath = j.next -- m.nextin;
- m.prevpath = m.prev .. (j.se - (0,3)) .. tension 1.5 .. (j.sw - (0,3)) .. (g.previn - (0,1));
- drawelement(a,d,g,j,m,ina,inj,root);
- internalnodeconnect(ina,a,d,g);
- internalnodeconnect(inj,m);
- internalnodeconnect(root,ina,inj);
- label.rt(btex \textsf{\textbf{~~XXX}} etex, inj.e);
-endfig;
-
-beginfig(3);
- % tree with M stubbed out
- boxjoin(b.w - a.e = (20,0));
- leafit.a(data[0], data[1], data[2]);
- leafit.d(data[3], data[4], data[5]);
- leafit.g(data[6], data[7], data[8]);
- leafit.j(data[9], data[10], data[11]);
- j.deleted = true;
- leafit.m(data[12], data[13], data[14]);
- boxjoin();
- internalnodeit.ina(data[3], data[6], nullpicture);
- internalnodeit.inj(data[12], nullpicture, nullpicture);
- inj.locked = true;
- internalnodeit.root(data[9], nullpicture, nullpicture);
- ina.s = .5[a.nw,g.ne] + (0,24);
- inj.s = .5[j.nw,m.ne] + (0,24);
- root.s = .5[ina.nw,inj.ne] + (0,24);
- fixelement(a,d,g,j,m);
- fixelement(root,ina,inj);
- leafconnect(a,d,g);
- fill nodecellsbpath(j,0,2) withcolor deletedelement;
- fill leafnextbpath(j) withcolor black;
- g.nextpath = g.next .. (j.nw + (0,3)) .. tension 1.5 .. (j.ne + (0,3)) .. (m.nextin + (0,1));
- j.prevpath = j.prev -- g.previn;
- j.nextpath = j.next -- m.nextin;
- m.prevpath = m.prev .. (j.se - (0,3)) .. tension 1.5 .. (j.sw - (0,3)) .. (g.previn - (0,1));
- drawelement(a,d,g,j,m,ina,inj,root);
- internalnodeconnect(ina,a,d,g);
- internalnodeconnectone(inj,m,1);
- drawterminationarrow(inj.child[0] -- (inj.child[0] - (0,5)));
- internalnodeconnect(root,ina,inj);
-endfig;
-
-beginfig(4);
- % tree with M replacing J at the root
- boxjoin(b.w - a.e = (20,0));
- leafit.a(data[0], data[1], data[2]);
- leafit.d(data[3], data[4], data[5]);
- leafit.g(data[6], data[7], data[8]);
- leafit.j(data[9], data[10], data[11]);
- j.deleted = true;
- leafit.m(data[12], data[13], data[14]);
- boxjoin();
- internalnodeit.ina(data[3], data[6], nullpicture);
- internalnodeit.inj(data[12], nullpicture, nullpicture);
- internalnodeit.root(data[12], nullpicture, nullpicture);
- root.locked = true;
- ina.s = .5[a.nw,g.ne] + (0,24);
- inj.s = .5[j.nw,m.ne] + (0,24);
- root.s = .5[ina.nw,inj.ne] + (0,24);
- fixelement(a,d,g,j,m);
- fixelement(root,ina,inj);
- leafconnect(a,d,g);
- fill nodecellsbpath(j,0,2) withcolor deletedelement;
- fill leafnextbpath(j) withcolor black;
- g.nextpath = g.next .. (j.nw + (0,3)) .. tension 1.5 .. (j.ne + (0,3)) .. (m.nextin + (0,1));
- j.prevpath = j.prev -- g.previn;
- j.nextpath = j.next -- m.nextin;
- m.prevpath = m.prev .. (j.se - (0,3)) .. tension 1.5 .. (j.sw - (0,3)) .. (g.previn - (0,1));
- drawelement(a,d,g,j,m,ina,inj,root);
- internalnodeconnect(ina,a,d,g);
- internalnodeconnectone(inj,m,1);
- drawterminationarrow(inj.child[0] -- (inj.child[0] - (0,5)));
- internalnodeconnect(root,ina,inj);
-endfig;
-
-beginfig(5);
- % tree with MNO removed
- boxjoin(b.w - a.e = (20,0));
- leafit.a(data[0], data[1], data[2]);
- leafit.d(data[3], data[4], data[5]);
- leafit.g(data[6], data[7], data[8]);
- leafit.j(data[9], data[10], data[11]);
- j.deleted = true;
- leafit.m(data[12], data[13], data[14]);
- m.locked = m.deleted = true;
- boxjoin();
- internalnodeit.ina(data[3], data[6], nullpicture);
- internalnodeit.inj(data[12], nullpicture, nullpicture);
- internalnodeit.root(data[12], nullpicture, nullpicture);
- ina.s = .5[a.nw,g.ne] + (0,24);
- inj.s = .5[j.nw,m.ne] + (0,24);
- root.s = .5[ina.nw,inj.ne] + (0,24);
- fixelement(a,d,g,j,m);
- fixelement(root,ina,inj);
- leafconnect(a,d,g);
- fill nodecellsbpath(m,0,2) withcolor deletedelement;
- fill leafnextbpath(m) withcolor black;
- m.prevpath = m.prev -- g.previn;
- drawelement(a,d,g,m,ina,inj,root);
- internalnodeconnect(ina,a,d,g);
- internalnodeconnectone(inj,m,1);
- drawterminationarrow(inj.child[0] -- (inj.child[0] - (0,5)));
- internalnodeconnect(root,ina,inj);
-endfig;
-
-beginfig(6);
- % tree with MNO removed and internal node removed
- boxjoin(b.w - a.e = (20,0));
- leafit.a(data[0], data[1], data[2]);
- leafit.d(data[3], data[4], data[5]);
- leafit.g(data[6], data[7], data[8]);
- leafit.j(data[9], data[10], data[11]);
- j.deleted = true;
- leafit.m(data[12], data[13], data[14]);
- m.deleted = true;
- boxjoin();
- internalnodeit.ina(data[3], data[6], nullpicture);
- internalnodeit.inj(nullpicture, nullpicture, nullpicture);
- inj.locked = inj.deleted = true;
- internalnodeit.root(data[12], nullpicture, nullpicture);
- ina.s = .5[a.nw,g.ne] + (0,24);
- inj.s = .5[j.nw,m.ne] + (0,24);
- root.s = .5[ina.nw,inj.ne] + (0,24);
- fixelement(a,d,g,j,m);
- fixelement(root,ina,inj);
- leafconnect(a,d,g);
- fill nodecellsbpath(m,0,2) withcolor deletedelement;
- fill leafnextbpath(m) withcolor black;
- m.prevpath = m.prev -- g.previn;
- drawelement(a,d,g,m,ina,inj,root);
- internalnodeconnect(ina,a,d,g);
- drawterminationarrow(inj.child[0] -- (inj.child[0] - (0,5)));
- internalnodeconnect(root,ina,inj);
-endfig;
-
-beginfig(7);
- % tree with MNO removed and internal node removed
- boxjoin(b.w - a.e = (20,0));
- leafit.a(data[0], data[1], data[2]);
- leafit.d(data[3], data[4], data[5]);
- leafit.g(data[6], data[7], data[8]);
- leafit.j(data[9], data[10], data[11]);
- j.deleted = true;
- leafit.m(data[12], data[13], data[14]);
- m.deleted = true;
- boxjoin();
- internalnodeit.ina(data[3], data[6], nullpicture);
- internalnodeit.inj(nullpicture, nullpicture, nullpicture);
- inj.deleted = true;
- internalnodeit.root(nullpicture, nullpicture, nullpicture);
- root.locked = true;
- ina.s = .5[a.nw,g.ne] + (0,24);
- inj.s = .5[j.nw,m.ne] + (0,24);
- root.s = .5[ina.nw,inj.ne] + (0,24);
- fixelement(a,d,g,j,m);
- fixelement(root,ina,inj);
- leafconnect(a,d,g);
- fill nodecellsbpath(m,0,2) withcolor deletedelement;
- fill leafnextbpath(m) withcolor black;
- m.prevpath = m.prev -- g.previn;
- drawelement(a,d,g,m,ina,inj,root);
- internalnodeconnect(ina,a,d,g);
- drawterminationarrow(inj.child[0] -- (inj.child[0] - (0,5)));
- internalnodeconnect(root,ina);
-endfig;
-
-beginfig(8);
- % tree with MNO removed, gclayer begin
- boxjoin(b.w - a.e = (20,0));
- leafit.a(data[0], data[1], data[2]);
- leafit.d(data[3], data[4], data[5]);
- leafit.g(data[6], data[7], data[8]);
- boxjoin();
- leafit.x(dotdata, dotdata, dotdata, dotdata);
- x.nextpath = x.prevpath = emptypath;
- internalnodeit.ina(data[3], data[6], nullpicture);
- internalnodeit.root(nullpicture, nullpicture, nullpicture);
- ina.s = .5[a.nw,g.ne] + (0,24);
- root.sw = ina.n + (0,24);
- .8[x.sw,x.se] = root.n + (0,24);
- fixelement(a,d,g);
- fixelement(root,ina,x);
- leafconnect(a,d,g);
- drawelement(a,d,g,ina,root);
- fillelement(x)(upperlayerfill); drawelement(x) withcolor upperlayer;
- begingroup
- interim linecap := butt;
- draw (x.sw - (5,10)) -- (x.se + (40,-10)) withpen pencircle scaled 5 dashed evenly scaled 3 withcolor upperlayerfill;
- endgroup;
- drawconnarrow x.value[1] {down} .. tension 2 .. {down} root.n withpen connectionpen scaled 2 withcolor upperlayer;
- internalnodeconnect(ina,a,d,g);
- internalnodeconnect(root,ina);
-endfig;
-
-beginfig(9);
- % tree with MNO removed, gclayer begin
- boxjoin(b.w - a.e = (20,0));
- leafit.a(data[0], data[1], data[2]);
- leafit.d(data[3], data[4], data[5]);
- leafit.g(data[6], data[7], data[8]);
- boxjoin();
- leafit.x(dotdata, dotdata, dotdata, dotdata);
- x.locked = true;
- x.nextpath = x.prevpath = emptypath;
- internalnodeit.ina(data[3], data[6], nullpicture);
- internalnodeit.root(nullpicture, nullpicture, nullpicture);
- ina.s = .5[a.nw,g.ne] + (0,24);
- root.sw = ina.n + (0,24);
- .8[x.sw,x.se] = root.n + (0,24);
- fixelement(a,d,g);
- fixelement(root,ina,x);
- leafconnect(a,d,g);
- drawelement(a,d,g,ina,root);
- fillelement(x)(upperlayerfill); drawelement(x) withcolor upperlayer;
- begingroup
- interim linecap := butt;
- draw (x.sw - (5,10)) -- (x.se + (40,-10)) withpen pencircle scaled 5 dashed evenly scaled 3 withcolor upperlayerfill;
- endgroup;
- drawconnarrow x.value[1] {down} .. tension 2 .. {down} root.n withpen connectionpen scaled 2 withcolor upperlayer;
- internalnodeconnect(ina,a,d,g);
- internalnodeconnect(root,ina);
-endfig;
-
-beginfig(11);
- % tree with MNO removed, gclayer begin
- boxjoin(b.w - a.e = (20,0));
- leafit.a(data[0], data[1], data[2]);
- leafit.d(data[3], data[4], data[5]);
- leafit.g(data[6], data[7], data[8]);
- boxjoin();
- leafit.x(dotdata, dotdata, dotdata, dotdata);
- x.locked = true;
- x.nextpath = x.prevpath = emptypath;
- internalnodeit.ina(data[3], data[6], nullpicture);
- internalnodeit.root(nullpicture, nullpicture, nullpicture);
- root.locked = true;
- ina.s = .5[a.nw,g.ne] + (0,24);
- root.sw = ina.n + (0,24);
- .8[x.sw,x.se] = root.n + (0,24);
- fixelement(a,d,g);
- fixelement(root,ina,x);
- leafconnect(a,d,g);
- drawelement(a,d,g,ina,root);
- fillelement(x)(upperlayerfill); drawelement(x) withcolor upperlayer;
- begingroup
- interim linecap := butt;
- draw (x.sw - (5,10)) -- (x.se + (40,-10)) withpen pencircle scaled 5 dashed evenly scaled 3 withcolor upperlayerfill;
- endgroup;
- drawconnarrow x.value[1] {down} .. tension 2 .. {down} root.n withpen connectionpen scaled 2 withcolor upperlayer;
- internalnodeconnect(ina,a,d,g);
- internalnodeconnect(root,ina);
-endfig;
-
-beginfig(12);
- % tree with MNO removed, gclayer begin
- boxjoin(b.w - a.e = (20,0));
- leafit.a(data[0], data[1], data[2]);
- leafit.d(data[3], data[4], data[5]);
- leafit.g(data[6], data[7], data[8]);
- boxjoin();
- leafit.x(dotdata, dotdata, dotdata, dotdata);
- x.locked = true;
- x.nextpath = x.prevpath = emptypath;
- internalnodeit.ina(data[3], data[6], nullpicture);
- internalnodeit.root(nullpicture, nullpicture, nullpicture);
- root.locked = true;
- ina.s = .5[a.nw,g.ne] + (0,24);
- root.sw = ina.n + (0,24);
- .8[x.sw,x.se] = root.n + (0,24);
- fixelement(a,d,g);
- fixelement(root,ina,x);
- leafconnect(a,d,g);
- drawelement(a,d,g,ina,root);
- fillelement(x)(upperlayerfill); drawelement(x) withcolor upperlayer;
- begingroup
- interim linecap := butt;
- draw (x.sw - (5,10)) -- (x.se + (40,-10)) withpen pencircle scaled 5 dashed evenly scaled 3 withcolor upperlayerfill;
- endgroup;
- drawconnarrow x.value[1] {down} .. tension 2 .. {down} (ina.n - (4,0)) withpen connectionpen scaled 2 withcolor upperlayer;
- internalnodeconnect(ina,a,d,g);
- internalnodeconnect(root,ina);
-endfig;
-
-beginfig(15);
- % tree with MNO removed, gclayer begin
- leafit.a(nullpicture, nullpicture, nullpicture);
- boxjoin();
- leafit.x(dotdata, dotdata, dotdata, dotdata);
- x.locked = true;
- x.nextpath = x.prevpath = emptypath;
- .5[x.sw,x.se] = a.n + (0,24);
- fixelement(a,x);
- leafconnect(a);
- drawelement(a,x);
- fillelement(x)(upperlayerfill); drawelement(x) withcolor upperlayer;
- begingroup
- interim linecap := butt;
- draw (x.sw - (5,10)) -- (x.se + (40,-10)) withpen pencircle scaled 5 dashed evenly scaled 3 withcolor upperlayerfill;
- endgroup;
- drawconnarrow x.value[1] {down} .. tension 2 .. {down} a.n withpen connectionpen scaled 2 withcolor upperlayer;
-endfig;
-
-beginfig(16);
- % tree with MNO removed, gclayer begin
- leafit.a(nullpicture, nullpicture, nullpicture);
- a.locked = true;
- boxjoin();
- leafit.x(dotdata, dotdata, dotdata, dotdata);
- x.locked = true;
- x.nextpath = x.prevpath = emptypath;
- .5[x.sw,x.se] = a.n + (0,24);
- fixelement(a,x);
- leafconnect(a);
- drawelement(a,x);
- fillelement(x)(upperlayerfill); drawelement(x) withcolor upperlayer;
- begingroup
- interim linecap := butt;
- draw (x.sw - (5,10)) -- (x.se + (40,-10)) withpen pencircle scaled 5 dashed evenly scaled 3 withcolor upperlayerfill;
- endgroup;
- drawconnarrow x.value[1] {down} .. tension 2 .. {down} a.n withpen connectionpen scaled 2 withcolor upperlayer;
-endfig;
-
-end
diff --git a/doc/spec.tex b/doc/spec.tex
deleted file mode 100644
index 2d57053..0000000
--- a/doc/spec.tex
+++ /dev/null
@@ -1,1397 +0,0 @@
-% -*- tex-command: "pdflatex" -*-
-\documentclass[11pt]{article}
-\usepackage{times,mathptmx,amsmath,amssymb,graphicx,float,titling}
-\usepackage[tmargin=1in,hmargin=.75in,height=9in,columnsep=2pc,letterpaper]{geometry}
-\usepackage[T1]{fontenc}
-\makeatletter
-\newcommand{\Keyset}{\ensuremath{\mathcal K}}
-\newcommand{\Valueset}{\ensuremath{\mathcal V}}
-\newcommand{\None}{\textsc{None}}
-\newcommand{\NONE}{\textsc{none}}
-\newcommand{\FALSE}{\textsc{false}}
-\newcommand{\TRUE}{\textsc{true}}
-\newcommand{\Bool}{\textsc{Bool}}
-\newcommand{\Nat}{\ensuremath{\mathbb N}}
-\newcommand{\sysGet}{\textit{Get}}
-\newcommand{\sysget}{\textit{get}}
-\newcommand{\sysgets}{\textit{get\/}s}
-\newcommand{\sysPut}{\textit{Put}}
-\newcommand{\sysput}{\textit{put}}
-\newcommand{\sysputs}{\textit{put\/}s}
-\newcommand{\sysScan}{\textit{Scan}}
-\newcommand{\sysscan}{\textit{scan}}
-\newcommand{\Inv}[1]{\ensuremath{\textrm{inv}(#1)}}
-\newcommand{\Res}[1]{\ensuremath{\textrm{res}(#1)}}
-\newcommand{\Precedes}{<}
-\newcommand{\NotPrecedes}{\not <}
-\newcommand{\Overlaps}{\approx}
-\newcommand{\PrecedesK}[1]{\ensuremath{\mathrel{<_{#1}}}}
-\newcommand{\PrecedesEqK}[1]{\ensuremath{\mathrel{\leq_{#1}}}}
-\newcommand{\V}[1]{\textit{#1}}
-\newcommand{\N}[1]{\text{\upshape{#1}}}
-\newcommand{\NIL}{\textsc{nil}}
-\newcommand{\NOTFOUND}{\textsc{notfound}}
-\newcommand{\VALUE}{\textsc{value}}
-\newcommand{\LAYER}{\textsc{layer}}
-\newcommand{\MARK}{\textsc{mark}}
-\newcommand{\INSERTING}{\textsc{inserting}}
-\newcommand{\REMOVING}{\textsc{removing}}
-\newcommand{\readnow}[1]{\ensuremath{{}^\circ#1}}
-\newcommand{\fencebase}{\text{- - - - - - - - -}}
-\newcommand{\rcufencebase}{\text{* * * * * * * * *}}
-\newcommand{\fence}{\ensuremath{\fencebase}}
-%\newcommand{\acquirefence}{\ensuremath{\fencebase_{\textsc{acquire}}}}
-%\newcommand{\releasefence}{\ensuremath{\fencebase_{\textsc{release}}}}
-\let\acquirefence\fence
-\let\releasefence\fence
-\newcommand{\rcufence}{\ensuremath{\rcufencebase_{\textsc{rcu
- fence}}}}
-\newcommand{\substring}[3]{\ensuremath{#1[#2\mathbin:#3]}}
-\newcommand{\Returns}[1]{\ensuremath{\rightarrow\text{#1}}}
-\newcommand{\Bplus}{B\(^+\)}
-\newcommand{\Blink}{B\(^{\text{link}}\)}
-\newcommand{\LEAFWIDTH}{\ensuremath{W_\text{leaf}}}
-\newcommand{\INTERIORWIDTH}{\ensuremath{W_\text{interior}}}
-\newcommand{\CX}[1]{\small{\textit{// #1}}}
-\newcommand{\C}[1]{\unskip\qquad\CX{#1}}
-\newcommand{\ITEM}[1]{\textsc{#1}}
-\newcommand{\AT}[2]{\ensuremath{#1_{@#2}}}
-\newenvironment{programtabbing}%
- {\begingroup\parskip\z@\begin{tabbing}\quad\qquad~~\= \quad~~\= \quad~~\= \quad~~\=\kill}%
- {\unskip\end{tabbing}\endgroup}
-\gdef\programtabskip{\leavevmode\raise1.5ex\hbox{\strut}}
-\gdef\_{\char`\_}
-\frenchspacing
-\makeatother
-\pretitle{\begin{raggedleft}\LARGE\fontseries{bx}\selectfont}
-\posttitle{\end{raggedleft}}
-\title{Masstree specification}
-\date{}
-\begin{document}
-
-\maketitle
-
-\section*{Note}
-
-As of 2019, this specification is not fully up to date relative to the
-Masstree code, particularly around remove. The code is more correct.
-
-\section{Definitions}
-
-A Masstree maps \emph{keys} to \emph{values}. Keys are variable-length strings. Values are arbitrary.
-Let \Keyset\ be the set of keys and \Valueset\ the set of values.
-
-Masstree's operations are \sysput, \sysget, and \sysscan.
-
-\begin{itemize}
-\item \(\sysput : \Keyset \times \Valueset \mapsto \None\)
-\item \(\sysget : \Keyset \mapsto (\Valueset \cup \None)\)
-\item \(\sysscan : \Keyset \times (\Keyset \times \Valueset \mapsto \Bool) \mapsto \None\)
-\end{itemize}
-
-\noindent%
-\sysPut\
-stores a value at a given key. \sysGet\ returns the value for a given
-key (or \NONE\ if there is no value). \sysScan\ fetches multiple keys
-and values. It takes an initial key $k_0$ and a function object $f$.
-The function object is called for every key $k \geq k_0$ in the
-Masstree, once per key, in increasing order by key. Its arguments are
-a key and the corresponding value. We expect the scan function will
-save copies of the keys and values in the object, or count them, or
-whatever is appropriate, and that scan's caller will extract those
-values once the scan returns. The scan stops when the Masstree
-runs out of values or the function object returns false, whichever
-happens first.
-
-Masstree can support other operations, such as remove and a compare-and-swap-style atomic put, but these three suffice to discuss correctness.
-
-
-\section{Correctness}
-
-Masstree is a concurrent data structure. This means that multiple threads access Masstree simultaneously, complicating notions of correctness. Roughly speaking, Masstree is correct if every \sysget\ returns the most recent \sysput\ for that key, but when \sysgets\ and \sysputs\ overlap this gets a little complicated.
-
-Multiple \emph{threads} submit \emph{invocations} to Masstree and
-receive \emph{responses}. An example invocation is \(\sysput(k,v)\): put
-value $v$ at key $k$. The pair of an invocation and its response is
-called an \emph{action}. An invocation or response is
-called a \emph{half-action}.
-
-We assume a \sysget\ or \sysput\ response contains \emph{the
-relevant \sysput\ action}. For \sysget, this is the \sysput\ action that
-stored the corresponding value (or \NONE\ if there was no previous
-\sysput\ for that key). For \sysput, this is the immediately preceding
-\sysput\ action for that key. This differs from the methods' normal
-signatures, but simplifies the model. The response to a \sysscan\ action
-is a \emph{set} of \sysput\ actions. Here's an example sequence of
-actions:
-
-\begin{flushleft}
-\begin{tabular}{@{}rll}
-& Invocation & Response \\
-$a_1$ & $\sysget(k)$ & \NONE \\
-$a_2$ & $\sysput(k, v_2)$ & \NONE \\
-$a_3$ & $\sysput(k, v_3)$ & $a_2$ \\
-$a_4$ & $\sysget(k)$ & $a_3$ \\
-$a_5$ & $\sysput(k+1, v_5)$ & \NONE \\
-$a_6$ & $\sysscan(k, \dots)$ & $\{a_3,a_5\}$ \\
-\end{tabular}
-\end{flushleft}
-
-We assume that all half-actions are sequenced into a total order, which
-we might call global time. In this time order, every invocation precedes
-its corresponding response---$\Inv{a} < \Res{a}$ for all
-$a$---though other half-actions may intervene between them.
-
-The total order on half-actions imposes a \emph{partial} order on
-actions. Given actions $a_1$ and $a_2$, we say that $a_1$
-\emph{precedes} $a_2$, written $a_1 \Precedes a_2$, if $a_1$'s
-response is before $a_2$'s invocation in the global time order:
-$\Res{a_1} < \Inv{a_2}$. We say that $a_1$ \emph{overlaps} $a_2$,
-written $a_1 \Overlaps a_2$, if $a_1 \NotPrecedes a_2$ and $a_2
-\NotPrecedes a_1$.
-
-Masstree is correct if there exist per-key total orders
-\(\PrecedesK{k}\), where \(\PrecedesK{k}\) for key $k$ contains the set
-of \sysput\ actions on $k$, so that the following properties are
-satisfied.
-
-\begin{enumerate}
-\item Every \sysput\ action on $k$ is in \(\PrecedesK{k}\).
-\item The \(\PrecedesK{k}\) order agrees with the partial order on actions. That is, if $a_1 \Precedes a_2$ and $a_1$ and $a_2$ are \sysput\ actions on $k$, then $a_1 \PrecedesK{k} a_2$.
-\item The \(\PrecedesK{k}\) order agrees with \sysput\ responses. That is, assuming $\mathord{\PrecedesK{k}} = [a_0, a_1, a_2, \dots]$, then $\Res{a_0} = \NONE$, $\Res{a_1} = a_0$, $\Res{a_2} = a_1$, and so forth.
-\item Given \(a\), a \sysget\ action on $k$ with response \NONE, no
- \(\sysput(k, \cdot)\) actions precede \(a\) in the partial order on actions.
-\item Given \(a\), a \sysget\ action on $k$ with response $b \neq \NONE$, $b$ is a \sysput\ on $k$ that either overlaps with $a$ or immediately precedes $a$ in $\PrecedesK{k}$. That is, $\Res{a} \NotPrecedes \Inv{b}$, and there is no $x$ with $b \PrecedesK{k} x \Precedes a$.
-\item Given \(a_1 \Precedes a_2\), two \sysget\ actions on the same $k$ that originate \emph{from the same thread}, we must have \(\Res{a_1} \PrecedesEqK{k} \Res{a_2}\). That is, no thread sees ``time'' go backwards.
-\item Given \(a\), a \sysscan\ action starting from $k$, a corresponding set of \sysget\ operations, returning the same results as the \sysscan, would also follow these rules. (As an example, if \(b = \sysput(k, \cdot) \in \Res{a}\), then $b$ must be a \sysput\ on $k$ that either overlaps with $a$ or immediately precedes $a$ in $\PrecedesK{k}$. A key $x$ in the scan range with no corresponding response is like a \sysget\ action that returned \NONE: no \(\sysput(x, \cdot)\) actions can precede \(a\).)
-\end{enumerate}
-
-
-\section{Data structures}
-
-Here's a picture of a Masstree layer.
-
-\begin{figure}[H]
-\includegraphics[scale=.8]{examples-1}
-\caption{Example Masstree layer.}
-\end{figure}
-
-\noindent
-Leaves are on the bottom row, interior nodes above. The letters are
-keys.
-
-Leaves are connected in a doubly-linked list (dotted arrows); this
-facilitates scans and reverse scans and speeds up some other operations.
-(See \Blink\ trees.) A leaf \(n\) contains \(n.\V{size}\) keys and
-values, where \(0 \leq n.\V{size} \leq \LEAFWIDTH = 15\).
-
-A leaf \(n\) is responsible for all keys \(k\) in the range
-\(\N{lowkey}(n) \leq k < \N{highkey}(n)\). Each leaf stores its lowkey
-explicitly, and that lowkey \emph{never changes}. The highkey is not
-stored in the leaf---it equals the \emph{next} leaf's lowkey---and can
-change over time. If leaf \(n\) splits, then \(\N{highkey}(n)\)
-decreases; if the leaf following \(n\) is deleted, then
-\(\N{highkey}(n)\) increases to cover the deleted leaf's key range. The
-layer's leftmost leaf has lowkey \(-\infty\).
-
-Interior nodes form a B$^+$ tree for fast access to data stored in
-leaves. Interior nodes are \emph{not} linked
-together.\footnote{\Blink\ trees also connect interior nodes in linked
-lists, but in our tests this didn't speed anything up, and it would
-complicate remove.}
-
-An interior node \(n\) contains \(n.\V{size}\) keys, where \(0 \leq
-n.\V{size} \leq \INTERIORWIDTH = 15\). A node with \(n.\V{size}\) keys
-contains exactly \(n.\V{size} + 1\) child pointers. Keys are stored in a
-\(n.\V{kslice}\) array (see \S\ref{s:keys} for more on key slices).
-Values for key \(k\) are found in one of the underlying children:
-%
-\[
-\text{The child containing \(k\)} =
-\begin{cases}
-n.\V{child}[0] & \text{if \(k < n.\V{kslice}[0]\),} \\
-n.\V{child}[1] & \text{if \(n.\V{kslice}[0] \leq k < n.\V{kslice}[1]\),} \\
-\vdots \\
-n.\V{child}[n.\V{size}] & \text{if \(k \geq n.\V{kslice}[n.\V{size}-1]\).}
-\end{cases}
-\]
-%
-If \(n.\V{size} = 0\), then all values are found in \(n.\V{child}[0]\).
-
-Although each interior node is responsible for a range of keys, this
-range (unlike with leaves) is \emph{implicit}, depending on keys higher
-in the tree. For example, in the figure, interior node (1) is
-responsible for the key range \([\textsc{j}, \infty)\), but bounds
-\textsc{j} and \(\infty\) are implicit from context: if the root node
-contained different keys, (1) might be responsible for
-\([\textsc{e},\textsc{q})\) or some other range. This means that we
-can't always tell from local information whether a key belongs in an
-interior node. This has consequences for the remove operation, as we
-will see.
-
-
-\section{Keys}
-\label{s:keys}
-
-Masstree \emph{keys} are variable-length strings ordered
-lexicographically.
-%
-Masstree divides these strings into eight-byte \emph{slices}; the
-B-tree structures that make up a Masstree store these slices as keys.
-Slice comparison produces the same result as lexicographic comparison
-of the underlying keys.
-
-Masstree code uses objects of type ``key.''
-%
-A key is basically a string decomposed into three parts, a prefix, a
-slice, and a suffix.
-%
-The \N{shift\_key} function, which is called when walking from one
-layer of Masstree to the next, shifts the current slice into the
-prefix, and shifts the next eight bytes of the suffix into the slice.
-
-If \(s\) is a string, let \(\substring{s}{a}{b}\) represent the
-substring of \(s\) starting at index \(a\) and ending just before
-index \(b\).
-%
-Thus, \(\substring{s}{0}{s.\V{length}} = s\), and
-\(\substring{s}{i}{i}\) is empty for any \(i\).
-%
-The substring operation constrains \(a\) and \(b\) to the appropriate
-bounds:
-%
-\[ \substring{s}{a}{b} =
-\substring{s}{\max(a,0)}{\min(b,s.\V{length})}. \]
-
-Then the key type's methods are as follows.
-
-\begin{flushleft}
-\begin{tabular}{@{}p{.5\hsize}@{}p{.5\hsize}@{}}
-\begin{programtabbing}
-\textbf{class key}: \\
-\> representation: string \(s\), int \(p\) \\
-\programtabskip
-\> constructor(string \(s\)): \\
-\>\> return \(\N{key}\{s = s, p = 0\}\) \\
-\programtabskip
-\> slice(key \(k\)) \Returns{string}: \\
-\>\> return \(\substring{k.s}{k.p}{k.p+8}\) \\
-\programtabskip
-\> suffix(key \(k\)) \Returns{string}: \\
-\>\> return \(\substring{k.s}{k.p+8}{k.s.\V{length}}\) \\
-\programtabskip
-\> length(key \(k\)) \Returns{int}: \\
-\>\> return \(k.s.\V{length} - k.p\)
-\end{programtabbing}
-&
-\begin{programtabbing}
-\\
-\\
-\programtabskip
-\> prefix(key \(k\)) \Returns{string}: \\
-\>\> return \(\substring{k.s}{0}{k.p}\) \\
-\programtabskip
-\> shift\_key(key \(k\)) \Returns{key}: \\
-\>\> // precondition: \(k.\V{length} > 8\) \\
-\>\> return \(\N{key}\{s = k.s, p = k.p + 8\}\) \\
-\programtabskip
-\> reset\_key(key \(k\)) \Returns{key}: \\
-\>\> // undoes all prior shift\_keys \\
-\>\> return \(\N{key}\{s = k.s, p = 0\}\)
-\end{programtabbing}
-\end{tabular}
-\end{flushleft}
-
-Masstree leaves store key slices, key lengths, and key suffixes. These
-are stored in separate arrays: the key at position \(\pi\) has slice
-\(n.\V{kslice}[\pi]\), length \(n.\V{klength}[\pi]\), and suffix
-\(n.\V{ksuffix}[\pi]\). (It is important to store slices and lengths
-inline, in the node, to allow prefetching. Suffixes, which can have
-arbitrary length, must be stored out of line.) Multiple keys with the
-same slice and \emph{different} suffixes are stored in a new layer.
-
-Interior nodes, unlike leaves, store \emph{only} key slices. (This
-speeds up the code, simplifies suffix memory management, and reduces
-memory usage.) As a consequence, all keys with slice \(k\) \emph{must}
-be stored in the same leaf. This choice constrains split.
-
-
-\section{Memory model}
-
-We write memory fences as rows of dashes\ \fence; on x86 (TSO) machines,
-these fences only affect the compiler's code generation---no machine
-instructions are required.
-
-We assume that, except as constrained by fences, the compiler may
-arbitrarily cache and reorder reads from and writes to node data. For
-example, the following code might read \(n.\V{version}\) once or twice,
-and might write \(n.\V{kslice}[0]\) once or twice:
-
-\begin{programtabbing}
-\(a \gets n.\V{version}\) \\
-\(++n.\V{kslice}[0]\) \\
-\(b \gets n.\V{version}\) \\
-\(n.\V{kslice}[0] \gets 0\)
-\end{programtabbing}
-
-\noindent
-However, this code \emph{must} read \(n.\V{version}\) twice, and write
-\(n.\V{kslice}[0]\) twice:
-
-\begin{programtabbing}
-\(a \gets n.\V{version}\) \\
-\(++n.\V{kslice}[0]\) \\
-\fence \\
-\(b \gets n.\V{version}\) \\
-\(n.\V{kslice}[0] \gets 0\)
-\end{programtabbing}
-
-We occasionally use a superscript O notation, as in
-\(\readnow{n.\V{version}}\), to indicate that a value is
-read from main memory at that point (i.e., no previously cached
-value allowed). Some unmarked reads must also complete from main
-memory, specifically those after fences.
-
-All reads and writes of individual variables are assumed to be atomic.
-We also assume one atomic compare-and-swap operation, cmpxchg, which has
-the following effect (but in one atomic step):
-
-\begin{programtabbing}
-\textbf{cmpxchg}(address \V{value}, value \V{expected}, value
-\V{desired}) \Returns{bool}: \\
-\> if \(\readnow{\V{value}} = \V{expected}\): \\
-\>\> \(\V{value} \gets \V{desired}\);~ return \TRUE \\
-\> else: \\
-\>\> return \FALSE
-\end{programtabbing}
-
-\section{Versions and locking}
-
-Each node has a 32- or 64-bit \emph{version}.\footnote{64-bit versions
-offer greater protection against overflow. A 32-bit version could wrap,
-causing unexpected reader behavior, if a reader blocked mid-computation
-for \(2^{22}\) inserts.} The version is located at the same place in
-leaf and interior nodes---it's the only common aspect of layout between
-node types. Versions can be manipulated with compare-and-swap operations
-and can be assigned atomically. They have the following fields:
-
-\begin{flushleft}
-\begin{tabular}{@{}lll}
-\V{locked} & 1 bit & whether node is locked \\
-\V{inserting} & 1 bit & whether node has been dirtied for insertion \\
-\V{splitting} & 1 bit & whether node has been dirtied for splitting \\
-&& Together, \V{inserting} and \V{splitting} are the \emph{dirty bits}. \\
-\V{vinsert} & 6 or 16 bits & insert version counter \\
-\V{vsplit} & 19 or 33 bits & split version counter \\
-&& \V{vinsert} overflows into \V{vsplit}; \V{vsplit} is a circular
-counter. \\
-\V{deleted} & 1 bit & whether node has been deleted \\
-\V{isroot} & 1 bit & whether node is root of its layer \\
-\V{isleaf} & 1 bit & whether node is a leaf \\
-\end{tabular}
-\end{flushleft}
-
-The dirty bits alert readers of potentially unstable states that require
-retry. Masstree updates generally involve multiple writes that should be
-considered atomic (for example, inserting a new child into an interior
-node, or updating the various parts of a key in a leaf). In some cases
-Masstree can arrange to ``publish'' an update atomically,
-but this is not always possible. Thus,
-before changing a node in a dangerous way, Masstree writers must mark
-the node as dirty by changing its version. Masstree readers execute
-\emph{read transactions} that are \emph{validated} using versions. First
-they snapshot the node's version, then they snapshot the relevant
-portion of its contents, and finally they compare the node's new version
-to its snapshot. If the versions differ in more than the \V{locked}
-bit (that is, if \(v \oplus \readnow{n.\V{version}} > \V{locked}\)),
-then the node changed in some potentially invalidating way during the
-read operation, and the reader must retry the read transaction.
-
-Masstree's lock and unlock functions are as follows. Note that they
-contain fences.
-
-\begin{figure}[H]
-\begin{programtabbing}
-\textbf{lock}(node $n$): \\
-\> $v \gets n.\V{version}$ \\
-\> while $v.\V{locked}$ or $!\text{cmpxchg}(n.\V{version}, v, v + \V{locked})$: \\
-\>\> $v \gets \readnow{n.\V{version}}$ \\
-\> \acquirefence \\
-\programtabskip
-%
-\textbf{unlock}(node $n$): \C{precondition: \(n\) is locked} \\
-\> $v \gets n.\V{version}$ \\
-\> if $v.\V{inserting}$: \\
-\>\> $v.\V{vinsert} \gets v.\V{vinsert} + 1$ \C{may overflow into \V{vsplit}} \\
-\> else if $v.\V{splitting}$: \\
-\>\> $v.\V{vsplit} \gets v.\V{vsplit} + 1$;~ $v.\V{isroot} \gets 0$ \C{the true root has never split} \\
-\> $v.\{\V{locked},\V{inserting},\V{splitting}\} \gets 0$ \\
-\> \releasefence \\
-\> $n.\V{version} \gets v$
-\end{programtabbing}
-\end{figure}
-
-A read transaction must first wait for its node to reach a stable
-state, so read transactions start with \N{stable\_version}, a form of
-``lock'' that doesn't actually involve locking.
-
-\begin{figure}[H]
-\begin{programtabbing}
-\textbf{stable\_version}(node $n$): \\
-\> $v \gets {n.\V{version}}$ \\
-\> while $v.\V{inserting}$ or $v.\V{splitting}$: \\
-\>\> $v \gets \readnow{n.\V{version}}$ \\
-\> \acquirefence \\
-\> return $v$
-\end{programtabbing}
-\end{figure}
-
-Nodes contain \V{parent} pointers for traversing up the tree. The
-\N{locked\_parent} and \N{true\_root} functions traverse these pointers.
-\N{locked\_parent} must retry because a node's parent can spontaneously
-change, even while the node is locked, if its parent interior node
-splits. The \N{true\_root} function traverses parent pointers until it
-finds the root of a tree.
-
-\begin{figure}[H]
-\begin{programtabbing}
-\textbf{locked\_parent}(node $n$): \C{precondition: \(n\) is locked} \\
-retry:~
-\> $p \gets n.\V{parent}$ \\
-\> if $p \neq \NIL$: \\
-\>\> lock($p$) \\
-\>\> if $p \neq \readnow{n.\V{parent}}$: \C{parent changed underneath us} \\
-\>\>\> unlock($p$);~ goto retry \\
-\> return $p$ \\
-\programtabskip
-%
-\textbf{true\_root}(node $n$): \\
-\> \(p \gets n.\V{parent}\) \\
-\> while \(p \neq \NIL\): \\
-\>\> \(n \gets p\);~ \(p \gets n.\V{parent}\) \\
-\> return \(n\)
-\end{programtabbing}
-\end{figure}
-
-
-\section{Invariants}
-
-We write \(\AT{x}{t}\) for the value of \(x\) at time \(t\).
-Times \(t\) and \(t'\) are \emph{RCU-overlapping} if a
-single reader could be active over some interval including both \(t\)
-and \(t'\).
-
-\paragraph{Root reachability}
-
-Assume that at time \(t\), the true root of a layer is node \(n\), and
-\(n\) is unlocked. Let \(x\) be the root of that layer as observed by
-a concurrent reader active at time \(t\). Then either \(x=n\), or
-\(x.\V{isroot} = \FALSE\) and \(n\) is reachable from \(x\) by a chain
-of \V{parent} pointers.
-
-\paragraph{Parent validity}
-
-If \(n\) is reachable, then \(n.\V{parent}\) is either another
-reachable node or \NIL.
-
-\paragraph{Key stability}
-
-Let \(n\) be a reachable leaf, and consider two times \(t<t'\) where
-\(n.\V{version}\) is the same at both times, and not dirty. Consider a
-slot \(\pi\) reachable via \(\AT{n.\V{permutation}}{t}\). Then
-slot \(\pi\) is also reachable via \(\AT{n.\V{permutation}}{t'}\), and
-all key information in slot \(\pi\) is unchanged from the corresponding values at time
-\(t\). (That is, \(\AT{n.\V{kslice}[\pi]}{t} =
-\AT{n.\V{kslice}[\pi]}{t'}\), \(\AT{n.\V{ksuffix}[\pi]}{t} =
-\AT{n.\V{ksuffix}[\pi]}{t'}\), and \(\AT{n.\V{klength}[\pi]}{t} =
-\AT{n.\V{klength}[\pi]}{t'}\).)
-
-\paragraph{Interior node validity}
-
-Assume that \(n\) is a reachable interior node with
-\(\AT{n.\V{size}}{t} = s\). Let \(t'>t\) be an RCU-overlapping time with
-\(t\), and let \(x = \AT{n.\V{child}[i]}{t'}\) for some \(i\) with \(0
-\leq i \leq s\). Then either \(x = \NIL\), or \(x\) is a pointer to a
-valid (though not necessarily reachable) node.
-
-\paragraph{Cleanliness}
-
-If node \(n\) is dirty at time \(t\), then \(n\) is locked at time \(t\).
-
-\paragraph{Locking discipline}
-
-If a Masstree interface function (such as get, put, or remove) locks a
-node, then it unlocks that node before returning to the user.
-
-Node locks are obtained sequentially \emph{down} from layer to layer, but
-within a layer, they are obtained \emph{up} from leaves toward the root.
-
-\paragraph{Leaf linkage}
-
-At all times, all of a tree's active nodes are reachable from the
-leftmost leaf by \V{next} pointers.
-
-Say that \(n\) and \(n'\) are active nodes in the same tree, with
-\(\N{lowkey}(n) < \N{lowkey}(n')\). Then either \(n\) is reachable from
-\(n'\) by \V{prev} pointers, or \(n\) is dirty (\V{splitting}).
-
-\section{Transaction types}
-
-Leaf values are accessed in read transactions validated by version.
-
-Node modifications use transactions protected by locks.
-
-Internal nodes are accessed during descent using hand-over-hand
-validation by version.
-
-Obtaining a locked pointer to a parent node requires hand-over-hand
-locking with validation.
-
-\section{Get}
-
-Get first descends to the leaf responsible for a key,
-then finds the key in that leaf. This proceeds a layer at a time.
-
-Two related procedures, \N{descend} and \N{advance}, find within a layer
-the leaf node responsible for key $k$. \N{Descend} is more fundamental:
-every lookup involves one or more calls to \N{descend}. \N{Advance} is
-used occasionally to fix lookups that happened concurrently with splits.
-
-Both procedures return a leaf node $n$ and corresponding version $v$.
-The version is never dirty (it was obtained by \N{stable\_version}).
-Either version $v$ of leaf $n$ is responsible for $k$, or
-$v.\V{deleted}$ is true.
-However, neither \N{descend} nor \N{advance} lock nodes or perform any
-writes to shared memory, so $n$ may not \emph{remain} responsible for
-$k$. $n$ could split, moving responsibility for $k$ to the right; or $n$
-could be deleted, moving responsibility for $k$ to the left. Users of
-\N{descend} must detect these situations using read transactions
-validated by a check of $n.\V{version}$. If $n.\V{version}$ indicates a
-split, the user should call \N{advance} to find the leaf truly
-responsible for $k$; if $n.\V{version}$ indicates a deletion, the user
-must call \N{descend} again.
-
-\begin{figure}[H]
-\begin{programtabbing}
-\textbf{descend}(node \V{root}, key $k$) \Returns{\(\langle
- \N{leaf \V{n}, version \V{v}}\rangle\)}: \\
-\CX{postcondition: either \(v.\V{deleted}\), or version \V{v} of leaf
-\V{n} is responsible for key $k$: \(\N{lowkey}(n) \leq k.\V{slice} <
-\N{highkey}(n_{@v})\)} \\
-retry:
-\> $n \gets \V{root}$;~ $v \gets \text{stable\_version}(n)$ \\
-\> if \(!v.\V{isroot}\): \\
-\>\> \(n \gets \text{true\_root}(n)\);~ goto retry \\
-descend:~
-if $v.\V{isleaf}$: \\
-\>\> return $\langle n, v\rangle$ \\
-\> $n_c \gets \text{child of \(n\) containing \(k\)}$ \\
-\> if $n_c = \NIL$: \C{retry from root if concurrent remove reshaped tree} \\
-\>\> goto retry \\
-\> $v_c \gets \text{stable\_version}(n_c)$ \C{hand-over-hand validation} \\
-\> \fence \\
-\> if $v \oplus \readnow{n.\V{version}} > \V{locked}$: \C{$n$ changed, must retry} \\
-\>\> $\V{oldv} \gets v$;~ $v \gets \text{stable\_version}(n)$ \\
-\>\> if $v.\V{vsplit} \neq \V{oldv}.\V{vsplit}$: \C{retry from root when internal node splits} \\
-\>\>\> goto retry \\
-\> else: \\
-\>\> $n \gets n_c$;~ $v \gets v_c$ \\
-\> goto descend
-\end{programtabbing}
-\label{fig:nlookup}
-\end{figure}
-
-\begin{figure}[H]
-\begin{programtabbing}
-\textbf{advance}(leaf $n$, key $k$) \Returns{\(\langle
- \N{leaf $n'$, version $v$}\rangle\)}: \\
-\CX{precondition: \(\N{lowkey}(n) \leq k.\V{slice}\)} \\
-\CX{postcondition: either $v.\V{deleted}$ (caller should rerun \N{descend}), or version $v$ of leaf $n'$ is responsible for $k$} \\
-\> $v \gets \text{stable\_version}(n)$;~ $\V{next} \gets n.\V{next}$ \\
-\> while $!v.\V{deleted}$ and $\V{next} \neq \NIL$ and
- $k \geq \text{lowkey}(\V{next})$: \\
-\>\> $n \gets \V{next}$;~ $v \gets \text{stable\_version}(n)$;~
- $\V{next} \gets n.\V{next}$ \\
-\> return $\langle n, v \rangle$
-\end{programtabbing}
-\end{figure}
-
-
-\begin{figure}[H]
-\begin{programtabbing}
-\textbf{find\_key}(leaf $n$, key $k$, permutation \V{perm})
-\Returns{\(\langle \N{int \V{i}}, \N{int \(\pi\)} \rangle\)}: \\
-\CX{Returns the insertion index and position of \(k\) in \(n\),
- considering only key slice and length.} \\
-\CX{postcondition: \(0 \leq i \leq \V{perm}.\V{size}\)} \\
-\CX{postcondition: \(\pi = \NONE\) iff \(k\) is not contained in \(n\)} \\
-\> $i \gets 0$;~ $\V{klength} = \min(k.\V{length}, 9)$ \\
-\> while $i < \V{perm}.\V{size}$: \C{linear search; we use binary search in practice} \\
-\>\> $\pi \gets \V{perm}.\V{pos}[i]$ \\
-\>\> if $n.\V{kslice}[\pi] < k.\V{slice}$ or ($n.\V{kslice}[\pi] =
-k.\V{slice}$ and $n.\V{klength}[\pi] < \V{klength}$): \\
-\>\>\> $i \gets i + 1$ \\
-\>\> else if $n.\V{kslice}[\pi] = k.\V{slice}$ and $n.\V{klength}[\pi] =
-\V{klength}$: \\
-\>\>\> return $\langle i, \pi \rangle$ \\
-\>\> else: \\
-\>\>\> break \\
-\> return $\langle i, \NONE \rangle$
-\end{programtabbing}
-\end{figure}
-
-\begin{figure}[H]
-\begin{programtabbing}
-\textbf{get}(node \V{root}, key $k$): \\
-descend:~
- $\langle n, v \rangle \gets \text{descend}(\V{root}, k)$ \\
-retry:~
-\> if $v.\V{deleted}$: \\
-\>\> goto descend \\
-\> \(\langle i, \pi \rangle \gets \text{find\_key}(n, k,
-n.\V{permutation})\) \\
-\> if \(\pi \neq \NONE\): \\
-\>\> \(t \gets n.\V{type}[\pi]\);~
- \(\V{suffix} \gets n.\V{ksuffix}[\pi]\);~
- \(\V{value} \gets n.\V{value}[\pi]\) \\
-\> \fence \\
-\> if \(v \oplus \readnow{n.\V{version}} > \V{locked}\): \\
-\>\> $\langle n, v \rangle \gets \text{advance}(n, k)$ \\
-\>\> goto retry \\
-\> else if \(\pi \neq \NONE\) and \(t = \LAYER\): \\
-\>\> \(root \gets \V{value}\);~ \(k \gets \N{shift\_key}(k)\) \\
-\>\> goto descend \\
-\> else if \(\pi = \NONE\) or \(\V{suffix} \neq k.\V{suffix}\): \\
-\>\> return \NONE \\
-\> else: \\
-\>\> return \V{value}
-\end{programtabbing}
-\caption{Find the value for a key.}
-\label{fig:nget}
-\end{figure}
-
-
-\section{Put}
-
-Put and remove, unlike get, must lock the leaf containing a key. The
-\N{descend\_and\_lock} function performs a combined traversal and lock.
-Unlike \N{descend}, \N{descend\_and\_lock} descends across multiple
-layers. It returns the unique node that would contain the value for
-\(k\) if \(k\) were in the Masstree.
-
-A natural implementation of \N{descend\_and\_lock} might lock a
-leaf node per layer. When encountering a pointer to a lower layer,
-\N{descend\_and\_lock} would unlock the leaf from the higher layer and
-then recurse into the lower. This, however, would perform far more
-locking operations than required. A goal of Masstree is to perform no
-memory writes for common-case tree traversal. Therefore,
-\N{descend\_and\_lock} includes an optimization that, in the common
-case, recurses into a lower layer without obtaining a lock.
-
-\N{Descend\_and\_lock} also performs a tree cleanup task. Consider a
-full lower-layer tree (linked from a higher layer, which we display
-above the dashed line):
-
-\begin{figure}[H]
-\includegraphics[scale=.8]{insert1-1}
-\end{figure}
-
-\noindent%
-Inserting a new item into this lower layer will cause a split. A new
-leaf node is created,
-
-\begin{figure}[H]
-\includegraphics[scale=.8]{insert1-2}
-\end{figure}
-
-\noindent%
-then a new internal node
-
-\begin{figure}[H]
-\includegraphics[scale=.8]{insert1-3}
-\end{figure}
-
-\noindent%
-and finally a new root.
-
-\begin{figure}[H]
-\includegraphics[scale=.8]{insert1-4}
-\end{figure}
-
-\noindent%
-But the higher layer connects to the \emph{old} root, not the new root.
-Correcting this requires more work, since a modification to the higher layer
-requires locking the higher-layer node. We choose to delay that work.
-There's no \emph{correctness} problem with leaving the tree this
-way---thanks to the root reachability invariant, the new root will be
-found by traversing parent pointers from the old. So we leave the tree
-this way until the next put or remove, which will retarget the higher
-layer's pointer:
-
-\begin{figure}[H]
-\includegraphics[scale=.8]{insert1-5}
-\end{figure}
-
-A Masstree layer can contain leaf nodes at different heights. Each interior
-node tracks its height, however, and the split procedure inserts missing
-interior nodes as required to maintain overall balance.
-
-\begin{figure}[H]
-\begin{programtabbing}
-\textbf{descend\_and\_lock}(node \V{toproot}, key \(k\))
- \Returns{\(\langle \N{leaf \V{n}}, \N{int \V{i}}, \N{int
- \(\pi\)}, \N{key \(k'\)}\rangle\)}: \\
-\CX{Find, lock, and return the node into which \(k\) would be inserted.} \\
-\CX{postcondition: \(k' = \N{shift\_key}^m(k)\) for some \(m \geq 0\)}\\
-\CX{postcondition: \(n\) is locked and \(n\) is responsible for \(k'\)}\\
-\CX{postcondition: \(i\) is the insertion index for \(k'\) in \(n\)}\\
-\CX{postcondition: \(\pi = \NONE\) iff \(k'\) is not present in \(n\)}\\
-retrytop:~
- $\V{root} \gets \V{toproot}$;~ \(k \gets \N{reset\_key}(k)\) \\
-descend:~ $\langle n, v \rangle \gets \text{descend}(\V{root}, k)$ \\
-\> if $k$ has suffix: \C{optimization: avoid some unnecessary traversal locks} \\
-\>\> \(x \gets \text{try\_descend\_layer}(n, v, k)\) \\
-\>\> if \(x \neq \NONE\): \\
-\>\>\> \(\V{root} \gets x\);~ \(k \gets \N{shift\_key}(k)\) \\
-\>\>\> goto descend \\
-%
-retry:~
-\> \(\N{lock}(n)\);~ \(v' \gets n.\V{version}\) \\
-\> if \(v'.\V{deleted}\): \\
-\>\> unlock(\(n\));~ goto descend \\
-\> else if \(n.\V{deletedlayer}\): \C{an entire layer has been
- deleted, start over}\\
-\>\> unlock(\(n\));~ goto retrytop \\
-\> else if \(v'.\V{vsplit} \neq v.\V{vsplit}\) and \(n.\V{next} \neq
-\NIL\) and \(k \geq \N{lowkey}(n.\V{next})\): \\
-\>\> \(\V{next} \gets n.\V{next}\);~ unlock(\(n\)) \\
-\>\> \(\langle n, v \rangle \gets \N{advance}(\V{next}, k)\) \\
-\>\> goto retry \\
-\> else: \\
-\>\> \(\langle i, \pi \rangle \gets \N{find\_key}(n, k, n.\V{permutation})\)
-\\
-\>\> if \(\pi \neq \NONE\) and \(n.\V{type}[\pi] = \LAYER\): \\
-\>\>\> \(\V{root} \gets n.\V{value}[\pi]\);~ \(k \gets \N{shift\_key}(k)\) \\
-\>\>\> if \(!\V{root}.\V{isroot}\): \\
-\>\>\>\> \(\V{root} \gets n.\V{value}[\pi] \gets \N{true\_root}(\V{root})\) \\
-\>\>\> unlock(\(n\));~ goto descend \\
-\>\> else: \\
-\>\>\> return \(\langle n, i, \pi, k \rangle\) \\
-\programtabskip
-%
-\textbf{try\_descend\_layer}(node \(n\), version \(v\), key \(k\)): \\
-\> \(\langle i, \pi \rangle \gets \text{find\_key}(n, k, n.\V{permutation})\) \\
-\> if \(\pi \neq \NONE\): \\
-\>\> \(t \gets n.\V{type}[\pi]\) \\
-\>\> \fence \\
-\>\> \(\V{value} \gets n.\V{value}[\pi]\) \\
-\>\> \fence \\
-\>\> if \(!v.\V{deleted}\) and \(t = \LAYER\) and \(v \oplus \readnow{n.\V{version}} \leq \V{locked}\)
-and \(\V{value}.\V{isroot}\): \\
-\>\>\> return \V{value} \\
-\> return \NONE
-\end{programtabbing}
-\caption{Lock and return the leaf that would contain a key.}
-\end{figure}
-
-\begin{figure}[H]
-\begin{programtabbing}
-\textbf{put}(node \V{root}, key \(k\), value \(\V{value}\)): \\
-\> \(\langle n, i, \pi, k \rangle \gets \N{descend\_and\_lock}(\V{toproot}, k)\) \\
-retry:~
-\> if \(\pi \neq \NONE\) and \(n.\V{ksuffix}[\pi] = k.\V{suffix}\): \\
-\>\> fork \(\N{free\_value}(n.\V{value}[\pi])\) \\
-\>\> \(n.\V{value}[\pi] \gets \V{value}\) \\
-\>\> unlock(\(n\));~ return \\
-\> else if \(\pi \neq \NONE\): \\
-\>\> \(n \gets \N{new\_layer}(n, \pi)\);~ \(k \gets \N{shift\_key}(k)\) \\
-\>\> \(\langle i, \pi \rangle \gets \N{find\_key}(n, k,
-n.\V{permutation})\) \\
-\>\> goto retry \\
-\> if \(n.\V{modstate} \neq \INSERTING\): \\
-\>\> \(n.\V{modstate} \gets \INSERTING\) \\
-\>\> \(n.\V{version}.\V{inserting} \gets 1\) \\
-\>\> \fence \\
-\> if \(n.\V{size} < \LEAFWIDTH\): \\
-\>\> \(\pi \gets \text{an unused position from \(n.\V{permutation}\)}\) \\
-\>\> \(n.\V{kslice}[\pi] \gets k.\V{slice}\) \\
-\>\> \(n.\V{ksuffix}[\pi] \gets k.\V{suffix}\) \\
-\>\> \(n.\V{klength}[\pi] \gets \min(k.\V{length}, 9)\) \\
-\>\> \(n.\V{type}[\pi] \gets \VALUE\) \\
-\>\> \(n.\V{value}[\pi] \gets \V{value}\) \\
-\>\> \fence \\
-\>\> \(n.\V{permutation} \gets \text{modified permutation with \(\pi\) at
- position \(i\)}\) \\
-\>\> unlock(\(n\));~ return \\
-\> \(\N{split}(n, k, \V{value})\) \\
-\programtabskip
-%
-\textbf{new\_layer}(leaf \(n\), int \(\pi\)): \\
-\> \(\V{suffix} = n.\V{ksuffix}[\pi]\);~ \(\V{value} \gets
-n.\V{value}[\pi]\) \\
-\> \(n' \gets \text{new locked leaf containing only \V{suffix} and
- \V{value}}\) \\
-\> \(n.\V{version}.\V{inserting} \gets 1\) \\
-\> \fence \\
-\> \(n.\V{value}[\pi] \gets n'\) \\
-\> \(n.\V{ksuffix}[\pi] \gets \text{empty string}\) \\
-%\> \fence \\
-\> \(n.\V{type}[\pi] \gets \LAYER\) \\
-\> unlock(\(n\)) \\
-\> return \(n'\)
-\end{programtabbing}
-\caption{Put implementation (except for split).}
-\end{figure}
-
-
-\begin{figure}[H]
-\begin{programtabbing}
-\textbf{split}(node $n$, key $k$, value \V{value}): \C{precondition: $n$ locked} \\
-\> $n' \gets \text{new empty leaf}$ \\
-\> \(n'.\V{version} \gets n.\V{version}\) \C{\(n'\) is initially locked} \\
-\> \(n'.\V{version}.\V{splitting} \gets 1\) \\
-\> copy the larger keys and values from \(n\) to \(n'\) \\
-\> assign \(n'.\V{permutation}\) appropriately \\
-\> \fence \\
-\> \(\N{link\_split\_leaf}(n, n')\) \\
-\> \(n.\V{version}.\V{splitting} \gets 1\) \\
-\> \fence \\
-\> assign \(n.\V{permutation}\) to contain only those keys not copied to
-\(n'\) \\
-\> insert \(k\) and \V{value} into \(n\) or \(n'\) as appropriate \\
-\> \(\V{height} \gets 1\) \\
-ascend:~
-\> $p \gets \text{locked\_parent}(n)$ \C{hand-over-hand locking} \\
-\> if $p \neq \NIL$: \\
-\>\> $p.\V{version}.\V{inserting} \gets 1$ \\
-\>\> \fence \\
-\> if $p = \NIL$ or $p.\V{height} > \V{height}$: \C{add new root or rectify imbalance} \\
-\>\> \(p' \gets \text{new interior node}\);~
-\(p'.\V{height} \gets \V{height}\);~ $p'.\V{isroot} \gets (p = \NIL)$ \\
-\>\> insert \(n\), \(n'\) into \(p'\) \\
-\>\> if $p \neq \NIL$: \\
-\>\>\> \(i \gets \text{index of $n$ in children of $p$}\) \\
-\>\>\> \(p.\V{child}[i] \gets p'\);~ \(p'.\V{parent} \gets p\) \\
-\>\> \(n'.\V{parent} \gets p'\) \\
-\>\> \fence \\
-\>\> \(n.\V{parent} \gets p'\) \\
-\>\> unlock($n$);~ unlock($n'$);~ if $p\neq\NIL$ then unlock($p$);~ return \\
-\> else if $p$ is not full: \\
-\>\> \(\N{unlock}(n)\) \\
-\>\> shift $p$ keys and children to include $n'$ \\
-\>\> \(n'.\V{parent} \gets p\) \\
-\>\> \fence \\
-\>\> \(p.\V{size} \gets p.\V{size} + 1\) \\
-\>\> unlock($n$);~ unlock($n'$);~ unlock($p$);~ return \\
-\> else: \\
-\>\> \(p.\V{version}.\V{splitting} \gets 1\) \\
-\>\> \fence \\
-\>\> \(\N{unlock}(n)\) \\
-\>\> \(p' \gets \text{new interior node}\);~
-\(p'.\V{version} \gets p.\V{version}\);~ \(p'.\V{height} \gets \V{height}\) \\
-\>\> \fence \\
-\>\> move larger keys from \(p\) into \(p'\), inserting \(n'\) where it belongs (and setting \(n'.\V{parent}\)) \\
-\>\> for all \(c \in \text{children of \(p'\)}\): \\
-\>\>\> \(c.\V{parent} \gets p'\) \\
-\>\> unlock($n'$);~ $n \gets p$;~ $n' \gets p'$;~
-\(\V{height} \gets \V{height} + 1\) \\
-\>\> goto ascend
-\end{programtabbing}
-\caption{Split a leaf and insert a key.}
-\label{fig:nsplit}
-\end{figure}
-
-\begin{figure}[H]
-\begin{programtabbing}
-\textbf{link\_split\_leaf}(leaf \(n\), leaf \(n'\)): \\
-\> \(n'.\V{prev} \gets n\) \\
-\> \(\V{next} \gets n.\V{next}\) \\
-\> while \(\V{next} \neq \NIL\) and
-(\V{next} has a \MARK\ or \(!cmpxchg(n.\V{next}, \V{next},
-\V{next} + \MARK)\)): \\
-\>\> \(\V{next} \gets \readnow{n.\V{next}}\) \\
-\> \(n'.\V{next} \gets \V{next}\) \\
-\> if \(\V{next} \neq \NIL\): \\
-\>\> \(\V{next}.\V{prev} \gets n'\) \\
-\> \fence \\
-\> \(n.\V{next} \gets n'\)
-\end{programtabbing}
-\end{figure}
-
-\section{Remove}
-
-The most complex Masstree operation is remove. It's complex because of
-the wide range of tree changes it can cause.
-
-\subsection{Removing an item}
-
-We start with a simple example tree.
-
-\begin{figure}[H]
-\includegraphics[scale=.8]{remove1-1}
-\end{figure}
-
-Removing a single item---say, \ITEM{e}---is relatively simple: all code
-is in two short functions, \N{remove} and \N{remove\_item}. We first use
-\N{descend\_and\_lock} to locate and lock the leaf containing the item, then pop
-the item out of the relevant leaf's permutation.
-
-\begin{figure}[H]
-\includegraphics[scale=.8]{remove1-2}
-\end{figure}
-
-\noindent
-The heavy circle on the leaf's upper left indicates a lock. The item's
-value is reclaimed by \N{free\_value}, but only after concurrent readers
-have completed.
-
-Note that \N{remove} does not actually modify the leaf's state for the
-item, namely \(n.\V{kslice}[\pi]\), \(n.\V{klength}[\pi]\),
-\(n.\V{ksuffix}[\pi]\), \(n.\V{type}[\pi]\), and \(n.\V{value}[\pi]\)!
-This allows concurrent readers who saw the \emph{old} permutation to
-extract a correct value for the item, namely the old value. This is
-correct---an overlapping reader can return the old value. Alternatively,
-\N{remove} could dirty the node, but this would force readers to retry,
-something we aim to avoid.
-
-However, there is a wrinkle. We must ensure that a change in the node's keys
-can be detected by examining the node's version---specifically, by examining
-the version and the current node size. Thus, the sequence of operations
-``insert(\textsc{x}); remove(\textsc{x})'' should change the node's version
-number. To this end, we keep track of a per-node modification state, called
-\(n.\V{modstate}\). This is either \INSERTING, if we are currently in the
-midst of inserting keys into the node, or \REMOVING, if we're currently
-removing keys from the node. Switching between modification states requires an
-explicit change to the node's version number.
-
-\subsection{Removing a leaf}
-
-A series of removes can empty out a leaf entirely. Empty nodes would
-waste memory and slow down lookup, so they are removed from the tree.
-
-The set of leaves in a Masstree are collectively responsible for a full
-range of keys. When a leaf is removed, responsibility for its key range
-shifts to another leaf. Since \N{lowkey} values never change,
-responsibility always passes to the left, to the node's predecessor in
-the doubly linked list. Remove must ensure that concurrent operations
-reach this predecessor. This requires remove to update both the leaf
-linked list, which is used during scans and some puts, and the
-\Bplus\ tree structure.
-
-As an exception, \N{remove} always preserves the leftmost leaf in a
-tree. This simplifies many operations and is logically required by our
-invariants: \N{lowkey} values never change, but deleting the leftmost
-node would effectively require setting its successor's \N{lowkey} to
-$-\infty$. The leftmost leaf can be deleted, but only when the entire
-subtree is empty.
-
-We demonstrate by removing all the elements in \ITEM{def}.
-
-Masstree first marks the node's version as \V{deleted}, which we show
-with a white X. Any operation that reaches a \V{deleted} node will retry
-from the root. This is important because the next steps will unlink the
-deleted node from the tree, and unlinked nodes no longer receive updates
-about other changes to the tree's structure. This means there is no
-reliable way to get back on track from a deleted node. Other operations
-will retry in a loop until the deleted leaf is fully unlinked. Also, a
-callback is registered to free the leaf's memory once concurrent readers
-have completed.
-
-We next remove the node from the leaf linked list. Logical ``locks''
-protect against neighbors' concurrent removes and splits, but to prevent
-deadlock, these per-node locks are different from the main per-node
-locks. The lock is a mark bit stolen from node \V{next} pointers. The mark
-bit in \(n.\V{next}\) protects the setting of \(n.\V{next}\), and the
-setting of \(n.\V{next}.\V{prev}\).
-
-The \N{unlink\_leaf} function contains the code. (The code for inserting
-new leaves during split is in \N{link\_split\_leaf}.) First, the
-\(\ITEM{def}.\V{next}\) pointer is locked with a mark, shown in black
-below. This lock protects both \(\ITEM{def}.\V{next}\) and
-\(\ITEM{ghi}.\V{prev}\), so \ITEM{ghi} now cannot remove itself from the
-list until \ITEM{def} is unlinked.
-
-\begin{figure}[H]
-\includegraphics[scale=.8]{remove1-3}
-\end{figure}
-
-\noindent%
-But removing \ITEM{def} requires modifying, and therefore locking,
-\(\ITEM{def}.\V{prev}.\V{next} = \ITEM{abc}.\V{next}\) as well. This
-modification requires a retry loop, in case \ITEM{abc} splits or is
-removed during the operation.
-
-\begin{figure}[H]
-\includegraphics[scale=.8]{remove1-31}
-\end{figure}
-
-\noindent%
-With \(\ITEM{def}.\V{prev}.\V{next}\) and \(\ITEM{def}.\V{next}\) both
-locked, the unlink is easy. First, \(\ITEM{ghi}.\V{prev}\) is modified,
-
-\begin{figure}[H]
-\includegraphics[scale=.8]{remove1-4}
-\end{figure}
-
-\noindent%
-and then, after a fence, \(\ITEM{abc}.\V{next}\). A single assignment
-both unlocks and changes the pointer.
-
-\begin{figure}[H]
-\includegraphics[scale=.8]{remove1-5}
-\end{figure}
-
-\noindent%
-This algorithm resembles some algorithms for lock-free
-doubly-linked-list insertion and removal, but it is a bit simpler. This
-is because Masstree leaf insertion is constrained---it only happens
-during split. As a result, insertion immediately after \(n\) cannot
-proceed concurrently with removal of \(n\).
-
-The leaf linked list is correct, but the \Bplus\ tree index is not.
-Masstree traverses to and locks the leaf's parent, then releases the
-lock on the leaf. It dirties the interior node, searches for the deleted
-leaf's \N{lowkey} (here, \(\N{lowkey}(\ITEM{def}) = \ITEM{d}\)), and removes
-the corresponding key and child pointer, creating this state.
-
-\begin{figure}[H]
-\includegraphics[scale=.8]{remove1-6}
-\end{figure}
-
-And this completes the removal of \ITEM{def}.
-
-\subsection{Collapsing trivial interior nodes}
-
-Now say \ITEM{ghi} is deleted. This process starts out as before.
-
-\begin{figure}[H]
-\includegraphics[scale=.8]{remove1-7}
-\end{figure}
-
-\noindent%
-But when \(\N{lowkey}(\ITEM{ghi}) = \ITEM{g}\) is removed from its
-parent interior node, that interior node becomes \emph{trivial}: it has
-no keys and only one child. Trivial interior nodes are redundant,
-wasting space and traversal time.
-
-\begin{figure}[H]
-\includegraphics[scale=.8]{remove1-8}
-\end{figure}
-
-Masstree detects trivial nodes and deletes them in the \N{collapse}
-function. \N{Collapse} marks trivial nodes' versions as \V{deleted} and
-frees them after the usual reader delay. It also walks up the tree, with
-hand-over-hand locking, and adjusts parent nodes' pointers to hop over
-the deleted trivial node.
-
-\begin{figure}[H]
-\includegraphics[scale=.8]{remove1-9}
-\end{figure}
-
-\noindent%
-This deletion procedure leaves the tree funnily shaped: leaves are at
-different heights. It's this property that prevents us from storing
-links on interior nodes.
-
-The \N{collapse} function won't ever delete the root node. A trivial
-root requires more work to fix, as we'll see.
-
-\subsection{Reshaping the tree}
-
-We return to the original example and, this time, remove leaf \ITEM{jkl}.
-
-\begin{figure}[H]
-\includegraphics[scale=.8]{remove2-1}
-\end{figure}
-
-Now, \ITEM{jkl} is the \emph{first} child of its parent. This requires
-special handling. To see why, note that
-the usual removal procedure would create this state:
-
-\begin{figure}[H]
-\includegraphics[scale=.8]{remove2-2}
-\end{figure}
-
-\noindent%
-Now consider a \N{put} operation on key \ITEM{j}. This \N{put} should
-reach leaf \ITEM{ghi}, which has inherited responsibility for
-the \ITEM{jkl} range. But the \Bplus\ tree structure will direct the
-\N{put} to leaf \ITEM{mno}!
-
-This mistake could be avoided if all leaf accesses examined the \N{lowkey}, but
-we fix the misdirection. When a subtree's leftmost
-leaf is deleted, Masstree detects the issue and executes a
-\N{redirect} procedure. This procedure first sets the subtree's leftmost child
-pointer to \(\NIL\), causing concurrent gets and puts that would hit that node
-to retry from the root.
-
-\begin{figure}[H]
-\includegraphics[scale=.8]{remove2-3}
-\end{figure}
-
-The procedure then works up the tree using hand-over-hand locking. At
-each step it replaces the deleted lowkey, here \ITEM{j}, with its
-logical successor, here \ITEM{m}. In this tree, that takes one step, but
-more complicated trees might require multiple replacements.
-
-\begin{figure}[H]
-\includegraphics[scale=.8]{remove2-4}
-\end{figure}
-
-This tree is valid and sends no puts down the wrong path. Although an interior
-node has a redundant key---the right-hand interior node contains key \ITEM{m},
-although it is responsible for no keys less than \ITEM{m}---we don't bother to
-clean it up. (We could clean it up by marked all interior nodes as
-\V{vsplitting} in the redirect procedure.)
-
-\subsection{Root trimming}
-
-When the \ITEM{mno} leaf is deleted,
-the entire right-hand subtree becomes redundant.
-
-\begin{figure}[H]
-\includegraphics[scale=.8]{remove2-5}
-\end{figure}
-
-After removing the \ITEM{mno} reference, \N{remove\_leaf} can tell that
-the right-hand interior node is redundant: its single child pointer is
-\NIL. The interior node can therefore be deleted
-
-\begin{figure}[H]
-\includegraphics[scale=.8]{remove2-6}
-\end{figure}
-
-\noindent%
-and this continues
-up the tree.
-
-\begin{figure}[H]
-\includegraphics[scale=.8]{remove2-7}
-\end{figure}
-
-\noindent%
-But now the \emph{root} node is trivial, having only a single child. How
-can we fix this? The root node may be referenced by
-a leaf node in a higher \emph{tree layer}, as shown here:
-
-\begin{figure}[H]
-\includegraphics[scale=.8]{remove2-8}
-\end{figure}
-
-\noindent%
-(Everything above the dashed line is in the next-higher layer.) We
-cannot delete the root node until this pointer is switched to its child.
-And any change to that higher layer's values requires locking the
-corresponding leaf.
-
-We solve this problem by registering a callback to trim the layer,
-called \N{gclayer}. This function locks the
-relevant leaf in the higher layer
-
-\begin{figure}[H]
-\includegraphics[scale=.8]{remove2-9}
-\end{figure}
-
-\noindent%
-locks the lower layer's root
-
-\begin{figure}[H]
-\includegraphics[scale=.8]{remove2-11}
-\end{figure}
-
-\noindent%
-and then marks its single child as the new root, switching the higher
-layer's value pointer to it.
-
-\begin{figure}[H]
-\includegraphics[scale=.8]{remove2-12}
-\end{figure}
-
-The original root is now garbage and can be reclaimed.
-
-But what about concurrent readers? The root reachability invariant
-requires that the true root of a layer always be reachable from any
-prior root. We therefore set the \emph{old} root's parent pointer to
-point to the \emph{new} root. This can cause an apparent anomaly: an
-internal node can have a leaf as its parent!
-
-\subsection{Deleting a layer}
-
-After the last item in a layer is removed, Masstree can remove the
-entire layer. This starts out like any gclayer call: the relevant leaf
-in the higher layer is locked
-
-\begin{figure}[H]
-\includegraphics[scale=.8]{remove2-15}
-\end{figure}
-
-\noindent
-and so is the empty root in the lower layer.
-
-\begin{figure}[H]
-\includegraphics[scale=.8]{remove2-16}
-\end{figure}
-
-\noindent%
-But the empty root is not deleted in the conventional way. We mark it
-not as deleted, but as a \emph{deleted layer}, using
-a flag that's checked only by concurrent \emph{writers}. Why?
-Normally, a lookup encountering a deleted node retries from the root of
-the current layer. Marking a layer's only node as deleted would cause a
-retry loop! Instead, concurrent readers that reach a deleted layer see a
-normal empty leaf and return ``not found.'' (This is correct: the
-deleted layer contains no keys.) Concurrent writers, however, must not
-write into a deleted layer, lest their updates be lost. On encountering
-a deleted layer, writers retry from the layer-0 root.
-
-\begin{figure}[H]
-\begin{programtabbing}
-\textbf{remove}(node \V{toproot}, key \(k\)): \\
-\> \(\langle n, i, \pi, k \rangle \gets \N{descend\_and\_lock}(\V{toproot}, k)\) \\
-\> if \(\pi = \NONE\) or \(n.\V{ksuffix}[\pi] \neq k.\V{suffix}\): \\
-\>\> return \FALSE \\
-\> \(\N{remove\_item}(n, i, \pi, k, \V{toproot})\) \\
-\> return \TRUE
-\end{programtabbing}
-\end{figure}
-
-\begin{figure}[H]
-\begin{programtabbing}
-\textbf{remove\_item}(leaf \(n\), int \(i\), int \(\pi\), key \(k\), node
-\V{toproot}): \\
-\> if \(n.\V{modstate} = \INSERTING\): \\
-\>\> \(n.\V{modstate} \gets \REMOVING\) \\
-\>\> \(n.\V{version}.\V{inserting} \gets 1 \) \\
-\>\> \fence \\
-\> \(n.\V{permutation} \gets \N{permremove}(n.\V{permutation}, i)\) \\
-\> fork \(\N{free\_value}(n.\V{value}[\pi])\) \\
-\> if \(n.\V{size} \neq 0\): \\
-\>\> \(\N{unlock}(n)\);~ return \\
-\> \(\N{remove\_leaf}(n, \V{toproot}, k.\V{layerprefix})\)
-\end{programtabbing}
-\end{figure}
-
-\begin{figure}[H]
-\begin{programtabbing}
-\textbf{remove\_leaf}(leaf \V{n}, node \V{toproot}, key \V{layerkey}): \\
-\> if \(n.\V{prev} = \NIL\): \\
-\>\> if \(n.\V{next} = \NIL\) and \(\V{layerkey}\) is not empty: \\
-\>\>\> fork \(\N{gclayer}(\V{toproot}, \V{layerkey})\) \\
-\>\> unlock(\(n\)) \\
-\>\> return \\
-\> \(n.\V{version}.\V{deleted} \gets 1\);~ fork \(\N{free\_node}(n)\) \\
-\> \(\N{unlink\_leaf}(n)\) \\
-\> \(k \gets \N{lowkey}(n)\);~ \(r \gets \NIL\) \\
-ascend:~
-\> \(p \gets \N{locked\_parent}(n)\);~ \(p.\V{version}.\V{inserting} \gets 1\) \\
-\> \fence \\
-\> \(i \gets \text{position of \V{k} in \V{p}}\) \\
-\> \(p.\V{child}[i] \gets r\) \\
-\> if \(r \neq \NIL\): \\
-\>\> \(r.\V{parent} \gets p\) \\
-\> else if \(i > 0\): \\
-\>\> remove the element at index \(i - 1\), shifting others down \\
-\> if \(i \leq 1\) and \(p.\V{nkeys} > 0\) and \(p.\V{child}[0] = \NIL\): \\
-\>\> \(\N{redirect}(p, \V{k}, p.\V{kslice}[0])\) \\
-\>\> \(\V{k} \gets p.\V{kslice}[0]\)\\
-\> \(\N{unlock}(n)\) \\
-\> if \(p.\V{isroot}\) or \(p.\V{size} > 0\): \\
-\>\> \(\N{unlock}(p)\);~ return \\
-\> \(p.\V{version}.\V{deleted} \gets 1\);~ fork \(\N{free\_node}(p)\) \\
-\> \(r \gets p.\V{child}[p.\V{size}]\);~ \(p.\V{child}[p.\V{size}] \gets \NIL\) \\
-\> \(n \gets p\);~ goto ascend
-\end{programtabbing}
-\caption{Remove a node.}
-\end{figure}
-
-\begin{figure}[H]
-\begin{programtabbing}
-\textbf{unlink\_leaf}(leaf \(n\)): \\
-\> \(\V{next} \gets n.\V{next}\) \\
-\> while \(\V{next} \neq \NIL\) and \(!\N{cmpxchg}(n.\V{next}, \V{next},
- \V{next} + \MARK)\): \\
-\>\> \(\V{next} \gets \readnow{n.\V{next}}\) \\
-\> \(\V{prev} \gets n.\V{prev}\) \\
-\> while \(!\N{cmpxchg}(\V{prev}.\V{next}, n, n + \MARK)\): \\
-\>\> \(\V{prev} \gets \readnow{n.\V{prev}}\) \\
-\> if \(\V{next} \neq \NIL\): \\
-\>\> \(\V{next}.\V{prev} \gets \V{prev}\) \\
-\> \fence \\
-\> \(\V{prev}.\V{next} \gets \V{next}\)
-\end{programtabbing}
-\end{figure}
-
-\begin{figure}[H]
-\begin{programtabbing}
-\textbf{redirect}(interiornode \(n\), key \V{k}, key \(k'\)): \\
-\> \(p \gets n\) \\
-ascend:~
-\> \(p' \gets \N{locked\_parent}(p)\) \\
-\> if \(p \neq n\): \\
-\>\> \(\N{unlock}(p)\) \\
-\> \(p \gets p'\) \\
-\> \(i \gets \text{position of \V{k} in \V{p}}\) \\
-\> if \(i > 0\): \\
-\>\> \(p.\V{kslice}[i - 1] \gets k'\) \\
-\> if \(i > 1\) or (\(i = 1\) and \(p.\V{child}[0] \neq \NIL\)): \\
-\>\> \(\N{unlock}(p)\);~ return \\
-\> goto ascend
-\end{programtabbing}
-\end{figure}
-
-\begin{figure}[H]
-\begin{programtabbing}
-\textbf{gclayer}(node \V{toproot}, key \V{layerkey}): \\
-\> \rcufence \\
-\> \(\langle n, i, \pi, \N{\_} \rangle \gets \N{descend\_and\_lock}(\V{toproot}, \V{layerkey})\) \\
-\> if \(\pi = \NONE\) or \(n.\V{type}[\pi] \neq \LAYER\): \\
-\>\> \(\N{unlock}(n)\);~ return \\
-retry:~
-\> \(\V{layer} \gets n.\V{value}[\pi]\) \\
-\> if \(!\V{layer}.\V{isroot}\): \\
-\>\> \(\V{layer} \gets n.\V{value}[\pi] \gets \N{true\_root}(\V{layer})\) \\
-\> if \(\V{layer}.\V{size} > 0\): \\
-\>\> \(\N{unlock}(n)\);~ return \\
-\> \(\N{lock}(\V{layer})\) \\
-\> if \(\V{layer}.\V{size} > 0\) or \(!\V{layer}.\V{isroot}\): \\
-\>\> \(\N{unlock}(\V{layer})\);~ \(\N{unlock}(\V{n})\);~ return \\
-\> if \(\V{layer}.\V{isleaf}\): \\
-\>\> \(\V{layer}.\V{deletedlayer} \gets 1\) \\
-\>\> \(\N{unlock}(\V{layer})\) \\
-\>\> \(\N{remove\_item}(n, i, \pi, \V{layerkey}, \V{toproot})\) \\
-\>\> fork \(\N{free\_node}(\V{layer})\) \\
-\>\> return \\
-\> else: \\
-\>\> \(\V{child} \gets \V{layer}.\V{child}[0]\) \\
-\>\> \(\V{child}.\V{isroot} \gets 1\) \\
-\>\> \(\V{child}.\V{parent} \gets \NIL\) \\
-\>\> \(n.\V{value}[\pi] \gets \V{child}\) \\
-\>\> \(\V{layer}.\V{isroot} \gets 0\) \\
-\>\> \(\V{layer}.\V{parent} \gets \V{child}\) \\
-\>\> \(\N{unlock}(\V{layer})\) \\
-\>\> fork \(\N{free\_node}(\V{layer})\) \\
-\>\> goto retry
-\end{programtabbing}
-\caption{Remove a twig.}
-\end{figure}
-
-\section{Other optimizations and left-off properties}
-
-In the implementation, \(\N{lowkey}(n)\) is stored in
-\(n.\V{kslice}[0]\).
-%
-Thus, the code must ensure that any values stored in position 0
-have this slice.
-%
-This isn't a problem unless the value in position 0 is removed.
-%
-To guard against inappropriate reuse, the \N{insert} procedure
-ensures that position 0 is only reused by a key with the right slice.
-
-In the implementation, \(n.\V{type}\) is stored implicitly; its values
-are inferred from \(n.\V{klength}\).
-
-
-node\_ts
-
-\end{document}
diff --git a/file.cc b/file.cc
deleted file mode 100644
index 71e0082..0000000
--- a/file.cc
+++ /dev/null
@@ -1,89 +0,0 @@
-/* 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.
- */
-#include "file.hh"
-#include "straccum.hh"
-#include <fcntl.h>
-
-lcdf::String read_file_contents(int fd) {
- lcdf::StringAccum sa;
- while (1) {
- char *buf = sa.reserve(4096);
- if (!buf) {
- errno = ENOMEM;
- return lcdf::String();
- }
-
- ssize_t x = read(fd, buf, 4096);
- if (x != -1 && x != 0)
- sa.adjust_length(x);
- else if (x == 0)
- break;
- else if (errno != EINTR)
- return lcdf::String();
- }
-
- errno = 0;
- return sa.take_string();
-}
-
-lcdf::String read_file_contents(const char *filename) {
- int fd = open(filename, O_RDONLY);
- if (fd == -1)
- return lcdf::String();
-
- lcdf::String text = read_file_contents(fd);
-
- if (text.empty() && errno) {
- int saved_errno = errno;
- close(fd);
- errno = saved_errno;
- } else
- close(fd);
- return text;
-}
-
-int sync_write_file_contents(const char *filename, const lcdf::String &contents,
- mode_t mode)
-{
- int fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, mode);
- if (fd == -1)
- return -1;
-
- ssize_t x = safe_write(fd, contents.data(), contents.length());
- if (x != contents.length()) {
- error:
- int saved_errno = errno;
- close(fd);
- errno = saved_errno;
- return -1;
- }
-
- if (fsync(fd) != 0)
- goto error;
-
- return close(fd);
-}
-
-int atomic_write_file_contents(const char *filename, const lcdf::String &contents,
- mode_t mode)
-{
- lcdf::String tmp_filename = lcdf::String(filename) + ".tmp";
- int r = sync_write_file_contents(tmp_filename.c_str(), contents, mode);
- if (r != 0)
- return -1;
-
- return rename(tmp_filename.c_str(), filename);
-}
diff --git a/file.hh b/file.hh
deleted file mode 100644
index 60ec903..0000000
--- a/file.hh
+++ /dev/null
@@ -1,83 +0,0 @@
-/* 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 KVDB_FILE_HH
-#define KVDB_FILE_HH 1
-#include <stdio.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <errno.h>
-#include "string.hh"
-
-inline ssize_t
-safe_read(int fd, void *buf, size_t count)
-{
- size_t pos = 0;
- while (pos != count) {
- ssize_t x = ::read(fd, buf, count - pos);
- if (x != -1 && x != 0) {
- buf = reinterpret_cast<char *>(buf) + x;
- pos += x;
- } else if (x == 0)
- break;
- else if (errno != EINTR && pos == 0)
- return -1;
- else if (errno != EINTR)
- break;
- }
- return pos;
-}
-
-inline ssize_t
-safe_write(int fd, const void *buf, size_t count)
-{
- size_t pos = 0;
- while (pos != count) {
- ssize_t x = ::write(fd, buf, count - pos);
- if (x != -1 && x != 0) {
- buf = reinterpret_cast<const char *>(buf) + x;
- pos += x;
- } else if (x == 0)
- break;
- else if (errno != EINTR && pos == 0)
- return -1;
- else if (errno != EINTR)
- break;
- }
- return pos;
-}
-
-inline void
-checked_write(int fd, const void *buf, size_t count)
-{
- ssize_t x = safe_write(fd, buf, count);
- always_assert(size_t(x) == count);
-}
-
-template <typename T> inline void
-checked_write(int fd, const T *x)
-{
- checked_write(fd, reinterpret_cast<const void *>(x), sizeof(*x));
-}
-
-
-lcdf::String read_file_contents(int fd);
-lcdf::String read_file_contents(const char *filename);
-int sync_write_file_contents(const char *filename, const lcdf::String &contents,
- mode_t mode = 0666);
-int atomic_write_file_contents(const char *filename, const lcdf::String &contents,
- mode_t mode = 0666);
-
-#endif
diff --git a/json.cc b/json.cc
deleted file mode 100644
index 29ddfbc..0000000
--- a/json.cc
+++ /dev/null
@@ -1,1231 +0,0 @@
-/* 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.
- */
-// -*- c-basic-offset: 4 -*-
-#include "json.hh"
-#include "compiler.hh"
-#include <ctype.h>
-namespace lcdf {
-
-/** @class Json
- @brief Json data.
-
- The Json class represents Json data: null values, booleans, numbers,
- strings, and combinations of these primitives into arrays and objects.
-
- Json objects are not references, and two Json values cannot share
- subobjects. This differs from Javascript. For example:
-
- <code>
- Json j1 = Json::make_object(), j2 = Json::make_object();
- j1.set("a", j2); // stores a COPY of j2 in j1
- j2.set("b", 1);
- assert(j1.unparse() == "{\"a\":{}}");
- assert(j2.unparse() == "{\"b\":1}");
- </code>
-
- Compare this with the Javascript code:
-
- <code>
- var j1 = {}, j2 = {};
- j1.a = j2; // stores a REFERENCE to j2 in j1
- j2.b = 1;
- assert(JSON.stringify(j1) == "{\"a\":{\"b\":1}}");
- </code>
-
- Most Json functions for extracting components and typed values behave
- liberally. For example, objects silently convert to integers, and
- extracting properties from non-objects is allowed. This should make it
- easier to work with untrusted objects. (Json objects often originate
- from untrusted sources, and may not have the types you expect.) If you
- prefer an assertion to fail when a Json object has an unexpected type,
- use the checked <tt>as_</tt> and <code>at</code> functions, rather than
- the liberal <tt>to_</tt>, <tt>get</tt>, and <tt>operator[]</code>
- functions. */
-
-const Json::null_t Json::null;
-const Json Json::null_json;
-static const String array_string("[Array]", 7);
-static const String object_string("[Object]", 8);
-
-// Array internals
-
-Json::ArrayJson* Json::ArrayJson::make(int n) {
- int cap = n < 8 ? 8 : n;
- char* buf = new char[sizeof(ArrayJson) + cap * sizeof(Json)];
- return new((void*) buf) ArrayJson(cap);
-}
-
-void Json::ArrayJson::destroy(ArrayJson* aj) {
- if (aj)
- for (int i = 0; i != aj->size; ++i)
- aj->a[i].~Json();
- delete[] reinterpret_cast<char*>(aj);
-}
-
-
-// Object internals
-
-Json::ObjectJson::ObjectJson(const ObjectJson &x)
- : ComplexJson(), os_(x.os_), n_(x.n_), capacity_(x.capacity_),
- hash_(x.hash_)
-{
- size = x.size;
- grow(true);
-}
-
-Json::ObjectJson::~ObjectJson()
-{
- ObjectItem *ob = os_, *oe = ob + n_;
- for (; ob != oe; ++ob)
- if (ob->next_ > -2)
- ob->~ObjectItem();
- delete[] reinterpret_cast<char *>(os_);
-}
-
-void Json::ObjectJson::grow(bool copy)
-{
- if (copy && !capacity_)
- return;
- int new_capacity;
- if (copy)
- new_capacity = capacity_;
- else if (capacity_)
- new_capacity = capacity_ * 2;
- else
- new_capacity = 8;
- ObjectItem *new_os = reinterpret_cast<ObjectItem *>(operator new[](sizeof(ObjectItem) * new_capacity));
- ObjectItem *ob = os_, *oe = ob + n_;
- for (ObjectItem *oi = new_os; ob != oe; ++oi, ++ob) {
- if (ob->next_ == -2)
- oi->next_ = -2;
- else if (copy)
- new((void*) oi) ObjectItem(ob->v_.first, ob->v_.second, ob->next_);
- else
- memcpy((void*) oi, ob, sizeof(ObjectItem));
- }
- if (!copy)
- operator delete[](reinterpret_cast<void *>(os_));
- os_ = new_os;
- capacity_ = new_capacity;
-}
-
-void Json::ObjectJson::rehash()
-{
- hash_.assign(hash_.size() * 2, -1);
- for (int i = n_ - 1; i >= 0; --i) {
- ObjectItem &oi = item(i);
- if (oi.next_ > -2) {
- int b = bucket(oi.v_.first.data(), oi.v_.first.length());
- oi.next_ = hash_[b];
- hash_[b] = i;
- }
- }
-}
-
-int Json::ObjectJson::find_insert(const String &key, const Json &value)
-{
- if (hash_.empty())
- hash_.assign(8, -1);
- int *b = &hash_[bucket(key.data(), key.length())], chain = 0;
- while (*b >= 0 && os_[*b].v_.first != key) {
- b = &os_[*b].next_;
- ++chain;
- }
- if (*b >= 0)
- return *b;
- else {
- *b = n_;
- if (n_ == capacity_)
- grow(false);
- // NB 'b' is invalid now
- new ((void *) &os_[n_]) ObjectItem(key, value, -1);
- ++n_;
- ++size;
- if (chain > 4)
- rehash();
- return n_ - 1;
- }
-}
-
-Json &Json::ObjectJson::get_insert(Str key)
-{
- if (hash_.empty())
- hash_.assign(8, -1);
- int *b = &hash_[bucket(key.data(), key.length())], chain = 0;
- while (*b >= 0 && os_[*b].v_.first != key) {
- b = &os_[*b].next_;
- ++chain;
- }
- if (*b >= 0)
- return os_[*b].v_.second;
- else {
- *b = n_;
- if (n_ == capacity_)
- grow(false);
- // NB 'b' is invalid now
- new ((void *) &os_[n_]) ObjectItem(String(key.data(), key.length()), null_json, -1);
- ++n_;
- ++size;
- if (chain > 4)
- rehash();
- return os_[n_ - 1].v_.second;
- }
-}
-
-void Json::ObjectJson::erase(int p) {
- const ObjectItem& oi = item(p);
- int* b = &hash_[bucket(oi.v_.first.data(), oi.v_.first.length())];
- while (*b >= 0 && *b != p)
- b = &os_[*b].next_;
- assert(*b == p);
- *b = os_[p].next_;
- os_[p].~ObjectItem();
- os_[p].next_ = -2;
- --size;
-}
-
-Json::size_type Json::ObjectJson::erase(Str key) {
- int* b = &hash_[bucket(key.data(), key.length())];
- while (*b >= 0 && os_[*b].v_.first != key)
- b = &os_[*b].next_;
- if (*b >= 0) {
- int p = *b;
- *b = os_[p].next_;
- os_[p].~ObjectItem();
- os_[p].next_ = -2;
- --size;
- return 1;
- } else
- return 0;
-}
-
-namespace {
-template <typename T> bool string_to_int_key(const char *first,
- const char *last, T& x)
-{
- if (first == last || !isdigit((unsigned char) *first)
- || (first[0] == '0' && first + 1 != last))
- return false;
- // XXX integer overflow
- x = *first - '0';
- for (++first; first != last && isdigit((unsigned char) *first); ++first)
- x = 10 * x + *first - '0';
- return first == last;
-}
-}
-
-void Json::hard_uniqueify_array(bool convert, int ncap_in) {
- if (!convert)
- precondition(is_null() || is_array());
-
- rep_type old_u = u_;
-
- unsigned ncap = std::max(ncap_in, 8);
- if (old_u.x.type == j_array && old_u.a.x)
- ncap = std::max(ncap, unsigned(old_u.a.x->size));
- // New capacity: Round up to a power of 2, up to multiples of 1<<14.
- unsigned xcap = iceil_log2(ncap);
- if (xcap <= (1U << 14))
- ncap = xcap;
- else
- ncap = ((ncap - 1) | ((1U << 14) - 1)) + 1;
- u_.a.x = ArrayJson::make(ncap);
- u_.a.type = j_array;
-
- if (old_u.x.type == j_array && old_u.a.x && old_u.a.x->refcount == 1) {
- u_.a.x->size = old_u.a.x->size;
- memcpy(u_.a.x->a, old_u.a.x->a, sizeof(Json) * u_.a.x->size);
- delete[] reinterpret_cast<char*>(old_u.a.x);
- } else if (old_u.x.type == j_array && old_u.a.x) {
- u_.a.x->size = old_u.a.x->size;
- Json* last = u_.a.x->a + u_.a.x->size;
- for (Json* it = u_.a.x->a, *oit = old_u.a.x->a; it != last; ++it, ++oit)
- new((void*) it) Json(*oit);
- old_u.a.x->deref(j_array);
- } else if (old_u.x.type == j_object && old_u.o.x) {
- ObjectItem *ob = old_u.o.x->os_, *oe = ob + old_u.o.x->n_;
- unsigned i;
- for (; ob != oe; ++ob)
- if (ob->next_ > -2
- && string_to_int_key(ob->v_.first.begin(),
- ob->v_.first.end(), i)) {
- if (i >= unsigned(u_.a.x->capacity))
- hard_uniqueify_array(false, i + 1);
- if (i >= unsigned(u_.a.x->size)) {
- memset(&u_.a.x->a[u_.a.x->size].u_, 0,
- sizeof(Json) * (i + 1 - u_.a.x->size));
- u_.a.x->size = i + 1;
- }
- u_.a.x->a[i] = ob->v_.second;
- }
- old_u.o.x->deref(j_object);
- } else if (old_u.x.type < 0)
- old_u.str.deref();
-}
-
-void Json::hard_uniqueify_object(bool convert) {
- if (!convert)
- precondition(is_null() || is_object());
- ObjectJson* noj;
- if (u_.x.type == j_object && u_.o.x) {
- noj = new ObjectJson(*u_.o.x);
- u_.o.x->deref(j_object);
- } else if (u_.x.type == j_array && u_.a.x) {
- noj = new ObjectJson;
- for (int i = 0; i != u_.a.x->size; ++i)
- noj->find_insert(String(i), u_.a.x->a[i]);
- u_.a.x->deref(j_array);
- } else {
- noj = new ObjectJson;
- if (u_.x.type < 0)
- u_.str.deref();
- }
- u_.o.x = noj;
- u_.o.type = j_object;
-}
-
-void Json::clear() {
- static_assert(offsetof(rep_type, i.type) == offsetof(rep_type, x.type), "odd Json::rep_type.i.type offset");
- static_assert(offsetof(rep_type, u.type) == offsetof(rep_type, x.type), "odd Json::rep_type.u.type offset");
- static_assert(offsetof(rep_type, d.type) == offsetof(rep_type, x.type), "odd Json::rep_type.d.type offset");
- static_assert(offsetof(rep_type, str.memo_offset) == offsetof(rep_type, x.type), "odd Json::rep_type.str.memo_offset offset");
- static_assert(offsetof(rep_type, a.type) == offsetof(rep_type, x.type), "odd Json::rep_type.a.type offset");
- static_assert(offsetof(rep_type, o.type) == offsetof(rep_type, x.type), "odd Json::rep_type.o.type offset");
-
- if (u_.x.type == j_array) {
- if (u_.a.x && u_.a.x->refcount == 1) {
- Json* last = u_.a.x->a + u_.a.x->size;
- for (Json* it = u_.a.x->a; it != last; ++it)
- it->~Json();
- u_.a.x->size = 0;
- } else if (u_.a.x) {
- u_.a.x->deref(j_array);
- u_.a.x = 0;
- }
- } else if (u_.x.type == j_object) {
- if (u_.o.x && u_.o.x->refcount == 1) {
- ObjectItem* last = u_.o.x->os_ + u_.o.x->n_;
- for (ObjectItem* it = u_.o.x->os_; it != last; ++it)
- if (it->next_ != -2)
- it->~ObjectItem();
- u_.o.x->n_ = u_.o.x->size = 0;
- u_.o.x->hash_.assign(u_.o.x->hash_.size(), -1);
- } else if (u_.o.x) {
- u_.o.x->deref(j_object);
- u_.o.x = 0;
- }
- } else {
- if (u_.x.type < 0)
- u_.str.deref();
- memset(&u_, 0, sizeof(u_));
- }
-}
-
-void* Json::uniqueify_array_insert(bool convert, size_type pos) {
- size_type size = u_.a.x ? u_.a.x->size : 0;
- uniqueify_array(convert, size + 1);
- if (pos == (size_type) -1)
- pos = size;
- precondition(pos >= 0 && pos <= size);
- if (pos != size)
- memmove(&u_.a.x->a[pos + 1].u_, &u_.a.x->a[pos].u_,
- (size - pos) * sizeof(Json));
- ++u_.a.x->size;
- return (void*) &u_.a.x->a[pos];
-}
-
-Json::array_iterator Json::erase(array_iterator first, array_iterator last) {
- if (first < last) {
- uniqueify_array(false, 0);
- size_type fpos = first - abegin();
- size_type lpos = last - abegin();
- size_type size = u_.a.x->size;
- for (size_type pos = fpos; pos != lpos; ++pos)
- u_.a.x->a[pos].~Json();
- if (lpos != size)
- memmove(&u_.a.x->a[fpos].u_, &u_.a.x->a[lpos].u_,
- (size - lpos) * sizeof(Json));
- u_.a.x->size -= lpos - fpos;
- }
- return first;
-}
-
-/** @brief Reserve the array Json to hold at least @a n items. */
-void Json::reserve(size_type n) {
- uniqueify_array(false, n);
-}
-
-/** @brief Resize the array Json to size @a n. */
-void Json::resize(size_type n) {
- uniqueify_array(false, n);
- while (u_.a.x->size > n && u_.a.x->size > 0) {
- --u_.a.x->size;
- u_.a.x->a[u_.a.x->size].~Json();
- }
- while (u_.a.x->size < n)
- push_back(Json());
-}
-
-
-// Primitives
-
-int64_t Json::hard_to_i() const {
- switch (u_.x.type) {
- case j_array:
- case j_object:
- return size();
- case j_bool:
- case j_int:
- return u_.i.x;
- case j_unsigned:
- return u_.u.x;
- case j_double:
- return int64_t(u_.d.x);
- case j_null:
- case j_string:
- default:
- if (!u_.x.x)
- return 0;
- invariant(u_.x.type <= 0);
- const char *b = reinterpret_cast<const String&>(u_.str).c_str();
- char *s;
-#if SIZEOF_LONG >= 8
- long x = strtol(b, &s, 0);
-#else
- long long x = strtoll(b, &s, 0);
-#endif
- if (s == b + u_.str.length)
- return x;
- else
- return (long) strtod(b, 0);
- }
-}
-
-uint64_t Json::hard_to_u() const {
- switch (u_.x.type) {
- case j_array:
- case j_object:
- return size();
- case j_bool:
- case j_int:
- return u_.i.x;
- case j_unsigned:
- return u_.u.x;
- case j_double:
- return uint64_t(u_.d.x);
- case j_null:
- case j_string:
- default:
- if (!u_.x.x)
- return 0;
- const char* b = reinterpret_cast<const String&>(u_.str).c_str();
- char *s;
-#if SIZEOF_LONG >= 8
- unsigned long x = strtoul(b, &s, 0);
-#else
- unsigned long long x = strtoull(b, &s, 0);
-#endif
- if (s == b + u_.str.length)
- return x;
- else
- return (uint64_t) strtod(b, 0);
- }
-}
-
-double Json::hard_to_d() const {
- switch (u_.x.type) {
- case j_array:
- case j_object:
- return size();
- case j_bool:
- case j_int:
- return u_.i.x;
- case j_unsigned:
- return u_.u.x;
- case j_double:
- return u_.d.x;
- case j_null:
- case j_string:
- default:
- if (!u_.x.x)
- return 0;
- else
- return strtod(reinterpret_cast<const String&>(u_.str).c_str(), 0);
- }
-}
-
-bool Json::hard_to_b() const {
- switch (u_.x.type) {
- case j_array:
- case j_object:
- return !empty();
- case j_bool:
- case j_int:
- case j_unsigned:
- return u_.i.x != 0;
- case j_double:
- return u_.d.x;
- case j_null:
- case j_string:
- default:
- return u_.str.length != 0;
- }
-}
-
-String Json::hard_to_s() const {
- switch (u_.x.type) {
- case j_array:
- return array_string;
- case j_object:
- return object_string;
- case j_bool:
- return String(bool(u_.i.x));
- case j_int:
- return String(u_.i.x);
- case j_unsigned:
- return String(u_.u.x);
- case j_double:
- return String(u_.d.x);
- case j_null:
- case j_string:
- default:
- if (!u_.x.x)
- return String::make_empty();
- else
- return String(u_.str);
- }
-}
-
-const Json& Json::hard_get(Str key) const {
- ArrayJson *aj;
- unsigned i;
- if (is_array() && (aj = ajson())
- && string_to_int_key(key.begin(), key.end(), i)
- && i < unsigned(aj->size))
- return aj->a[i];
- else
- return make_null();
-}
-
-const Json& Json::hard_get(size_type x) const {
- if (is_object() && u_.o.x)
- return get(String(x));
- else
- return make_null();
-}
-
-Json& Json::hard_get_insert(size_type x) {
- if (is_object())
- return get_insert(String(x));
- else {
- uniqueify_array(true, x + 1);
- if (u_.a.x->size <= x) {
- memset(&u_.a.x->a[u_.a.x->size].u_, 0,
- sizeof(Json) * (x + 1 - u_.a.x->size));
- u_.a.x->size = x + 1;
- }
- return u_.a.x->a[x];
- }
-}
-
-bool operator==(const Json& a, const Json& b) {
- if ((a.u_.x.type > 0 || b.u_.x.type > 0)
- && a.u_.x.type != b.u_.x.type)
- return a.u_.u.x == b.u_.u.x
- && a.u_.i.x >= 0
- && a.is_int()
- && b.is_int();
- else if (a.u_.x.type == Json::j_int
- || a.u_.x.type == Json::j_unsigned
- || a.u_.x.type == Json::j_bool)
- return a.u_.u.x == b.u_.u.x;
- else if (a.u_.x.type == Json::j_double)
- return a.u_.d.x == b.u_.d.x;
- else if (a.u_.x.type > 0 || !a.u_.x.x || !b.u_.x.x)
- return a.u_.x.x == b.u_.x.x;
- else
- return String(a.u_.str) == String(b.u_.str);
-}
-
-
-// Unparsing
-
-Json::unparse_manipulator Json::default_manipulator;
-
-bool Json::unparse_is_complex() const {
- if (is_object()) {
- if (ObjectJson *oj = ojson()) {
- if (oj->size > 5)
- return true;
- ObjectItem *ob = oj->os_, *oe = ob + oj->n_;
- for (; ob != oe; ++ob)
- if (ob->next_ > -2 && !ob->v_.second.empty() && !ob->v_.second.is_primitive())
- return true;
- }
- } else if (is_array()) {
- if (ArrayJson *aj = ajson()) {
- if (aj->size > 1024)
- return true;
- for (Json* it = aj->a; it != aj->a + aj->size; ++it)
- if (!it->empty()
- && (!it->is_primitive()
- || (it->is_string() && it - aj->a >= 4 && it->as_s().length() > 40)))
- return true;
- }
- }
- return false;
-}
-
-void Json::unparse_indent(StringAccum &sa, const unparse_manipulator &m, int depth)
-{
- sa << '\n';
- depth *= (m.tab_width() ? m.tab_width() : 8);
- sa.append_fill('\t', depth / 8);
- sa.append_fill(' ', depth % 8);
-}
-
-namespace {
-const char* const upx_normal[] = {":", ","};
-const char* const upx_expanded[] = {": ", ","};
-const char* const upx_separated[] = {": ", ", "};
-}
-
-void Json::hard_unparse(StringAccum &sa, const unparse_manipulator &m, int depth) const
-{
- if (is_object() || is_array()) {
- bool expanded = depth < m.indent_depth() && unparse_is_complex();
- const char* const* upx;
- if (expanded)
- upx = upx_expanded;
- else if (m.space_separator())
- upx = upx_separated;
- else
- upx = upx_normal;
-
- if (is_object() && !u_.x.x)
- sa << "{}";
- else if (is_object()) {
- sa << '{';
- bool rest = false;
- ObjectJson *oj = ojson();
- ObjectItem *ob = oj->os_, *oe = ob + oj->n_;
- for (; ob != oe; ++ob)
- if (ob->next_ > -2) {
- if (rest)
- sa << upx[1];
- if (expanded)
- unparse_indent(sa, m, depth + 1);
- sa << '\"';
- ob->v_.first.encode_json(sa);
- sa << '\"' << upx[0];
- ob->v_.second.hard_unparse(sa, m, depth + 1);
- rest = true;
- }
- if (expanded)
- unparse_indent(sa, m, depth);
- sa << '}';
- } else if (!u_.x.x)
- sa << "[]";
- else {
- sa << '[';
- bool rest = false;
- ArrayJson* aj = ajson();
- for (Json* it = aj->a; it != aj->a + aj->size; ++it) {
- if (rest)
- sa << upx[1];
- if (expanded)
- unparse_indent(sa, m, depth + 1);
- it->hard_unparse(sa, m, depth + 1);
- rest = true;
- }
- if (expanded)
- unparse_indent(sa, m, depth);
- sa << ']';
- }
- } else if (u_.x.type == j_null && !u_.x.x)
- sa.append("null", 4);
- else if (u_.x.type <= 0) {
- sa << '\"';
- reinterpret_cast<const String&>(u_.str).encode_json(sa);
- sa << '\"';
- } else if (u_.x.type == j_bool) {
- bool b = u_.i.x;
- sa.append(&"false\0true"[-b & 6], 5 - b);
- } else if (u_.x.type == j_int)
- sa << u_.i.x;
- else if (u_.x.type == j_unsigned)
- sa << u_.u.x;
- else if (u_.x.type == j_double)
- sa << u_.d.x;
-
- if (depth == 0 && m.newline_terminator())
- sa << '\n';
-}
-
-
-bool
-Json::assign_parse(const char* first, const char* last, const String& str)
-{
- using std::swap;
- Json::streaming_parser jsp;
- first = jsp.consume(first, last, str, true);
- if (first != last && (*first == ' ' || *first == '\n' || *first == '\r'
- || *first == '\t'))
- ++first;
- if (first == last && jsp.success()) {
- swap(jsp.result(), *this);
- return true;
- } else
- return false;
-}
-
-static inline bool in_range(uint8_t x, unsigned low, unsigned high) {
- return (unsigned) x - low < high - low;
-}
-
-static inline bool in_range(int x, unsigned low, unsigned high) {
- return (unsigned) x - low < high - low;
-}
-
-inline const uint8_t* Json::streaming_parser::error_at(const uint8_t* here) {
- state_ = st_error;
- return here;
-}
-
-inline Json* Json::streaming_parser::current() {
- return stack_.empty() ? &json_ : stack_.back();
-}
-
-const uint8_t*
-Json::streaming_parser::consume(const uint8_t* first,
- const uint8_t* last,
- const String& str,
- bool complete) {
- using std::swap;
- Json j;
-
- if (state_ >= 0 && (state_ & st_partmask)) {
- if ((state_ & st_stringpart) && state_ >= st_object_colon)
- goto string_object_key;
- else if (state_ & st_stringpart)
- goto string_value;
- else if (state_ & st_primitivepart)
- goto primitive;
- else
- goto number;
- }
-
- while (state_ >= 0 && first != last)
- switch (*first) {
- case ' ':
- case '\n':
- case '\r':
- case '\t':
- while (first != last && *first <= 32
- && (*first == ' ' || *first == '\n' || *first == '\r'
- || *first == '\t'))
- ++first;
- break;
-
- case ',':
- if (state_ == st_array_delim)
- state_ = st_array_value;
- else if (state_ == st_object_delim)
- state_ = st_object_key;
- else
- goto error_here;
- ++first;
- break;
-
- case ':':
- if (state_ == st_object_colon) {
- state_ = st_object_value;
- ++first;
- break;
- } else
- goto error_here;
-
- case '{':
- if (state_ <= st_object_value) {
- if (stack_.size() == max_depth)
- goto error_here;
- ++first;
- if (state_ == st_initial && json_.is_o()) {
- swap(j, json_);
- j.clear();
- } else
- j = Json::make_object();
- goto value;
- } else
- goto error_here;
-
- case '}':
- if (state_ == st_object_initial || state_ == st_object_delim) {
- ++first;
- goto close_value;
- } else
- goto error_here;
-
- case '[':
- if (state_ <= st_object_value) {
- if (stack_.size() == max_depth)
- goto error_here;
- ++first;
- if (state_ == st_initial && json_.is_a()) {
- swap(j, json_);
- j.clear();
- } else
- j = Json::make_array();
- goto value;
- } else
- goto error_here;
-
- case ']':
- if (state_ == st_array_initial || state_ == st_array_delim) {
- ++first;
- goto close_value;
- } else
- goto error_here;
-
- case '\"':
- if (state_ <= st_object_value) {
- str_ = String();
- ++first;
- string_value:
- first = consume_string(first, last, str);
- if (state_ >= 0 && !(state_ & st_stringpart)) {
- j = Json(std::move(str_));
- goto value;
- }
- } else if (state_ == st_object_initial || state_ == st_object_key) {
- state_ = st_object_colon;
- str_ = String();
- ++first;
- string_object_key:
- first = consume_string(first, last, str);
- if (state_ >= 0 && !(state_ & st_stringpart)) {
- stack_.push_back(&current()->get_insert(std::move(str_)));
- continue;
- }
- } else
- goto error_here;
- break;
-
- case 'n':
- case 'f':
- case 't':
- if (state_ <= st_object_value) {
- primitive:
- first = consume_primitive(first, last, j);
- if (state_ >= 0 && !(state_ & st_primitivepart))
- goto value;
- } else
- goto error_here;
- break;
-
- case '-':
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- if (state_ <= st_object_value) {
- number:
- first = consume_number(first, last, str, complete, j);
- if (state_ >= 0 && !(state_ & st_numberpart))
- goto value;
- } else
- goto error_here;
- break;
-
- default:
- error_here:
- return error_at(first);
-
- value: {
- Json* jp = current();
- if (state_ != st_initial && jp->is_a()) {
- jp->push_back(std::move(j));
- jp = &jp->back();
- } else
- swap(*jp, j);
-
- if (state_ == st_object_value)
- stack_.pop_back();
- if (jp->is_a() || jp->is_o()) {
- if (state_ != st_initial)
- stack_.push_back(jp);
- state_ = jp->is_a() ? st_array_initial : st_object_initial;
- } else if (state_ == st_object_value)
- state_ = st_object_delim;
- else if (state_ == st_array_initial || state_ == st_array_value)
- state_ = st_array_delim;
- else {
- state_ = st_final;
- return first;
- }
- break;
- }
-
- close_value:
- if (stack_.empty()) {
- state_ = st_final;
- return first;
- } else {
- stack_.pop_back();
- state_ = current()->is_a() ? st_array_delim : st_object_delim;
- }
- break;
- }
-
- return first;
-}
-
-const uint8_t*
-Json::streaming_parser::consume_string(const uint8_t* first,
- const uint8_t* last,
- const String& str) {
- StringAccum sa = StringAccum::make_transfer(str_);
-
- if (unlikely(state_ & st_partlenmask)) {
- first = consume_stringpart(sa, first, last);
- if (state_ == st_error)
- return first;
- }
-
- const uint8_t* prev = first;
- while (first != last) {
- if (likely(in_range(*first, 32, 128)) && *first != '\\' && *first != '\"')
- ++first;
- else if (*first == '\\') {
- sa.append(prev, first);
- prev = first = consume_backslash(sa, first, last);
- if (state_ == st_error)
- return first;
- } else if (*first == '\"')
- break;
- else if (unlikely(!in_range(*first, 0xC2, 0xF5)))
- return error_at(first);
- else {
- int want = 2 + (*first >= 0xE0) + (*first >= 0xF0);
- int n = last - first < want ? last - first : want;
- if ((n > 1 && unlikely(!in_range(first[1], 0x80, 0xC0)))
- || (*first >= 0xE0
- && ((*first == 0xE0 && first[1] < 0xA0) /* overlong */
- || (*first == 0xED && first[1] >= 0xA0) /* surrogate */
- || (*first == 0xF0 && first[1] < 0x90) /* overlong */
- || (*first == 0xF4 && first[1] >= 0x90) /* not a char */
- )))
- return error_at(first + 1);
- else if (n > 2 && unlikely(!in_range(first[2], 0x80, 0xC0)))
- return error_at(first + 2);
- else if (n > 3 && unlikely(!in_range(first[3], 0x80, 0xC0)))
- return error_at(first + 3);
- first += n;
- if (n != want) {
- state_ |= n;
- break;
- }
- }
- }
-
- if (!sa.empty() || first == last)
- sa.append(prev, first);
- if (first != last) {
- if (!sa.empty())
- str_ = sa.take_string();
- else if (prev >= str.ubegin() && first <= str.uend())
- str_ = str.fast_substring(prev, first);
- else
- str_ = String(prev, first);
- state_ &= ~st_stringpart;
- return first + 1;
- } else {
- state_ |= st_stringpart;
- str_ = sa.take_string();
- return first;
- }
-}
-
-const uint8_t*
-Json::streaming_parser::consume_stringpart(StringAccum& sa,
- const uint8_t* first,
- const uint8_t* last) {
- while ((state_ & st_partlenmask) && first != last) {
- int part = state_ & st_partlenmask;
- uint8_t tag = sa[sa.length() - part];
- if ((tag != '\\' && (*first & 0xC0) != 0x80)
- || (tag == '\\' && part == 6 && *first != '\\')
- || (tag == '\\' && part == 7 && *first != 'u')
- || (tag == '\\' && part != 1 && part != 6 && part != 7
- && !isxdigit(*first))) {
- state_ = st_error;
- break;
- }
- sa.append(*first);
- if ((tag != '\\' && part == 1 + (tag >= 0xE0) + (tag >= 0xF0))
- || (tag == '\\' && (part == 1 || part == 5 || part == 11))) {
- uint8_t buf[12];
- memcpy(buf, sa.end() - part - 1, part + 1);
- sa.adjust_length(-part - 1);
- state_ -= st_stringpart | part;
- str_ = sa.take_string();
- (void) consume_string(buf, buf + part + 1, String());
- if (state_ == st_error)
- break;
- sa = StringAccum::make_transfer(str_);
- } else
- ++state_;
- ++first;
- }
- return first;
-}
-
-const uint8_t*
-Json::streaming_parser::consume_backslash(StringAccum& sa,
- const uint8_t* first,
- const uint8_t* last) {
- const uint8_t* prev = first;
- int ch = 0;
-
- if (first + 1 == last)
- goto incomplete;
- else if (first[1] == '\"' || first[1] == '\\' || first[1] == '/')
- ch = first[1];
- else if (first[1] == 'b')
- ch = '\b';
- else if (first[1] == 'f')
- ch = '\f';
- else if (first[1] == 'n')
- ch = '\n';
- else if (first[1] == 'r')
- ch = '\r';
- else if (first[1] == 't')
- ch = '\t';
- else if (first[1] == 'u') {
- for (int i = 2; i < 6; ++i) {
- if (first + i == last)
- goto incomplete;
- else if (in_range(first[i], '0', '9' + 1))
- ch = 16 * ch + first[i] - '0';
- else if (in_range(first[i], 'A', 'F' + 1))
- ch = 16 * ch + first[i] - 'A' + 10;
- else if (in_range(first[i], 'a', 'f' + 1))
- ch = 16 * ch + first[i] - 'a' + 10;
- else
- return error_at(&first[i]);
- }
- first += 4;
- // special handling required for surrogate pairs
- if (unlikely(in_range(ch, 0xD800, 0xE000))) {
- if (ch >= 0xDC00)
- return error_at(&first[1]);
- else if (first + 2 == last)
- goto incomplete;
- else if (first[2] != '\\')
- return error_at(&first[2]);
- else if (first + 3 == last)
- goto incomplete;
- else if (first[3] != 'u')
- return error_at(&first[3]);
- int ch2 = 0;
- for (int i = 4; i < 8; ++i) {
- if (first + i == last)
- goto incomplete;
- else if (in_range(first[i], '0', '9' + 1))
- ch2 = 16 * ch2 + first[i] - '0';
- else if (in_range(first[i], 'A', 'F' + 1))
- ch2 = 16 * ch2 + first[i] - 'A' + 10;
- else if (in_range(first[i], 'A', 'F' + 1))
- ch2 = 16 * ch2 + first[i] - 'a' + 10;
- else
- return error_at(&first[i]);
- }
- if (!in_range(ch2, 0xDC00, 0xE000))
- return error_at(&first[7]);
- ch = 0x10000 + (ch - 0xD800) * 0x400 + (ch2 - 0xDC00);
- first += 6;
- }
- }
-
- if (!ch || !sa.append_utf8(ch))
- return error_at(&first[1]);
- return first + 2;
-
- incomplete:
- state_ |= last - prev;
- sa.append(prev, last);
- return last;
-}
-
-const uint8_t*
-Json::streaming_parser::consume_primitive(const uint8_t* first,
- const uint8_t* last,
- Json& j) {
- const char* t = "null\0false\0true";
- int n;
- if (unlikely(state_ & st_primitivepart)) {
- n = state_ & st_partlenmask;
- state_ &= ~st_partmask;
- } else {
- n = (*first == 'n' ? 1 : (*first == 'f' ? 6 : 12));
- ++first;
- }
-
- for (; first != last && t[n]; ++n, ++first)
- if (t[n] != *first)
- return error_at(first);
-
- if (t[n])
- state_ |= st_primitivepart | n;
- else if (n == 4)
- j = Json();
- else if (n == 10)
- j = Json(false);
- else
- j = Json(true);
- return first;
-}
-
-const uint8_t*
-Json::streaming_parser::consume_number(const uint8_t* first,
- const uint8_t* last,
- const String& str,
- bool complete,
- Json& j) {
- const uint8_t* prev = first;
- int position = state_ & st_partlenmask;
-
- switch (position) {
- case 0:
- if (*first == '-')
- ++first;
- /* fallthru */
- case 2:
- if (first != last && *first == '0') {
- position = 3;
- ++first;
- } else if (first != last && in_range(*first, '1', '9' + 1)) {
- position = 1;
- ++first;
- case 1:
- while (first != last && in_range(*first, '0', '9' + 1))
- ++first;
- } else
- position = 2;
- /* fallthru */
- case 3:
- if (first != last && *first == '.') {
- ++first;
- goto decimal;
- }
- maybe_exponent:
- if (first != last && (*first == 'e' || *first == 'E')) {
- ++first;
- goto exponent;
- }
- break;
-
- decimal:
- case 4:
- position = 4;
- if (first != last && in_range(*first, '0', '9' + 1)) {
- ++first;
- position = 5;
- }
- /* fallthru */
- case 5:
- while (first != last && in_range(*first, '0', '9' + 1))
- ++first;
- if (first != last && position == 5)
- goto maybe_exponent;
- break;
-
- exponent:
- case 6:
- position = 6;
- if (first != last && (*first == '+' || *first == '-'))
- ++first;
- else if (first == last)
- break;
- /* fallthru */
- case 8:
- position = 8;
- if (first != last && in_range(*first, '0', '9' + 1)) {
- ++first;
- position = 9;
- }
- /* fallthru */
- case 9:
- while (first != last && in_range(*first, '0', '9' + 1))
- ++first;
- break;
- }
-
- if (first != last || complete) {
- if (!(position & 1))
- goto error_here;
- last = first;
- if (state_ & st_partlenmask) {
- str_.append(prev, first);
- prev = str_.ubegin();
- first = str_.uend();
- }
- if (prev + 1 == first)
- j = Json(int(*prev - '0'));
- else if (position < 4) {
- bool negative = *prev == '-';
- prev += int(negative);
- uint64_t x = 0;
- while (prev != first) {
- x = (x * 10) + *prev - '0';
- ++prev;
- }
- if (negative)
- j = Json(-int64_t(x));
- else
- j = Json(x);
- } else {
- if (!(state_ & st_partlenmask))
- str_ = String(prev, first);
- double x = strtod(str_.c_str(), 0);
- j = Json(x);
- }
- state_ &= ~st_partmask;
- str_ = String();
- return last;
- }
-
- if (state_ & st_partmask)
- str_.append(prev, first);
- else if (prev >= str.ubegin() && first <= str.uend())
- str_ = str.substring(prev, first);
- else
- str_ = String(prev, first);
- state_ = (state_ & ~st_partmask) | st_numberpart | position;
- return first;
-
- error_here:
- str_ = String();
- return error_at(first);
-}
-
-} // namespace lcdf
diff --git a/json.hh b/json.hh
deleted file mode 100644
index 9238ba3..0000000
--- a/json.hh
+++ /dev/null
@@ -1,3196 +0,0 @@
-/* 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.
- */
-// -*- c-basic-offset: 4 -*-
-#ifndef JSON_HH
-#define JSON_HH
-#include "straccum.hh"
-#include "str.hh"
-#include <vector>
-#include <utility>
-#include <stdlib.h>
-namespace lcdf {
-
-template <typename P> class Json_proxy_base;
-template <typename T> class Json_object_proxy;
-template <typename T> class Json_object_str_proxy;
-template <typename T> class Json_array_proxy;
-class Json_get_proxy;
-
-template <typename T, size_t S = sizeof(String::rep_type) - sizeof(T)>
-struct Json_rep_item;
-template <typename T> struct Json_rep_item<T, 4> {
- T x;
- int type;
-};
-template <typename T> struct Json_rep_item<T, 8> {
- T x;
- int padding;
- int type;
-};
-
-class Json {
- enum json_type { // order matters
- j_string = -1, j_null = 0,
- j_array = 1, j_object = 2,
- j_int = 3, j_unsigned = 4, j_double = 5, j_bool = 6
- };
-
- public:
- struct null_t {
- inline constexpr null_t() { }
- };
- static const null_t null;
- static const Json null_json;
-
- typedef int size_type;
-
- typedef std::pair<const String, Json> object_value_type;
- class object_iterator;
- class const_object_iterator;
-
- typedef Json array_value_type;
- class array_iterator;
- class const_array_iterator;
-
- class iterator;
- class const_iterator;
-
- typedef bool (Json::*unspecified_bool_type)() const;
- class unparse_manipulator;
- class streaming_parser;
-
- // Constructors
- inline Json();
- inline Json(const Json& x);
- template <typename P> inline Json(const Json_proxy_base<P>& x);
- inline Json(Json&& x);
- inline Json(const null_t& x);
- inline Json(int x);
- inline Json(unsigned x);
- inline Json(long x);
- inline Json(unsigned long x);
- inline Json(long long x);
- inline Json(unsigned long long x);
- inline Json(double x);
- inline Json(bool x);
- inline Json(const String& x);
- inline Json(const std::string& x);
- inline Json(Str x);
- inline Json(const char* x);
- template <typename T> inline Json(const std::vector<T>& x);
- template <typename T> inline Json(T first, T last);
- inline ~Json();
-
- static inline const Json& make_null();
- static inline Json make_array();
- static inline Json make_array_reserve(int n);
- template <typename... Args>
- static inline Json array(Args&&... rest);
- static inline Json make_object();
- template <typename... Args>
- static inline Json object(Args&&... rest);
- static inline Json make_string(const String& x);
- static inline Json make_string(const std::string& x);
- static inline Json make_string(const char* s, int len);
-
- // Type information
- inline bool truthy() const;
- inline bool falsy() const;
- inline operator unspecified_bool_type() const;
- inline bool operator!() const;
-
- inline bool is_null() const;
- inline bool is_int() const;
- inline bool is_i() const;
- inline bool is_unsigned() const;
- inline bool is_u() const;
- inline bool is_signed() const;
- inline bool is_nonnegint() const;
- inline bool is_double() const;
- inline bool is_d() const;
- inline bool is_number() const;
- inline bool is_n() const;
- inline bool is_bool() const;
- inline bool is_b() const;
- inline bool is_string() const;
- inline bool is_s() const;
- inline bool is_array() const;
- inline bool is_a() const;
- inline bool is_object() const;
- inline bool is_o() const;
- inline bool is_primitive() const;
-
- inline bool empty() const;
- inline size_type size() const;
- inline bool shared() const;
-
- void clear();
-
- // Primitive extractors
- inline int64_t to_i() const;
- inline uint64_t to_u() const;
- inline uint64_t to_u64() const;
- inline bool to_i(int& x) const;
- inline bool to_i(unsigned& x) const;
- inline bool to_i(long& x) const;
- inline bool to_i(unsigned long& x) const;
- inline bool to_i(long long& x) const;
- inline bool to_i(unsigned long long& x) const;
- inline int64_t as_i() const;
- inline int64_t as_i(int64_t default_value) const;
- inline uint64_t as_u() const;
- inline uint64_t as_u(uint64_t default_value) const;
-
- inline double to_d() const;
- inline bool to_d(double& x) const;
- inline double as_d() const;
- inline double as_d(double default_value) const;
-
- inline bool to_b() const;
- inline bool to_b(bool& x) const;
- inline bool as_b() const;
- inline bool as_b(bool default_value) const;
-
- inline String to_s() const;
- inline bool to_s(Str& x) const;
- inline bool to_s(String& x) const;
- inline String& as_s();
- inline const String& as_s() const;
- inline const String& as_s(const String& default_value) const;
-
- // Object methods
- inline size_type count(Str key) const;
- inline const Json& get(Str key) const;
- inline Json& get_insert(const String& key);
- inline Json& get_insert(Str key);
- inline Json& get_insert(const char* key);
-
- inline long get_i(Str key) const;
- inline double get_d(Str key) const;
- inline bool get_b(Str key) const;
- inline String get_s(Str key) const;
-
- inline const Json_get_proxy get(Str key, Json& x) const;
- inline const Json_get_proxy get(Str key, int& x) const;
- inline const Json_get_proxy get(Str key, unsigned& x) const;
- inline const Json_get_proxy get(Str key, long& x) const;
- inline const Json_get_proxy get(Str key, unsigned long& x) const;
- inline const Json_get_proxy get(Str key, long long& x) const;
- inline const Json_get_proxy get(Str key, unsigned long long& x) const;
- inline const Json_get_proxy get(Str key, double& x) const;
- inline const Json_get_proxy get(Str key, bool& x) const;
- inline const Json_get_proxy get(Str key, Str& x) const;
- inline const Json_get_proxy get(Str key, String& x) const;
-
- const Json& operator[](Str key) const;
- inline Json_object_proxy<Json> operator[](const String& key);
- inline Json_object_str_proxy<Json> operator[](const std::string& key);
- inline Json_object_str_proxy<Json> operator[](Str key);
- inline Json_object_str_proxy<Json> operator[](const char* key);
-
- inline const Json& at(Str key) const;
- inline Json& at_insert(const String& key);
- inline Json& at_insert(Str key);
- inline Json& at_insert(const char* key);
-
- inline Json& set(const String& key, Json value);
- template <typename P>
- inline Json& set(const String& key, const Json_proxy_base<P>& value);
- inline Json& unset(Str key);
-
- inline Json& set_list();
- template <typename T, typename... Args>
- inline Json& set_list(const String& key, T value, Args&&... rest);
-
- inline std::pair<object_iterator, bool> insert(const object_value_type& x);
- inline object_iterator insert(object_iterator position,
- const object_value_type& x);
- inline object_iterator erase(object_iterator it);
- inline size_type erase(Str key);
-
- inline Json& merge(const Json& x);
- template <typename P> inline Json& merge(const Json_proxy_base<P>& x);
-
- // Array methods
- inline const Json& get(size_type x) const;
- inline Json& get_insert(size_type x);
-
- inline const Json& operator[](size_type x) const;
- inline Json_array_proxy<Json> operator[](size_type x);
-
- inline const Json& at(size_type x) const;
- inline Json& at_insert(size_type x);
-
- inline const Json& back() const;
- inline Json& back();
-
- inline Json& push_back(Json x);
- template <typename P> inline Json& push_back(const Json_proxy_base<P>& x);
- inline void pop_back();
-
- inline Json& push_back_list();
- template <typename T, typename... Args>
- inline Json& push_back_list(T first, Args&&... rest);
-
- inline array_iterator insert(array_iterator position, Json x);
- template <typename P>
- inline array_iterator insert(array_iterator position, const Json_proxy_base<P>& x);
- array_iterator erase(array_iterator first, array_iterator last);
- inline array_iterator erase(array_iterator position);
-
- void reserve(size_type n);
- void resize(size_type n);
-
- inline Json* array_data();
- inline const Json* array_data() const;
- inline const Json* array_cdata() const;
- inline Json* end_array_data();
- inline const Json* end_array_data() const;
- inline const Json* end_array_cdata() const;
-
- // Iteration
- inline const_object_iterator obegin() const;
- inline const_object_iterator oend() const;
- inline object_iterator obegin();
- inline object_iterator oend();
- inline const_object_iterator cobegin() const;
- inline const_object_iterator coend() const;
-
- inline const_array_iterator abegin() const;
- inline const_array_iterator aend() const;
- inline array_iterator abegin();
- inline array_iterator aend();
- inline const_array_iterator cabegin() const;
- inline const_array_iterator caend() const;
-
- inline const_iterator begin() const;
- inline const_iterator end() const;
- inline iterator begin();
- inline iterator end();
- inline const_iterator cbegin() const;
- inline const_iterator cend() const;
-
- // Unparsing
- static inline unparse_manipulator indent_depth(int x);
- static inline unparse_manipulator tab_width(int x);
- static inline unparse_manipulator newline_terminator(bool x);
- static inline unparse_manipulator space_separator(bool x);
-
- inline String unparse() const;
- inline String unparse(const unparse_manipulator& m) const;
- inline void unparse(StringAccum& sa) const;
- inline void unparse(StringAccum& sa, const unparse_manipulator& m) const;
-
- // Parsing
- inline bool assign_parse(const String& str);
- inline bool assign_parse(const char* first, const char* last);
-
- static inline Json parse(const String& str);
- static inline Json parse(const char* first, const char* last);
-
- // Assignment
- inline Json& operator=(const Json& x);
- inline Json& operator=(Json&& x);
- inline Json& operator=(int x);
- inline Json& operator=(unsigned x);
- inline Json& operator=(long x);
- inline Json& operator=(unsigned long x);
- inline Json& operator=(long long x);
- inline Json& operator=(unsigned long long x);
- inline Json& operator=(double x);
- inline Json& operator=(bool x);
- inline Json& operator=(const String& x);
- template <typename P> inline Json& operator=(const Json_proxy_base<P>& x);
-
- inline Json& operator++();
- inline void operator++(int);
- inline Json& operator--();
- inline void operator--(int);
- inline Json& operator+=(int x);
- inline Json& operator+=(unsigned x);
- inline Json& operator+=(long x);
- inline Json& operator+=(unsigned long x);
- inline Json& operator+=(long long x);
- inline Json& operator+=(unsigned long long x);
- inline Json& operator+=(double x);
- inline Json& operator+=(const Json& x);
- inline Json& operator-=(int x);
- inline Json& operator-=(unsigned x);
- inline Json& operator-=(long x);
- inline Json& operator-=(unsigned long x);
- inline Json& operator-=(long long x);
- inline Json& operator-=(unsigned long long x);
- inline Json& operator-=(double x);
- inline Json& operator-=(const Json& x);
-
- friend bool operator==(const Json& a, const Json& b);
-
- inline void swap(Json& x);
-
- private:
- enum {
- st_initial = 0, st_array_initial = 1, st_array_delim = 2,
- st_array_value = 3, st_object_initial = 4, st_object_delim = 5,
- st_object_key = 6, st_object_colon = 7, st_object_value = 8,
- max_depth = 2048
- };
-
- struct ComplexJson {
- int refcount;
- int size;
- ComplexJson()
- : refcount(1) {
- }
- inline void ref();
- inline void deref(json_type j);
- private:
- ComplexJson(const ComplexJson& x); // does not exist
- };
-
- struct ArrayJson;
- struct ObjectItem;
- struct ObjectJson;
-
- union rep_type {
- Json_rep_item<int64_t> i;
- Json_rep_item<uint64_t> u;
- Json_rep_item<double> d;
- String::rep_type str;
- Json_rep_item<ArrayJson*> a;
- Json_rep_item<ObjectJson*> o;
- Json_rep_item<ComplexJson*> x;
- } u_;
-
- inline void deref();
-
- inline ObjectJson* ojson() const;
- inline ArrayJson* ajson() const;
-
- int64_t hard_to_i() const;
- uint64_t hard_to_u() const;
- double hard_to_d() const;
- bool hard_to_b() const;
- String hard_to_s() const;
- inline void force_number();
- inline void force_double();
- inline Json& add(double x);
- template <typename T> inline Json& add(T x);
- inline Json& subtract(double x);
- template <typename T> inline Json& subtract(T x);
-
- const Json& hard_get(Str key) const;
- const Json& hard_get(size_type x) const;
- Json& hard_get_insert(size_type x);
-
- inline void uniqueify_object(bool convert);
- void hard_uniqueify_object(bool convert);
- inline void uniqueify_array(bool convert, int ncap);
- void hard_uniqueify_array(bool convert, int ncap);
- void* uniqueify_array_insert(bool convert, size_type pos);
-
- static unparse_manipulator default_manipulator;
- bool unparse_is_complex() const;
- static void unparse_indent(StringAccum &sa, const unparse_manipulator &m, int depth);
- void hard_unparse(StringAccum &sa, const unparse_manipulator &m, int depth) const;
-
- bool assign_parse(const char* first, const char* last, const String &str);
-
- friend class object_iterator;
- friend class const_object_iterator;
- friend class array_iterator;
- friend class const_array_iterator;
- friend Json operator+(Json);
- friend Json operator-(Json);
-};
-
-
-struct Json::ArrayJson : public ComplexJson {
- int capacity;
- Json a[0];
-
- inline ArrayJson(int cap)
- : capacity(cap) {
- size = 0;
- }
- static ArrayJson* make(int n);
- static void destroy(ArrayJson* a);
-};
-
-struct Json::ObjectItem {
- std::pair<const String, Json> v_;
- int next_;
- explicit ObjectItem(const String &key, const Json& value, int next)
- : v_(key, value), next_(next) {
- }
-};
-
-struct Json::ObjectJson : public ComplexJson {
- ObjectItem *os_;
- int n_;
- int capacity_;
- std::vector<int> hash_;
- ObjectJson()
- : os_(), n_(0), capacity_(0) {
- size = 0;
- }
- ObjectJson(const ObjectJson& x);
- ~ObjectJson();
- void grow(bool copy);
- int bucket(const char* s, int len) const {
- return String::hashcode(s, s + len) & (hash_.size() - 1);
- }
- ObjectItem& item(int p) const {
- return os_[p];
- }
- int find(const char* s, int len) const {
- if (hash_.size()) {
- int p = hash_[bucket(s, len)];
- while (p >= 0) {
- ObjectItem &oi = item(p);
- if (oi.v_.first.equals(s, len))
- return p;
- p = oi.next_;
- }
- }
- return -1;
- }
- int find_insert(const String& key, const Json& value);
- inline Json& get_insert(const String& key) {
- int p = find_insert(key, make_null());
- return item(p).v_.second;
- }
- Json& get_insert(Str key);
- void erase(int p);
- size_type erase(Str key);
- void rehash();
-};
-
-inline const Json& Json::make_null() {
- return null_json;
-}
-
-inline void Json::ComplexJson::ref() {
- if (refcount >= 0)
- ++refcount;
-}
-
-inline void Json::ComplexJson::deref(json_type j) {
- if (refcount >= 1 && --refcount == 0) {
- if (j == j_object)
- delete static_cast<ObjectJson*>(this);
- else
- ArrayJson::destroy(static_cast<ArrayJson*>(this));
- }
-}
-
-inline Json::ArrayJson* Json::ajson() const {
- precondition(u_.x.type == j_null || u_.x.type == j_array);
- return u_.a.x;
-}
-
-inline Json::ObjectJson* Json::ojson() const {
- precondition(u_.x.type == j_null || u_.x.type == j_object);
- return u_.o.x;
-}
-
-inline void Json::uniqueify_array(bool convert, int ncap) {
- if (u_.x.type != j_array || !u_.a.x || u_.a.x->refcount != 1
- || (ncap > 0 && ncap > u_.a.x->capacity))
- hard_uniqueify_array(convert, ncap);
-}
-
-inline void Json::uniqueify_object(bool convert) {
- if (u_.x.type != j_object || !u_.o.x || u_.o.x->refcount != 1)
- hard_uniqueify_object(convert);
-}
-
-
-class Json::const_object_iterator { public:
- typedef std::pair<const String, Json> value_type;
- typedef const value_type* pointer_type;
- typedef const value_type& reference_type;
- typedef std::forward_iterator_tag iterator_category;
-
- const_object_iterator() {
- }
- typedef bool (const_object_iterator::*unspecified_bool_type)() const;
- operator unspecified_bool_type() const {
- return live() ? &const_object_iterator::live : 0;
- }
- bool live() const {
- return i_ >= 0;
- }
- const value_type& operator*() const {
- return j_->ojson()->item(i_).v_;
- }
- const value_type* operator->() const {
- return &(**this);
- }
- const String& key() const {
- return (**this).first;
- }
- const Json& value() const {
- return (**this).second;
- }
- void operator++() {
- ++i_;
- fix();
- }
- void operator++(int) {
- ++(*this);
- }
- private:
- const Json* j_;
- int i_;
- const_object_iterator(const Json* j, int i)
- : j_(j), i_(i) {
- if (i_ >= 0)
- fix();
- }
- void fix() {
- ObjectJson* oj = j_->ojson();
- retry:
- if (!oj || i_ >= oj->n_)
- i_ = -1;
- else if (oj->item(i_).next_ == -2) {
- ++i_;
- goto retry;
- }
- }
- friend class Json;
- friend bool operator==(const const_object_iterator&, const const_object_iterator&);
-};
-
-class Json::object_iterator : public const_object_iterator { public:
- typedef value_type* pointer_type;
- typedef value_type& reference_type;
-
- object_iterator() {
- }
- value_type& operator*() const {
- const_cast<Json*>(j_)->uniqueify_object(false);
- return j_->ojson()->item(i_).v_;
- }
- value_type* operator->() const {
- return &(**this);
- }
- Json& value() const {
- return (**this).second;
- }
- private:
- object_iterator(Json* j, int i)
- : const_object_iterator(j, i) {
- }
- friend class Json;
-};
-
-inline bool operator==(const Json::const_object_iterator& a, const Json::const_object_iterator& b) {
- return a.j_ == b.j_ && a.i_ == b.i_;
-}
-
-inline bool operator!=(const Json::const_object_iterator& a, const Json::const_object_iterator& b) {
- return !(a == b);
-}
-
-class Json::const_array_iterator { public:
- typedef Json::size_type difference_type;
- typedef Json value_type;
- typedef const Json* pointer;
- typedef const Json& reference;
- typedef std::random_access_iterator_tag iterator_category;
-
- const_array_iterator() {
- }
- typedef bool (const_array_iterator::*unspecified_bool_type)() const;
- operator unspecified_bool_type() const {
- return live() ? &const_array_iterator::live : 0;
- }
- bool live() const {
- ArrayJson* aj = j_->ajson();
- return aj && i_ < aj->size;
- }
- const Json& operator*() const {
- return j_->ajson()->a[i_];
- }
- const Json& operator[](difference_type i) const {
- return j_->ajson()->a[i_ + i];
- }
- const Json* operator->() const {
- return &(**this);
- }
- const Json& value() const {
- return **this;
- }
- void operator++(int) {
- ++i_;
- }
- const_array_iterator& operator++() {
- ++i_;
- return *this;
- }
- void operator--(int) {
- --i_;
- }
- const_array_iterator& operator--() {
- --i_;
- return *this;
- }
- const_array_iterator& operator+=(difference_type x) {
- i_ += x;
- return *this;
- }
- const_array_iterator& operator-=(difference_type x) {
- i_ -= x;
- return *this;
- }
- private:
- const Json* j_;
- int i_;
- const_array_iterator(const Json* j, int i)
- : j_(j), i_(i) {
- }
- friend class Json;
- friend class Json::array_iterator;
- friend bool operator==(const const_array_iterator&, const const_array_iterator&);
- friend bool operator<(const const_array_iterator&, const const_array_iterator&);
- friend difference_type operator-(const const_array_iterator&, const const_array_iterator&);
-};
-
-class Json::array_iterator : public const_array_iterator { public:
- typedef const Json* pointer;
- typedef const Json& reference;
-
- array_iterator() {
- }
- Json& operator*() const {
- const_cast<Json*>(j_)->uniqueify_array(false, 0);
- return j_->ajson()->a[i_];
- }
- Json& operator[](difference_type i) const {
- const_cast<Json*>(j_)->uniqueify_array(false, 0);
- return j_->ajson()->a[i_ + i];
- }
- Json* operator->() const {
- return &(**this);
- }
- Json& value() const {
- return **this;
- }
- array_iterator& operator++() {
- ++i_;
- return *this;
- }
- array_iterator& operator--() {
- --i_;
- return *this;
- }
- array_iterator& operator+=(difference_type x) {
- i_ += x;
- return *this;
- }
- array_iterator& operator-=(difference_type x) {
- i_ -= x;
- return *this;
- }
- private:
- array_iterator(Json* j, int i)
- : const_array_iterator(j, i) {
- }
- friend class Json;
-};
-
-inline bool operator==(const Json::const_array_iterator& a, const Json::const_array_iterator& b) {
- return a.j_ == b.j_ && a.i_ == b.i_;
-}
-
-inline bool operator<(const Json::const_array_iterator& a, const Json::const_array_iterator& b) {
- return a.j_ < b.j_ || (a.j_ == b.j_ && a.i_ < b.i_);
-}
-
-inline bool operator!=(const Json::const_array_iterator& a, const Json::const_array_iterator& b) {
- return !(a == b);
-}
-
-inline bool operator<=(const Json::const_array_iterator& a, const Json::const_array_iterator& b) {
- return !(b < a);
-}
-
-inline bool operator>(const Json::const_array_iterator& a, const Json::const_array_iterator& b) {
- return b < a;
-}
-
-inline bool operator>=(const Json::const_array_iterator& a, const Json::const_array_iterator& b) {
- return !(a < b);
-}
-
-inline Json::const_array_iterator operator+(Json::const_array_iterator a, Json::const_array_iterator::difference_type i) {
- return a += i;
-}
-inline Json::array_iterator operator+(Json::array_iterator a, Json::array_iterator::difference_type i) {
- return a += i;
-}
-
-inline Json::const_array_iterator operator-(Json::const_array_iterator a, Json::const_array_iterator::difference_type i) {
- return a -= i;
-}
-inline Json::array_iterator operator-(Json::array_iterator a, Json::array_iterator::difference_type i) {
- return a -= i;
-}
-
-inline Json::const_array_iterator::difference_type operator-(const Json::const_array_iterator& a, const Json::const_array_iterator& b) {
- precondition(a.j_ == b.j_);
- return a.i_ - b.i_;
-}
-
-class Json::const_iterator { public:
- typedef std::pair<const String, Json&> value_type;
- typedef const value_type* pointer;
- typedef const value_type& reference;
- typedef std::forward_iterator_tag iterator_category;
-
- const_iterator()
- : value_(String(), *(Json*) 0) {
- }
- typedef bool (const_iterator::*unspecified_bool_type)() const;
- operator unspecified_bool_type() const {
- return live() ? &const_iterator::live : 0;
- }
- bool live() const {
- return i_ >= 0;
- }
- const value_type& operator*() const {
- return value_;
- }
- const value_type* operator->() const {
- return &(**this);
- }
- const String& key() const {
- return (**this).first;
- }
- const Json& value() const {
- return (**this).second;
- }
- void operator++() {
- ++i_;
- fix();
- }
- void operator++(int) {
- ++(*this);
- }
- private:
- const Json* j_;
- int i_;
- value_type value_;
- const_iterator(const Json* j, int i)
- : j_(j), i_(i), value_(String(), *(Json*) 0) {
- if (i_ >= 0)
- fix();
- }
- void fix() {
- if (j_->u_.x.type == j_object) {
- ObjectJson* oj = j_->ojson();
- retry:
- if (!oj || i_ >= oj->n_)
- i_ = -1;
- else if (oj->item(i_).next_ == -2) {
- ++i_;
- goto retry;
- } else {
- value_.~pair();
- new((void *) &value_) value_type(oj->item(i_).v_.first,
- oj->item(i_).v_.second);
- }
- } else {
- ArrayJson *aj = j_->ajson();
- if (!aj || unsigned(i_) >= unsigned(aj->size))
- i_ = -1;
- else {
- value_.~pair();
- new((void *) &value_) value_type(String(i_), aj->a[i_]);
- }
- }
- }
- friend class Json;
- friend bool operator==(const const_iterator &, const const_iterator &);
-};
-
-class Json::iterator : public const_iterator { public:
- typedef value_type* pointer;
- typedef value_type& reference;
-
- iterator() {
- }
- value_type& operator*() const {
- if (j_->u_.x.x->refcount != 1)
- uniqueify();
- return const_cast<value_type&>(const_iterator::operator*());
- }
- value_type* operator->() const {
- return &(**this);
- }
- Json& value() const {
- return (**this).second;
- }
- private:
- iterator(Json *j, int i)
- : const_iterator(j, i) {
- }
- void uniqueify() const {
- if (j_->u_.x.type == j_object)
- const_cast<Json*>(j_)->hard_uniqueify_object(false);
- else
- const_cast<Json*>(j_)->hard_uniqueify_array(false, 0);
- const_cast<iterator*>(this)->fix();
- }
- friend class Json;
-};
-
-inline bool operator==(const Json::const_iterator& a, const Json::const_iterator& b) {
- return a.j_ == b.j_ && a.i_ == b.i_;
-}
-
-inline bool operator!=(const Json::const_iterator& a, const Json::const_iterator& b) {
- return !(a == b);
-}
-
-
-template <typename P>
-class Json_proxy_base {
- public:
- const Json& cvalue() const {
- return static_cast<const P *>(this)->cvalue();
- }
- Json& value() {
- return static_cast<P *>(this)->value();
- }
- operator const Json&() const {
- return cvalue();
- }
- operator Json&() {
- return value();
- }
- bool truthy() const {
- return cvalue().truthy();
- }
- bool falsy() const {
- return cvalue().falsy();
- }
- operator Json::unspecified_bool_type() const {
- return cvalue();
- }
- bool operator!() const {
- return !cvalue();
- }
- bool is_null() const {
- return cvalue().is_null();
- }
- bool is_int() const {
- return cvalue().is_int();
- }
- bool is_i() const {
- return cvalue().is_i();
- }
- bool is_unsigned() const {
- return cvalue().is_unsigned();
- }
- bool is_u() const {
- return cvalue().is_u();
- }
- bool is_signed() const {
- return cvalue().is_signed();
- }
- bool is_nonnegint() const {
- return cvalue().is_nonnegint();
- }
- bool is_double() const {
- return cvalue().is_double();
- }
- bool is_d() const {
- return cvalue().is_d();
- }
- bool is_number() const {
- return cvalue().is_number();
- }
- bool is_n() const {
- return cvalue().is_n();
- }
- bool is_bool() const {
- return cvalue().is_bool();
- }
- bool is_b() const {
- return cvalue().is_b();
- }
- bool is_string() const {
- return cvalue().is_string();
- }
- bool is_s() const {
- return cvalue().is_s();
- }
- bool is_array() const {
- return cvalue().is_array();
- }
- bool is_a() const {
- return cvalue().is_a();
- }
- bool is_object() const {
- return cvalue().is_object();
- }
- bool is_o() const {
- return cvalue().is_o();
- }
- bool is_primitive() const {
- return cvalue().is_primitive();
- }
- bool empty() const {
- return cvalue().empty();
- }
- Json::size_type size() const {
- return cvalue().size();
- }
- int64_t to_i() const {
- return cvalue().to_i();
- }
- uint64_t to_u() const {
- return cvalue().to_u();
- }
- uint64_t to_u64() const {
- return cvalue().to_u64();
- }
- bool to_i(int& x) const {
- return cvalue().to_i(x);
- }
- bool to_i(unsigned& x) const {
- return cvalue().to_i(x);
- }
- bool to_i(long& x) const {
- return cvalue().to_i(x);
- }
- bool to_i(unsigned long& x) const {
- return cvalue().to_i(x);
- }
- bool to_i(long long& x) const {
- return cvalue().to_i(x);
- }
- bool to_i(unsigned long long& x) const {
- return cvalue().to_i(x);
- }
- int64_t as_i() const {
- return cvalue().as_i();
- }
- int64_t as_i(int64_t default_value) const {
- return cvalue().as_i(default_value);
- }
- uint64_t as_u() const {
- return cvalue().as_u();
- }
- uint64_t as_u(uint64_t default_value) const {
- return cvalue().as_u(default_value);
- }
- double to_d() const {
- return cvalue().to_d();
- }
- bool to_d(double& x) const {
- return cvalue().to_d(x);
- }
- double as_d() const {
- return cvalue().as_d();
- }
- double as_d(double default_value) const {
- return cvalue().as_d(default_value);
- }
- bool to_b() const {
- return cvalue().to_b();
- }
- bool to_b(bool& x) const {
- return cvalue().to_b(x);
- }
- bool as_b() const {
- return cvalue().as_b();
- }
- bool as_b(bool default_value) const {
- return cvalue().as_b(default_value);
- }
- String to_s() const {
- return cvalue().to_s();
- }
- bool to_s(Str& x) const {
- return cvalue().to_s(x);
- }
- bool to_s(String& x) const {
- return cvalue().to_s(x);
- }
- const String& as_s() const {
- return cvalue().as_s();
- }
- const String& as_s(const String& default_value) const {
- return cvalue().as_s(default_value);
- }
- Json::size_type count(Str key) const {
- return cvalue().count(key);
- }
- const Json& get(Str key) const {
- return cvalue().get(key);
- }
- Json& get_insert(const String& key) {
- return value().get_insert(key);
- }
- Json& get_insert(Str key) {
- return value().get_insert(key);
- }
- Json& get_insert(const char* key) {
- return value().get_insert(key);
- }
- long get_i(Str key) const {
- return cvalue().get_i(key);
- }
- double get_d(Str key) const {
- return cvalue().get_d(key);
- }
- bool get_b(Str key) const {
- return cvalue().get_b(key);
- }
- String get_s(Str key) const {
- return cvalue().get_s(key);
- }
- inline const Json_get_proxy get(Str key, Json& x) const;
- inline const Json_get_proxy get(Str key, int& x) const;
- inline const Json_get_proxy get(Str key, unsigned& x) const;
- inline const Json_get_proxy get(Str key, long& x) const;
- inline const Json_get_proxy get(Str key, unsigned long& x) const;
- inline const Json_get_proxy get(Str key, long long& x) const;
- inline const Json_get_proxy get(Str key, unsigned long long& x) const;
- inline const Json_get_proxy get(Str key, double& x) const;
- inline const Json_get_proxy get(Str key, bool& x) const;
- inline const Json_get_proxy get(Str key, Str& x) const;
- inline const Json_get_proxy get(Str key, String& x) const;
- const Json& operator[](Str key) const {
- return cvalue().get(key);
- }
- Json_object_proxy<P> operator[](const String& key) {
- return Json_object_proxy<P>(*static_cast<P*>(this), key);
- }
- Json_object_str_proxy<P> operator[](std::string key) {
- return Json_object_str_proxy<P>(*static_cast<P*>(this), Str(key.data(), key.length()));
- }
- Json_object_str_proxy<P> operator[](Str key) {
- return Json_object_str_proxy<P>(*static_cast<P*>(this), key);
- }
- Json_object_str_proxy<P> operator[](const char* key) {
- return Json_object_str_proxy<P>(*static_cast<P*>(this), key);
- }
- const Json& at(Str key) const {
- return cvalue().at(key);
- }
- Json& at_insert(const String& key) {
- return value().at_insert(key);
- }
- Json& at_insert(Str key) {
- return value().at_insert(key);
- }
- Json& at_insert(const char* key) {
- return value().at_insert(key);
- }
- inline Json& set(const String& key, Json value) {
- return this->value().set(key, value);
- }
- template <typename Q>
- inline Json& set(const String& key, const Json_proxy_base<Q>& value) {
- return this->value().set(key, value);
- }
- Json& unset(Str key) {
- return value().unset(key);
- }
- template <typename... Args>
- inline Json& set_list(Args&&... args) {
- return value().set_list(std::forward<Args>(args)...);
- }
- std::pair<Json::object_iterator, bool> insert(const Json::object_value_type &x) {
- return value().insert(x);
- }
- Json::object_iterator insert(Json::object_iterator position, const Json::object_value_type &x) {
- return value().insert(position, x);
- }
- Json::object_iterator erase(Json::object_iterator it) {
- return value().erase(it);
- }
- Json::size_type erase(Str key) {
- return value().erase(key);
- }
- Json& merge(const Json& x) {
- return value().merge(x);
- }
- template <typename P2> Json& merge(const Json_proxy_base<P2>& x) {
- return value().merge(x.cvalue());
- }
- const Json& get(Json::size_type x) const {
- return cvalue().get(x);
- }
- Json& get_insert(Json::size_type x) {
- return value().get_insert(x);
- }
- const Json& operator[](int key) const {
- return cvalue().at(key);
- }
- Json_array_proxy<P> operator[](int key) {
- return Json_array_proxy<P>(*static_cast<P*>(this), key);
- }
- const Json& at(Json::size_type x) const {
- return cvalue().at(x);
- }
- Json& at_insert(Json::size_type x) {
- return value().at_insert(x);
- }
- const Json& back() const {
- return cvalue().back();
- }
- Json& back() {
- return value().back();
- }
- Json& push_back(Json x) {
- return value().push_back(std::move(x));
- }
- template <typename Q> Json& push_back(const Json_proxy_base<Q>& x) {
- return value().push_back(x);
- }
- void pop_back() {
- value().pop_back();
- }
- template <typename... Args> Json& push_back_list(Args&&... args) {
- return value().push_back_list(std::forward<Args>(args)...);
- }
- Json::array_iterator insert(Json::array_iterator position, Json x) {
- return value().insert(position, std::move(x));
- }
- template <typename Q> Json::array_iterator insert(Json::array_iterator position, const Json_proxy_base<Q>& x) {
- return value().insert(position, x);
- }
- Json::array_iterator erase(Json::array_iterator first, Json::array_iterator last) {
- return value().erase(first, last);
- }
- Json::array_iterator erase(Json::array_iterator position) {
- return value().erase(position);
- }
- void resize(Json::size_type n) {
- value().resize(n);
- }
- void unparse(StringAccum& sa) const {
- return cvalue().unparse(sa);
- }
- void unparse(StringAccum& sa, const Json::unparse_manipulator& m) const {
- return cvalue().unparse(sa, m);
- }
- String unparse() const {
- return cvalue().unparse();
- }
- String unparse(const Json::unparse_manipulator& m) const {
- return cvalue().unparse(m);
- }
- bool assign_parse(const String& str) {
- return value().assign_parse(str);
- }
- bool assign_parse(const char* first, const char* last) {
- return value().assign_parse(first, last);
- }
- void swap(Json& x) {
- value().swap(x);
- }
- Json& operator++() {
- return ++value();
- }
- void operator++(int) {
- value()++;
- }
- Json& operator--() {
- return --value();
- }
- void operator--(int) {
- value()--;
- }
- Json& operator+=(int x) {
- return value() += x;
- }
- Json& operator+=(unsigned x) {
- return value() += x;
- }
- Json& operator+=(long x) {
- return value() += x;
- }
- Json& operator+=(unsigned long x) {
- return value() += x;
- }
- Json& operator+=(long long x) {
- return value() += x;
- }
- Json& operator+=(unsigned long long x) {
- return value() += x;
- }
- Json& operator+=(double x) {
- return value() += x;
- }
- Json& operator+=(const Json& x) {
- return value() += x;
- }
- Json& operator-=(int x) {
- return value() -= x;
- }
- Json& operator-=(unsigned x) {
- return value() -= x;
- }
- Json& operator-=(long x) {
- return value() -= x;
- }
- Json& operator-=(unsigned long x) {
- return value() -= x;
- }
- Json& operator-=(long long x) {
- return value() -= x;
- }
- Json& operator-=(unsigned long long x) {
- return value() -= x;
- }
- Json& operator-=(double x) {
- return value() -= x;
- }
- Json& operator-=(const Json& x) {
- return value() -= x;
- }
- Json::const_object_iterator obegin() const {
- return cvalue().obegin();
- }
- Json::const_object_iterator oend() const {
- return cvalue().oend();
- }
- Json::object_iterator obegin() {
- return value().obegin();
- }
- Json::object_iterator oend() {
- return value().oend();
- }
- Json::const_object_iterator cobegin() const {
- return cvalue().cobegin();
- }
- Json::const_object_iterator coend() const {
- return cvalue().coend();
- }
- Json::const_array_iterator abegin() const {
- return cvalue().abegin();
- }
- Json::const_array_iterator aend() const {
- return cvalue().aend();
- }
- Json::array_iterator abegin() {
- return value().abegin();
- }
- Json::array_iterator aend() {
- return value().aend();
- }
- Json::const_array_iterator cabegin() const {
- return cvalue().cabegin();
- }
- Json::const_array_iterator caend() const {
- return cvalue().caend();
- }
- Json::const_iterator begin() const {
- return cvalue().begin();
- }
- Json::const_iterator end() const {
- return cvalue().end();
- }
- Json::iterator begin() {
- return value().begin();
- }
- Json::iterator end() {
- return value().end();
- }
- Json::const_iterator cbegin() const {
- return cvalue().cbegin();
- }
- Json::const_iterator cend() const {
- return cvalue().cend();
- }
-};
-
-template <typename T>
-class Json_object_proxy : public Json_proxy_base<Json_object_proxy<T> > {
- public:
- const Json& cvalue() const {
- return base_.get(key_);
- }
- Json& value() {
- return base_.get_insert(key_);
- }
- Json& operator=(const Json& x) {
- return value() = x;
- }
- Json& operator=(Json&& x) {
- return value() = std::move(x);
- }
- Json& operator=(const Json_object_proxy<T>& x) {
- return value() = x.cvalue();
- }
- template <typename P> Json& operator=(const Json_proxy_base<P>& x) {
- return value() = x.cvalue();
- }
- Json_object_proxy(T& ref, const String& key)
- : base_(ref), key_(key) {
- }
- T &base_;
- String key_;
-};
-
-template <typename T>
-class Json_object_str_proxy : public Json_proxy_base<Json_object_str_proxy<T> > {
- public:
- const Json& cvalue() const {
- return base_.get(key_);
- }
- Json& value() {
- return base_.get_insert(key_);
- }
- Json& operator=(const Json& x) {
- return value() = x;
- }
- Json& operator=(Json&& x) {
- return value() = std::move(x);
- }
- Json& operator=(const Json_object_str_proxy<T>& x) {
- return value() = x.cvalue();
- }
- template <typename P> Json& operator=(const Json_proxy_base<P>& x) {
- return value() = x.cvalue();
- }
- Json_object_str_proxy(T& ref, Str key)
- : base_(ref), key_(key) {
- }
- T &base_;
- Str key_;
-};
-
-template <typename T>
-class Json_array_proxy : public Json_proxy_base<Json_array_proxy<T> > {
- public:
- const Json& cvalue() const {
- return base_.get(key_);
- }
- Json& value() {
- return base_.get_insert(key_);
- }
- Json& operator=(const Json& x) {
- return value() = x;
- }
- Json& operator=(Json&& x) {
- return value() = std::move(x);
- }
- Json& operator=(const Json_array_proxy<T>& x) {
- return value() = x.cvalue();
- }
- template <typename P> Json& operator=(const Json_proxy_base<P>& x) {
- return value() = x.cvalue();
- }
- Json_array_proxy(T& ref, int key)
- : base_(ref), key_(key) {
- }
- T &base_;
- int key_;
-};
-
-class Json_get_proxy : public Json_proxy_base<Json_get_proxy> {
- public:
- const Json& cvalue() const {
- return base_;
- }
- operator Json::unspecified_bool_type() const {
- return status_ ? &Json::is_null : 0;
- }
- bool operator!() const {
- return !status_;
- }
- bool status() const {
- return status_;
- }
- const Json_get_proxy& status(bool& x) const {
- x = status_;
- return *this;
- }
- Json_get_proxy(const Json& ref, bool status)
- : base_(ref), status_(status) {
- }
- const Json& base_;
- bool status_;
- private:
- Json_get_proxy& operator=(const Json_get_proxy& x);
-};
-
-template <typename T>
-inline const Json_get_proxy Json_proxy_base<T>::get(Str key, Json& x) const {
- return cvalue().get(key, x);
-}
-template <typename T>
-inline const Json_get_proxy Json_proxy_base<T>::get(Str key, int& x) const {
- return cvalue().get(key, x);
-}
-template <typename T>
-inline const Json_get_proxy Json_proxy_base<T>::get(Str key, unsigned& x) const {
- return cvalue().get(key, x);
-}
-template <typename T>
-inline const Json_get_proxy Json_proxy_base<T>::get(Str key, long& x) const {
- return cvalue().get(key, x);
-}
-template <typename T>
-inline const Json_get_proxy Json_proxy_base<T>::get(Str key, unsigned long& x) const {
- return cvalue().get(key, x);
-}
-template <typename T>
-inline const Json_get_proxy Json_proxy_base<T>::get(Str key, long long& x) const {
- return cvalue().get(key, x);
-}
-template <typename T>
-inline const Json_get_proxy Json_proxy_base<T>::get(Str key, unsigned long long& x) const {
- return cvalue().get(key, x);
-}
-template <typename T>
-inline const Json_get_proxy Json_proxy_base<T>::get(Str key, double& x) const {
- return cvalue().get(key, x);
-}
-template <typename T>
-inline const Json_get_proxy Json_proxy_base<T>::get(Str key, bool& x) const {
- return cvalue().get(key, x);
-}
-template <typename T>
-inline const Json_get_proxy Json_proxy_base<T>::get(Str key, Str& x) const {
- return cvalue().get(key, x);
-}
-template <typename T>
-inline const Json_get_proxy Json_proxy_base<T>::get(Str key, String& x) const {
- return cvalue().get(key, x);
-}
-
-
-/** @brief Construct a null Json. */
-inline Json::Json() {
- memset(&u_, 0, sizeof(u_));
-}
-/** @brief Construct a null Json. */
-inline Json::Json(const null_t&) {
- memset(&u_, 0, sizeof(u_));
-}
-/** @brief Construct a Json copy of @a x. */
-inline Json::Json(const Json& x)
- : u_(x.u_) {
- if (u_.x.type < 0)
- u_.str.ref();
- if (u_.x.x && (u_.x.type == j_array || u_.x.type == j_object))
- u_.x.x->ref();
-}
-/** @overload */
-template <typename P> inline Json::Json(const Json_proxy_base<P>& x)
- : u_(x.cvalue().u_) {
- if (u_.x.type < 0)
- u_.str.ref();
- if (u_.x.x && (u_.x.type == j_array || u_.x.type == j_object))
- u_.x.x->ref();
-}
-/** @overload */
-inline Json::Json(Json&& x)
- : u_(std::move(x.u_)) {
- memset(&x.u_, 0, sizeof(x.u_));
-}
-/** @brief Construct simple Json values. */
-inline Json::Json(int x) {
- u_.i.x = x;
- u_.i.type = j_int;
-}
-inline Json::Json(unsigned x) {
- u_.u.x = x;
- u_.u.type = j_unsigned;
-}
-inline Json::Json(long x) {
- u_.i.x = x;
- u_.i.type = j_int;
-}
-inline Json::Json(unsigned long x) {
- u_.u.x = x;
- u_.u.type = j_unsigned;
-}
-inline Json::Json(long long x) {
- u_.i.x = x;
- u_.i.type = j_int;
-}
-inline Json::Json(unsigned long long x) {
- u_.u.x = x;
- u_.u.type = j_unsigned;
-}
-inline Json::Json(double x) {
- u_.d.x = x;
- u_.d.type = j_double;
-}
-inline Json::Json(bool x) {
- u_.i.x = x;
- u_.i.type = j_bool;
-}
-inline Json::Json(const String& x) {
- u_.str = x.internal_rep();
- u_.str.ref();
-}
-inline Json::Json(const std::string& x) {
- u_.str.reset_ref();
- String str(x);
- str.swap(u_.str);
-}
-inline Json::Json(Str x) {
- u_.str.reset_ref();
- String str(x);
- str.swap(u_.str);
-}
-inline Json::Json(const char* x) {
- u_.str.reset_ref();
- String str(x);
- str.swap(u_.str);
-}
-/** @brief Construct an array Json containing the elements of @a x. */
-template <typename T>
-inline Json::Json(const std::vector<T> &x) {
- u_.a.type = j_array;
- u_.a.x = ArrayJson::make(int(x.size()));
- for (typename std::vector<T>::const_iterator it = x.begin();
- it != x.end(); ++it) {
- new((void*) &u_.a.x->a[u_.a.x->size]) Json(*it);
- ++u_.a.x->size;
- }
-}
-
-template <typename T> struct Json_iterator_initializer {
- template <typename I>
- static inline void run(Json& j, I first, I last) {
- for (j = Json::make_array(); first != last; ++first)
- j.push_back(Json(*first));
- }
-};
-template <typename T, typename U>
-struct Json_iterator_initializer<std::pair<T, U> > {
- template <typename I>
- static inline void run(Json& j, I first, I last) {
- for (j = Json::make_object(); first != last; ++first)
- j.set((*first).first, (*first).second);
- }
-};
-
-/** @brief Construct an array Json containing the elements in [@a first,
- @a last). */
-template <typename T>
-inline Json::Json(T first, T last) {
- u_.a.type = 0;
- Json_iterator_initializer<typename std::iterator_traits<T>::value_type>::run(*this, first, last);
-}
-
-/** @cond never */
-inline void Json::deref() {
- if (u_.x.type < 0)
- u_.str.deref();
- else if (u_.x.x && unsigned(u_.x.type - j_array) < 2)
- u_.x.x->deref(json_type(u_.x.type));
-}
-/** @endcond never */
-
-inline Json::~Json() {
- deref();
-}
-
-/** @brief Return an empty array-valued Json. */
-inline Json Json::make_array() {
- Json j;
- j.u_.x.type = j_array;
- return j;
-}
-/** @brief Return an array-valued Json containing @a args. */
-template <typename... Args>
-inline Json Json::array(Args&&... args) {
- Json j;
- j.u_.x.type = j_array;
- j.reserve(sizeof...(args));
- j.push_back_list(std::forward<Args>(args)...);
- return j;
-}
-/** @brief Return an empty array-valued Json with reserved space for @a n items. */
-inline Json Json::make_array_reserve(int n) {
- Json j;
- j.u_.a.type = j_array;
- j.u_.a.x = n ? ArrayJson::make(n) : 0;
- return j;
-}
-/** @brief Return an empty object-valued Json. */
-inline Json Json::make_object() {
- Json j;
- j.u_.o.type = j_object;
- return j;
-}
-/** @brief Return an empty object-valued Json. */
-template <typename... Args>
-inline Json Json::object(Args&&... rest) {
- Json j;
- j.u_.o.type = j_object;
- j.set_list(std::forward<Args>(rest)...);
- return j;
-}
-/** @brief Return a string-valued Json. */
-inline Json Json::make_string(const String& x) {
- return Json(x);
-}
-/** @overload */
-inline Json Json::make_string(const std::string& x) {
- return Json(x);
-}
-/** @overload */
-inline Json Json::make_string(const char *s, int len) {
- return Json(String(s, len));
-}
-
-/** @brief Test if this Json is truthy. */
-inline bool Json::truthy() const {
- return (u_.x.x ? u_.x.type >= 0 || u_.str.length
- : (unsigned) (u_.x.type - 1) < (unsigned) (j_int - 1));
-}
-/** @brief Test if this Json is falsy. */
-inline bool Json::falsy() const {
- return !truthy();
-}
-/** @brief Test if this Json is truthy.
- @sa empty() */
-inline Json::operator unspecified_bool_type() const {
- return truthy() ? &Json::is_null : 0;
-}
-/** @brief Return true if this Json is falsy. */
-inline bool Json::operator!() const {
- return !truthy();
-}
-
-/** @brief Test this Json's type. */
-inline bool Json::is_null() const {
- return !u_.x.x && u_.x.type == j_null;
-}
-inline bool Json::is_int() const {
- return unsigned(u_.x.type - j_int) <= unsigned(j_unsigned - j_int);
-}
-inline bool Json::is_i() const {
- return is_int();
-}
-inline bool Json::is_unsigned() const {
- return u_.x.type == j_unsigned;
-}
-inline bool Json::is_u() const {
- return is_unsigned();
-}
-inline bool Json::is_signed() const {
- return u_.x.type == j_int;
-}
-inline bool Json::is_nonnegint() const {
- return u_.x.type == j_unsigned
- || (u_.x.type == j_int && u_.i.x >= 0);
-}
-inline bool Json::is_double() const {
- return u_.x.type == j_double;
-}
-inline bool Json::is_d() const {
- return is_double();
-}
-inline bool Json::is_number() const {
- return unsigned(u_.x.type - j_int) <= unsigned(j_double - j_int);
-}
-inline bool Json::is_n() const {
- return is_number();
-}
-inline bool Json::is_bool() const {
- return u_.x.type == j_bool;
-}
-inline bool Json::is_b() const {
- return is_bool();
-}
-inline bool Json::is_string() const {
- return u_.x.x && u_.x.type <= 0;
-}
-inline bool Json::is_s() const {
- return is_string();
-}
-inline bool Json::is_array() const {
- return u_.x.type == j_array;
-}
-inline bool Json::is_a() const {
- return is_array();
-}
-inline bool Json::is_object() const {
- return u_.x.type == j_object;
-}
-inline bool Json::is_o() const {
- return is_object();
-}
-/** @brief Test if this Json is a primitive value, not including null. */
-inline bool Json::is_primitive() const {
- return u_.x.type >= j_int || (u_.x.x && u_.x.type <= 0);
-}
-
-/** @brief Return true if this Json is null, an empty array, or an empty
- object. */
-inline bool Json::empty() const {
- return unsigned(u_.x.type) < unsigned(j_int)
- && (!u_.x.x || u_.x.x->size == 0);
-}
-/** @brief Return the number of elements in this complex Json.
- @pre is_array() || is_object() || is_null() */
-inline Json::size_type Json::size() const {
- precondition(unsigned(u_.x.type) < unsigned(j_int));
- return u_.x.x ? u_.x.x->size : 0;
-}
-/** @brief Test if this complex Json is shared. */
-inline bool Json::shared() const {
- return u_.x.x && (u_.x.type == j_array || u_.x.type == j_object)
- && u_.x.x->refcount != 1;
-}
-
-// Primitive methods
-
-/** @brief Return this Json converted to an integer.
-
- Converts any Json to an integer value. Numeric Jsons convert as you'd
- expect. Null Jsons convert to 0; false boolean Jsons to 0 and true
- boolean Jsons to 1; string Jsons to a number parsed from their initial
- portions; and array and object Jsons to size().
- @sa as_i() */
-inline int64_t Json::to_i() const {
- if (is_int())
- return u_.i.x;
- else
- return hard_to_i();
-}
-
-inline uint64_t Json::to_u() const {
- if (is_int())
- return u_.u.x;
- else
- return hard_to_u();
-}
-
-/** @brief Extract this integer Json's value into @a x.
- @param[out] x value storage
- @return True iff this Json stores an integer value.
-
- If false is returned (!is_number() or the number is not parseable as a
- pure integer), @a x remains unchanged. */
-inline bool Json::to_i(int& x) const {
- if (is_int()) {
- x = u_.i.x;
- return true;
- } else if (is_double() && u_.d.x == double(int(u_.d.x))) {
- x = int(u_.d.x);
- return true;
- } else
- return false;
-}
-
-/** @overload */
-inline bool Json::to_i(unsigned& x) const {
- if (is_int()) {
- x = u_.u.x;
- return true;
- } else if (is_double() && u_.d.x == double(unsigned(u_.d.x))) {
- x = unsigned(u_.d.x);
- return true;
- } else
- return false;
-}
-
-/** @overload */
-inline bool Json::to_i(long& x) const {
- if (is_int()) {
- x = u_.i.x;
- return true;
- } else if (is_double() && u_.d.x == double(long(u_.d.x))) {
- x = long(u_.d.x);
- return true;
- } else
- return false;
-}
-
-/** @overload */
-inline bool Json::to_i(unsigned long& x) const {
- if (is_int()) {
- x = u_.u.x;
- return true;
- } else if (is_double() && u_.d.x == double((unsigned long) u_.d.x)) {
- x = (unsigned long) u_.d.x;
- return true;
- } else
- return false;
-}
-
-/** @overload */
-inline bool Json::to_i(long long& x) const {
- if (is_int()) {
- x = u_.i.x;
- return true;
- } else if (is_double() && u_.d.x == double((long long) u_.d.x)) {
- x = (long long) u_.d.x;
- return true;
- } else
- return false;
-}
-
-/** @overload */
-inline bool Json::to_i(unsigned long long& x) const {
- if (is_int()) {
- x = u_.u.x;
- return true;
- } else if (is_double() && u_.d.x == double((unsigned long long) u_.d.x)) {
- x = (unsigned long long) u_.d.x;
- return true;
- } else
- return false;
-}
-
-/** @brief Return this Json converted to a 64-bit unsigned integer.
-
- See to_i() for the conversion rules. */
-inline uint64_t Json::to_u64() const {
- return to_u();
-}
-
-/** @brief Return the integer value of this numeric Json.
- @pre is_number()
- @sa to_i() */
-inline int64_t Json::as_i() const {
- precondition(is_int() || is_double());
- return is_int() ? u_.i.x : int64_t(u_.d.x);
-}
-
-/** @brief Return the integer value of this numeric Json or @a default_value. */
-inline int64_t Json::as_i(int64_t default_value) const {
- if (is_int() || is_double())
- return as_i();
- else
- return default_value;
-}
-
-/** @brief Return the unsigned integer value of this numeric Json.
- @pre is_number()
- @sa to_i() */
-inline uint64_t Json::as_u() const {
- precondition(is_int() || is_double());
- return is_int() ? u_.u.x : uint64_t(u_.d.x);
-}
-
-/** @brief Return the integer value of this numeric Json or @a default_value. */
-inline uint64_t Json::as_u(uint64_t default_value) const {
- if (is_int() || is_double())
- return as_u();
- else
- return default_value;
-}
-
-/** @brief Return this Json converted to a double.
-
- Converts any Json to an integer value. Numeric Jsons convert as you'd
- expect. Null Jsons convert to 0; false boolean Jsons to 0 and true
- boolean Jsons to 1; string Jsons to a number parsed from their initial
- portions; and array and object Jsons to size().
- @sa as_d() */
-inline double Json::to_d() const {
- if (is_double())
- return u_.d.x;
- else
- return hard_to_d();
-}
-
-/** @brief Extract this numeric Json's value into @a x.
- @param[out] x value storage
- @return True iff is_number().
-
- If !is_number(), @a x remains unchanged. */
-inline bool Json::to_d(double& x) const {
- if (is_double() || is_int()) {
- x = to_d();
- return true;
- } else
- return false;
-}
-
-/** @brief Return the double value of this numeric Json.
- @pre is_number()
- @sa to_d() */
-inline double Json::as_d() const {
- precondition(is_double() || is_int());
- if (is_double())
- return u_.d.x;
- else if (u_.x.type == j_int)
- return u_.i.x;
- else
- return u_.u.x;
-}
-
-/** @brief Return the double value of this numeric Json or @a default_value. */
-inline double Json::as_d(double default_value) const {
- if (!is_double() && !is_int())
- return default_value;
- else
- return as_d();
-}
-
-/** @brief Return this Json converted to a boolean.
-
- Converts any Json to a boolean value. Boolean Jsons convert as you'd
- expect. Null Jsons convert to false; zero-valued numeric Jsons to false,
- and other numeric Jsons to true; empty string Jsons to false, and other
- string Jsons to true; and array and object Jsons to !empty().
- @sa as_b() */
-inline bool Json::to_b() const {
- if (is_bool())
- return u_.i.x;
- else
- return hard_to_b();
-}
-
-/** @brief Extract this boolean Json's value into @a x.
- @param[out] x value storage
- @return True iff is_bool().
-
- If !is_bool(), @a x remains unchanged. */
-inline bool Json::to_b(bool& x) const {
- if (is_bool()) {
- x = u_.i.x;
- return true;
- } else
- return false;
-}
-
-/** @brief Return the value of this boolean Json.
- @pre is_bool()
- @sa to_b() */
-inline bool Json::as_b() const {
- precondition(is_bool());
- return u_.i.x;
-}
-
-/** @brief Return the boolean value of this numeric Json or @a default_value. */
-inline bool Json::as_b(bool default_value) const {
- if (is_bool())
- return as_b();
- else
- return default_value;
-}
-
-/** @brief Return this Json converted to a string.
-
- Converts any Json to a string value. String Jsons convert as you'd expect.
- Null Jsons convert to the empty string; numeric Jsons to their string
- values; boolean Jsons to "false" or "true"; and array and object Jsons to
- "[Array]" and "[Object]", respectively.
- @sa as_s() */
-inline String Json::to_s() const {
- if (u_.x.type <= 0 && u_.x.x)
- return String(u_.str);
- else
- return hard_to_s();
-}
-
-/** @brief Extract this string Json's value into @a x.
- @param[out] x value storage
- @return True iff is_string().
-
- If !is_string(), @a x remains unchanged. */
-inline bool Json::to_s(Str& x) const {
- if (u_.x.type <= 0 && u_.x.x) {
- x.assign(u_.str.data, u_.str.length);
- return true;
- } else
- return false;
-}
-
-/** @brief Extract this string Json's value into @a x.
- @param[out] x value storage
- @return True iff is_string().
-
- If !is_string(), @a x remains unchanged. */
-inline bool Json::to_s(String& x) const {
- if (u_.x.type <= 0 && u_.x.x) {
- x.assign(u_.str);
- return true;
- } else
- return false;
-}
-
-/** @brief Return the value of this string Json.
- @pre is_string()
- @sa to_s() */
-inline const String& Json::as_s() const {
- precondition(u_.x.type <= 0 && u_.x.x);
- return reinterpret_cast<const String&>(u_.str);
-}
-
-/** @overload */
-inline String& Json::as_s() {
- precondition(u_.x.type <= 0 && u_.x.x);
- return reinterpret_cast<String&>(u_.str);
-}
-
-/** @brief Return the value of this string Json or @a default_value. */
-inline const String& Json::as_s(const String& default_value) const {
- if (u_.x.type > 0 || !u_.x.x)
- return default_value;
- else
- return as_s();
-}
-
-inline void Json::force_number() {
- precondition((u_.x.type == j_null && !u_.x.x) || u_.x.type == j_int || u_.x.type == j_double);
- if (u_.x.type == j_null && !u_.x.x)
- u_.x.type = j_int;
-}
-
-inline void Json::force_double() {
- precondition((u_.x.type == j_null && !u_.x.x) || u_.x.type == j_int || u_.x.type == j_double);
- if (u_.x.type == j_null && !u_.x.x)
- u_.x.type = j_double;
- else if (u_.x.type == j_int) {
- u_.d.x = u_.i.x;
- u_.d.type = j_double;
- } else if (u_.x.type == j_unsigned) {
- u_.d.x = u_.u.x;
- u_.d.type = j_double;
- }
-}
-
-
-// Object methods
-
-/** @brief Return 1 if this object Json contains @a key, 0 otherwise.
-
- Returns 0 if this is not an object Json. */
-inline Json::size_type Json::count(Str key) const {
- precondition(u_.x.type == j_null || u_.x.type == j_object);
- return u_.o.x ? ojson()->find(key.data(), key.length()) >= 0 : 0;
-}
-
-/** @brief Return the value at @a key in an object or array Json.
-
- If this is an array Json, and @a key is the simplest base-10
- representation of an integer <em>i</em>, then returns get(<em>i</em>). If
- this is neither an array nor an object, returns a null Json. */
-inline const Json& Json::get(Str key) const {
- int i;
- ObjectJson *oj;
- if (is_object() && (oj = ojson())
- && (i = oj->find(key.data(), key.length())) >= 0)
- return oj->item(i).v_.second;
- else
- return hard_get(key);
-}
-
-/** @brief Return a reference to the value of @a key in an object Json.
-
- This Json is first converted to an object. Arrays are converted to objects
- with numeric keys. Other types of Json are converted to empty objects.
- If !count(@a key), then a null Json is inserted at @a key. */
-inline Json& Json::get_insert(const String &key) {
- uniqueify_object(true);
- return ojson()->get_insert(key);
-}
-
-/** @overload */
-inline Json& Json::get_insert(Str key) {
- uniqueify_object(true);
- return ojson()->get_insert(key);
-}
-
-/** @overload */
-inline Json& Json::get_insert(const char *key) {
- uniqueify_object(true);
- return ojson()->get_insert(Str(key));
-}
-
-/** @brief Return get(@a key).to_i(). */
-inline long Json::get_i(Str key) const {
- return get(key).to_i();
-}
-
-/** @brief Return get(@a key).to_d(). */
-inline double Json::get_d(Str key) const {
- return get(key).to_d();
-}
-
-/** @brief Return get(@a key).to_b(). */
-inline bool Json::get_b(Str key) const {
- return get(key).to_b();
-}
-
-/** @brief Return get(@a key).to_s(). */
-inline String Json::get_s(Str key) const {
- return get(key).to_s();
-}
-
-/** @brief Extract this object Json's value at @a key into @a x.
- @param[out] x value storage
- @return proxy for *this
-
- @a x is assigned iff count(@a key). The return value is a proxy
- object that mostly behaves like *this. However, the proxy is "truthy"
- iff count(@a key) and @a x was assigned. The proxy also has status()
- methods that return the extraction status. For example:
-
- <code>
- Json j = Json::parse("{\"a\":1,\"b\":2}"), x;
- assert(j.get("a", x)); // extraction succeeded
- assert(x == Json(1));
- assert(!j.get("c", x)); // no "c" key
- assert(x == Json(1)); // x remains unchanged
- assert(!j.get("c", x).status()); // can use ".status()" for clarity
-
- // Can chain .get() methods to extract multiple values
- Json a, b, c;
- j.get("a", a).get("b", b);
- assert(a == Json(1) && b == Json(2));
-
- // Use .status() to return or assign extraction status
- bool a_status, b_status, c_status;
- j.get("a", a).status(a_status)
- .get("b", b).status(b_status)
- .get("c", c).status(c_status);
- assert(a_status && b_status && !c_status);
- </code>
-
- Overloaded versions of @a get() can extract integer, double, boolean,
- and string values for specific keys. These versions succeed iff
- count(@a key) and the corresponding value has the expected type. For
- example:
-
- <code>
- Json j = Json::parse("{\"a\":1,\"b\":\"\"}");
- int a, b;
- bool a_status, b_status;
- j.get("a", a).status(a_status).get("b", b).status(b_status);
- assert(a_status && a == 1 && !b_status);
- </code> */
-inline const Json_get_proxy Json::get(Str key, Json& x) const {
- int i;
- ObjectJson *oj;
- if (is_object() && (oj = ojson())
- && (i = oj->find(key.data(), key.length())) >= 0) {
- x = oj->item(i).v_.second;
- return Json_get_proxy(*this, true);
- } else
- return Json_get_proxy(*this, false);
-}
-
-/** @overload */
-inline const Json_get_proxy Json::get(Str key, int &x) const {
- return Json_get_proxy(*this, get(key).to_i(x));
-}
-
-/** @overload */
-inline const Json_get_proxy Json::get(Str key, unsigned& x) const {
- return Json_get_proxy(*this, get(key).to_i(x));
-}
-
-/** @overload */
-inline const Json_get_proxy Json::get(Str key, long& x) const {
- return Json_get_proxy(*this, get(key).to_i(x));
-}
-
-/** @overload */
-inline const Json_get_proxy Json::get(Str key, unsigned long& x) const {
- return Json_get_proxy(*this, get(key).to_i(x));
-}
-
-/** @overload */
-inline const Json_get_proxy Json::get(Str key, long long& x) const {
- return Json_get_proxy(*this, get(key).to_i(x));
-}
-
-/** @overload */
-inline const Json_get_proxy Json::get(Str key, unsigned long long& x) const {
- return Json_get_proxy(*this, get(key).to_i(x));
-}
-
-/** @overload */
-inline const Json_get_proxy Json::get(Str key, double& x) const {
- return Json_get_proxy(*this, get(key).to_d(x));
-}
-
-/** @overload */
-inline const Json_get_proxy Json::get(Str key, bool& x) const {
- return Json_get_proxy(*this, get(key).to_b(x));
-}
-
-/** @overload */
-inline const Json_get_proxy Json::get(Str key, Str& x) const {
- return Json_get_proxy(*this, get(key).to_s(x));
-}
-
-/** @overload */
-inline const Json_get_proxy Json::get(Str key, String& x) const {
- return Json_get_proxy(*this, get(key).to_s(x));
-}
-
-
-/** @brief Return the value at @a key in an object or array Json.
- @sa Json::get() */
-inline const Json& Json::operator[](Str key) const {
- return get(key);
-}
-
-/** @brief Return a proxy reference to the value at @a key in an object Json.
-
- Returns the current @a key value if it exists. Otherwise, returns a proxy
- that acts like a null Json. If this proxy is assigned, this Json is
- converted to an object as by get_insert(), and then extended as necessary
- to contain the new value. */
-inline Json_object_proxy<Json> Json::operator[](const String& key) {
- return Json_object_proxy<Json>(*this, key);
-}
-
-/** @overload */
-inline Json_object_str_proxy<Json> Json::operator[](const std::string& key) {
- return Json_object_str_proxy<Json>(*this, Str(key.data(), key.length()));
-}
-
-/** @overload */
-inline Json_object_str_proxy<Json> Json::operator[](Str key) {
- return Json_object_str_proxy<Json>(*this, key);
-}
-
-/** @overload */
-inline Json_object_str_proxy<Json> Json::operator[](const char* key) {
- return Json_object_str_proxy<Json>(*this, key);
-}
-
-/** @brief Return the value at @a key in an object Json.
- @pre is_object() && count(@a key) */
-inline const Json& Json::at(Str key) const {
- precondition(is_object() && u_.o.x);
- ObjectJson *oj = ojson();
- int i = oj->find(key.data(), key.length());
- precondition(i >= 0);
- return oj->item(i).v_.second;
-}
-
-/** @brief Return a reference to the value at @a key in an object Json.
- @pre is_object()
-
- Returns a newly-inserted null Json if !count(@a key). */
-inline Json& Json::at_insert(const String &key) {
- precondition(is_object());
- return get_insert(key);
-}
-
-/** @overload */
-inline Json& Json::at_insert(Str key) {
- precondition(is_object());
- return get_insert(key);
-}
-
-/** @overload */
-inline Json& Json::at_insert(const char *key) {
- precondition(is_object());
- return get_insert(Str(key));
-}
-
-/** @brief Set the value of @a key to @a value in this object Json.
- @return this Json
-
- An array Json is converted to an object Json with numeric keys. Other
- non-object Jsons are converted to empty objects. */
-inline Json& Json::set(const String& key, Json value) {
- uniqueify_object(true);
- ojson()->get_insert(key) = std::move(value);
- return *this;
-}
-
-/** @overload */
-template <typename P>
-inline Json& Json::set(const String& key, const Json_proxy_base<P>& value) {
- uniqueify_object(true);
- ojson()->get_insert(key) = value.cvalue();
- return *this;
-}
-
-/** @brief Remove the value of @a key from an object Json.
- @return this Json
- @sa erase() */
-inline Json& Json::unset(Str key) {
- if (is_object()) {
- uniqueify_object(true);
- ojson()->erase(key);
- }
- return *this;
-}
-
-/** @brief Add the key-value pairs [key, value, ...] to the object.
- @return this Json
-
- An array Json is converted to an object Json with numeric keys. Other
- non-object Jsons are converted to empty objects. */
-template <typename T, typename... Args>
-inline Json& Json::set_list(const String& key, T value, Args&&... rest) {
- set(key, std::move(value));
- set_list(std::forward<Args>(rest)...);
- return *this;
-}
-
-/** @overload */
-inline Json& Json::set_list() {
- return *this;
-}
-
-/** @brief Insert element @a x in this object Json.
- @param x Pair of key and value.
- @return Pair of iterator pointing to key's value and bool indicating
- whether the value is newly inserted.
- @pre is_object()
-
- An existing element with key @a x.first is not replaced. */
-inline std::pair<Json::object_iterator, bool> Json::insert(const object_value_type& x) {
- precondition(is_object());
- uniqueify_object(false);
- ObjectJson *oj = ojson();
- int n = oj->n_, i = oj->find_insert(x.first, x.second);
- return std::make_pair(object_iterator(this, i), i == n);
-}
-
-/** @brief Insert element @a x in this object Json.
- @param position Ignored.
- @param x Pair of key and value.
- @return Pair of iterator pointing to key's value and bool indicating
- whether the value is newly inserted.
- @pre is_object()
-
- An existing element with key @a x.first is not replaced. */
-inline Json::object_iterator Json::insert(object_iterator position, const object_value_type& x) {
- (void) position;
- return insert(x).first;
-}
-
-/** @brief Remove the value pointed to by @a it from an object Json.
- @pre is_object()
- @return Next iterator */
-inline Json::object_iterator Json::erase(Json::object_iterator it) {
- precondition(is_object() && it.live() && it.j_ == this);
- uniqueify_object(false);
- ojson()->erase(it.i_);
- ++it;
- return it;
-}
-
-/** @brief Remove the value of @a key from an object Json.
- @pre is_object()
- @return Number of items removed */
-inline Json::size_type Json::erase(Str key) {
- precondition(is_object());
- uniqueify_object(false);
- return ojson()->erase(key);
-}
-
-/** @brief Merge the values of object Json @a x into this object Json.
- @pre (is_object() || is_null()) && (x.is_object() || x.is_null())
- @return this Json
-
- The key-value pairs in @a x are assigned to this Json. Null Jsons are
- silently converted to empty objects, except that if @a x and this Json are
- both null, then this Json is left as null. */
-inline Json& Json::merge(const Json& x) {
- precondition(is_object() || is_null());
- precondition(x.is_object() || x.is_null());
- if (x.u_.o.x) {
- uniqueify_object(false);
- ObjectJson *oj = ojson(), *xoj = x.ojson();
- const ObjectItem *xb = xoj->os_, *xe = xb + xoj->n_;
- for (; xb != xe; ++xb)
- if (xb->next_ > -2)
- oj->get_insert(xb->v_.first) = xb->v_.second;
- }
- return *this;
-}
-
-/** @cond never */
-template <typename U>
-inline Json& Json::merge(const Json_proxy_base<U>& x) {
- return merge(x.cvalue());
-}
-/** @endcond never */
-
-
-// ARRAY METHODS
-
-/** @brief Return the @a x th array element.
-
- If @a x is out of range of this array, returns a null Json. If this is an
- object Json, then returns get(String(@a x)). If this is neither an object
- nor an array, returns a null Json. */
-inline const Json& Json::get(size_type x) const {
- ArrayJson *aj;
- if (u_.x.type == j_array && (aj = ajson()) && unsigned(x) < unsigned(aj->size))
- return aj->a[x];
- else
- return hard_get(x);
-}
-
-/** @brief Return a reference to the @a x th array element.
-
- If this Json is an object, returns get_insert(String(x)). Otherwise this
- Json is first converted to an array; non-arrays are converted to empty
- arrays. The array is extended if @a x is out of range. */
-inline Json& Json::get_insert(size_type x) {
- ArrayJson *aj;
- if (u_.x.type == j_array && (aj = ajson()) && aj->refcount == 1
- && unsigned(x) < unsigned(aj->size))
- return aj->a[x];
- else
- return hard_get_insert(x);
-}
-
-/** @brief Return the @a x th element in an array Json.
- @pre is_array()
-
- A null Json is treated like an empty array. */
-inline const Json& Json::at(size_type x) const {
- precondition(is_array());
- return get(x);
-}
-
-/** @brief Return a reference to the @a x th element in an array Json.
- @pre is_array()
-
- The array is extended if @a x is out of range. */
-inline Json& Json::at_insert(size_type x) {
- precondition(is_array());
- return get_insert(x);
-}
-
-/** @brief Return the @a x th array element.
-
- If @a x is out of range of this array, returns a null Json. If this is an
- object Json, then returns get(String(@a x)). If this is neither an object
- nor an array, returns a null Json. */
-inline const Json& Json::operator[](size_type x) const {
- return get(x);
-}
-
-/** @brief Return a proxy reference to the @a x th array element.
-
- If this Json is an object, returns operator[](String(x)). If this Json is
- an array and @a x is in range, returns that element. Otherwise, returns a
- proxy that acts like a null Json. If this proxy is assigned, this Json is
- converted to an array, and then extended as necessary to contain the new
- value. */
-inline Json_array_proxy<Json> Json::operator[](size_type x) {
- return Json_array_proxy<Json>(*this, x);
-}
-
-/** @brief Return the last array element.
- @pre is_array() && !empty() */
-inline const Json& Json::back() const {
- precondition(is_array() && u_.a.x && u_.a.x->size > 0);
- return u_.a.x->a[u_.a.x->size - 1];
-}
-
-/** @brief Return a reference to the last array element.
- @pre is_array() && !empty() */
-inline Json& Json::back() {
- precondition(is_array() && u_.a.x && u_.a.x->size > 0);
- uniqueify_array(false, 0);
- return u_.a.x->a[u_.a.x->size - 1];
-}
-
-/** @brief Push an element onto the back of the array.
- @pre is_array() || is_null()
- @return this Json
-
- A null Json is promoted to an array. */
-inline Json& Json::push_back(Json x) {
- new(uniqueify_array_insert(false, -1)) Json(std::move(x));
- return *this;
-}
-
-/** @overload */
-template <typename P> inline Json& Json::push_back(const Json_proxy_base<P>& x) {
- new(uniqueify_array_insert(false, -1)) Json(x.cvalue());
- return *this;
-}
-
-/** @brief Remove the last element from an array.
- @pre is_array() && !empty() */
-inline void Json::pop_back() {
- precondition(is_array() && u_.a.x && u_.a.x->size > 0);
- uniqueify_array(false, 0);
- --u_.a.x->size;
- u_.a.x->a[u_.a.x->size].~Json();
-}
-
-inline Json& Json::push_back_list() {
- return *this;
-}
-
-/** @brief Insert the items [first, rest...] onto the back of this array.
- @pre is_array() || is_null()
- @return this Json
-
- A null Json is promoted to an array. */
-template <typename T, typename... Args>
-inline Json& Json::push_back_list(T first, Args&&... rest) {
- push_back(std::move(first));
- push_back_list(std::forward<Args>(rest)...);
- return *this;
-}
-
-
-/** @brief Insert an element into the array.
- @pre is_array() || is_null()
- @return this Json
-
- A null Json is promoted to an array. */
-inline Json::array_iterator Json::insert(array_iterator position, Json x) {
- precondition(position >= abegin() && position <= aend());
- size_type pos = position - abegin();
- new(uniqueify_array_insert(false, pos)) Json(std::move(x));
- return abegin() + pos;
-}
-
-/** @overload */
-template <typename P> inline Json::array_iterator Json::insert(array_iterator position, const Json_proxy_base<P>& x) {
- precondition(position >= abegin() && position <= aend());
- size_type pos = position - abegin();
- new(uniqueify_array_insert(false, pos)) Json(x.cvalue());
- return abegin() + pos;
-}
-
-inline Json::array_iterator Json::erase(array_iterator position) {
- return erase(position, position + 1);
-}
-
-
-inline Json* Json::array_data() {
- precondition(is_null() || is_array());
- uniqueify_array(false, 0);
- return u_.a.x ? u_.a.x->a : 0;
-}
-
-inline const Json* Json::array_data() const {
- precondition(is_null() || is_array());
- return u_.a.x ? u_.a.x->a : 0;
-}
-
-inline const Json* Json::array_cdata() const {
- precondition(is_null() || is_array());
- return u_.a.x ? u_.a.x->a : 0;
-}
-
-inline Json* Json::end_array_data() {
- precondition(is_null() || is_array());
- uniqueify_array(false, 0);
- return u_.a.x ? u_.a.x->a + u_.a.x->size : 0;
-}
-
-inline const Json* Json::end_array_data() const {
- precondition(is_null() || is_array());
- return u_.a.x ? u_.a.x->a + u_.a.x->size : 0;
-}
-
-inline const Json* Json::end_array_cdata() const {
- precondition(is_null() || is_array());
- return u_.a.x ? u_.a.x->a + u_.a.x->size : 0;
-}
-
-
-inline Json::const_object_iterator Json::cobegin() const {
- precondition(is_null() || is_object());
- return const_object_iterator(this, 0);
-}
-
-inline Json::const_object_iterator Json::coend() const {
- precondition(is_null() || is_object());
- return const_object_iterator(this, -1);
-}
-
-inline Json::const_object_iterator Json::obegin() const {
- return cobegin();
-}
-
-inline Json::const_object_iterator Json::oend() const {
- return coend();
-}
-
-inline Json::object_iterator Json::obegin() {
- precondition(is_null() || is_object());
- return object_iterator(this, 0);
-}
-
-inline Json::object_iterator Json::oend() {
- precondition(is_null() || is_object());
- return object_iterator(this, -1);
-}
-
-inline Json::const_array_iterator Json::cabegin() const {
- precondition(is_null() || is_array());
- return const_array_iterator(this, 0);
-}
-
-inline Json::const_array_iterator Json::caend() const {
- precondition(is_null() || is_array());
- ArrayJson *aj = ajson();
- return const_array_iterator(this, aj ? aj->size : 0);
-}
-
-inline Json::const_array_iterator Json::abegin() const {
- return cabegin();
-}
-
-inline Json::const_array_iterator Json::aend() const {
- return caend();
-}
-
-inline Json::array_iterator Json::abegin() {
- precondition(is_null() || is_array());
- return array_iterator(this, 0);
-}
-
-inline Json::array_iterator Json::aend() {
- precondition(is_null() || is_array());
- ArrayJson *aj = ajson();
- return array_iterator(this, aj ? aj->size : 0);
-}
-
-inline Json::const_iterator Json::cbegin() const {
- return const_iterator(this, 0);
-}
-
-inline Json::const_iterator Json::cend() const {
- return const_iterator(this, -1);
-}
-
-inline Json::iterator Json::begin() {
- return iterator(this, 0);
-}
-
-inline Json::iterator Json::end() {
- return iterator(this, -1);
-}
-
-inline Json::const_iterator Json::begin() const {
- return cbegin();
-}
-
-inline Json::const_iterator Json::end() const {
- return cend();
-}
-
-
-// Unparsing
-class Json::unparse_manipulator {
- public:
- unparse_manipulator()
- : indent_depth_(0), tab_width_(0), newline_terminator_(false),
- space_separator_(false) {
- }
- int indent_depth() const {
- return indent_depth_;
- }
- unparse_manipulator indent_depth(int x) const {
- unparse_manipulator m(*this);
- m.indent_depth_ = x;
- return m;
- }
- int tab_width() const {
- return tab_width_;
- }
- unparse_manipulator tab_width(int x) const {
- unparse_manipulator m(*this);
- m.tab_width_ = x;
- return m;
- }
- bool newline_terminator() const {
- return newline_terminator_;
- }
- unparse_manipulator newline_terminator(bool x) const {
- unparse_manipulator m(*this);
- m.newline_terminator_ = x;
- return m;
- }
- bool space_separator() const {
- return space_separator_;
- }
- unparse_manipulator space_separator(bool x) const {
- unparse_manipulator m(*this);
- m.space_separator_ = x;
- return m;
- }
- bool empty() const {
- return !indent_depth_ && !newline_terminator_ && !space_separator_;
- }
- private:
- int indent_depth_;
- int tab_width_;
- bool newline_terminator_;
- bool space_separator_;
-};
-
-inline Json::unparse_manipulator Json::indent_depth(int x) {
- return unparse_manipulator().indent_depth(x);
-}
-inline Json::unparse_manipulator Json::tab_width(int x) {
- return unparse_manipulator().tab_width(x);
-}
-inline Json::unparse_manipulator Json::newline_terminator(bool x) {
- return unparse_manipulator().newline_terminator(x);
-}
-inline Json::unparse_manipulator Json::space_separator(bool x) {
- return unparse_manipulator().space_separator(x);
-}
-
-/** @brief Return the string representation of this Json. */
-inline String Json::unparse() const {
- StringAccum sa;
- hard_unparse(sa, default_manipulator, 0);
- return sa.take_string();
-}
-
-/** @brief Return the string representation of this Json.
- @param add_newline If true, add a final newline. */
-inline String Json::unparse(const unparse_manipulator &m) const {
- StringAccum sa;
- hard_unparse(sa, m, 0);
- return sa.take_string();
-}
-
-/** @brief Unparse the string representation of this Json into @a sa. */
-inline void Json::unparse(StringAccum &sa) const {
- hard_unparse(sa, default_manipulator, 0);
-}
-
-/** @brief Unparse the string representation of this Json into @a sa. */
-inline void Json::unparse(StringAccum &sa, const unparse_manipulator &m) const {
- hard_unparse(sa, m, 0);
-}
-
-
-// Parsing
-
-class Json::streaming_parser {
- public:
- inline streaming_parser();
- inline void reset();
-
- inline bool done() const;
- inline bool success() const;
- inline bool error() const;
-
- inline size_t consume(const char* first, size_t length,
- const String& str = String(),
- bool complete = false);
- inline const char* consume(const char* first, const char* last,
- const String& str = String(),
- bool complete = false);
- const uint8_t* consume(const uint8_t* first, const uint8_t* last,
- const String& str = String(),
- bool complete = false);
-
- inline Json& result();
- inline const Json& result() const;
-
- private:
- enum {
- st_final = -2, st_error = -1,
- st_partlenmask = 0x0F, st_partmask = 0xFF,
- st_stringpart = 0x10, st_primitivepart = 0x20, st_numberpart = 0x40,
-
- st_initial = 0x000,
- st_array_initial = 0x100,
- st_array_value = 0x200,
- st_object_value = 0x300,
-
- st_array_delim = 0x400,
- st_object_delim = 0x500,
- st_object_initial = 0x600,
- st_object_key = 0x700,
- st_object_colon = 0x800,
-
- max_depth = 2048
- };
-
- int state_;
- std::vector<Json*> stack_;
- String str_;
- Json json_;
-
- inline Json* current();
- const uint8_t* error_at(const uint8_t* here);
- const uint8_t* consume_string(const uint8_t* first, const uint8_t* last, const String& str);
- const uint8_t* consume_backslash(StringAccum& sa, const uint8_t* first, const uint8_t* last);
- const uint8_t* consume_stringpart(StringAccum& sa, const uint8_t* first, const uint8_t* last);
- const uint8_t* consume_primitive(const uint8_t* first, const uint8_t* last, Json& j);
- const uint8_t* consume_number(const uint8_t* first, const uint8_t* last, const String& str, bool complete, Json& j);
-};
-
-inline Json::streaming_parser::streaming_parser()
- : state_(st_initial) {
-}
-
-inline void Json::streaming_parser::reset() {
- state_ = st_initial;
- stack_.clear();
-}
-
-inline bool Json::streaming_parser::done() const {
- return state_ < 0;
-}
-
-inline bool Json::streaming_parser::success() const {
- return state_ == st_final;
-}
-
-inline bool Json::streaming_parser::error() const {
- return state_ == st_error;
-}
-
-inline size_t Json::streaming_parser::consume(const char* first, size_t length,
- const String& str,
- bool complete) {
- const uint8_t* ufirst = reinterpret_cast<const uint8_t*>(first);
- return consume(ufirst, ufirst + length, str, complete) - ufirst;
-}
-
-inline const char* Json::streaming_parser::consume(const char* first,
- const char* last,
- const String& str,
- bool complete) {
- return reinterpret_cast<const char*>
- (consume(reinterpret_cast<const uint8_t*>(first),
- reinterpret_cast<const uint8_t*>(last), str, complete));
-}
-
-inline Json& Json::streaming_parser::result() {
- return json_;
-}
-
-inline const Json& Json::streaming_parser::result() const {
- return json_;
-}
-
-
-/** @brief Parse @a str as UTF-8 JSON into this Json object.
- @return true iff the parse succeeded.
-
- An unsuccessful parse does not modify *this. */
-inline bool Json::assign_parse(const String &str) {
- return assign_parse(str.begin(), str.end(), str);
-}
-
-/** @brief Parse [@a first, @a last) as UTF-8 JSON into this Json object.
- @return true iff the parse succeeded.
-
- An unsuccessful parse does not modify *this. */
-inline bool Json::assign_parse(const char *first, const char *last) {
- return assign_parse(first, last, String());
-}
-
-/** @brief Return @a str parsed as UTF-8 JSON.
-
- Returns a null JSON object if the parse fails. */
-inline Json Json::parse(const String &str) {
- Json j;
- (void) j.assign_parse(str);
- return j;
-}
-
-/** @brief Return [@a first, @a last) parsed as UTF-8 JSON.
-
- Returns a null JSON object if the parse fails. */
-inline Json Json::parse(const char *first, const char *last) {
- Json j;
- (void) j.assign_parse(first, last);
- return j;
-}
-
-
-// Assignment
-
-inline Json& Json::operator=(const Json& x) {
- if (x.u_.x.type < 0)
- x.u_.str.ref();
- else if (x.u_.x.x && (x.u_.x.type == j_array || x.u_.x.type == j_object))
- x.u_.x.x->ref();
- deref();
- u_ = x.u_;
- return *this;
-}
-
-inline Json& Json::operator=(Json&& x) {
- using std::swap;
- swap(u_, x.u_);
- return *this;
-}
-
-/** @cond never */
-template <typename U>
-inline Json& Json::operator=(const Json_proxy_base<U> &x) {
- return *this = x.cvalue();
-}
-
-inline Json& Json::operator=(int x) {
- deref();
- u_.i.x = x;
- u_.i.type = j_int;
- return *this;
-}
-
-inline Json& Json::operator=(unsigned x) {
- deref();
- u_.u.x = x;
- u_.u.type = j_unsigned;
- return *this;
-}
-
-inline Json& Json::operator=(long x) {
- deref();
- u_.i.x = x;
- u_.i.type = j_int;
- return *this;
-}
-
-inline Json& Json::operator=(unsigned long x) {
- deref();
- u_.u.x = x;
- u_.u.type = j_unsigned;
- return *this;
-}
-
-inline Json& Json::operator=(long long x) {
- deref();
- u_.i.x = x;
- u_.i.type = j_int;
- return *this;
-}
-
-inline Json& Json::operator=(unsigned long long x) {
- deref();
- u_.u.x = x;
- u_.u.type = j_unsigned;
- return *this;
-}
-
-inline Json& Json::operator=(double x) {
- deref();
- u_.d.x = x;
- u_.d.type = j_double;
- return *this;
-}
-
-inline Json& Json::operator=(bool x) {
- deref();
- u_.i.x = x;
- u_.i.type = j_bool;
- return *this;
-}
-
-inline Json& Json::operator=(const String& x) {
- deref();
- u_.str = x.internal_rep();
- u_.str.ref();
- return *this;
-}
-/** @endcond never */
-
-inline Json& Json::operator++() {
- return *this += 1;
-}
-inline void Json::operator++(int) {
- ++(*this);
-}
-inline Json& Json::operator--() {
- return *this += -1;
-}
-inline void Json::operator--(int) {
- --(*this);
-}
-inline Json& Json::add(double x) {
- force_double();
- u_.d.x += x;
- return *this;
-}
-template <typename T>
-inline Json& Json::add(T x) {
- force_number();
- if (is_int())
- u_.u.x += x;
- else
- u_.d.x += x;
- return *this;
-}
-inline Json& Json::subtract(double x) {
- force_double();
- u_.d.x -= x;
- return *this;
-}
-template <typename T>
-inline Json& Json::subtract(T x) {
- force_number();
- if (is_int())
- u_.u.x -= x;
- else
- u_.d.x -= x;
- return *this;
-}
-inline Json& Json::operator+=(int x) {
- return add(x);
-}
-inline Json& Json::operator+=(unsigned x) {
- return add(x);
-}
-inline Json& Json::operator+=(long x) {
- return add(x);
-}
-inline Json& Json::operator+=(unsigned long x) {
- return add(x);
-}
-inline Json& Json::operator+=(long long x) {
- return add(x);
-}
-inline Json& Json::operator+=(unsigned long long x) {
- return add(x);
-}
-inline Json& Json::operator+=(double x) {
- return add(x);
-}
-inline Json& Json::operator-=(int x) {
- return subtract(x);
-}
-inline Json& Json::operator-=(unsigned x) {
- return subtract(x);
-}
-inline Json& Json::operator-=(long x) {
- return subtract(x);
-}
-inline Json& Json::operator-=(unsigned long x) {
- return subtract(x);
-}
-inline Json& Json::operator-=(long long x) {
- return subtract(x);
-}
-inline Json& Json::operator-=(unsigned long long x) {
- return subtract(x);
-}
-inline Json& Json::operator-=(double x) {
- return subtract(x);
-}
-inline Json& Json::operator+=(const Json& x) {
- if (x.is_unsigned())
- add(u_.u.x);
- else if (x.is_int())
- add(u_.i.x);
- else if (!x.is_null())
- add(x.as_d());
- return *this;
-}
-inline Json& Json::operator-=(const Json& x) {
- if (x.is_unsigned())
- subtract(u_.u.x);
- else if (x.is_int())
- subtract(u_.i.x);
- else if (!x.is_null())
- subtract(x.as_d());
- return *this;
-}
-inline Json operator+(Json x) {
- x.force_number();
- return x;
-}
-inline Json operator-(Json x) {
- x.force_number();
- if (x.is_int())
- x.u_.u.x = -x.u_.u.x;
- else
- x.u_.d.x = -x.u_.d.x;
- return x;
-}
-
-/** @brief Swap this Json with @a x. */
-inline void Json::swap(Json& x) {
- using std::swap;
- swap(u_, x.u_);
-}
-
-
-inline StringAccum &operator<<(StringAccum &sa, const Json& json) {
- json.unparse(sa);
- return sa;
-}
-
-template <typename P>
-inline StringAccum &operator<<(StringAccum &sa, const Json_proxy_base<P> &json) {
- return (sa << json.cvalue());
-}
-
-inline std::ostream &operator<<(std::ostream& f, const Json& json) {
- StringAccum sa;
- json.unparse(sa);
- return f.write(sa.data(), sa.length());
-}
-
-template <typename P>
-inline std::ostream &operator<<(std::ostream& f, const Json_proxy_base<P>& json) {
- return (f << json.cvalue());
-}
-
-bool operator==(const Json& a, const Json& b);
-
-template <typename T>
-inline bool operator==(const Json_proxy_base<T>& a, const Json& b) {
- return a.cvalue() == b;
-}
-
-template <typename T>
-inline bool operator==(const Json& a, const Json_proxy_base<T>& b) {
- return a == b.cvalue();
-}
-
-template <typename T, typename U>
-inline bool operator==(const Json_proxy_base<T>& a,
- const Json_proxy_base<U>& b) {
- return a.cvalue() == b.cvalue();
-}
-
-inline bool operator!=(const Json& a, const Json& b) {
- return !(a == b);
-}
-
-template <typename T>
-inline bool operator!=(const Json_proxy_base<T>& a, const Json& b) {
- return !(a == b);
-}
-
-template <typename T>
-inline bool operator!=(const Json& a, const Json_proxy_base<T>& b) {
- return !(a == b);
-}
-
-template <typename T, typename U>
-inline bool operator!=(const Json_proxy_base<T>& a,
- const Json_proxy_base<U>& b) {
- return !(a == b);
-}
-
-inline void swap(Json& a, Json& b) {
- a.swap(b);
-}
-
-} // namespace lcdf
-#endif
diff --git a/jsontest.cc b/jsontest.cc
deleted file mode 100644
index 2b103f8..0000000
--- a/jsontest.cc
+++ /dev/null
@@ -1,609 +0,0 @@
-// -*- c-basic-offset: 4 -*-
-/*
- * jsontest.{cc,hh} -- regression tests for Json
- * Eddie Kohler
- *
- * Copyright (c) 2012-2014 Eddie Kohler
- *
- * 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 Click 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 Click LICENSE file; the license in that file is
- * legally binding.
- */
-
-#include "json.hh"
-#include <unordered_map>
-using namespace lcdf;
-
-#define CHECK(x) do { if (!(x)) { std::cerr << __FILE__ << ":" << __LINE__ << ": test '" << #x << "' failed\n"; exit(1); } } while (0)
-#define CHECK_JUP(x, str) do { if ((x).unparse() != (str)) { std::cerr << __FILE__ << ":" << __LINE__ << ": '" #x "' is '" << (x) << "', not '" << (str) << "'\n"; exit(1); } } while (0)
-
-
-#if 0
-template <typename T> void incr(T& x) __attribute__((noinline));
-template <typename T>
-void incr(T& x) {
- ++x;
-}
-#endif
-
-void benchmark_parse() {
- std::vector<String> parse_examples;
- parse_examples.push_back("{}");
- parse_examples.push_back("{\"foo\":\"bar\",\"baz\":\"flim\"}");
- parse_examples.push_back("[1,2,3,4,5]");
- parse_examples.push_back("[]");
- parse_examples.push_back("[{},{\"b\":[]}]");
- Json j;
-#if 0
- for (int i = 0; i < 10000000; ++i)
- j.assign_parse(parse_examples[rand() % parse_examples.size()]);
-#else
- using std::swap;
- Json::streaming_parser jsp;
- for (int i = 0; i < 10000000; ++i) {
- jsp.reset();
- swap(jsp.result(), j);
- const String& str = parse_examples[rand() % parse_examples.size()];
- jsp.consume(str.begin(), str.end(), str, true);
- }
-#endif
- exit(0);
-}
-
-int main(int argc, char** argv) {
- (void) argc, (void) argv;
- //benchmark_parse();
-
- Json j;
- CHECK(j.empty());
- CHECK(!j);
-
- j = Json::make_object();
- CHECK(j.empty());
- CHECK(j);
-
- j.set("foo", "bar");
- CHECK(j["foo"]);
- CHECK(j["foo"].to_s() == "bar");
- CHECK(j.size() == 1);
-
- j.set("baz", "flim");
- CHECK(j.size() == 2);
- CHECK(j.unparse() == "{\"foo\":\"bar\",\"baz\":\"flim\"}");
-
- j.erase("foo");
- CHECK(j.size() == 1);
-
- j.assign_parse("2");
- CHECK(j == 2);
-
- j.assign_parse("null");
- CHECK(j == Json());
-
- j.assign_parse("\"a\"");
- CHECK(j == "a");
-
- j.assign_parse("[1,2]");
- CHECK(j.unparse() == "[1,2]");
-
- j.assign_parse("[[[]],{\"a\":{}}]");
- CHECK(j.unparse() == "[[[]],{\"a\":{}}]");
-
- j = Json::parse("{\"x22\":{\n\
- \"git-revision\":\"ebbd3d4767847300f552b181a10bda57a926f554M\",\n\
- \"time\":\"Tue Feb 7 20:20:33 2012\",\n\
- \"machine\":\"rtshanks-laptop\",\n\
- \"cores\":2,\n\
- \"runs\":[\"x22\\/rw2\\/mb\\/0\"]\n\
- },\n\
- \"x23\":{\n\
- \"git-revision\":\"ebbd3d4767847300f552b181a10bda57a926f554M\",\n\
- \"time\":\"Tue Feb 7 20:31:05 2012\",\n\
- \"machine\":\"rtshanks-laptop\",\n\
- \"cores\":2,\n\
- \"runs\":[\"x23\\/rw2\\/mb\\/0\",\"x23\\/rw1\\/mb\\/0\",\"x23\\/rw3\\/mb\\/0\"]\n\
- },\n\
- \"x24\":{\n\
- \"git-revision\":\"62e9970ca8ae9c6eebf2d71b7065ea694fb25282M\",\n\
- \"time\":\"Sat Feb 11 15:54:01 2012\",\n\
- \"machine\":\"rtshanks-laptop\",\n\
- \"cores\":2,\n\
- \"runs\":[\"x24\\/rw1\\/b\\/0\"]\n\
- },\"b\":\"c\",\"c\":\"d\"}");
- CHECK(j["x22"]["time"] == "Tue Feb 7 20:20:33 2012");
- CHECK(j["x22"]["cores"] == 2);
- {
- Json::object_iterator it = j.obegin();
- CHECK(it.key() == "x22");
- ++it;
- CHECK(it.key() == "x23");
- ++it;
- CHECK(it.key() == "x24");
- ++it;
- CHECK(it.key() == "b");
- ++it;
- CHECK(it.key() == "c");
- }
-
- {
- Json jcopy = j;
- CHECK(j.size() == 5);
- int count = 0;
- for (Json::iterator it = jcopy.begin(); it != jcopy.end(); ++it) {
- it->second = Json();
- ++count;
- }
- CHECK(!jcopy["x22"]);
- CHECK(j["x22"]["cores"] == 2);
- CHECK(count == jcopy.size());
- }
-
- CHECK(!j["x49"]);
- CHECK(j.size() == 5);
- CHECK(!j["x49"]["45"][2]["a"]);
- CHECK(j.size() == 5);
- j["x49"]["45"][2]["a"] = 1;
- CHECK(j.size() == 6);
- CHECK(j["x22"].is_object() && j.get("x22").is_object());
- CHECK(j["x23"].is_object() && j.get("x23").is_object());
- CHECK(j["b"].is_string() && j.get("b").is_string());
- CHECK(j["x49"].is_object() && j.get("x49").is_object());
- CHECK(j["x49"]["45"].is_array());
- CHECK(j["x49"]["45"].size() == 3 && j["x49"]["45"][2].is_object());
-
- j = Json::make_object();
- j["a"] = j["b"];
- CHECK(j.size() == 1);
- CHECK(j["a"].is_null());
- CHECK(j.count("a") == 1);
-
- Json k = Json::make_array();
- {
- CHECK(k.size() == 0);
- Json::array_iterator it = k.abegin();
- CHECK(!it.live());
- }
- j = Json::make_object();
- j["a"] = k[2];
- CHECK(j.size() == 1);
- CHECK(k.size() == 0);
- CHECK(j["a"].is_null());
- CHECK(j.count("a") == 1);
-
- j = Json(1);
- CHECK(j.get("a").is_null());
-
- j.assign_parse("{\"a\":1,\"b\":true,\"c\":\"\"}");
- {
- int i = 0;
- bool b = false;
- String s;
- CHECK(j.get("a", i));
- CHECK(i == 1);
- CHECK(j.get("a", i).get("b", b));
- CHECK(b == true);
- CHECK(!j.get("a", s).status());
- CHECK(!j.get("a", s).status(b));
- CHECK(b == false);
- CHECK(j.get("a", k));
- CHECK(k == Json(1));
- CHECK(!j.get("cc", k));
- }
-
- j["a"] = Json(5);
- j.set("a", Json(5));
- j["b"] = Json::parse("[]");
-
- {
- Json j1 = Json::make_object(), j2 = Json::make_object();
- j1.set("a", j2); // stores a COPY of j2 in j1
- j2.set("b", 1);
- CHECK(j1.unparse() == "{\"a\":{}}");
- CHECK(j2.unparse() == "{\"b\":1}");
- }
-
- {
- Json j = Json::parse("{\"a\":true}");
- if (j["foo"]["bar"])
- CHECK(false);
- CHECK(j.unparse() == "{\"a\":true}");
- j["foo"]["bar"] = true;
- CHECK(j.unparse() == "{\"a\":true,\"foo\":{\"bar\":true}}");
- j["a"]["2"] = false;
- CHECK(j.unparse() == "{\"a\":{\"2\":false},\"foo\":{\"bar\":true}}");
- j[3] = true;
- CHECK(j.unparse() == "{\"a\":{\"2\":false},\"foo\":{\"bar\":true},\"3\":true}");
- CHECK(j[3] == Json(true));
- j = Json::parse("[1,2,3,4]");
- CHECK(j["2"] == Json(3));
- CHECK(j.unparse() == "[1,2,3,4]");
- j["a"] = true;
- CHECK(j.unparse() == "{\"0\":1,\"1\":2,\"2\":3,\"3\":4,\"a\":true}");
- CHECK(j["2"] == Json(3));
- }
-
- {
- Json j = Json::parse("{}");
- j.set("foo", String::make_out_of_memory()).set(String::make_out_of_memory(), 2);
- CHECK(j.unparse() == "{\"foo\":\"\360\237\222\243ENOMEM\360\237\222\243\",\"\360\237\222\243ENOMEM\360\237\222\243\":2}");
- j.clear();
- CHECK(j.unparse() == "{}");
- CHECK(j.get("foo") == Json());
- }
-
- {
- Json j = Json::array(1, 2, 3, 4, 5, 6, 7, 8);
- CHECK(j.unparse() == "[1,2,3,4,5,6,7,8]");
- Json jcopy = j;
- CHECK(j.unparse() == "[1,2,3,4,5,6,7,8]");
- CHECK(jcopy.unparse() == "[1,2,3,4,5,6,7,8]");
- j.push_back(9);
- CHECK(j.unparse() == "[1,2,3,4,5,6,7,8,9]");
- CHECK(jcopy.unparse() == "[1,2,3,4,5,6,7,8]");
- jcopy.push_back(10);
- CHECK(j.unparse() == "[1,2,3,4,5,6,7,8,9]");
- CHECK(jcopy.unparse() == "[1,2,3,4,5,6,7,8,10]");
- }
-
- {
- unsigned long s = 77;
- Json j = Json::array(0, 0, 0);
- Json k = Json::array(0, s, "foo");
- CHECK(j[1].is_i());
- CHECK(k[1].is_u());
- j[1] = k[1];
- CHECK(j[1].is_u());
- }
-
-#if 0
- {
- Json j = Json::array(1, 2, 3);
- //int j[3] = {1, 2, 3};
- for (int i = 0; i < 1000000; ++i)
- incr(j[1].value());
- std::cout << j << "\n";
- }
-#endif
-
- {
- Json::streaming_parser jsp;
- const uint8_t* x = reinterpret_cast<const uint8_t*>("\"abcdef\"");
-
- const uint8_t* r = jsp.consume(x, x + 8, String());
- CHECK(r == x + 8);
- CHECK(jsp.success());
- CHECK(jsp.result().unparse() == "\"abcdef\"");
-
- x = reinterpret_cast<const uint8_t*>("\"\\\"\\\\fartbox\" amksdnsndfa");
- jsp.reset();
- r = jsp.consume(x, x + 9, String());
- CHECK(r == x + 9);
- CHECK(!jsp.done());
- r = jsp.consume(x + 9, x + 17, String());
- CHECK(r == x + 13);
- CHECK(jsp.success());
- CHECK(jsp.result().unparse() == "\"\\\"\\\\fartbox\"");
-
- jsp.reset();
- r = jsp.consume(x, x + 3, String());
- CHECK(r == x + 3);
- r = jsp.consume(x + 3, x + 6, String());
- CHECK(r == x + 6);
- r = jsp.consume(x + 6, x + 9, String());
- CHECK(r == x + 9);
- r = jsp.consume(x + 9, x + 13, String());
- CHECK(r == x + 13);
- CHECK(jsp.success());
- CHECK(jsp.result().unparse() == "\"\\\"\\\\fartbox\"");
-
- jsp.reset();
- x = reinterpret_cast<const uint8_t*>("\"\\uD834\\uDD1E\"f");
- r = jsp.consume(x, x + 15, String());
- CHECK(r == x + 14);
- CHECK(jsp.success());
- CHECK(jsp.result().unparse() == "\"\xF0\x9D\x84\x9E\"");
-
- jsp.reset();
- r = jsp.consume(x, x + 2, String());
- CHECK(r == x + 2);
- r = jsp.consume(x + 2, x + 4, String());
- CHECK(r == x + 4);
- r = jsp.consume(x + 4, x + 6, String());
- CHECK(r == x + 6);
- r = jsp.consume(x + 6, x + 8, String());
- CHECK(r == x + 8);
- r = jsp.consume(x + 8, x + 10, String());
- CHECK(r == x + 10);
- r = jsp.consume(x + 10, x + 15, String());
- CHECK(r == x + 14);
- CHECK(jsp.success());
- CHECK(jsp.result().unparse() == "\"\xF0\x9D\x84\x9E\"");
-
- x = reinterpret_cast<const uint8_t*>("\"\xF0\x9D\x84\x9E\" fart");
- jsp.reset();
- r = jsp.consume(x, x + 3, String());
- CHECK(r == x + 3);
- r = jsp.consume(x + 5, x + 7, String());
- CHECK(r == x + 5);
- CHECK(jsp.error());
-
- jsp.reset();
- r = jsp.consume(x, x + 7, String());
- CHECK(r == x + 6);
- CHECK(jsp.success());
- CHECK(jsp.result().unparse() == "\"\xF0\x9D\x84\x9E\"");
-
- jsp.reset();
- r = jsp.consume(x, x + 2, String());
- CHECK(r == x + 2);
- r = jsp.consume(x + 2, x + 4, String());
- CHECK(r == x + 4);
- r = jsp.consume(x + 4, x + 7, String());
- CHECK(r == x + 6);
- CHECK(jsp.success());
- CHECK(jsp.result().unparse() == "\"\xF0\x9D\x84\x9E\"");
-
- jsp.reset();
- x = reinterpret_cast<const uint8_t*>("{}");
- r = jsp.consume(x, x + 2, String());
- CHECK(r == x + 2);
- CHECK(jsp.success());
- CHECK(jsp.result().unparse() == "{}");
-
- jsp.reset();
- x = reinterpret_cast<const uint8_t*>("\"ab cde\" ");
- r = jsp.consume(x, x + 9, String());
- CHECK(r == x + 8);
- CHECK(jsp.success());
- CHECK(jsp.result().unparse() == "\"ab cde\"");
-
- jsp.reset();
- x = reinterpret_cast<const uint8_t*>("{\"a\":\"b\"}");
- r = jsp.consume(x, x + 9, String());
- CHECK(r == x + 9);
- CHECK(jsp.success());
- CHECK(jsp.result().unparse() == "{\"a\":\"b\"}");
-
- jsp.reset();
- x = reinterpret_cast<const uint8_t*>("[]");
- r = jsp.consume(x, x + 2, String());
- CHECK(r == x + 2);
- CHECK(jsp.success());
- CHECK(jsp.result().unparse() == "[]");
-
- jsp.reset();
- x = reinterpret_cast<const uint8_t*>("[\"a\",\"b\"]");
- r = jsp.consume(x, x + 9, String());
- CHECK(r == x + 9);
- CHECK(jsp.success());
- CHECK(jsp.result().unparse() == "[\"a\",\"b\"]");
-
- jsp.reset();
- x = reinterpret_cast<const uint8_t*>("[[\"a\"],[[\"b\"]]]");
- r = jsp.consume(x, x + 15, String());
- CHECK(r == x + 15);
- CHECK(jsp.success());
- CHECK(jsp.result().unparse() == "[[\"a\"],[[\"b\"]]]");
-
- jsp.reset();
- x = reinterpret_cast<const uint8_t*>("[[],[[]]]");
- r = jsp.consume(x, x + 9, String());
- CHECK(r == x + 9);
- CHECK(jsp.success());
- CHECK(jsp.result().unparse() == "[[],[[]]]");
-
- jsp.reset();
- x = reinterpret_cast<const uint8_t*>("{\"abc\":\"def\"}");
- r = jsp.consume(x, x + 3, String());
- CHECK(r == x + 3);
- r = jsp.consume(x + 3, x + 6, String());
- CHECK(r == x + 6);
- r = jsp.consume(x + 6, x + 9, String());
- CHECK(r == x + 9);
- r = jsp.consume(x + 9, x + 12, String());
- CHECK(r == x + 12);
- r = jsp.consume(x + 12, x + 13, String());
- CHECK(r == x + 13);
- CHECK(jsp.success());
- CHECK(jsp.result().unparse() == "{\"abc\":\"def\"}");
-
- jsp.reset();
- x = reinterpret_cast<const uint8_t*>("{\"abc\":true }");
- r = jsp.consume(x, x + 3, String());
- CHECK(r == x + 3);
- r = jsp.consume(x + 3, x + 6, String());
- CHECK(r == x + 6);
- r = jsp.consume(x + 6, x + 9, String());
- CHECK(r == x + 9);
- r = jsp.consume(x + 9, x + 12, String());
- CHECK(r == x + 12);
- r = jsp.consume(x + 12, x + 13, String());
- CHECK(r == x + 13);
- CHECK(jsp.success());
- CHECK(jsp.result().unparse() == "{\"abc\":true}");
-
- jsp.reset();
- x = reinterpret_cast<const uint8_t*>("{\"abc\": null}");
- r = jsp.consume(x, x + 3, String());
- CHECK(r == x + 3);
- r = jsp.consume(x + 3, x + 6, String());
- CHECK(r == x + 6);
- r = jsp.consume(x + 6, x + 9, String());
- CHECK(r == x + 9);
- r = jsp.consume(x + 9, x + 12, String());
- CHECK(r == x + 12);
- r = jsp.consume(x + 12, x + 13, String());
- CHECK(r == x + 13);
- CHECK(jsp.success());
- CHECK(jsp.result().unparse() == "{\"abc\":null}");
-
- jsp.reset();
- x = reinterpret_cast<const uint8_t*>("{\"abc\":false}");
- r = jsp.consume(x, x + 3, String());
- CHECK(r == x + 3);
- r = jsp.consume(x + 3, x + 6, String());
- CHECK(r == x + 6);
- r = jsp.consume(x + 6, x + 9, String());
- CHECK(r == x + 9);
- r = jsp.consume(x + 9, x + 12, String());
- CHECK(r == x + 12);
- r = jsp.consume(x + 12, x + 13, String());
- CHECK(r == x + 13);
- CHECK(jsp.success());
- CHECK(jsp.result().unparse() == "{\"abc\":false}");
-
- jsp.reset();
- x = reinterpret_cast<const uint8_t*>("{\"abc\": -13 }");
- r = jsp.consume(x, x + 3, String());
- CHECK(r == x + 3);
- r = jsp.consume(x + 3, x + 6, String());
- CHECK(r == x + 6);
- r = jsp.consume(x + 6, x + 9, String());
- CHECK(r == x + 9);
- r = jsp.consume(x + 9, x + 12, String());
- CHECK(r == x + 12);
- r = jsp.consume(x + 12, x + 13, String());
- CHECK(r == x + 13);
- CHECK(jsp.success());
- CHECK(jsp.result().unparse() == "{\"abc\":-13}");
-
- jsp.reset();
- x = reinterpret_cast<const uint8_t*>("{\"abc\":0 }");
- r = jsp.consume(x, x + 3, String());
- CHECK(r == x + 3);
- r = jsp.consume(x + 3, x + 6, String());
- CHECK(r == x + 6);
- r = jsp.consume(x + 6, x + 9, String());
- CHECK(r == x + 9);
- r = jsp.consume(x + 9, x + 12, String());
- CHECK(r == x + 12);
- r = jsp.consume(x + 12, x + 13, String());
- CHECK(r == x + 13);
- CHECK(jsp.success());
- CHECK(jsp.result().unparse() == "{\"abc\":0}");
-
- jsp.reset();
- x = reinterpret_cast<const uint8_t*>("[0,1,2,3,4e5,10.2]");
- r = jsp.consume(x, x + 3, String());
- CHECK(r == x + 3);
- r = jsp.consume(x + 3, x + 6, String());
- CHECK(r == x + 6);
- r = jsp.consume(x + 6, x + 9, String());
- CHECK(r == x + 9);
- r = jsp.consume(x + 9, x + 12, String());
- CHECK(r == x + 12);
- r = jsp.consume(x + 12, x + 18, String());
- CHECK(r == x + 18);
- CHECK(jsp.success());
- CHECK(jsp.result().unparse() == "[0,1,2,3,400000,10.2]");
- }
-
- {
- unsigned long s = 77;
- Json j = Json::array(0, s, "foo");
- CHECK(j.is_a());
- CHECK(j[1].is_u());
- CHECK(j[1] == s);
- CHECK(j[1] == 77);
-
- j = Json::array((uint64_t) -1, (int64_t) -1,
- (uint64_t) 1, (int64_t) 1,
- (uint64_t) 2, (int64_t) 2);
- CHECK(j[0] != j[1]);
- CHECK(j[2] == j[3]);
- CHECK(j[4] == j[5]);
- CHECK(j[0] != j[2]);
- CHECK(j[2] != j[4]);
- CHECK_JUP(j, "[18446744073709551615,-1,1,1,2,2]");
- }
-
- {
- std::vector<int> v = {1, 2, 3, 4, 5};
- Json j(v.begin(), v.end());
- CHECK(j.unparse() == "[1,2,3,4,5]");
- }
-
- {
- std::unordered_map<String, String> h;
- h["a"] = "b";
- h["c"] = "d";
- h["x"] = "e";
- Json j(h.begin(), h.end());
- CHECK(j.is_o());
- CHECK(j.size() == 3);
- CHECK(j["a"] == "b");
- CHECK(j["c"] == "d");
- CHECK(j["x"] == "e");
- }
-
- {
- Json j = Json::array(1, 2, 3, 4, 5, 6, 7, 8);
- CHECK(j.unparse() == "[1,2,3,4,5,6,7,8]");
- Json jcopy = j;
- CHECK(j.unparse() == "[1,2,3,4,5,6,7,8]");
- CHECK(jcopy.unparse() == "[1,2,3,4,5,6,7,8]");
- auto it = j.erase(j.abegin() + 4);
- CHECK_JUP(j, "[1,2,3,4,6,7,8]");
- CHECK_JUP(jcopy, "[1,2,3,4,5,6,7,8]");
- CHECK(it == j.abegin() + 4);
- it = j.erase(j.aend(), j.aend());
- CHECK_JUP(j, "[1,2,3,4,6,7,8]");
- CHECK(it == j.aend());
- it = j.erase(j.abegin(), j.abegin());
- CHECK_JUP(j, "[1,2,3,4,6,7,8]");
- CHECK(it == j.abegin());
- it = j.erase(j.abegin(), j.abegin() + 3);
- CHECK_JUP(j, "[4,6,7,8]");
- CHECK(it == j.abegin());
- }
-
- {
- Json j((uint64_t) 1 << 63);
- CHECK(j.is_u());
- CHECK(j.unparse() == "9223372036854775808");
- j = Json::parse("9223372036854775808");
- CHECK(j.is_u());
- CHECK(j.to_u() == (uint64_t) 1 << 63);
- }
-
- {
- Json j = Json::object("a", 1, "b", Json(2), "c", "9137471");
- CHECK(j.unparse() == "{\"a\":1,\"b\":2,\"c\":\"9137471\"}");
- }
-
- {
- Json a = Json::parse("[{\"a\":1},{\"b\":2},{\"c\":3},{\"d\":4}]");
- Json b = Json::array(a[1], a[3], a[2], a[0]);
- CHECK(&a[0]["a"].cvalue() == &b[3]["a"].cvalue());
- b.push_back(a[5]);
- CHECK(b[4] == Json());
- CHECK(a.size() == 4);
- b = Json::object("a5", a[5], "a3", a[3]);
- CHECK(b.unparse() == "{\"a5\":null,\"a3\":{\"d\":4}}");
- CHECK(a.size() == 4);
- b.set_list("a6", a[6], "a2", a[2]);
- CHECK(b.unparse() == "{\"a5\":null,\"a3\":{\"d\":4},\"a6\":null,\"a2\":{\"c\":3}}");
- CHECK(a.size() == 4);
- }
-
- {
- Json a = Json::parse("[{\"a\":\"\\\"\\\\\\/\"}]");
- CHECK(a[0]["a"] == "\"\\/");
- CHECK(a.unparse() == "[{\"a\":\"\\\"\\\\\\/\"}]");
- }
-
- CHECK(String("\\").encode_json() == "\\\\");
- CHECK(String("\011\002\xE2\x80\xA9\xE2\x80\xAA").encode_json() == "\\t\\u0002\\u2029\xE2\x80\xAA");
- CHECK(String("a").encode_uri_component() == "a");
- CHECK(String(" ").encode_uri_component() == "%20");
- CHECK(String("Ab -Ef").encode_uri_component() == "Ab%20-Ef");
-
- std::cout << "All tests pass!\n";
- return 0;
-}
diff --git a/kpermuter.hh b/kpermuter.hh
index 51556b2..b4f54e3 100644
--- a/kpermuter.hh
+++ b/kpermuter.hh
@@ -103,6 +103,7 @@ template <int W> class kpermuter {
int operator[](int i) const {
return (x_ >> ((i << 2) + 4)) & 15;
}
+ // Get next free entry in permutation
int back() const {
return (*this)[W - 1];
}
@@ -268,6 +269,7 @@ template <int W> class kpermuter {
return !(*this == x);
}
+ // The number of ikeys the leaf currently holds. Each ikey points to a value or to a lower layer
static inline int size(value_type p) {
return p & 15;
}
diff --git a/ksearch.hh b/ksearch.hh
index aeb4a28..8645686 100644
--- a/ksearch.hh
+++ b/ksearch.hh
@@ -60,6 +60,7 @@ inline int key_upper_bound(const KA& ka, const T& n)
return key_upper_bound_by(ka, n, key_comparator<KA, T>());
}
+// Binary search
template <typename KA, typename T, typename F>
key_indexed_position key_lower_bound_by(const KA& ka, const T& n, F comparator)
{
@@ -85,7 +86,16 @@ inline key_indexed_position key_lower_bound(const KA& ka, const T& n)
return key_lower_bound_by(ka, n, key_comparator<KA, T>());
}
+/* For internode internal search:
+ Return the index of the child (leaf) that might contain the provided ikey (ka)
+ This is done be linear searching for the ikey that the provided ikey is the tightest upper boundary of (provided ikey is larger than the ikey in boundary array)
+ e.g. internode - boundary ikeys array: ikey0_ = {40, 50, 100, 110} children in indexes 0, 1, 2, 3, 4
+ For ka.ikey0_ == 100, 3 is returned because node in child_[3] might contain the ikey
+ For ka.ikey0_ == 150, 4 is returned
+ For ka.ikey0_ == 10, 0 is returned
+ For ka.ikey0_ == 50, 2 is returned */
+// Linear search
template <typename KA, typename T, typename F>
int key_find_upper_bound_by(const KA& ka, const T& n, F comparator)
{
@@ -102,6 +112,19 @@ int key_find_upper_bound_by(const KA& ka, const T& n, F comparator)
return l;
}
+/* In use in leafs for permutation search
+ Find the ikey in node which equal or that the provided ikey is the tightest lower boundary of (provided ikey is smaller than the ikey in node). If match, also compare the keylen
+ Return value: key_indexed_position which contains i and p variables
+ i - Index inside the permutation. if key was found, index of the key
+ If key was not found, index of the ikey which is tightest lower bounded by the provided key (ka)
+ If no such exists, return perm.size() (invalid\not used location - currently outsize of the permutation)
+ p - Position in child arrays ( == permutation[i]). if key was not found, p = -1
+ e.g. Leaf - ikeys, perm { 0, 3, 1, 2 } ikey0_ = {40, 100, 110, 50}
+ For ka.ikey0_ == 100, 110 is returned (ikey0_[2] == 110) --> i = 2, p = 3
+ For ka.ikey0_ == 120, -1 is returned --> i = 4, p = -1
+ For ka.ikey0_ == 85, -1 is returned --> i = 1, p = -1 */
+
+// Linear search
template <typename KA, typename T, typename F>
key_indexed_position key_find_lower_bound_by(const KA& ka, const T& n, F comparator)
{
diff --git a/kvio.cc b/kvio.cc
deleted file mode 100644
index 9208df2..0000000
--- a/kvio.cc
+++ /dev/null
@@ -1,112 +0,0 @@
-/* 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.
- */
-// buffered read and write for kvc/kvd.
-// stdio is good but not quite what I want.
-// need to be able to check if any input
-// available, and do non-blocking check.
-// also, fwrite just isn't very fast, at
-// least on the Mac.
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <sys/select.h>
-#include <sys/time.h>
-#include <assert.h>
-#include <errno.h>
-#include "kvio.hh"
-
-
-// API to allocate a new kvout.
-kvout* new_kvout(int fd, int buflen) {
- kvout* kv = (kvout*) malloc(sizeof(kvout));
- assert(kv);
- memset(kv, 0, sizeof(*kv));
- kv->capacity = buflen;
- kv->buf = (char*) malloc(kv->capacity);
- assert(kv->buf);
- kv->fd = fd;
- return kv;
-}
-
-// API to allocate a new kvout for a buffer, no fd.
-kvout* new_bufkvout() {
- kvout *kv = (kvout*) malloc(sizeof(kvout));
- assert(kv);
- memset(kv, 0, sizeof(*kv));
- kv->capacity = 256;
- kv->buf = (char*) malloc(kv->capacity);
- assert(kv->buf);
- kv->n = 0;
- kv->fd = -1;
- return kv;
-}
-
-// API to clear out a buf kvout.
-void kvout_reset(kvout* kv) {
- assert(kv->fd < 0);
- kv->n = 0;
-}
-
-// API to free a kvout.
-// does not close() the fd.
-void free_kvout(kvout* kv) {
- if (kv->buf)
- free(kv->buf);
- kv->buf = 0;
- free(kv);
-}
-
-void kvflush(kvout* kv) {
- assert(kv->fd >= 0);
- size_t sent = 0;
- while (kv->n > sent) {
- ssize_t cc = write(kv->fd, kv->buf + sent, kv->n - sent);
- if (cc <= 0) {
- if (errno == EWOULDBLOCK) {
- usleep(1);
- continue;
- }
- perror("kvflush write");
- return;
- }
- sent += cc;
- }
- kv->n = 0;
-}
-
-// API
-void kvout::grow(unsigned want) {
- if (fd >= 0)
- kvflush(this);
- if (want == 0)
- want = capacity + 1;
- while (want > capacity)
- capacity *= 2;
- buf = (char*) realloc(buf, capacity);
- assert(buf);
-}
-
-int kvwrite(kvout* kv, const void* buf, unsigned n) {
- if (kv->n + n > kv->capacity && kv->fd >= 0)
- kvflush(kv);
- if (kv->n + n > kv->capacity)
- kv->grow(kv->n + n);
- memcpy(kv->buf + kv->n, buf, n);
- kv->n += n;
- return n;
-}
diff --git a/kvio.hh b/kvio.hh
deleted file mode 100644
index 5aea020..0000000
--- a/kvio.hh
+++ /dev/null
@@ -1,67 +0,0 @@
-/* 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 KVIO_H
-#define KVIO_H
-#include <string>
-#include <vector>
-#include <stdlib.h>
-#include "string.hh"
-#include "str.hh"
-
-struct kvout {
- int fd;
- char* buf;
- unsigned capacity; // allocated size of buf
- unsigned n; // # of chars we've written to buf
-
- inline void append(char c);
- inline char* reserve(int n);
- inline void adjust_length(int delta);
- inline void set_end(char* end);
- void grow(unsigned want);
-};
-
-kvout* new_kvout(int fd, int buflen);
-kvout* new_bufkvout();
-void kvout_reset(kvout* kv);
-void free_kvout(kvout* kv);
-int kvwrite(kvout* kv, const void* buf, unsigned int n);
-void kvflush(kvout* kv);
-
-inline void kvout::append(char c) {
- if (n == capacity)
- grow(0);
- buf[n] = c;
- ++n;
-}
-
-inline char* kvout::reserve(int nchars) {
- if (n + nchars > capacity)
- grow(n + nchars);
- return buf + n;
-}
-
-inline void kvout::adjust_length(int delta) {
- masstree_precondition(n + delta <= capacity);
- n += delta;
-}
-
-inline void kvout::set_end(char* x) {
- masstree_precondition(x >= buf && x <= buf + capacity);
- n = x - buf;
-}
-
-#endif
diff --git a/kvproto.hh b/kvproto.hh
deleted file mode 100644
index d14b207..0000000
--- a/kvproto.hh
+++ /dev/null
@@ -1,57 +0,0 @@
-/* Masstree
- * Eddie Kohler, Yandong Mao, Robert Morris
- * Copyright (c) 2012-2013 President and Fellows of Harvard College
- * Copyright (c) 2012-2013 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 KVPROTO_HH
-#define KVPROTO_HH
-#include "compiler.hh"
-
-enum {
- Cmd_None = 0,
- Cmd_Get = 2,
- Cmd_Scan = 4,
- Cmd_Put = 6,
- Cmd_Replace = 8,
- Cmd_Remove = 10,
- Cmd_Checkpoint = 12,
- Cmd_Handshake = 14,
- Cmd_Max
-};
-
-enum result_t {
- NotFound = -2,
- Retry,
- OutOfDate,
- Inserted,
- Updated,
- Found,
- ScanDone
-};
-
-enum ckptrav_order_t {
- ckptrav_inorder = 0,
- ckptrav_preorder
-};
-
-struct row_marker {
- enum { mt_remove = 1, mt_delta = 2 };
- int marker_type_;
-};
-
-template <typename R>
-inline bool row_is_marker(const R* row) {
- return row->timestamp() & 1;
-}
-
-#endif
diff --git a/kvrandom.cc b/kvrandom.cc
deleted file mode 100644
index a70a785..0000000
--- a/kvrandom.cc
+++ /dev/null
@@ -1,38 +0,0 @@
-/* Masstree
- * Eddie Kohler, Yandong Mao, Robert Morris
- * Copyright (c) 2012-2013 President and Fellows of Harvard College
- * Copyright (c) 2012-2013 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.
- */
-#include "kvrandom.hh"
-#include "compiler.hh"
-#include <stdio.h>
-
-const uint32_t kvrandom_psdes_nr::c1[] = {
- 0xBAA96887U, 0x1E17D32CU, 0x03BCDC3CU, 0x0F33D1B2U
-};
-const uint32_t kvrandom_psdes_nr::c2[] = {
- 0x4B0F3B58U, 0xE874F0C3U, 0x6955C5A6U, 0x55A7CA46U
-};
-
-uint32_t kvrandom_psdes_nr::psdes(uint32_t lword, uint32_t irword) {
- for (int i = 0; i < niter; ++i) {
- uint32_t iswap = irword;
- uint32_t ia = irword ^ c1[i];
- uint32_t il = ia & 0xFFFF, ih = ia >> 16;
- uint32_t ib = il * il + ~(ih * ih);
- ia = (ib >> 16) | (ib << 16);
- irword = lword ^ ((ia ^ c2[i]) + il * ih);
- lword = iswap;
- }
- return irword;
-}
diff --git a/kvrandom.hh b/kvrandom.hh
deleted file mode 100644
index cc31821..0000000
--- a/kvrandom.hh
+++ /dev/null
@@ -1,150 +0,0 @@
-/* Masstree
- * Eddie Kohler, Yandong Mao, Robert Morris
- * Copyright (c) 2012-2013 President and Fellows of Harvard College
- * Copyright (c) 2012-2013 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 KVRANDOM_HH
-#define KVRANDOM_HH 1
-#include <inttypes.h>
-#include <stdlib.h>
-#include <random>
-
-// A simple LCG with parameters from Numerical Recipes.
-class kvrandom_lcg_nr_simple {
-public:
- using result_type = uint32_t;
- using seed_type = uint32_t;
- static constexpr result_type min() {
- return 0;
- }
- static constexpr result_type max() {
- return 0xFFFFFFFFU;
- }
-
- kvrandom_lcg_nr_simple()
- : seed_(default_seed) {
- }
- explicit kvrandom_lcg_nr_simple(seed_type s)
- : seed_(s) {
- }
- void seed(seed_type s) {
- seed_ = s;
- }
- result_type operator()() {
- seed_ = seed_ * a + c;
- return (seed_ = seed_ * a + c);
- }
-
-private:
- uint32_t seed_;
- enum { default_seed = 819234718U, a = 1664525U, c = 1013904223U };
-};
-
-// A combination version of the NR LCG that uses only its higher order
-// digits. (In the default NR LCG the lowest bits have less randomness; e.g.,
-// the low bit flips between 0 and 1 with every call.)
-class kvrandom_lcg_nr : public kvrandom_lcg_nr_simple {
-public:
- static constexpr result_type max() {
- return 0x7FFFFFFFU;
- }
-
- result_type operator()() {
- uint32_t x0 = kvrandom_lcg_nr_simple::operator()();
- uint32_t x1 = kvrandom_lcg_nr_simple::operator()();
- return (x0 >> 15) | ((x1 & 0x7FFE) << 16);
- }
-};
-
-// A random number generator taken from NR's ran4. Based on hashing.
-class kvrandom_psdes_nr {
-public:
- using result_type = uint32_t;
- using seed_type = uint32_t;
- static constexpr result_type min() {
- return 0;
- }
- static constexpr result_type max() {
- return 0xFFFFFFFFU;
- }
-
- kvrandom_psdes_nr() {
- seed(1);
- }
- explicit kvrandom_psdes_nr(seed_type s) {
- seed(s);
- }
- void seed(seed_type s) {
- seed_ = s;
- next_ = 1;
- }
- result_type operator()() {
- uint32_t value = psdes(seed_, next_);
- ++next_;
- return value;
- }
- result_type operator[](uint32_t index) const {
- return psdes(seed_, index);
- }
-private:
- uint32_t seed_;
- uint32_t next_;
- enum { niter = 4 };
- static const uint32_t c1[niter], c2[niter];
- static uint32_t psdes(uint32_t lword, uint32_t irword);
-};
-
-// a wrapper around random(), for backwards compatibility
-class kvrandom_random {
-public:
- using result_type = uint32_t;
- static constexpr result_type min() {
- return 0;
- }
- static constexpr result_type max() {
- return 0x7FFFFFFFU;
- }
-
- kvrandom_random() {
- }
- void seed(uint32_t s) {
- srandom(s);
- }
- result_type operator()() {
- return random();
- }
-};
-
-// a modulus-based, i.e. incorrect, version of uniform_int_distribution
-// that is faster than the standard
-template <typename T = int>
-class kvrandom_uniform_int_distribution {
-public:
- using result_type = T;
-
- kvrandom_uniform_int_distribution(T a, T b)
- : a_(a), n_(b - a + 1) {
- }
- template <typename G>
- result_type operator()(G& g) const {
- return a_ + g() % n_;
- }
-private:
- result_type a_;
- result_type n_;
-};
-
-// the std::bernoulli_distribution is fast enough
-using kvrandom_bernoulli_distribution = std::bernoulli_distribution;
-
-#endif
diff --git a/kvrow.hh b/kvrow.hh
deleted file mode 100644
index a92c9b2..0000000
--- a/kvrow.hh
+++ /dev/null
@@ -1,349 +0,0 @@
-/* Masstree
- * Eddie Kohler, Yandong Mao, Robert Morris
- * Copyright (c) 2012-2013 President and Fellows of Harvard College
- * Copyright (c) 2012-2013 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 KVROW_HH
-#define KVROW_HH 1
-#include "kvthread.hh"
-#include "kvproto.hh"
-#include "log.hh"
-#include "json.hh"
-#include <algorithm>
-
-#if MASSTREE_ROW_TYPE_ARRAY
-# include "value_array.hh"
-typedef value_array row_type;
-#elif MASSTREE_ROW_TYPE_ARRAY_VER
-# include "value_versioned_array.hh"
-typedef value_versioned_array row_type;
-#elif MASSTREE_ROW_TYPE_STR
-# include "value_string.hh"
-typedef value_string row_type;
-#else
-# include "value_bag.hh"
-typedef value_bag<uint16_t> row_type;
-#endif
-
-template <typename R>
-struct query_helper {
- inline const R* snapshot(const R* row, const std::vector<typename R::index_type>&, threadinfo&) {
- return row;
- }
-};
-
-template <typename R> class query_json_scanner;
-
-template <typename R>
-class query {
- public:
- typedef lcdf::Json Json;
-
- template <typename T>
- void run_get(T& table, Json& req, threadinfo& ti);
- template <typename T>
- bool run_get1(T& table, Str key, int col, Str& value, threadinfo& ti);
-
- template <typename T>
- result_t run_put(T& table, Str key,
- const Json* firstreq, const Json* lastreq, threadinfo& ti);
- template <typename T>
- result_t run_replace(T& table, Str key, Str value, threadinfo& ti);
- template <typename T>
- bool run_remove(T& table, Str key, threadinfo& ti);
-
- template <typename T>
- void run_scan(T& table, Json& request, threadinfo& ti);
- template <typename T>
- void run_scan_versions(T& table, Json& request, std::vector<uint64_t>& scan_versions, threadinfo& ti);
- template <typename T>
- void run_rscan(T& table, Json& request, threadinfo& ti);
-
- const loginfo::query_times& query_times() const {
- return qtimes_;
- }
-
- private:
- std::vector<typename R::index_type> f_;
- loginfo::query_times qtimes_;
- query_helper<R> helper_;
- lcdf::String scankey_;
- int scankeypos_;
-
- void emit_fields(const R* value, Json& req, threadinfo& ti);
- void emit_fields1(const R* value, Json& req, threadinfo& ti);
- void assign_timestamp(threadinfo& ti);
- void assign_timestamp(threadinfo& ti, kvtimestamp_t t);
- inline bool apply_put(R*& value, bool found, const Json* firstreq,
- const Json* lastreq, threadinfo& ti);
- inline bool apply_replace(R*& value, bool found, Str new_value,
- threadinfo& ti);
- inline void apply_remove(R*& value, kvtimestamp_t& node_ts, threadinfo& ti);
-
- template <typename RR> friend class query_json_scanner;
-};
-
-
-template <typename R>
-void query<R>::emit_fields(const R* value, Json& req, threadinfo& ti) {
- const R* snapshot = helper_.snapshot(value, f_, ti);
- if (f_.empty()) {
- for (int i = 0; i != snapshot->ncol(); ++i)
- req.push_back(lcdf::String::make_stable(snapshot->col(i)));
- } else {
- for (int i = 0; i != (int) f_.size(); ++i)
- req.push_back(lcdf::String::make_stable(snapshot->col(f_[i])));
- }
-}
-
-template <typename R>
-void query<R>::emit_fields1(const R* value, Json& req, threadinfo& ti) {
- const R* snapshot = helper_.snapshot(value, f_, ti);
- if ((f_.empty() && snapshot->ncol() == 1) || f_.size() == 1)
- req = lcdf::String::make_stable(snapshot->col(f_.empty() ? 0 : f_[0]));
- else if (f_.empty()) {
- for (int i = 0; i != snapshot->ncol(); ++i)
- req.push_back(lcdf::String::make_stable(snapshot->col(i)));
- } else {
- for (int i = 0; i != (int) f_.size(); ++i)
- req.push_back(lcdf::String::make_stable(snapshot->col(f_[i])));
- }
-}
-
-
-template <typename R> template <typename T>
-void query<R>::run_get(T& table, Json& req, threadinfo& ti) {
- typename T::unlocked_cursor_type lp(table, req[2].as_s());
- bool found = lp.find_unlocked(ti);
- if (found && row_is_marker(lp.value()))
- found = false;
- if (found) {
- f_.clear();
- for (int i = 3; i != req.size(); ++i) {
- f_.push_back(req[i].as_i());
- }
- req.resize(2);
- emit_fields(lp.value(), req, ti);
- }
-}
-
-template <typename R> template <typename T>
-bool query<R>::run_get1(T& table, Str key, int col, Str& value, threadinfo& ti) {
- typename T::unlocked_cursor_type lp(table, key);
- bool found = lp.find_unlocked(ti);
- if (found && row_is_marker(lp.value()))
- found = false;
- if (found)
- value = lp.value()->col(col);
- return found;
-}
-
-
-template <typename R>
-inline void query<R>::assign_timestamp(threadinfo& ti) {
- qtimes_.ts = ti.update_timestamp();
- qtimes_.prev_ts = 0;
-}
-
-template <typename R>
-inline void query<R>::assign_timestamp(threadinfo& ti, kvtimestamp_t min_ts) {
- qtimes_.ts = ti.update_timestamp(min_ts);
- qtimes_.prev_ts = min_ts;
-}
-
-
-template <typename R> template <typename T>
-result_t query<R>::run_put(T& table, Str key,
- const Json* firstreq, const Json* lastreq,
- threadinfo& ti) {
- typename T::cursor_type lp(table, key);
- bool found = lp.find_insert(ti);
- if (!found) {
- ti.observe_phantoms(lp.node());
- }
- bool inserted = apply_put(lp.value(), found, firstreq, lastreq, ti);
- lp.finish(1, ti);
- return inserted ? Inserted : Updated;
-}
-
-template <typename R>
-inline bool query<R>::apply_put(R*& value, bool found, const Json* firstreq,
- const Json* lastreq, threadinfo& ti) {
- if (loginfo* log = ti.logger()) {
- log->acquire();
- qtimes_.epoch = global_log_epoch;
- }
-
- if (!found) {
- insert:
- assign_timestamp(ti);
- value = R::create(firstreq, lastreq, qtimes_.ts, ti);
- return true;
- }
-
- R* old_value = value;
- assign_timestamp(ti, old_value->timestamp());
- if (row_is_marker(old_value)) {
- old_value->deallocate_rcu(ti);
- goto insert;
- }
-
- R* updated = old_value->update(firstreq, lastreq, qtimes_.ts, ti);
- if (updated != old_value) {
- value = updated;
- old_value->deallocate_rcu_after_update(firstreq, lastreq, ti);
- }
- return false;
-}
-
-template <typename R> template <typename T>
-result_t query<R>::run_replace(T& table, Str key, Str value, threadinfo& ti) {
- typename T::cursor_type lp(table, key);
- bool found = lp.find_insert(ti);
- if (!found) {
- ti.observe_phantoms(lp.node());
- }
- bool inserted = apply_replace(lp.value(), found, value, ti);
- lp.finish(1, ti);
- return inserted ? Inserted : Updated;
-}
-
-template <typename R>
-inline bool query<R>::apply_replace(R*& value, bool found, Str new_value,
- threadinfo& ti) {
- if (loginfo* log = ti.logger()) {
- log->acquire();
- qtimes_.epoch = global_log_epoch;
- }
-
- bool inserted = !found || row_is_marker(value);
- if (!found) {
- assign_timestamp(ti);
- } else {
- assign_timestamp(ti, value->timestamp());
- value->deallocate_rcu(ti);
- }
-
- value = R::create1(new_value, qtimes_.ts, ti);
- return inserted;
-}
-
-template <typename R> template <typename T>
-bool query<R>::run_remove(T& table, Str key, threadinfo& ti) {
- typename T::cursor_type lp(table, key);
- bool found = lp.find_locked(ti);
- if (found)
- apply_remove(lp.value(), lp.node()->phantom_epoch_[0], ti);
- lp.finish(-1, ti);
- return found;
-}
-
-template <typename R>
-inline void query<R>::apply_remove(R*& value, kvtimestamp_t& node_ts,
- threadinfo& ti) {
- if (loginfo* log = ti.logger()) {
- log->acquire();
- qtimes_.epoch = global_log_epoch;
- }
-
- R* old_value = value;
- assign_timestamp(ti, old_value->timestamp());
- if (circular_int<kvtimestamp_t>::less_equal(node_ts, qtimes_.ts)) {
- node_ts = qtimes_.ts + 2;
- }
- old_value->deallocate_rcu(ti);
-}
-
-
-template <typename R>
-class query_json_scanner {
- public:
- query_json_scanner(query<R>& q, lcdf::Json& request, std::vector<uint64_t>* scan_versions)
- : q_(q), nleft_(request[3].as_i()), request_(request),
- scan_versions_(scan_versions) {
- std::swap(request[2].value().as_s(), firstkey_);
- request_.resize(2);
- q_.scankeypos_ = 0;
- }
- const lcdf::String& firstkey() const {
- return firstkey_;
- }
- template <typename SS, typename K>
- void visit_leaf(const SS& scanstack, const K&, threadinfo&) {
- if (scan_versions_) {
- scan_versions_->push_back(reinterpret_cast<uint64_t>(scanstack.node()));
- scan_versions_->push_back(scanstack.full_version_value());
- }
- }
- bool visit_value(Str key, R* value, threadinfo& ti) {
- if (row_is_marker(value)) {
- return true;
- }
- // NB the `key` is not stable! We must save space for it.
- while (q_.scankeypos_ + key.length() > q_.scankey_.length()) {
- q_.scankey_ = lcdf::String::make_uninitialized(q_.scankey_.length() ? q_.scankey_.length() * 2 : 1024);
- q_.scankeypos_ = 0;
- }
- memcpy(const_cast<char*>(q_.scankey_.data() + q_.scankeypos_),
- key.data(), key.length());
- request_.push_back(q_.scankey_.substr(q_.scankeypos_, key.length()));
- q_.scankeypos_ += key.length();
- request_.push_back(lcdf::Json());
- q_.emit_fields1(value, request_.back(), ti);
- --nleft_;
- return nleft_ != 0;
- }
- private:
- query<R>& q_;
- int nleft_;
- lcdf::Json& request_;
- lcdf::String firstkey_;
- std::vector<uint64_t>* scan_versions_;
-};
-
-template <typename R> template <typename T>
-void query<R>::run_scan(T& table, Json& request, threadinfo& ti) {
- assert(request[3].as_i() > 0);
- f_.clear();
- for (int i = 4; i != request.size(); ++i) {
- f_.push_back(request[i].as_i());
- }
- query_json_scanner<R> scanf(*this, request, nullptr);
- table.scan(scanf.firstkey(), true, scanf, ti);
-}
-
-template <typename R> template <typename T>
-void query<R>::run_scan_versions(T& table, Json& request,
- std::vector<uint64_t>& scan_versions,
- threadinfo& ti) {
- assert(request[3].as_i() > 0);
- f_.clear();
- for (int i = 4; i != request.size(); ++i) {
- f_.push_back(request[i].as_i());
- }
- query_json_scanner<R> scanf(*this, request, &scan_versions);
- table.scan(scanf.firstkey(), true, scanf, ti);
-}
-
-template <typename R> template <typename T>
-void query<R>::run_rscan(T& table, Json& request, threadinfo& ti) {
- assert(request[3].as_i() > 0);
- f_.clear();
- for (int i = 4; i != request.size(); ++i) {
- f_.push_back(request[i].as_i());
- }
- query_json_scanner<R> scanf(*this, request, nullptr);
- table.rscan(scanf.firstkey(), true, scanf, ti);
-}
-
-#endif
diff --git a/kvstats.hh b/kvstats.hh
deleted file mode 100644
index c4775e3..0000000
--- a/kvstats.hh
+++ /dev/null
@@ -1,53 +0,0 @@
-/* Masstree
- * Eddie Kohler, Yandong Mao, Robert Morris
- * Copyright (c) 2012-2013 President and Fellows of Harvard College
- * Copyright (c) 2012-2013 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 KVSTATS_HH
-#define KVSTATS_HH 1
-#include <stdlib.h>
-
-struct kvstats {
- double min, max, sum, sumsq;
- long count;
- kvstats()
- : min(-1), max(-1), sum(0), sumsq(0), count(0) {
- }
- void add(double x) {
- if (!count || x < min)
- min = x;
- if (max < x)
- max = x;
- sum += x;
- sumsq += x * x;
- count += 1;
- }
- typedef void (kvstats::*unspecified_bool_type)(double);
- operator unspecified_bool_type() const {
- return count ? &kvstats::add : 0;
- }
- void print_report(const char *name) const {
- if (count)
- printf("%s: n %ld, total %.0f, average %.0f, min %.0f, max %.0f, stddev %.0f\n",
- name, count, sum, sum / count, min, max,
- sqrt((sumsq - sum * sum / count) / (count - 1)));
- }
- double avg() {
- if (count)
- return sum / count;
- else
- return 0;
- }
-};
-
-#endif
diff --git a/kvtest.hh b/kvtest.hh
deleted file mode 100644
index 41c9da8..0000000
--- a/kvtest.hh
+++ /dev/null
@@ -1,1876 +0,0 @@
-/* Masstree
- * Eddie Kohler, Yandong Mao, Robert Morris
- * Copyright (c) 2012-2013 President and Fellows of Harvard College
- * Copyright (c) 2012-2013 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 KVTEST_HH
-#define KVTEST_HH
-#include "json.hh"
-#include "misc.hh"
-#include "kvproto.hh"
-#include "kvrandom.hh"
-#include <vector>
-#include <fstream>
-#include <random>
-
-using lcdf::Str;
-using lcdf::String;
-using lcdf::Json;
-extern int kvtest_first_seed;
-// Templated KV tests, so we can run them either client/server or linked with
-// the kvd binary.
-
-template <typename N>
-inline Json& kvtest_set_time(Json& result, const lcdf::String& base, N n, double delta_t)
-{
- result.set(base, n);
- if (delta_t > 0)
- result.set(base + "_per_sec", n / delta_t);
- return result;
-}
-
-template <typename N>
-inline Json kvtest_set_time(const Json& result, const lcdf::String& base, N n, double delta_t) {
- Json x(result);
- kvtest_set_time(x, base, n, delta_t);
- return x;
-}
-
-template <typename C>
-void kvtest_sync_rw1_seed(C &client, int seed)
-{
- client.rand.seed(seed);
- double tp0 = client.now();
- unsigned n;
- for (n = 0; !client.timeout(0) && n <= client.limit(); ++n) {
- int32_t x = (int32_t) client.rand();
- client.put_sync(x, x + 1);
- }
- client.wait_all();
- double tp1 = client.now();
-
- client.puts_done();
- client.notice("now getting\n");
- int32_t *a = (int32_t *) malloc(sizeof(int32_t) * n);
- assert(a);
- client.rand.seed(seed);
- for (unsigned i = 0; i < n; ++i) {
- a[i] = (int32_t) client.rand();
- }
- kvrandom_uniform_int_distribution<unsigned> swapd(0, n - 1);
- for (unsigned i = 0; i < n; ++i) {
- std::swap(a[i], a[swapd(client.rand)]);
- }
-
- double tg0 = client.now();
- unsigned g;
- for (g = 0; g < n && !client.timeout(1); ++g) {
- client.get_check_sync(a[g], a[g] + 1);
- }
- client.wait_all();
- double tg1 = client.now();
-
- Json result = Json();
- kvtest_set_time(result, "puts", n, tp1 - tp0);
- kvtest_set_time(result, "gets", g, tg1 - tg0);
- kvtest_set_time(result, "ops", n + g, (tp1 - tp0) + (tg1 - tg0));
- client.report(result);
- free(a);
-}
-
-template <typename C>
-void kvtest_sync_rw1(C &client)
-{
- kvtest_sync_rw1_seed(client, kvtest_first_seed + client.id() % 48);
-}
-
-template <typename C>
-unsigned kvtest_rw1puts_seed(C& client, int seed) {
- client.rand.seed(seed);
- double tp0 = client.now();
- unsigned n;
- for (n = 0; !client.timeout(0) && n <= client.limit(); ++n) {
- int32_t x = (int32_t) client.rand();
- client.put(x, x + 1);
- }
- client.wait_all();
- double tp1 = client.now();
- client.puts_done();
-
- client.report(kvtest_set_time(Json(), "puts", n, tp1 - tp0));
- return n;
-}
-
-// do a bunch of inserts to distinct keys, then check that they all showed up.
-// sometimes overwrites, but only w/ same value.
-// different clients might use same key sometimes.
-template <typename C>
-void kvtest_rw1_seed(C &client, int seed)
-{
- unsigned n = kvtest_rw1puts_seed(client, seed);
-
- client.notice("now getting\n");
- int32_t *a = (int32_t *) malloc(sizeof(int32_t) * n);
- assert(a);
- client.rand.seed(seed);
- for (unsigned i = 0; i < n; ++i) {
- a[i] = (int32_t) client.rand();
- }
- kvrandom_uniform_int_distribution<unsigned> swapd(0, n - 1);
- for (unsigned i = 0; i < n; ++i) {
- std::swap(a[i], a[swapd(client.rand)]);
- }
-
- double tg0 = client.now();
- unsigned g;
-#if 0
-#define BATCH 8
- for(g = 0; g+BATCH < n && !client.timeout(1); g += BATCH){
- long key[BATCH], expected[BATCH];
- for(int i = 0; i < BATCH; i++){
- key[i] = a[g+i];
- expected[i] = a[g+i] + 1;
- }
- client.many_get_check(BATCH, key, expected);
- }
-#else
- for (g = 0; g < n && !client.timeout(1); ++g) {
- client.get_check(a[g], a[g] + 1);
- }
-#endif
- client.wait_all();
- double tg1 = client.now();
-
- Json result = client.report(Json());
- kvtest_set_time(result, "gets", g, tg1 - tg0);
- double delta_puts = n / result["puts_per_sec"].as_d();
- kvtest_set_time(result, "ops", n + g, delta_puts + (tg1 - tg0));
- client.report(result);
- free(a);
-}
-
-template <typename C>
-void kvtest_rw1puts(C &client)
-{
- kvtest_rw1puts_seed(client, kvtest_first_seed + client.id() % 48);
-}
-
-template <typename C>
-void kvtest_rw1(C &client)
-{
- kvtest_rw1_seed(client, kvtest_first_seed + client.id() % 48);
-}
-
-// do a bunch of inserts to distinct keys, then check that they all showed up.
-// sometimes overwrites, but only w/ same value.
-// different clients might use same key sometimes.
-template <typename C>
-void kvtest_rw1long_seed(C &client, int seed)
-{
- const char * const formats[] = {
- "user%u", "machine%u", "opening%u", "fartparade%u"
- };
- char buf[64];
-
- client.rand.seed(seed);
- double tp0 = client.now();
- unsigned n;
- kvrandom_uniform_int_distribution<unsigned> fmtd(0, 3);
- for (n = 0; !client.timeout(0) && n <= client.limit(); ++n) {
- int32_t x = (int32_t) client.rand();
- client.put(Str::snprintf(buf, sizeof(buf), formats[fmtd(client.rand)], x), x + 1);
- }
- client.wait_all();
- double tp1 = client.now();
-
- client.puts_done();
- client.notice("now getting\n");
- int32_t *a = (int32_t *) malloc(sizeof(int32_t) * n * 2);
- assert(a);
- client.rand.seed(seed);
- for (unsigned i = 0; i < n * 2; ++i) {
- a[i] = (int32_t) client.rand();
- }
- kvrandom_uniform_int_distribution<unsigned> swapd(0, n - 1);
- for (unsigned i = 0; i < n; ++i) {
- unsigned x = swapd(client.rand);
- std::swap(a[2 * i], a[2 * x]);
- std::swap(a[2 * i + 1], a[2 * x + 1]);
- }
-
- double tg0 = client.now();
- unsigned g;
- for (g = 0; g < n && !client.timeout(1); ++g) {
- unsigned fmt = a[2 * g];
- int32_t x = (int32_t) a[2 * g + 1];
- client.get_check(Str::snprintf(buf, sizeof(buf), formats[fmt % 4], x), x + 1);
- }
- client.wait_all();
- double tg1 = client.now();
-
- Json result = Json();
- kvtest_set_time(result, "puts", n, tp1 - tp0);
- kvtest_set_time(result, "gets", g, tg1 - tg0);
- kvtest_set_time(result, "ops", n + g, (tp1 - tp0) + (tg1 - tg0));
- client.report(result);
- free(a);
-}
-
-template <typename C>
-void kvtest_rw1long(C &client)
-{
- kvtest_rw1long_seed(client, kvtest_first_seed + client.id() % 48);
-}
-
-// interleave inserts and gets for random keys.
-template <typename C>
-void kvtest_rw2_seed(C &client, int seed, double getfrac)
-{
- client.rand.seed(seed);
- const unsigned c = 2654435761U;
- const unsigned offset = client.rand();
-
- double t0 = client.now();
- uint64_t puts = 0, gets = 0;
- kvrandom_bernoulli_distribution getd(getfrac);
- while (!client.timeout(0) && (puts + gets) <= client.limit()) {
- if (puts == 0 || !getd(client.rand)) {
- // insert
- unsigned x = (offset + puts) * c;
- client.put(x, x + 1);
- ++puts;
- } else {
- // get
- unsigned x = (offset + (client.rand() % puts)) * c;
- client.get_check(x, x + 1);
- ++gets;
- }
- }
- client.wait_all();
- double t1 = client.now();
-
- Json result = Json().set("puts", puts).set("gets", gets);
- kvtest_set_time(result, "ops", puts + gets, t1 - t0);
- client.report(result);
-}
-
-template <typename C>
-void kvtest_rw2(C &client)
-{
- kvtest_rw2_seed(client, kvtest_first_seed + client.id() % 48, 0.5);
-}
-
-template <typename C>
-void kvtest_rw2g90(C &client)
-{
- kvtest_rw2_seed(client, kvtest_first_seed + client.id() % 48, 0.9);
-}
-
-template <typename C>
-void kvtest_rw2g98(C &client)
-{
- kvtest_rw2_seed(client, kvtest_first_seed + client.id() % 48, 0.98);
-}
-
-// interleave inserts and gets for random keys.
-template <typename C>
-void kvtest_rw2fixed_seed(C &client, int seed, double getfrac)
-{
- client.rand.seed(seed);
- const unsigned c = 2654435761U;
- const unsigned offset = client.rand();
-
- double t0 = client.now();
- uint64_t puts = 0, gets = 0;
- kvrandom_bernoulli_distribution getd(getfrac);
- while (!client.timeout(0) && (puts + gets) <= client.limit()) {
- if (puts == 0 || !getd(client.rand)) {
- // insert
- unsigned x = (offset + puts) * c;
- x %= 100000000;
- client.put(x, x + 1);
- ++puts;
- } else {
- // get
- unsigned x = (offset + (client.rand() % puts)) * c;
- x %= 100000000;
- client.get_check(x, x + 1);
- ++gets;
- }
- }
- client.wait_all();
- double t1 = client.now();
-
- Json result = Json().set("puts", puts).set("gets", gets);
- kvtest_set_time(result, "ops", puts + gets, t1 - t0);
- client.report(result);
-}
-
-template <typename C>
-void kvtest_rw2fixed(C &client)
-{
- kvtest_rw2fixed_seed(client, kvtest_first_seed + client.id() % 48, 0.5);
-}
-
-template <typename C>
-void kvtest_rw2fixedg90(C &client)
-{
- kvtest_rw2fixed_seed(client, kvtest_first_seed + client.id() % 48, 0.9);
-}
-
-template <typename C>
-void kvtest_rw2fixedg98(C &client)
-{
- kvtest_rw2fixed_seed(client, kvtest_first_seed + client.id() % 48, 0.98);
-}
-
-// do a bunch of inserts to sequentially increasing keys,
-// then check that they all showed up.
-// different clients might use same key sometimes.
-template <typename C>
-void kvtest_rw3(C &client)
-{
- double t0 = client.now();
- uint64_t n;
- for (n = 0; !client.timeout(0) && n <= client.limit(); ++n) {
- client.put_key8(n, n + 1);
- }
- client.wait_all();
-
- client.puts_done();
- client.notice("now getting\n");
-
- double t1 = client.now();
- for (unsigned i = 0; i < n; ++i) {
- client.get_check_key8(i, i + 1);
- }
- client.wait_all();
-
- double t2 = client.now();
-
- Json result = Json();
- kvtest_set_time(result, "puts", n, t1 - t0);
- kvtest_set_time(result, "gets", n, t2 - t1);
- kvtest_set_time(result, "ops", n + n, t2 - t0);
- client.report(result);
-}
-
-// do a bunch of inserts to sequentially decreasing keys,
-// then check that they all showed up.
-// different clients might use same key sometimes.
-template <typename C>
-void kvtest_rw4(C &client)
-{
- const int top = 2147483647;
-
- double t0 = client.now();
- unsigned n;
- for (n = 0; !client.timeout(0) && n <= client.limit(); ++n) {
- client.put_key8(top - n, n + 1);
- }
- client.wait_all();
-
- client.puts_done();
- client.notice("now getting\n");
-
- double t1 = client.now();
- for (unsigned i = 0; i < n; ++i) {
- client.get_check_key8(top - i, i + 1);
- }
- client.wait_all();
-
- double t2 = client.now();
-
- Json result = Json();
- kvtest_set_time(result, "puts", n, t1 - t0);
- kvtest_set_time(result, "gets", n, t2 - t1);
- kvtest_set_time(result, "ops", n + n, t2 - t0);
- client.report(result);
-}
-
-// do a bunch of inserts to sequentially decreasing 8B keys,
-// then check that they all showed up.
-// different clients might use same key sometimes.
-template <typename C>
-void kvtest_rw4fixed(C &client)
-{
- const int top = 99999999;
-
- double t0 = client.now();
- unsigned n;
- for (n = 0; !client.timeout(0) && n <= client.limit(); ++n) {
- client.put_key8(top - n, n + 1);
- }
- client.wait_all();
-
- client.puts_done();
- client.notice("now getting\n");
-
- double t1 = client.now();
- for (unsigned i = 0; i < n; ++i) {
- client.get_check_key8(top - i, i + 1);
- }
- client.wait_all();
-
- double t2 = client.now();
-
- Json result = Json();
- kvtest_set_time(result, "puts", n, t1 - t0);
- kvtest_set_time(result, "gets", n, t2 - t1);
- kvtest_set_time(result, "ops", n + n, t2 - t0);
- client.report(result);
-}
-
-// update the same small set of keys over and over,
-// to uncover concurrent update bugs in the server.
-template <typename C>
-void kvtest_same_seed(C &client, int seed)
-{
- client.rand.seed(seed);
-
- double t0 = client.now();
- unsigned n;
- kvrandom_uniform_int_distribution<unsigned> uid(0, 9);
- for (n = 0; !client.timeout(0) && n <= client.limit(); ++n) {
- unsigned x = uid(client.rand);
- client.put(x, x + 1);
- }
- client.wait_all();
- double t1 = client.now();
-
- Json result = Json();
- kvtest_set_time(result, "puts", n, t1 - t0);
- client.report(result);
-}
-
-template <typename C>
-void kvtest_same(C &client)
-{
- kvtest_same_seed(client, kvtest_first_seed + client.id() % 48);
-}
-
-// update the same small set of keys over and over, with interspersed gets.
-template <typename C>
-void kvtest_rwsmall_seed(C &client, int nkeys, int seed)
-{
- client.rand.seed(seed);
-
- double t0 = client.now();
- unsigned n;
- kvrandom_uniform_int_distribution<unsigned> uid(0, (nkeys << 3) - 1);
- for (n = 0; !client.timeout(0) && n <= client.limit(); ++n) {
- unsigned x = uid(client.rand);
- if (x & 7) {
- client.get(x >> 3);
- } else {
- client.put(x >> 3, n);
- }
- }
- client.wait_all();
- double t1 = client.now();
-
- Json result = Json();
- kvtest_set_time(result, "ops", n, t1 - t0);
- client.report(result);
-}
-
-template <typename C>
-void kvtest_rwsmall24(C &client)
-{
- kvtest_rwsmall_seed(client, 24, kvtest_first_seed + client.id() % 48);
-}
-
-// update the same small set of keys over and over, with interspersed gets.
-// but ensure that the keys are all on different cache lines.
-template <typename C>
-void kvtest_rwsep_seed(C &client, int nkeys, int clientid, int seed)
-{
- for (int n = clientid * (32 + nkeys); n < (clientid + 1) * (32 + nkeys); ++n) {
- client.put(1000000 + n, n);
- }
-
- client.rand.seed(seed);
-
- double t0 = client.now();
- unsigned n;
- kvrandom_uniform_int_distribution<unsigned> uid(0, (nkeys << 3) - 1);
- for (n = 0; !client.timeout(0) && n <= client.limit(); ++n) {
- unsigned x = uid(client.rand);
- if (x & 7) {
- client.get(1000000 + clientid * (32 + nkeys) + (x >> 3));
- } else {
- client.put(1000000 + clientid * (32 + nkeys) + (x >> 3), n);
- }
- }
- client.wait_all();
- double t1 = client.now();
-
- Json result = Json();
- kvtest_set_time(result, "ops", n, t1 - t0);
- client.report(result);
-}
-
-template <typename C>
-void kvtest_rwsep24(C &client)
-{
- kvtest_rwsep_seed(client, 24, client.id(), kvtest_first_seed + client.id() % 48);
-}
-
-// Same as rw1, except that the keys are no more than 8 bytes
-template <typename C>
-void kvtest_rw1fixed_seed(C &client, int seed)
-{
- client.rand.seed(seed);
- double tp0 = client.now();
- unsigned n;
- kvrandom_uniform_int_distribution<unsigned> uid(0, 99999999);
- for (n = 0; !client.timeout(0) && n <= client.limit(); ++n) {
- int32_t x = uid(client.rand);
- client.put(x, x + 1);
- }
- client.wait_all();
- double tp1 = client.now();
-
- client.puts_done();
- client.notice("now getting\n");
- int32_t *a = (int32_t *) malloc(sizeof(int32_t) * n);
- assert(a);
- client.rand.seed(seed);
- for (unsigned i = 0; i < n; ++i) {
- a[i] = uid(client.rand);
- }
- kvrandom_uniform_int_distribution<unsigned> swapd(0, n - 1);
- for (unsigned i = 0; i < n; ++i) {
- std::swap(a[i], a[swapd(client.rand)]);
- }
-
- double tg0 = client.now();
- unsigned g;
-#if 0
-#define BATCH 8
- for(g = 0; g+BATCH < n && !client.timeout(1); g += BATCH){
- long key[BATCH], expected[BATCH];
- for(int i = 0; i < BATCH; i++){
- key[i] = a[g+i];
- expected[i] = a[g+i] + 1;
- }
- client.many_get_check(BATCH, key, expected);
- }
-#else
- for (g = 0; g < n && !client.timeout(1); ++g) {
- client.get_check(a[g], a[g] + 1);
- }
-#endif
- client.wait_all();
- double tg1 = client.now();
-
- Json result = Json();
- kvtest_set_time(result, "puts", n, tp1 - tp0);
- kvtest_set_time(result, "gets", g, tg1 - tg0);
- kvtest_set_time(result, "ops", n + g, (tp1 - tp0) + (tg1 - tg0));
- client.report(result);
- free(a);
-}
-
-template <typename C>
-void kvtest_rw1fixed(C &client)
-{
- kvtest_rw1fixed_seed(client, kvtest_first_seed + client.id() % 48);
-}
-
-// Same as rw1, except that keys are 16-bytes (prefixed with "0"s)
-template <typename C>
-void kvtest_rw16_seed(C &client, int seed)
-{
- client.rand.seed(seed);
- double tp0 = client.now();
- int n;
- char key[256];
- char val[256];
- for (n = 0; !client.timeout(0); ++n) {
- int32_t x = (int32_t) client.rand();
- sprintf(key, "%016d", x);
- sprintf(val, "%016d", x + 1);
- client.put(key, val);
- }
- client.wait_all();
- double tp1 = client.now();
-
- client.puts_done();
- client.notice("now getting\n");
- int32_t *a = (int32_t *) malloc(sizeof(int32_t) * n);
- assert(a);
- client.rand.seed(seed);
- for (int i = 0; i < n; ++i) {
- a[i] = (int32_t) client.rand();
- }
- kvrandom_uniform_int_distribution<unsigned> swapd(0, n - 1);
- for (int i = 0; i < n; ++i) {
- std::swap(a[i], a[swapd(client.rand)]);
- }
-
- double tg0 = client.now();
- int g;
- for (g = 0; g < n && !client.timeout(1); ++g) {
- sprintf(key, "%016d", a[g]);
- sprintf(val, "%016d", a[g] + 1);
- client.get_check(key, val);
- }
- client.wait_all();
- double tg1 = client.now();
-
- Json result = Json();
- kvtest_set_time(result, "puts", n, tp1 - tp0);
- kvtest_set_time(result, "gets", g, tg1 - tg0);
- kvtest_set_time(result, "ops", n + g, (tp1 - tp0) + (tg1 - tg0));
- client.report(result);
- free(a);
-}
-
-template <typename C>
-void kvtest_rw16(C &client)
-{
- kvtest_rw16_seed(client, kvtest_first_seed + client.id() % 48);
-}
-
-
-// A writer and a deleter; the deleter chases the writer
-template <typename C>
-void kvtest_wd1(unsigned initial_pos, int incr, C &client)
-{
- incr = std::max(incr, client.nthreads() / 2);
- unsigned pos = initial_pos + ((client.id() / 2) % incr);
- unsigned n = 0;
- Json result = Json();
-
- double t0 = client.now();
- if (client.id() % 2) {
- while (!client.get_sync(pos + 16 * incr)) {
- /* spin */
- }
- while (!client.timeout(0) && n <= client.limit()) {
- ++n;
- if (client.remove_sync(pos)) {
- pos += incr;
- }
- if ((n % (1 << 6)) == 0) {
- client.rcu_quiesce();
- }
- }
- result.set("removepos", pos);
- } else {
- while (!client.timeout(0) && n <= client.limit()) {
- ++n;
- client.put(pos, pos + 1);
- pos += incr;
- if ((n % (1 << 6)) == 0) {
- client.rcu_quiesce();
- }
- }
- result.set("putpos", pos);
- }
- client.wait_all();
- double t1 = client.now();
-
- kvtest_set_time(result, "ops", n, t1 - t0);
- client.report(result);
-}
-
-template <typename C>
-void kvtest_wd1_check(unsigned initial_pos, int incr, C &client)
-{
- incr = std::max(incr, client.nthreads() / 2);
- unsigned pos = initial_pos + ((client.id() / 2) % incr);
- unsigned n = 0;
- Json result = Json();
-
- double t0 = client.now();
- if (client.id() % 2 == 0) {
- unsigned max_remove = -1, min_post_remove = -1, max_post_remove = -1;
- unsigned bugs = 0;
- bool found_putpos = false;
- constexpr int nbatch = 20;
- Str gotten[nbatch];
- char gottenbuf[nbatch * 16];
- for (int i = 0; i < nbatch; ++i) {
- gotten[i].s = &gottenbuf[i * 16];
- }
-
- while (!client.timeout(0)
- && (!found_putpos || pos < max_post_remove + 100000)) {
- for (int i = 0; i < nbatch; ++i) {
- gotten[i].len = 16;
- client.get(pos + i * incr, &gotten[i]);
- }
- client.wait_all();
- for (int i = 0; i < nbatch; ++i) {
- if (gotten[i].len) {
- if (min_post_remove == unsigned(-1)) {
- min_post_remove = max_post_remove = pos;
- } else if (!found_putpos) {
- max_post_remove = pos;
- } else if (++bugs == 1) {
- fprintf(stderr, "%u: present unexpectedly\n", pos);
- }
- } else {
- if (min_post_remove == unsigned(-1)) {
- max_remove = pos;
- } else {
- found_putpos = true;
- }
- }
- pos += incr;
- }
- }
-
- result.set("removepos", max_remove + incr);
- result.set("putpos", max_post_remove + incr);
- if (bugs) {
- result.set("buggykeys", bugs);
- }
- }
- client.wait_all();
- double t1 = client.now();
-
- kvtest_set_time(result, "ops", n, t1 - t0);
- client.report(result);
-}
-
-template <typename C>
-void kvtest_wd2(C &client)
-{
- char sbuf[32], kbuf[32], next_kbuf[32];
- const int sep = 26;
- const int p_remove = 1000, p_put2 = 10000, p_remove2 = 20000;
- int x = 0;
- quick_istr xstr(0);
-
- client.put(Str("n"), client.nthreads());
- always_assert(client.nthreads() > 1);
-
- // set up status keys
- snprintf(sbuf, sizeof(sbuf), "s%03d", client.id());
- for (int i = 0; i < sep; ++i) {
- sbuf[4] = 'A' + i;
- client.put(Str(sbuf, 5), Str());
- }
- client.put(Str(sbuf, 4), xstr.string());
-
- // set up main keys
- snprintf(kbuf, sizeof(kbuf), "k%03d", client.id());
- for (int i = 0; i < sep; ++i) {
- kbuf[4] = 'A' + i;
- client.put(Str(kbuf, 5), Str());
- }
- client.put(Str(kbuf, 4), Str());
-
- snprintf(next_kbuf, sizeof(next_kbuf), "k%03d", (client.id() + 1) % client.nthreads());
-
- // main loop
- double t0 = client.now();
- int put_status = 0;
- long nrounds = 0;
- while (!client.timeout(0)) {
- ++nrounds;
- client.put(Str(kbuf, 4), xstr.string(), &put_status);
- if ((client.rand() % 65536) < p_remove) {
- client.remove(Str(next_kbuf, 4));
- }
-
- int rand = client.rand() % 65536;
- if (rand < p_put2) {
- for (int i = sep - 1; i >= 0; --i) {
- next_kbuf[4] = 'A' + i;
- client.put(Str(next_kbuf, 5), Str());
- }
- } else if (rand < p_remove2) {
- for (int i = sep - 1; i >= 0; --i) {
- next_kbuf[4] = 'A' + i;
- client.remove(Str(next_kbuf, 5));
- }
- } else {
- /* do nothing */
- }
-
- client.wait_all();
-
- if (put_status == Inserted) {
- ++x;
- xstr.set(x);
- client.put(Str(sbuf, 4), xstr.string());
- }
- }
- double t1 = client.now();
-
- Json result;
- kvtest_set_time(result, "rounds", nrounds, t1 - t0);
- client.report(result);
-}
-
-template <typename C>
-void kvtest_wd2_check(C &client)
-{
- if (client.id() != 0) {
- return;
- }
-
- int n;
- client.get(Str("n"), &n);
- client.wait_all();
- always_assert(n > 1);
- Json result;
-
- char buf[32];
- for (int i = 0; i < n; ++i) {
- int s, k;
- snprintf(buf, sizeof(buf), "k%03d", i);
- client.get(Str(buf, 4), &k);
- snprintf(buf, sizeof(buf), "s%03d", i);
- client.get(Str(buf, 4), &s);
- client.wait_all();
- if (!(s >= 0 && (s == k || s == k + 1 || k == -1))) {
- fprintf(stderr, "problem: s%03d=%d vs. k%03d=%d\n",
- i, s, i, k);
- }
- result.set("thread" + String(i), Json().push_back(s).push_back(k));
- }
-
- client.report(result);
-}
-
-template <typename C>
-void kvtest_wd3(C& client, uint64_t nk_total)
-{
- if (client.has_param("rangesize")) {
- nk_total = client.param("rangesize").to_u64() * client.nthreads();
- } else if (client.has_param("nkeys")) {
- nk_total = client.param("nkeys").to_u64();
- }
- uint64_t nk = (nk_total + client.nthreads() - 1) / client.nthreads();
- quick_istr k0(nk * client.id(), 8);
-
- String prefix = client.param("prefix").to_s();
- size_t plen = prefix.length();
- char buf[128];
- always_assert(plen + k0.length() < sizeof(buf));
- memcpy(buf, prefix.data(), plen);
- char* ebuf = buf + plen + k0.length();
-
- double t0 = client.now();
- unsigned long nrounds = 0;
- while (!client.timeout(0)) {
- ++nrounds;
- memcpy(ebuf - k0.length(), k0.data(), k0.length());
- for (uint64_t i = nk; i != 0 && !client.timeout(0); --i) {
- client.insert_check(Str(buf, ebuf), Str(ebuf - 8, ebuf));
- quick_istr::increment_from_end(ebuf);
- }
-
- memcpy(ebuf - k0.length(), k0.data(), k0.length());
- for (uint64_t i = nk; i != 0 && !client.timeout(0); --i) {
- client.get_check(Str(buf, ebuf), Str(ebuf - 8, ebuf));
- quick_istr::increment_from_end(ebuf);
- }
-
- memcpy(ebuf - k0.length(), k0.data(), k0.length());
- for (uint64_t i = nk; i != 0 && !client.timeout(0); --i) {
- client.remove_check(Str(buf, ebuf));
- quick_istr::increment_from_end(ebuf);
- }
-
- memcpy(ebuf - k0.length(), k0.data(), k0.length());
- for (uint64_t i = nk; i != 0 && !client.timeout(0); --i) {
- client.get_check_absent(Str(buf, ebuf));
- quick_istr::increment_from_end(ebuf);
- }
- }
- client.wait_all();
- double t1 = client.now();
-
- Json result;
- result.set("rangesize", nk);
- memcpy(ebuf - k0.length(), k0.data(), k0.length());
- result.set("first", Str(buf, ebuf));
- kvtest_set_time(result, "rounds", nrounds, t1 - t0);
- kvtest_set_time(result, "ops", nrounds * nk * 4, t1 - t0);
- client.report(result);
-}
-
-template <typename C>
-void kvtest_conflictscan1(C& client)
-{
- unsigned prefixlen = client.param("prefixlen", 4).to_u64();
- unsigned rangelen = client.param("rangelen", 4).to_u64();
- unsigned keylen = prefixlen + rangelen;
- unsigned rangesize = client.param("rangesize", 100).to_u64();
- unsigned scansize = client.param("scansize", 20).to_u64();
- assert(rangelen > 0 && rangelen <= 8);
- assert(prefixlen > 0 && prefixlen <= 8);
- assert(rangelen == 8 || rangesize < (1UL << (rangelen * 8)));
- assert(scansize < rangesize);
- using leaf_type = typename C::table_type::leaf_type;
-
- union {
- uint64_t u;
- char c[8];
- } x;
-
- if (client.id() == 0) {
- // scan worker
- char hbuf[16];
- uint64_t nscans = 0, naborts = 0;
- kvrandom_uniform_int_distribution<uint64_t> ntd(1, client.nthreads() - 1);
- std::vector<Str> keys, values;
- client.wait_all();
-
- while (!client.timeout(0)) {
- ++nscans;
- memset(hbuf, 0, 16);
- x.u = host_to_net_order(ntd(client.rand));
- memcpy(hbuf, x.c + (8 - prefixlen), prefixlen);
- while (!client.timeout(0)) {
- client.scan_versions_sync(Str(hbuf, keylen), scansize, keys, values);
- for (size_t i = 0; i != client.scan_versions().size(); i += 2) {
- leaf_type* l = reinterpret_cast<leaf_type*>(client.scan_versions()[i]);
- uint64_t uv = l->full_unlocked_version_value();
- if (uv != client.scan_versions()[i + 1]) {
- goto abort;
- }
- }
- break;
- abort:
- ++naborts;
- }
- }
- Json result;
- result.set("keylen", keylen).set("prefixlen", prefixlen).set("scansize", scansize).set("scans", nscans).set("aborts", naborts);
- client.report(result);
-
- } else {
- // insert/delete worker
- char hbuf[16], tbuf[16];
- x.u = host_to_net_order(uint64_t(client.id()));
- memcpy(hbuf, x.c + (8 - prefixlen), prefixlen);
- memset(hbuf + prefixlen, 0, rangelen);
- memcpy(tbuf, hbuf, 16);
- char lastp = tbuf[prefixlen - 1];
-
- // set initial values
- for (unsigned i = 0; i != rangesize; ++i) {
- client.insert_check(Str(tbuf, keylen), Str(tbuf, 8));
- quick_istr::binary_increment_from_end(tbuf + keylen);
- }
- client.wait_all();
-
- // insert/delete
- uint64_t ninsert = 0, nremove = 0, cursize = rangesize;
- while (cursize && !client.timeout(0)) {
- if (tbuf[prefixlen - 1] == lastp
- && cursize < rangesize * 2
- && (cursize == scansize
- || client.rand() % 65536 < 32768)) {
- client.insert_check(Str(tbuf, keylen), Str(tbuf + keylen - 8, 8));
- quick_istr::binary_increment_from_end(tbuf + keylen);
- ++ninsert;
- ++cursize;
- } else {
- client.remove_check(Str(hbuf, keylen));
- quick_istr::binary_increment_from_end(hbuf + keylen);
- ++nremove;
- --cursize;
- }
- }
- Json result;
- result.set("rangesize", rangesize).set("inserts", ninsert).set("removes", nremove);
- client.report(result);
- }
-}
-
-// Create a range of keys [initial_pos, initial_pos + n)
-// where key k == initial_pos + i has value (n - 1 - i).
-// Many overwrites.
-template <typename C>
-void kvtest_tri1(unsigned initial_pos, int incr, C &client)
-{
- incr = std::max(incr, client.nthreads());
- unsigned n = 0;
- Json result = Json();
-
- double t0 = client.now();
- for (unsigned x = 0; x < client.limit(); ++x) {
- for (unsigned y = 0, z = x; y <= x; ++y, --z, ++n) {
- client.put(initial_pos + y * incr, z);
- }
- }
- client.wait_all();
- double t1 = client.now();
-
- kvtest_set_time(result, "puts", n, t1 - t0);
- kvtest_set_time(result, "ops", n, t1 - t0);
- client.report(result);
-}
-
-template <typename C>
-void kvtest_tri1_check(unsigned initial_pos, int incr, C &client)
-{
- incr = std::max(incr, client.nthreads());
- unsigned n = 0;
- Json result = Json();
-
- double t0 = client.now();
- for (unsigned x = 0; x < client.limit(); ++x, ++n) {
- client.get_check(initial_pos + x * incr, client.limit() - 1 - x);
- }
- client.wait_all();
- double t1 = client.now();
-
- kvtest_set_time(result, "gets", n, t1 - t0);
- kvtest_set_time(result, "ops", n, t1 - t0);
- client.report(result);
-}
-
-
-#define PALMN 128000000
-enum { PalmBatch = 8192 / 24 };
-#define PALM_DEBUG 1 // use get_check in palmb, which force palm::get
- // to touch the cachline of the value
-template <typename C>
-void kvtest_palma(C &client)
-{
- Json result = Json();
- double t0 = client.now();
- for (int i = 0; i < PALMN; i++) {
- uint64_t v = i + 1;
- client.put(i, v);
- }
- client.wait_all();
- double t1 = client.now();
- kvtest_set_time(result, "ops", PALMN, t1 - t0);
- client.report(result);
-}
-
-inline int compare_int(const void *a, const void *b)
-{
- return compare(*(uint64_t *)a, *(uint64_t *)b);
-}
-
-template <typename C>
-void kvtest_palmb_seed(C &client, int seed)
-{
- Json result = Json();
- client.rand.seed(seed);
- double t0 = client.now();
- int n;
- int nquery = 0;
- uint64_t a[PalmBatch];
- for (n = 0; !client.timeout(0); ++n) {
- uint64_t x = (uint64_t) client.rand();
- x %= (PALMN / 10);
- a[nquery++] = x;
- if (nquery == PalmBatch) {
- qsort(a, PalmBatch, sizeof(a[0]), compare_int);
- for (int j = 0; j < PalmBatch && !client.timeout(0); j++) {
-#if PALM_DEBUG
- uint64_t v = a[j] + 1;
- client.get_check(a[j], v);
-#else
- client.get(a[j]);
-#endif
- }
- nquery = 0;
- }
- }
- client.wait_all();
- double t1 = client.now();
- kvtest_set_time(result, "ops", n, t1 - t0);
- client.report(result);
-}
-
-template <typename C>
-void kvtest_palmb(C &client)
-{
- kvtest_palmb_seed(client, kvtest_first_seed + client.id() % 48);
-}
-
-template <typename C>
-void kvtest_ycsbk_seed(C &client, int seed)
-{
- client.rand.seed(seed);
- double tp0 = client.now();
- int n;
- char key[512], val[512];
- for (n = 0; !client.timeout(0) && n < 1000000; ++n) {
- strcpy(key, "user");
- int p = 4;
- for (int i = 0; i < 18; i++, p++) {
- key[p] = '0' + (client.rand() % 10);
- }
- key[p] = 0;
- int32_t v = (int32_t) client.rand();
- sprintf(val, "%d", v);
- client.put(Str(key, strlen(key)), Str(val, strlen(val)));
- }
- client.wait_all();
- double tp1 = client.now();
-
- client.puts_done();
- client.notice("now getting\n");
- client.rand.seed(seed);
- double tg0 = client.now();
- int g;
- for (g = 0; g < n && !client.timeout(1); ++g) {
- strcpy(key, "user");
- int p = 4;
- for (int i = 0; i < 18; i++, p++) {
- key[p] = '0' + (client.rand() % 10);
- }
- key[p] = 0;
- int32_t v = (int32_t) client.rand();
- sprintf(val, "%d", v);
- client.get_check(Str(key, strlen(key)), Str(val, strlen(val)));
- }
- client.wait_all();
- double tg1 = client.now();
-
- Json result = Json();
- kvtest_set_time(result, "puts", n, tp1 - tp0);
- kvtest_set_time(result, "gets", g, tg1 - tg0);
- kvtest_set_time(result, "ops", n + g, (tp1 - tp0) + (tg1 - tg0));
- client.report(result);
-}
-
-template <typename C>
-void kvtest_ycsbk(C &client)
-{
- kvtest_ycsbk_seed(client, kvtest_first_seed + client.id() % 48);
-}
-
-template <typename C>
-void
-kvtest_bdb(C &client)
-{
- enum { nrec = 500000, keylen = 8, datalen = 32 };
- char key[keylen + 1];
- char val[datalen + 1];
- memset(val, '^', sizeof(val));
- val[datalen] = 0;
- key[keylen] = 0;
- srandom(0);
- for (int n = 0; n < nrec; n++) {
- for (int i = 0; i < keylen; i++) {
- key[i] = 'a' + random() % 26;
- }
- client.put(key, val);
- }
- client.wait_all();
-
- srandom(0);
- double t0 = now();
- unsigned long n;
- for (n = 0; n < 10000000; n++) {
- for (int i = 0; i < keylen; i++) {
- key[i] = 'a' + random() % 26;
- }
- client.get_check(key, val);
- if (n % nrec == 0) {
- srandom(0);
- }
- }
- double t1 = now();
- Json result = Json();
- kvtest_set_time(result, "ops", n, t1 - t0);
- client.report(result);
-}
-
-enum { NLongParts = 16 };
-
-template <typename C>
-void
-kvtest_long_init(C &client)
-{
- assert(client.id() < NLongParts);
- int seed = kvtest_first_seed + client.id();
- client.rand.seed(seed);
- const int keylen = client.keylen();
- const int prefixLen = client.prefixLen();
- const char minkltr = client.minkeyletter();
- const char maxkltr = client.maxkeyletter();
- assert(prefixLen < keylen);
- const uint32_t nkeysPerPart = client.nkeys() / NLongParts;
- char key[512], val[512];
- val[8] = 0;
- memset(key, '^', prefixLen);
- double t0 = now();
- unsigned long n;
- for(n = 0; n < nkeysPerPart; ++n){
- for (int i = prefixLen; i < keylen; i++) {
- key[i] = minkltr + client.rand() % (maxkltr - minkltr + 1);
- }
- key[keylen] = 0;
- memcpy(val, key + keylen - 8, 8);
- client.put(key, val);
- client.rand();
- }
- client.wait_all();
- double t1 = now();
-
- Json result = Json();
- kvtest_set_time(result, "puts", n, t1 - t0);
- client.report(result);
-}
-
-template <typename C>
-void
-kvtest_long_go(C &client)
-{
- const int keylen = client.keylen();
- const int prefixLen = client.prefixLen();
- assert(prefixLen < keylen);
- const uint32_t nKeysPerPart = client.nkeys() / NLongParts;
- const char minkltr = client.minkeyletter();
- const char maxkltr = client.maxkeyletter();
- char key[512], val[512];
- memset(key, '^', prefixLen);
- val[8] = 0;
- double t0 = now();
- long n = 0;
- int cur_cid = client.id() % NLongParts;
- while (!client.timeout(0)) {
- client.rand.seed(kvtest_first_seed + cur_cid);
- uint32_t op;
- for(op = 0; !client.timeout(0) && op < nKeysPerPart; op++){
- for (int i = prefixLen; i < keylen; i++) {
- key[i] = minkltr + client.rand() % (maxkltr - minkltr + 1);
- }
- memcpy(val, key + keylen - 8, 8);
- key[keylen] = 0;
- if (client.rand() % 100 < client.getratio()) {
- client.get_check(key, val);
- } else {
- client.put(key, val);
- }
- }
- cur_cid = (cur_cid + 1) % NLongParts;
- n += op;
- }
- client.wait_all();
- double t1 = now();
-
- Json result = Json();
- kvtest_set_time(result, "ops", n, t1 - t0);
- client.report(result);
-}
-
-template <typename C>
-void
-kvtest_wscale(C &client)
-{
- double t0 = now();
- client.rand.seed(kvtest_first_seed + client.id() % 48);
- long n;
- for(n = 0; !client.timeout(0); n++){
- long x = client.rand();
- client.put(x, x + 1);
- }
- client.wait_all();
- double t1 = now();
- Json result = Json();
- kvtest_set_time(result, "puts", n, t1 -t0);
- client.report(result);
-}
-
-template <typename C>
-void
-kvtest_ruscale_init(C &client)
-{
- double t0 = now();
- client.rand.seed(kvtest_first_seed + client.id() % 48);
- const int ruscale_partsz = client.ruscale_partsz();
- const int firstkey = ruscale_partsz * client.ruscale_init_part_no();
- // Insert in random order
- int *keys = (int *) malloc(sizeof(int) * ruscale_partsz);
- always_assert(keys);
- for(int i = 0; i < ruscale_partsz; i++) {
- keys[i] = i + firstkey;
- }
- for(int i = 0; i < ruscale_partsz; i++) {
- std::swap(keys[i], keys[client.rand() % ruscale_partsz]);
- }
- for(int i = 0; i < ruscale_partsz; i++){
- long x = keys[i];
- client.put(x, x + 1);
- }
- client.wait_all();
- double t1 = now();
- Json result = Json();
- kvtest_set_time(result, "puts", ruscale_partsz, t1 - t0);
- client.report(result);
- free(keys);
-}
-
-template <typename C>
-void
-kvtest_rscale(C &client)
-{
- client.rand.seed(kvtest_first_seed + client.id() % 48);
- const long nseqkeys = client.nseqkeys();
- double t0 = now();
- long n;
- for(n = 0; !client.timeout(0); n++){
- long x = client.rand() % nseqkeys;
- client.get_check(x, x + 1);
- }
- client.wait_all();
- double t1 = now();
- Json result = Json();
- kvtest_set_time(result, "gets", n, t1 - t0);
- client.report(result);
-}
-
-template <typename C>
-void
-kvtest_uscale(C &client)
-{
- client.rand.seed(kvtest_first_seed + client.id());
- const long nseqkeys = client.nseqkeys();
- double t0 = now();
- long n;
- for(n = 0; !client.timeout(0); n++){
- long x = client.rand() % nseqkeys;
- client.put(x, x + 1);
- }
- client.wait_all();
- double t1 = now();
- Json result = Json();
- kvtest_set_time(result, "puts", n, t1 - t0);
- client.report(result);
-}
-
-template <typename C>
-void kvtest_udp1_seed(C &client, int seed)
-{
- client.rand.seed(seed);
- double tp0 = client.now();
- unsigned n;
- for (n = 0; !client.timeout(0); ++n) {
- client.put(0, 1);
- }
- client.wait_all();
- double tp1 = client.now();
-
- client.puts_done();
- client.notice("now getting\n");
- int32_t *a = (int32_t *) malloc(sizeof(int32_t) * n);
- assert(a);
- client.rand.seed(seed);
- for (unsigned i = 0; i < n; ++i) {
- a[i] = (int32_t) client.rand();
- }
- for (unsigned i = 0; i < n; ++i) {
- std::swap(a[i], a[client.rand() % n]);
- }
-
- double tg0 = client.now();
- unsigned g;
- for (g = 0; !client.timeout(1); ++g) {
- client.get_check(0, 1);
- }
- client.wait_all();
- double tg1 = client.now();
-
- Json result = Json();
- kvtest_set_time(result, "puts", n, tp1 - tp0);
- kvtest_set_time(result, "gets", g, tg1 - tg0);
- kvtest_set_time(result, "ops", n + g, (tp1 - tp0) + (tg1 - tg0));
- client.report(result);
- free(a);
-}
-
-template <typename C>
-void kvtest_udp1(C &client)
-{
- kvtest_udp1_seed(client, kvtest_first_seed + client.id() % 48);
-}
-
-// do four million of inserts to distinct keys.
-// sometimes overwrites, but only w/ same value.
-// different clients might use same key sometimes.
-template <typename C>
-void kvtest_w1_seed(C &client, int seed)
-{
- int n;
- if (client.limit() == ~(uint64_t) 0) {
- n = 4000000;
- } else {
- n = std::min(client.limit(), (uint64_t) INT_MAX);
- }
- client.rand.seed(seed);
-
- double t0 = now();
- for (int i = 0; i < n; i++) {
- long x = client.rand();
- client.put_key10(x, x + 1);
- }
- client.wait_all();
- double t1 = now();
-
- Json result = Json().set("total", (long) (n / (t1 - t0)))
- .set("puts", n)
- .set("puts_per_sec", n / (t1 - t0));
- client.report(result);
-}
-
-// do four million gets.
-// in a random order.
-// if we get in the same order that w1 put, performance is
-// about 15% better for b-tree.
-template <typename C>
-void kvtest_r1_seed(C &client, int seed)
-{
- int n;
- if (client.limit() == ~(uint64_t) 0) {
- n = 4000000;
- } else {
- n = std::min(client.limit(), (uint64_t) INT_MAX);
- }
- long *a = (long *) malloc(sizeof(long) * n);
- always_assert(a);
-
- client.rand.seed(seed);
- for (int i = 0; i < n; i++) {
- a[i] = client.rand();
- }
- for (int i = 0; i < n; i++) {
- int i1 = client.rand() % n;
- long tmp = a[i];
- a[i] = a[i1];
- a[i1] = tmp;
- }
-
- double t0 = now();
- for (int i = 0; i < n; i++) {
- client.get_check_key10(a[i], a[i] + 1);
- }
- client.wait_all();
- double t1 = now();
-
- Json result = Json().set("total", (long) (n / (t1 - t0)))
- .set("gets", n)
- .set("gets_per_sec", n / (t1 - t0));
- client.report(result);
-}
-
-// do four million of inserts to distinct keys.
-// sometimes overwrites, but only w/ same value.
-// different clients might use same key sometimes.
-template <typename C>
-void kvtest_wcol1at(C &client, int col, int seed, long maxkeys)
-{
- int n;
- if (client.limit() == ~(uint64_t) 0) {
- n = 4000000;
- } else {
- n = std::min(client.limit(), (uint64_t) INT_MAX);
- }
- client.rand.seed(seed);
-
- double t0 = now();
- for (int i = 0; i < n; i++) {
- long x = client.rand() % maxkeys;
- client.put_col_key10(x, col, x + 1);
- }
- client.wait_all();
- double t1 = now();
-
- Json result = Json().set("total", (long) (n / (t1 - t0)))
- .set("puts", n)
- .set("puts_per_sec", n / (t1 - t0));
- client.report(result);
-}
-
-// do four million gets.
-// in a random order.
-// if we get in the same order that w1 put, performance is
-// about 15% better for b-tree.
-template <typename C>
-void kvtest_rcol1at(C &client, int col, int seed, long maxkeys)
-{
- int n;
- if (client.limit() == ~(uint64_t) 0) {
- n = 4000000;
- } else {
- n = std::min(client.limit(), (uint64_t) INT_MAX);
- }
- long *a = (long *) malloc(sizeof(long) * n);
- always_assert(a);
-
- client.rand.seed(seed);
- for (int i = 0; i < n; i++) {
- a[i] = client.rand() % maxkeys;
- }
- for (int i = 0; i < n && 0; i++) {
- int i1 = client.rand() % n;
- long tmp = a[i];
- a[i] = a[i1];
- a[i1] = tmp;
- }
-
- double t0 = now();
- for (int i = 0; i < n; i++) {
- client.get_col_check_key10(a[i], col, a[i] + 1);
- }
- client.wait_all();
- double t1 = now();
-
- Json result = Json().set("total", (long) (n / (t1 - t0)))
- .set("gets", n)
- .set("gets_per_sec", n / (t1 - t0));
- client.report(result);
-}
-
-// test scans with parallel inserts
-template <typename C>
-void kvtest_scan1(C &client, double writer_quiet)
-{
- int n, wq65536 = int(writer_quiet * 65536);
- if (client.limit() == ~(uint64_t) 0) {
- n = 10000;
- } else {
- n = std::min(client.limit(), (uint64_t) 97655);
- }
- Json result;
-
- if (client.id() % 24 == 0) {
- for (int i = 0; i < n; ++i) {
- client.put_key8(i * 1024, i);
- }
- client.wait_all();
-
- int pos = 0, mypos = 0, scansteps = 0;
- quick_istr key;
- std::vector<Str> keys, values;
- Json errj;
- while (!client.timeout(0) && errj.size() < 1000) {
- key.set(pos, 8);
- client.scan_sync(key.string(), 100, keys, values);
- if (keys.size() == 0) {
- if (mypos < n * 1024) {
- errj.push_back("missing " + String(mypos) + " through " + String((n - 1) * 1024));
- }
- pos = mypos = 0;
- } else {
- for (size_t i = 0; i < keys.size(); ++i) {
- int val = keys[i].to_i();
- if (val < 0) {
- errj.push_back("unexpected key " + String(keys[i].s, keys[i].len));
- continue;
- }
- if (val < pos) {
- errj.push_back("got " + String(keys[i].s, keys[i].len) + ", expected " + String(pos) + " or later");
- }
- pos = val + 1;
- while (val > mypos) {
- errj.push_back("got " + String(keys[i].s, keys[i].len) + ", missing " + String(mypos) + " @" + String(scansteps) + "+" + String(i));
- mypos += 1024;
- }
- if (val == mypos) {
- mypos = val + 1024;
- ++scansteps;
- }
- }
- }
- client.rcu_quiesce();
- }
- if (errj.size() >= 1000) {
- errj.push_back("too many errors, giving up");
- }
- result.set("ok", errj.empty()).set("scansteps", scansteps);
- if (errj) {
- result.set("errors", errj);
- }
-
- } else {
- int delta = 1 + (client.id() % 30) * 32, rounds = 0;
- while (!client.timeout(0)) {
- int first = (client.rand() % n) * 1024 + delta;
- int rand = client.rand() % 65536;
- if (rand < wq65536) {
- for (int d = 0; d < 31; ++d) {
- relax_fence();
- }
- } else if (rounds > 100 && (rand % 2) == 1) {
- for (int d = 0; d < 31; ++d) {
- client.remove_key8(d + first);
- }
- } else {
- for (int d = 0; d < 31; ++d) {
- client.put_key8(d + first, d + first);
- }
- }
- ++rounds;
- client.rcu_quiesce();
- }
- }
-
- client.report(result);
-}
-
-// test reverse scans with parallel inserts
-template <typename C>
-void kvtest_rscan1(C &client, double writer_quiet)
-{
- int n, wq65536 = int(writer_quiet * 65536);
- if (client.limit() == ~(uint64_t) 0) {
- n = 10000;
- } else {
- n = std::min(client.limit(), (uint64_t) 97655);
- }
- Json result;
-
- if (client.id() % 24 == 0) {
- for (int i = 1; i <= n; ++i) {
- client.put_key8(i * 1024, i);
- }
- client.wait_all();
-
- int pos = (n + 1) * 1024, mypos = n * 1024, scansteps = 0;
- quick_istr key;
- std::vector<Str> keys, values;
- Json errj;
- while (!client.timeout(0) && errj.size() < 1000) {
- key.set(pos, 8);
- client.rscan_sync(key.string(), 100, keys, values);
- if (keys.size() == 0) {
- if (mypos > 0) {
- errj.push_back("missing 1024 through " + String(mypos) + " @" + String(scansteps));
- }
- pos = (n + 1) * 1024, mypos = n * 1024;
- } else {
- for (size_t i = 0; i < keys.size(); ++i) {
- int val = keys[i].to_i();
- if (val < 0) {
- errj.push_back("unexpected key " + String(keys[i].s, keys[i].len));
- continue;
- }
- if (val > pos) {
- errj.push_back("got " + String(keys[i].s, keys[i].len) + ", expected " + String(pos) + " or less");
- }
- pos = val - 1;
- while (val < mypos) {
- String last;
- if (i) {
- last = String(keys[i-1].s, keys[i-1].len);
- } else {
- last = String(key.string().s, key.string().len);
- }
- errj.push_back("got " + String(keys[i].s, keys[i].len) + ", missing " + String(mypos) + " @" + String(scansteps) + "+" + String(i) + ", last " + last);
- mypos -= 1024;
- }
- if (val == mypos) {
- mypos = val - 1024;
- ++scansteps;
- }
- }
- }
- client.rcu_quiesce();
- }
- if (errj.size() >= 1000) {
- errj.push_back("too many errors, giving up");
- }
- result.set("ok", errj.empty()).set("scansteps", scansteps);
- if (errj) {
- result.set("errors", errj);
- }
-
- } else {
- int delta = 1 + (client.id() % 30) * 32, rounds = 0;
- while (!client.timeout(0)) {
- int first = (client.rand() % n + 1) * 1024 + delta;
- int rand = client.rand() % 65536;
- if (rand < wq65536) {
- for (int d = 0; d < 31; ++d) {
- relax_fence();
- }
- } else if (rounds > 100 && (rand % 2) == 1) {
- for (int d = 0; d < 31; ++d) {
- client.remove_key8(d + first);
- }
- } else {
- for (int d = 0; d < 31; ++d) {
- client.put_key8(d + first, d + first);
- }
- }
- ++rounds;
- client.rcu_quiesce();
- }
- }
-
- client.report(result);
-}
-
-// test concurrent splits with removes in lower layers
-template <typename C>
-void kvtest_splitremove1(C &client)
-{
- // XXX these parameters depend on masstree constants...
- int leaf_width = 15, internode_width = 15;
- int num_keys = leaf_width * (internode_width + 1) + 1;
- int trigger_key = num_keys - 15;
- int rounds = 0;
- Json result, errj;
-
- if (client.id() == 0) {
- while (1) {
- for (int i = 0; i < num_keys; ++i) {
- client.put_key16(i + 100, i + 101);
- }
- client.rcu_quiesce();
- for (int i = trigger_key + 1; i < num_keys + 10; ++i) {
- client.remove_key16(i + 100);
- }
- client.rcu_quiesce();
- for (int i = 0; i < leaf_width * internode_width; ++i) {
- client.put_key16(i, i + 1);
- }
-
- client.put(client.nthreads(), client.nthreads() + 1);
- for (int i = 1; i < client.nthreads(); ++i) {
- client.put(i, i + 1);
- }
- for (int i = 1; i < client.nthreads(); ++i) {
- while (!client.timeout(0) && client.get_sync(i)) {
- /* do nothing */
- }
- }
- client.remove_key16(trigger_key);
- client.remove(client.nthreads());
- if (client.timeout(0)) {
- break;
- }
-
- for (int i = 0; i < num_keys; ++i) {
- client.remove_key16(i);
- client.remove_key16(i + 100);
- }
- for (int i = 0; i < 10; ++i) {
- client.rcu_quiesce();
- }
- ++rounds;
- }
-
- } else {
- quick_istr me(client.id()), trigger(trigger_key, 16);
- while (1) {
- while (!client.timeout(0) && !client.get_sync_key16(trigger_key)) {
- client.rcu_quiesce();
- }
- if (client.timeout(0)) {
- break;
- }
-
- for (int i = 0; !client.get_sync(me.string()); ++i) {
- if (!client.get_sync(trigger.string()) && !client.timeout(0)) {
- if (errj.size() == 100) {
- errj.push_back("more errors");
- } else if (errj.size() < 100) {
- errj.push_back("key " + String(trigger.string()) + " missing after " + String(rounds) + " rounds, counter " + String(i));
- }
- break;
- }
- client.rcu_quiesce();
- }
-
- while (!client.timeout(0) && !client.get_sync(me.string())) {
- client.rcu_quiesce();
- }
- client.remove(me.string());
- while (!client.timeout(0) && client.get_sync(client.nthreads())) {
- client.rcu_quiesce();
- }
- if (client.timeout(0)) {
- break;
- }
-
- for (int i = 0; i < 10; ++i) {
- client.rcu_quiesce();
- }
- ++rounds;
- }
- }
-
- result.set("ok", errj.empty()).set("rounds", rounds);
- if (errj) {
- result.set("errors", errj);
- }
- client.report(result);
-}
-
-template <typename C>
-void kvtest_url_seed(C &client)
-{
- if (!client.param("file").is_s()) {
- client.report(Json::object("ok", false, "error", "need 'file=URLFILE' parameter"));
- return;
- }
-
- std::ifstream infile_url_init(client.param("file").to_s());
- std::ifstream infile_url_del_get(client.param("file").to_s());
- std::string ops;
- std::string url;
- unsigned count_i = 0;
- unsigned count_d = 0;
- unsigned count_g = 0;
-
- double t0 = client.now();
- while (count_i < client.limit() && infile_url_init.good()) {
- //do the following alternately:
- //insert 10 urls, then delete 5 inserted urls
- for (int i = 0; i != 10 && infile_url_init >> ops >> url; ++i, ++count_i) {
- client.put(url, 2014);
- }
- for (int i = 0; i != 5 && infile_url_del_get >> ops >> url; ++i, ++count_d) {
- client.remove(url);
- }
- }
- client.wait_all();
- client.puts_done();
- double t1 = client.now();
- infile_url_init.close();
- client.notice("\ninsert done\n");
-
- //query all the inserted urls
- double t2 = client.now();
- while (count_d + count_g != count_i && infile_url_del_get >> ops >> url) {
- client.get_check(Str(url), 2014);
- ++count_g;
- }
- client.wait_all();
- double t3 = client.now();
-
- // client.notice("Total pool memory: %d\n", client.ti_->poolmem);
- // client.notice("Total general memory: %d\n", client.ti_->genmem);
- // client.notice("Total MEMORY: %d\n", client.ti_->poolmem + client.ti_->genmem);
-
- Json result = Json::object("puts", count_i, "removes", count_d);
- kvtest_set_time(result, "gets", count_g, t3 - t2);
- kvtest_set_time(result, "ops", count_i + count_d, t1 - t0);
- client.report(result);
-}
-
-template <typename C>
-void kvtest_url(C &client)
-{
- kvtest_url_seed(client);
-}
-
-#endif
diff --git a/kvthread.cc b/kvthread.cc
deleted file mode 100644
index cf199fb..0000000
--- a/kvthread.cc
+++ /dev/null
@@ -1,254 +0,0 @@
-/* Masstree
- * Eddie Kohler, Yandong Mao, Robert Morris
- * Copyright (c) 2012-2016 President and Fellows of Harvard College
- * Copyright (c) 2012-2016 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.
- */
-#include "kvthread.hh"
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <new>
-#include <sys/mman.h>
-#if HAVE_SUPERPAGE && !NOSUPERPAGE
-#include <sys/types.h>
-#include <dirent.h>
-#endif
-
-threadinfo *threadinfo::allthreads;
-#if ENABLE_ASSERTIONS
-int threadinfo::no_pool_value;
-#endif
-
-inline threadinfo::threadinfo(int purpose, int index) {
- gc_epoch_ = perform_gc_epoch_ = 0;
- logger_ = nullptr;
- next_ = nullptr;
- purpose_ = purpose;
- index_ = index;
-
- for (size_t i = 0; i != sizeof(pool_) / sizeof(pool_[0]); ++i) {
- pool_[i] = nullptr;
- }
-
- void *limbo_space = allocate(sizeof(limbo_group), memtag_limbo);
- mark(tc_limbo_slots, limbo_group::capacity);
- limbo_head_ = limbo_tail_ = new(limbo_space) limbo_group;
- ts_ = 2;
-
- for (size_t i = 0; i != sizeof(counters_) / sizeof(counters_[0]); ++i) {
- counters_[i] = 0;
- }
-}
-
-threadinfo *threadinfo::make(int purpose, int index) {
- static int threads_initialized;
-
- threadinfo* ti = new(malloc(8192)) threadinfo(purpose, index);
- ti->next_ = allthreads;
- allthreads = ti;
-
- if (!threads_initialized) {
-#if ENABLE_ASSERTIONS
- const char* s = getenv("_");
- no_pool_value = s && strstr(s, "valgrind") != 0;
-#endif
- threads_initialized = 1;
- }
-
- return ti;
-}
-
-void threadinfo::refill_rcu() {
- if (!limbo_tail_->next_) {
- void *limbo_space = allocate(sizeof(limbo_group), memtag_limbo);
- mark(tc_limbo_slots, limbo_group::capacity);
- limbo_tail_->next_ = new(limbo_space) limbo_group;
- }
- limbo_tail_ = limbo_tail_->next_;
- assert(limbo_tail_->head_ == 0 && limbo_tail_->tail_ == 0);
-}
-
-inline unsigned limbo_group::clean_until(threadinfo& ti, mrcu_epoch_type epoch_bound,
- unsigned count) {
- epoch_type epoch = 0;
- while (head_ != tail_) {
- if (e_[head_].ptr_) {
- ti.free_rcu(e_[head_].ptr_, e_[head_].u_.tag);
- ti.mark(tc_gc);
- --count;
- if (!count) {
- e_[head_].ptr_ = nullptr;
- e_[head_].u_.epoch = epoch;
- break;
- }
- } else {
- epoch = e_[head_].u_.epoch;
- if (signed_epoch_type(epoch_bound - epoch) < 0)
- break;
- }
- ++head_;
- }
- if (head_ == tail_)
- head_ = tail_ = 0;
- return count;
-}
-
-void threadinfo::hard_rcu_quiesce() {
- limbo_group* empty_head = nullptr;
- limbo_group* empty_tail = nullptr;
- unsigned count = rcu_free_count;
-
- mrcu_epoch_type epoch_bound = active_epoch - 1;
- if (limbo_head_->head_ == limbo_head_->tail_
- || mrcu_signed_epoch_type(epoch_bound - limbo_head_->first_epoch()) < 0)
- goto done;
-
- // clean [limbo_head_, limbo_tail_]
- while (count) {
- count = limbo_head_->clean_until(*this, epoch_bound, count);
- if (limbo_head_->head_ != limbo_head_->tail_)
- break;
- if (!empty_head)
- empty_head = limbo_head_;
- empty_tail = limbo_head_;
- if (limbo_head_ == limbo_tail_) {
- limbo_head_ = limbo_tail_ = empty_head;
- goto done;
- }
- limbo_head_ = limbo_head_->next_;
- }
- // hook empties after limbo_tail_
- if (empty_head) {
- empty_tail->next_ = limbo_tail_->next_;
- limbo_tail_->next_ = empty_head;
- }
-
-done:
- if (!count)
- perform_gc_epoch_ = epoch_bound; // do GC again immediately
- else
- perform_gc_epoch_ = epoch_bound + 1;
-}
-
-void threadinfo::report_rcu(void *ptr) const
-{
- for (limbo_group *lg = limbo_head_; lg; lg = lg->next_) {
- int status = 0;
- limbo_group::epoch_type e = 0;
- for (unsigned i = 0; i < lg->capacity; ++i) {
- if (i == lg->head_)
- status = 1;
- if (i == lg->tail_) {
- status = 0;
- e = 0;
- }
- if (lg->e_[i].ptr_ == ptr)
- fprintf(stderr, "thread %d: rcu %p@%d: %s as %x @%" PRIu64 "\n",
- index_, lg, i, status ? "waiting" : "freed",
- lg->e_[i].u_.tag, e);
- else if (!lg->e_[i].ptr_)
- e = lg->e_[i].u_.epoch;
- }
- }
-}
-
-void threadinfo::report_rcu_all(void *ptr)
-{
- for (threadinfo *ti = allthreads; ti; ti = ti->next())
- ti->report_rcu(ptr);
-}
-
-
-#if HAVE_SUPERPAGE && !NOSUPERPAGE
-static size_t read_superpage_size() {
- if (DIR* d = opendir("/sys/kernel/mm/hugepages")) {
- size_t n = (size_t) -1;
- while (struct dirent* de = readdir(d))
- if (de->d_type == DT_DIR
- && strncmp(de->d_name, "hugepages-", 10) == 0
- && de->d_name[10] >= '0' && de->d_name[10] <= '9') {
- size_t x = strtol(&de->d_name[10], 0, 10) << 10;
- n = (x < n ? x : n);
- }
- closedir(d);
- return n;
- } else
- return 2 << 20;
-}
-
-static size_t superpage_size = 0;
-#endif
-
-static void initialize_pool(void* pool, size_t sz, size_t unit) {
- char* p = reinterpret_cast<char*>(pool);
- void** nextptr = reinterpret_cast<void**>(p);
- for (size_t off = unit; off + unit <= sz; off += unit) {
- *nextptr = p + off;
- nextptr = reinterpret_cast<void**>(p + off);
- }
- *nextptr = 0;
-}
-
-void threadinfo::refill_pool(int nl) {
- assert(!pool_[nl - 1]);
-
- if (!use_pool()) {
- pool_[nl - 1] = malloc(nl * CACHE_LINE_SIZE);
- if (pool_[nl - 1])
- *reinterpret_cast<void**>(pool_[nl - 1]) = 0;
- return;
- }
-
- void* pool = 0;
- size_t pool_size = 0;
- int r;
-
-#if HAVE_SUPERPAGE && !NOSUPERPAGE
- if (!superpage_size)
- superpage_size = read_superpage_size();
- if (superpage_size != (size_t) -1) {
- pool_size = superpage_size;
-# if MADV_HUGEPAGE
- if ((r = posix_memalign(&pool, pool_size, pool_size)) != 0) {
- fprintf(stderr, "posix_memalign superpage: %s\n", strerror(r));
- pool = 0;
- superpage_size = (size_t) -1;
- } else if (madvise(pool, pool_size, MADV_HUGEPAGE) != 0) {
- perror("madvise superpage");
- superpage_size = (size_t) -1;
- }
-# elif MAP_HUGETLB
- pool = mmap(0, pool_size, PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0);
- if (pool == MAP_FAILED) {
- perror("mmap superpage");
- pool = 0;
- superpage_size = (size_t) -1;
- }
-# else
- superpage_size = (size_t) -1;
-# endif
- }
-#endif
-
- if (!pool) {
- pool_size = 2 << 20;
- if ((r = posix_memalign(&pool, CACHE_LINE_SIZE, pool_size)) != 0) {
- fprintf(stderr, "posix_memalign: %s\n", strerror(r));
- abort();
- }
- }
-
- initialize_pool(pool, pool_size, nl * CACHE_LINE_SIZE);
- pool_[nl - 1] = pool;
-}
diff --git a/value_string.cc b/kvthread.cpp
similarity index 60%
rename from value_string.cc
rename to kvthread.cpp
index 9ab70e3..07aa6e7 100644
--- a/value_string.cc
+++ b/kvthread.cpp
@@ -1,7 +1,7 @@
/* 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
+ * Copyright (c) 2012-2016 President and Fellows of Harvard College
+ * Copyright (c) 2012-2016 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"),
@@ -13,6 +13,26 @@
* notice is a summary of the Masstree LICENSE file; the license in that file
* is legally binding.
*/
-#include "kvrow.hh"
-#include "value_string.hh"
+
+#include "kvthread.hh"
#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <new>
+#include <sys/mman.h>
+#if HAVE_SUPERPAGE && !NOSUPERPAGE
+#include <sys/types.h>
+#include <dirent.h>
+#endif
+
+//volatile mrcu_epoch_type active_epoch;
+
+//threadinfo *threadinfo::allthreads;
+
+
+#if ENABLE_ASSERTIONS
+int threadinfo::no_pool_value;
+#endif
+
+
+
diff --git a/kvthread.hh b/kvthread.hh
index fc03339..96275ce 100644
--- a/kvthread.hh
+++ b/kvthread.hh
@@ -24,21 +24,79 @@
#include <pthread.h>
#include <sys/mman.h>
#include <stdlib.h>
+#include <vector>
+
+enum {
+ MT_MERR_OK = 0,
+ // Errors that will cause operation failure. bad flows are handled
+ MT_MERR_MAKE_SPLIT_PRE_ALLOC = 1,
+ MT_MERR_MAKE_SPLIT_LEAF_ALLOC = 2,
+ MT_MERR_MAKE_NEW_LAYER_LEAF_ALLOC_1 = 3,
+ MT_MERR_MAKE_NEW_LAYER_LEAF_ALLOC_2 = 4,
+ MT_MERR_MAKE_NEW_LAYER_KSUFFIX_ALLOC_1 = 5,
+ MT_MERR_MAKE_NEW_LAYER_KSUFFIX_ALLOC_2 = 6,
+ MT_MERR_FIND_INSERT_ASSIGN_SUFFIX = 7,
+ MT_MERR_SPLIT_INTO_ASSIGN_INITALIZE_1 = 8,
+ MT_MERR_SPLIT_INTO_ASSIGN_INITALIZE_2 = 9,
+ MT_MERR_GC_LAYER_REMOVAL_MAKE = 10,
+ MT_MERR_MAKE_SPLIT_ASSIGN_SUFFIX = 11,
+ MT_MERR_MAKE_SPLIT_PERM_EXCHANGE = 12,
+
+ // Errors that are being handled internally (Operation should succeed even if last error contains them)
+ MT_MERR_NON_DISRUPTIVE_ERRORS = 15,
+ MT_MERR_MAKE_INTERNODE_USE_RESERVED = 16,
+ MT_MERR_MAKE_INTERNODE_USE_RESERVED_2 = 17,
+
+ // We should not reach the following errors as they should be covered with other errors in more upper layer
+ MT_MERR_NOT_RETURNED_TO_USER_ERRORS = 20,
+ MT_MERR_ASSIGN_KSUF = 21,
+ MT_MERR_MAKE_LEAF = 22,
+ MT_MERR_MAKE_ROOT_LEAF = 23,
+ MT_MERR_MAKE_INTERNODE = 24,
+ MT_MERR_LEAF_ASSIGN = 25,
+ MT_MERR_ASSIGN_INITALIZE_1 = 26,
+ MT_MERR_ASSIGN_INITALIZE_2 = 27,
+
+ // We should not reach the following errors
+ MT_MERR_UNREACHABLE_ERRORS = 30,
+ MT_MERR_MAKE_SPLIT_INTERNODE_ALLOC_NOT_EXPECTED,
+ MT_MERR_MAKE_SPLIT_INTERNODE_ALLOC_NOT_EXPECTED_2,
+ MT_MERR_MAKE_SPLIT_INTERNODE_ALLOC_EMPTY_PRE_ALLOC_NOT_EXPECTED,
+
+ MT_MERR_NOT_IN_USE_LAST_ENTRY = 40
+};
+
+#define MAX_ALLOC_ERROR_TYPES MT_MERR_NOT_IN_USE_LAST_ENTRY
+
class threadinfo;
class loginfo;
+extern __thread threadinfo * mtSessionThreadInfo;
+
typedef uint64_t mrcu_epoch_type;
typedef int64_t mrcu_signed_epoch_type;
extern volatile mrcu_epoch_type globalepoch; // global epoch, updated regularly
extern volatile mrcu_epoch_type active_epoch;
-struct limbo_group {
+// Memtags max allocation size
+#define MAX_MEMTAG_MASSTREE_LEAF_ALLOCATION_SIZE iceil(sizeof(leaf<P>) + 128, 64)
+#define MAX_MEMTAG_MASSTREE_INTERNODE_ALLOCATION_SIZE sizeof(internode<P>)
+#define MAX_MEMTAG_MASSTREE_LIMBO_GROUP_ALLOCATION_SIZE sizeof(mt_limbo_group)
+
+// Upper bound for the ksuffixes structure max size.
+#define MAX_MEMTAG_MASSTREE_KSUFFIXES_ALLOCATION_SIZE(width) iceil_log2(leaf<P>::external_ksuf_type::safe_size(width, MASSTREE_MAXKEYLEN * width));
+
+inline uint64_t ng_getGlobalEpoch() {
+ return globalepoch;
+}
+
+typedef struct mt_limbo_group {
typedef mrcu_epoch_type epoch_type;
typedef mrcu_signed_epoch_type signed_epoch_type;
- struct limbo_element {
+ struct mt_limbo_element {
void* ptr_;
union {
memtag tag;
@@ -46,13 +104,13 @@ struct limbo_group {
} u_;
};
- enum { capacity = (4076 - sizeof(epoch_type) - sizeof(limbo_group*)) / sizeof(limbo_element) };
+ enum { capacity = (4076 - sizeof(epoch_type) - sizeof(mt_limbo_group*)) / sizeof(mt_limbo_element) };
unsigned head_;
unsigned tail_;
epoch_type epoch_;
- limbo_group* next_;
- limbo_element e_[capacity];
- limbo_group()
+ mt_limbo_group* next_;
+ mt_limbo_element e_[capacity];
+ mt_limbo_group()
: head_(0), tail_(0), next_() {
}
epoch_type first_epoch() const {
@@ -72,7 +130,7 @@ struct limbo_group {
++tail_;
}
inline unsigned clean_until(threadinfo& ti, mrcu_epoch_type epoch_bound, unsigned count);
-};
+} mt_limbo_group;
template <int N> struct has_threadcounter {
static bool test(threadcounter ci) {
@@ -88,22 +146,29 @@ template <> struct has_threadcounter<0> {
struct mrcu_callback {
virtual ~mrcu_callback() {
}
+ virtual size_t operator()(bool drop_index) = 0;
virtual void operator()(threadinfo& ti) = 0;
};
-class threadinfo {
+class alignas(64) threadinfo {
public:
enum {
TI_MAIN, TI_PROCESS, TI_LOG, TI_CHECKPOINT
};
+ typedef struct rcu_entry {
+ void* p;
+ size_t sz;
+ memtag tag;
+ } rcu_entry_t;
+
static threadinfo* allthreads;
threadinfo* next() const {
return next_;
}
- static threadinfo* make(int purpose, int index);
+ static threadinfo* make(void * obj_mem, int purpose, int index, int rcu_max_free_count = 0);
// XXX destructor
// thread information
@@ -202,35 +267,33 @@ class threadinfo {
}
// memory allocation
- void* allocate(size_t sz, memtag tag) {
- void* p = malloc(sz + memdebug_size);
- p = memdebug::make(p, sz, tag);
- if (p)
- mark(threadcounter(tc_alloc + (tag > memtag_value)), sz);
- return p;
- }
- void deallocate(void* p, size_t sz, memtag tag) {
- // in C++ allocators, 'p' must be nonnull
- assert(p);
- p = memdebug::check_free(p, sz, tag);
- free(p);
- mark(threadcounter(tc_alloc + (tag > memtag_value)), -sz);
- }
+ void* allocate(size_t sz, memtag tag, size_t * actual_size = NULL);
+
+ // memory deallocation
+ void deallocate(void* p, size_t sz, memtag tag);
+
void deallocate_rcu(void* p, size_t sz, memtag tag) {
assert(p);
- memdebug::check_rcu(p, sz, tag);
- record_rcu(p, tag);
- mark(threadcounter(tc_alloc + (tag > memtag_value)), -sz);
+ dealloc_rcu.push_back({p, sz, tag});
}
void* pool_allocate(size_t sz, memtag tag) {
+ void* p = NULL;
int nl = (sz + memdebug_size + CACHE_LINE_SIZE - 1) / CACHE_LINE_SIZE;
- assert(nl <= pool_max_nlines);
- if (unlikely(!pool_[nl - 1]))
- refill_pool(nl);
- void* p = pool_[nl - 1];
- if (p) {
- pool_[nl - 1] = *reinterpret_cast<void **>(p);
+ if (use_pool()) {
+ masstree_invariant(false); // internal memory pool is currently disabled
+ assert(nl <= pool_max_nlines);
+ if (unlikely(!pool_[nl - 1]))
+ refill_pool(nl);
+ p = pool_[nl - 1];
+ if (p) {
+ pool_[nl - 1] = *reinterpret_cast<void **> (p);
+ p = memdebug::make(p, sz, memtag(tag + nl));
+ mark(threadcounter(tc_alloc + (tag > memtag_value)),
+ nl * CACHE_LINE_SIZE);
+ }
+ } else {
+ p = allocate(sz, tag);
p = memdebug::make(p, sz, memtag(tag + nl));
mark(threadcounter(tc_alloc + (tag > memtag_value)),
nl * CACHE_LINE_SIZE);
@@ -242,27 +305,43 @@ class threadinfo {
assert(p && nl <= pool_max_nlines);
p = memdebug::check_free(p, sz, memtag(tag + nl));
if (use_pool()) {
+ masstree_invariant(false); // internal memory pool is currently disabled
*reinterpret_cast<void **>(p) = pool_[nl - 1];
pool_[nl - 1] = p;
} else
- free(p);
+ deallocate(p, sz, tag); // external memory pool deallocation
mark(threadcounter(tc_alloc + (tag > memtag_value)),
-nl * CACHE_LINE_SIZE);
}
void pool_deallocate_rcu(void* p, size_t sz, memtag tag) {
- int nl = (sz + memdebug_size + CACHE_LINE_SIZE - 1) / CACHE_LINE_SIZE;
- assert(p && nl <= pool_max_nlines);
- memdebug::check_rcu(p, sz, memtag(tag + nl));
- record_rcu(p, memtag(tag + nl));
- mark(threadcounter(tc_alloc + (tag > memtag_value)),
- -nl * CACHE_LINE_SIZE);
+ if (unlikely(use_pool())) {
+ int nl = (sz + memdebug_size + CACHE_LINE_SIZE - 1) / CACHE_LINE_SIZE;
+ assert(p && nl <= pool_max_nlines);
+ memdebug::check_rcu(p, sz, memtag(tag + nl));
+ mark(threadcounter(tc_alloc + (tag > memtag_value)),
+ -nl * CACHE_LINE_SIZE);
+ dealloc_rcu.push_back({p, sz, memtag(tag + nl)});
+ } else {
+ dealloc_rcu.push_back({p, sz, tag});
+ }
+ }
+
+ void add_nodes_to_gc() {
+ for (uint32_t i = 0 ; i < dealloc_rcu.size() ; i++) {
+ masstree_invariant(dealloc_rcu[i].p);
+ record_rcu(dealloc_rcu[i].p, dealloc_rcu[i].sz, dealloc_rcu[i].tag);
+ dealloc_rcu[i].p = nullptr;
+ }
+ dealloc_rcu.clear();
}
// RCU
- enum { rcu_free_count = 128 }; // max # of entries to free per rcu_quiesce() call
void rcu_start() {
- if (gc_epoch_ != globalepoch)
- gc_epoch_ = globalepoch;
+ if (gc_epoch_ != ng_getGlobalEpoch())
+ gc_epoch_ = ng_getGlobalEpoch();
+ }
+ void rcu_end() {
+ gc_epoch_ = 0;
}
void rcu_stop() {
if (perform_gc_epoch_ != active_epoch)
@@ -275,8 +354,8 @@ class threadinfo {
hard_rcu_quiesce();
}
typedef ::mrcu_callback mrcu_callback;
- void rcu_register(mrcu_callback* cb) {
- record_rcu(cb, memtag(-1));
+ void rcu_register(mrcu_callback* cb, size_t size) {
+ record_rcu(cb, size, memtag_masstree_gc);
}
// thread management
@@ -287,11 +366,44 @@ class threadinfo {
return pthreadid_;
}
+ inline void set_last_error(int error) { masstree_invariant(error < MT_MERR_UNREACHABLE_ERRORS); last_error = error; }
+ inline int get_last_error() { return last_error; }
+ inline bool non_disruptive_error() { return last_error == 0 ||
+ (last_error > MT_MERR_NON_DISRUPTIVE_ERRORS && last_error < MT_MERR_NOT_RETURNED_TO_USER_ERRORS); }
+
void report_rcu(void* ptr) const;
static void report_rcu_all(void* ptr);
static inline mrcu_epoch_type min_active_epoch();
+ void set_rcu_free_count(int rcu_count) { rcu_free_count = rcu_count; }
+ int get_rcu_free_count() { return rcu_free_count; }
+
+ void set_gc_session(void * gc_session);
+ void * get_gc_session();
+
+ inline uint32_t get_occupied_elements() { return total_limbo_inuse_elements; }
+
+ void set_working_index (void * index) { cur_working_index = index; }
+ void * get_working_index () { return cur_working_index; }
+
+ // This function is now used to defer between Masstree internal memory pool (use_pool == true) vs external memory pool (use_pool == false)
+ // Masstree internal memory pool is currently disabled
+ static bool use_pool() {
+#if ENABLE_ASSERTIONS
+ return !no_pool_value;
+#else
+ return false;
+#endif
+ }
+
+ bool is_empty_rcu_array() {
+ return dealloc_rcu.size() == 0;
+ }
+
private:
+ void * cur_working_index;
+ int last_error = MT_MERR_OK;
+ std::vector<struct rcu_entry> dealloc_rcu;
union {
struct {
mrcu_epoch_type gc_epoch_;
@@ -310,17 +422,21 @@ class threadinfo {
enum { pool_max_nlines = 20 };
void* pool_[pool_max_nlines];
+ int rcu_free_count;
+ mt_limbo_group* limbo_head_;
+ mt_limbo_group* limbo_tail_;
+ void * gc_session_;
+ uint32_t total_limbo_inuse_elements;
- limbo_group* limbo_head_;
- limbo_group* limbo_tail_;
mutable kvtimestamp_t ts_;
//enum { ncounters = (int) tc_max };
enum { ncounters = 0 };
uint64_t counters_[ncounters];
+ uint64_t insertions_ = 0;
- void refill_pool(int nl);
- void refill_rcu();
+ void refill_pool(int nl) { assert(0); }
+ void refill_rcu() { assert(0); }
void free_rcu(void *p, memtag tag) {
if ((tag & memtag_pool_mask) == 0) {
@@ -336,36 +452,37 @@ class threadinfo {
}
}
- void record_rcu(void* ptr, memtag tag) {
+ void ng_record_rcu(void* ptr, int size, memtag tag);
+
+ void record_rcu(void* ptr, int size, memtag tag) {
+ if (unlikely(use_pool())) {
+ masstree_invariant(false); // internal memory pool is currently disabled
if (limbo_tail_->tail_ + 2 > limbo_tail_->capacity)
- refill_rcu();
- uint64_t epoch = globalepoch;
+ refill_rcu();
+ uint64_t epoch = ng_getGlobalEpoch();
limbo_tail_->push_back(ptr, tag, epoch);
+ ++total_limbo_inuse_elements;
+ } else {
+ ng_record_rcu(ptr, size, tag);
+ }
}
#if ENABLE_ASSERTIONS
static int no_pool_value;
#endif
- static bool use_pool() {
-#if ENABLE_ASSERTIONS
- return !no_pool_value;
-#else
- return true;
-#endif
- }
- inline threadinfo(int purpose, int index);
+ inline threadinfo(int purpose, int index, int rcu_max_free_count);
threadinfo(const threadinfo&) = delete;
~threadinfo() {}
threadinfo& operator=(const threadinfo&) = delete;
void hard_rcu_quiesce();
- friend struct limbo_group;
+ friend struct mt_limbo_group;
};
inline mrcu_epoch_type threadinfo::min_active_epoch() {
- mrcu_epoch_type ae = globalepoch;
+ mrcu_epoch_type ae = ng_getGlobalEpoch();
for (threadinfo* ti = allthreads; ti; ti = ti->next()) {
prefetch((const void*) ti->next());
mrcu_epoch_type te = ti->gc_epoch_;
diff --git a/log.cc b/log.cc
deleted file mode 100644
index 0cad68b..0000000
--- a/log.cc
+++ /dev/null
@@ -1,867 +0,0 @@
-/* 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.
- */
-#include "log.hh"
-#include "kvthread.hh"
-#include "kvrow.hh"
-#include "file.hh"
-#include "query_masstree.hh"
-#include "masstree_tcursor.hh"
-#include "masstree_insert.hh"
-#include "masstree_remove.hh"
-#include "misc.hh"
-#include "msgpack.hh"
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <errno.h>
-using lcdf::String;
-
-kvepoch_t global_log_epoch;
-kvepoch_t global_wake_epoch;
-struct timeval log_epoch_interval;
-static struct timeval log_epoch_time;
-extern Masstree::default_table* tree;
-extern volatile bool recovering;
-
-kvepoch_t rec_ckp_min_epoch;
-kvepoch_t rec_ckp_max_epoch;
-logreplay::info_type *rec_log_infos;
-kvepoch_t rec_replay_min_epoch;
-kvepoch_t rec_replay_max_epoch;
-kvepoch_t rec_replay_min_quiescent_last_epoch;
-
-struct logrec_base {
- uint32_t command_;
- uint32_t size_;
-
- static size_t size() {
- return sizeof(logrec_base);
- }
- static size_t store(char *buf, uint32_t command) {
- // XXX check alignment on some architectures
- logrec_base *lr = reinterpret_cast<logrec_base *>(buf);
- lr->command_ = command;
- lr->size_ = sizeof(*lr);
- return sizeof(*lr);
- }
- static bool check(const char *buf) {
- const logrec_base *lr = reinterpret_cast<const logrec_base *>(buf);
- return lr->size_ >= sizeof(*lr);
- }
- static uint32_t command(const char *buf) {
- const logrec_base *lr = reinterpret_cast<const logrec_base *>(buf);
- return lr->command_;
- }
-};
-
-struct logrec_epoch {
- uint32_t command_;
- uint32_t size_;
- kvepoch_t epoch_;
-
- static size_t size() {
- return sizeof(logrec_epoch);
- }
- static size_t store(char *buf, uint32_t command, kvepoch_t epoch) {
- // XXX check alignment on some architectures
- logrec_epoch *lr = reinterpret_cast<logrec_epoch *>(buf);
- lr->command_ = command;
- lr->size_ = sizeof(*lr);
- lr->epoch_ = epoch;
- return sizeof(*lr);
- }
- static bool check(const char *buf) {
- const logrec_epoch *lr = reinterpret_cast<const logrec_epoch *>(buf);
- return lr->size_ >= sizeof(*lr);
- }
-};
-
-struct logrec_kv {
- uint32_t command_;
- uint32_t size_;
- kvtimestamp_t ts_;
- uint32_t keylen_;
- char buf_[0];
-
- static size_t size(uint32_t keylen, uint32_t vallen) {
- return sizeof(logrec_kv) + keylen + vallen;
- }
- static size_t store(char *buf, uint32_t command,
- Str key, Str val,
- kvtimestamp_t ts) {
- // XXX check alignment on some architectures
- logrec_kv *lr = reinterpret_cast<logrec_kv *>(buf);
- lr->command_ = command;
- lr->size_ = sizeof(*lr) + key.len + val.len;
- lr->ts_ = ts;
- lr->keylen_ = key.len;
- memcpy(lr->buf_, key.s, key.len);
- memcpy(lr->buf_ + key.len, val.s, val.len);
- return sizeof(*lr) + key.len + val.len;
- }
- static bool check(const char *buf) {
- const logrec_kv *lr = reinterpret_cast<const logrec_kv *>(buf);
- return lr->size_ >= sizeof(*lr)
- && lr->size_ >= sizeof(*lr) + lr->keylen_;
- }
-};
-
-struct logrec_kvdelta {
- uint32_t command_;
- uint32_t size_;
- kvtimestamp_t ts_;
- kvtimestamp_t prev_ts_;
- uint32_t keylen_;
- char buf_[0];
-
- static size_t size(uint32_t keylen, uint32_t vallen) {
- return sizeof(logrec_kvdelta) + keylen + vallen;
- }
- static size_t store(char *buf, uint32_t command,
- Str key, Str val,
- kvtimestamp_t prev_ts, kvtimestamp_t ts) {
- // XXX check alignment on some architectures
- logrec_kvdelta *lr = reinterpret_cast<logrec_kvdelta *>(buf);
- lr->command_ = command;
- lr->size_ = sizeof(*lr) + key.len + val.len;
- lr->ts_ = ts;
- lr->prev_ts_ = prev_ts;
- lr->keylen_ = key.len;
- memcpy(lr->buf_, key.s, key.len);
- memcpy(lr->buf_ + key.len, val.s, val.len);
- return sizeof(*lr) + key.len + val.len;
- }
- static bool check(const char *buf) {
- const logrec_kvdelta *lr = reinterpret_cast<const logrec_kvdelta *>(buf);
- return lr->size_ >= sizeof(*lr)
- && lr->size_ >= sizeof(*lr) + lr->keylen_;
- }
-};
-
-
-logset* logset::make(int size) {
- static_assert(sizeof(loginfo) == 2 * CACHE_LINE_SIZE, "unexpected sizeof(loginfo)");
- assert(size > 0 && size <= 64);
- char* x = new char[sizeof(loginfo) * size + sizeof(loginfo::logset_info) + CACHE_LINE_SIZE];
- char* ls_pos = x + sizeof(loginfo::logset_info);
- uintptr_t left = reinterpret_cast<uintptr_t>(ls_pos) % CACHE_LINE_SIZE;
- if (left)
- ls_pos += CACHE_LINE_SIZE - left;
- logset* ls = reinterpret_cast<logset*>(ls_pos);
- ls->li_[-1].lsi_.size_ = size;
- ls->li_[-1].lsi_.allocation_offset_ = (int) (x - ls_pos);
- for (int i = 0; i != size; ++i)
- new((void*) &ls->li_[i]) loginfo(ls, i);
- return ls;
-}
-
-void logset::free(logset* ls) {
- for (int i = 0; i != ls->size(); ++i)
- ls->li_[i].~loginfo();
- delete[] (reinterpret_cast<char*>(ls) + ls->li_[-1].lsi_.allocation_offset_);
-}
-
-
-loginfo::loginfo(logset* ls, int logindex) {
- f_.lock_ = 0;
- f_.waiting_ = 0;
- f_.filename_ = String().internal_rep();
- f_.filename_.ref();
-
- len_ = 20 * 1024 * 1024;
- pos_ = 0;
- buf_ = (char *) malloc(len_);
- always_assert(buf_);
- log_epoch_ = 0;
- quiescent_epoch_ = 0;
- wake_epoch_ = 0;
- flushed_epoch_ = 0;
-
- ti_ = 0;
- f_.logset_ = ls;
- logindex_ = logindex;
-
- (void) padding1_;
-}
-
-loginfo::~loginfo() {
- f_.filename_.deref();
- free(buf_);
-}
-
-void* loginfo::trampoline(void* x) {
- loginfo* li = reinterpret_cast<loginfo*>(x);
- li->ti_->pthread() = pthread_self();
- return li->run();
-}
-
-void loginfo::initialize(const String& logfile) {
- assert(!ti_);
-
- f_.filename_.deref();
- f_.filename_ = logfile.internal_rep();
- f_.filename_.ref();
-
- ti_ = threadinfo::make(threadinfo::TI_LOG, logindex_);
- int r = pthread_create(&ti_->pthread(), 0, trampoline, this);
- always_assert(r == 0);
-}
-
-// one logger thread per logs[].
-static void check_epoch() {
- struct timeval tv;
- gettimeofday(&tv, 0);
- if (timercmp(&tv, &log_epoch_time, >)) {
- log_epoch_time = tv;
- timeradd(&log_epoch_time, &log_epoch_interval, &log_epoch_time);
- global_log_epoch = global_log_epoch.next_nonzero(); // 0 isn't valid
- }
-}
-
-void* loginfo::run() {
- {
- logreplay replayer(f_.filename_);
- replayer.replay(ti_->index(), ti_);
- }
-
- int fd = open(String(f_.filename_).c_str(),
- O_WRONLY | O_APPEND | O_CREAT, 0666);
- always_assert(fd >= 0);
- char *x_buf = (char *) malloc(len_);
- always_assert(x_buf);
-
- while (1) {
- uint32_t nb = 0;
- acquire();
- kvepoch_t ge = global_log_epoch, we = global_wake_epoch;
- if (wake_epoch_ != we) {
- wake_epoch_ = we;
- quiescent_epoch_ = 0;
- }
- // If the writing threads appear quiescent, and aren't about to write
- // to the log (f_.waiting_ != 0), then write a quiescence
- // notification.
- if (!recovering && pos_ == 0 && !quiescent_epoch_
- && ge != log_epoch_ && ge != we && !f_.waiting_) {
- quiescent_epoch_ = log_epoch_ = ge;
- char *p = buf_;
- p += logrec_epoch::store(p, logcmd_epoch, log_epoch_);
- if (log_epoch_ == wake_epoch_)
- p += logrec_base::store(p, logcmd_wake);
- p += logrec_base::store(p, logcmd_quiesce);
- pos_ = p - buf_;
- }
- if (!recovering && pos_ > 0) {
- uint32_t x_pos = pos_;
- std::swap(buf_, x_buf);
- pos_ = 0;
- kvepoch_t x_epoch = log_epoch_;
- release();
- ssize_t r = write(fd, x_buf, x_pos);
- always_assert(r == ssize_t(x_pos));
- fsync(fd);
- flushed_epoch_ = x_epoch;
- // printf("log %d %d\n", ti_->index(), x_pos);
- nb = x_pos;
- } else
- release();
- if (nb < len_ / 4)
- napms(200);
- if (ti_->index() == 0)
- check_epoch();
- }
-
- return 0;
-}
-
-
-
-// log entry format: see log.hh
-void loginfo::record(int command, const query_times& qtimes,
- Str key, Str value) {
- assert(!recovering);
- size_t n = logrec_kvdelta::size(key.len, value.len)
- + logrec_epoch::size() + logrec_base::size();
- waitlist wait = { &wait };
- int stalls = 0;
- while (1) {
- if (len_ - pos_ >= n
- && (wait.next == &wait || f_.waiting_ == &wait)) {
- kvepoch_t we = global_wake_epoch;
-
- // Potentially record a new epoch.
- if (qtimes.epoch != log_epoch_) {
- log_epoch_ = qtimes.epoch;
- pos_ += logrec_epoch::store(buf_ + pos_, logcmd_epoch, qtimes.epoch);
- }
-
- if (quiescent_epoch_) {
- // We're recording a new log record on a log that's been
- // quiescent for a while. If the quiescence marker has been
- // flushed, then all epochs less than the query epoch are
- // effectively on disk.
- if (flushed_epoch_ == quiescent_epoch_)
- flushed_epoch_ = qtimes.epoch;
- quiescent_epoch_ = 0;
- while (we < qtimes.epoch)
- we = cmpxchg(&global_wake_epoch, we, qtimes.epoch);
- }
-
- // Log epochs should be recorded in monotonically increasing
- // order, but the wake epoch may be ahead of the query epoch (if
- // the query took a while). So potentially record an EARLIER
- // wake_epoch. This will get fixed shortly by the next log
- // record.
- if (we != wake_epoch_ && qtimes.epoch < we)
- we = qtimes.epoch;
- if (we != wake_epoch_) {
- wake_epoch_ = we;
- pos_ += logrec_base::store(buf_ + pos_, logcmd_wake);
- }
-
- if (command == logcmd_put && qtimes.prev_ts
- && !(qtimes.prev_ts & 1))
- pos_ += logrec_kvdelta::store(buf_ + pos_,
- logcmd_modify, key, value,
- qtimes.prev_ts, qtimes.ts);
- else
- pos_ += logrec_kv::store(buf_ + pos_,
- command, key, value, qtimes.ts);
-
- if (f_.waiting_ == &wait)
- f_.waiting_ = wait.next;
- release();
- return;
- }
-
- // Otherwise must spin
- if (wait.next == &wait) {
- waitlist** p = &f_.waiting_;
- while (*p)
- p = &(*p)->next;
- *p = &wait;
- wait.next = 0;
- }
- release();
- if (stalls == 0)
- printf("stall\n");
- else if (stalls % 25 == 0)
- printf("stall %d\n", stalls);
- ++stalls;
- napms(50);
- acquire();
- }
-}
-
-void loginfo::record(int command, const query_times& qtimes, Str key,
- const lcdf::Json* req, const lcdf::Json* end_req) {
- lcdf::StringAccum sa(128);
- msgpack::unparser<lcdf::StringAccum> cu(sa);
- cu.write_array_header(end_req - req);
- for (; req != end_req; ++req)
- cu << *req;
- record(command, qtimes, key, Str(sa.data(), sa.length()));
-}
-
-
-// replay
-
-logreplay::logreplay(const String &filename)
- : filename_(filename), errno_(0), buf_()
-{
- int fd = open(filename_.c_str(), O_RDONLY);
- if (fd == -1) {
- fail:
- errno_ = errno;
- buf_ = 0;
- if (fd != -1)
- (void) close(fd);
- return;
- }
-
- struct stat sb;
- int r = fstat(fd, &sb);
- if (r == -1)
- goto fail;
-
- size_ = sb.st_size;
- if (size_ != 0) {
- // XXX what if filename_ is too big to mmap in its entirety?
- // XXX should support mmaping/writing in pieces
- buf_ = (char *) ::mmap(0, size_, PROT_READ, MAP_FILE | MAP_PRIVATE,
- fd, 0);
- if (buf_ == MAP_FAILED)
- goto fail;
- }
-
- (void) close(fd);
-}
-
-logreplay::~logreplay()
-{
- unmap();
-}
-
-int
-logreplay::unmap()
-{
- int r = 0;
- if (buf_) {
- r = munmap(buf_, size_);
- buf_ = 0;
- }
- return r;
-}
-
-
-struct logrecord {
- uint32_t command;
- Str key;
- Str val;
- kvtimestamp_t ts;
- kvtimestamp_t prev_ts;
- kvepoch_t epoch;
-
- const char *extract(const char *buf, const char *end);
-
- template <typename T>
- void run(T& table, std::vector<lcdf::Json>& jrepo, threadinfo& ti);
-
- private:
- inline void apply(row_type*& value, bool found,
- std::vector<lcdf::Json>& jrepo, threadinfo& ti);
-};
-
-const char *
-logrecord::extract(const char *buf, const char *end)
-{
- const logrec_base *lr = reinterpret_cast<const logrec_base *>(buf);
- if (unlikely(size_t(end - buf) < sizeof(*lr)
- || lr->size_ < sizeof(*lr)
- || size_t(end - buf) < lr->size_
- || lr->command_ == logcmd_none)) {
- fail:
- command = logcmd_none;
- return end;
- }
-
- command = lr->command_;
- if (command == logcmd_put || command == logcmd_replace
- || command == logcmd_remove) {
- const logrec_kv *lk = reinterpret_cast<const logrec_kv *>(buf);
- if (unlikely(lk->size_ < sizeof(*lk)
- || lk->keylen_ > MASSTREE_MAXKEYLEN
- || sizeof(*lk) + lk->keylen_ > lk->size_))
- goto fail;
- ts = lk->ts_;
- key.assign(lk->buf_, lk->keylen_);
- val.assign(lk->buf_ + lk->keylen_, lk->size_ - sizeof(*lk) - lk->keylen_);
- } else if (command == logcmd_modify) {
- const logrec_kvdelta *lk = reinterpret_cast<const logrec_kvdelta *>(buf);
- if (unlikely(lk->keylen_ > MASSTREE_MAXKEYLEN
- || sizeof(*lk) + lk->keylen_ > lk->size_))
- goto fail;
- ts = lk->ts_;
- prev_ts = lk->prev_ts_;
- key.assign(lk->buf_, lk->keylen_);
- val.assign(lk->buf_ + lk->keylen_, lk->size_ - sizeof(*lk) - lk->keylen_);
- } else if (command == logcmd_epoch) {
- const logrec_epoch *lre = reinterpret_cast<const logrec_epoch *>(buf);
- if (unlikely(lre->size_ < logrec_epoch::size()))
- goto fail;
- epoch = lre->epoch_;
- }
-
- return buf + lr->size_;
-}
-
-template <typename T>
-void logrecord::run(T& table, std::vector<lcdf::Json>& jrepo, threadinfo& ti) {
- row_marker m;
- if (command == logcmd_remove) {
- ts |= 1;
- m.marker_type_ = row_marker::mt_remove;
- val = Str((const char*) &m, sizeof(m));
- }
-
- typename T::cursor_type lp(table, key);
- bool found = lp.find_insert(ti);
- if (!found)
- ti.observe_phantoms(lp.node());
- apply(lp.value(), found, jrepo, ti);
- lp.finish(1, ti);
-}
-
-static lcdf::Json* parse_changeset(Str changeset,
- std::vector<lcdf::Json>& jrepo) {
- msgpack::parser mp(changeset.udata());
- unsigned index = 0;
- Str value;
- size_t pos = 0;
- while (mp.position() != changeset.end()) {
- if (pos == jrepo.size())
- jrepo.resize(pos + 2);
- mp >> index >> value;
- jrepo[pos] = index;
- jrepo[pos + 1] = String::make_stable(value);
- pos += 2;
- }
- return jrepo.data() + pos;
-}
-
-inline void logrecord::apply(row_type*& value, bool found,
- std::vector<lcdf::Json>& jrepo, threadinfo& ti) {
- row_type** cur_value = &value;
- if (!found)
- *cur_value = 0;
-
- // find point to insert change (may be after some delta markers)
- while (*cur_value && row_is_delta_marker(*cur_value)
- && (*cur_value)->timestamp() > ts)
- cur_value = &row_get_delta_marker(*cur_value)->prev_;
-
- // check out of date
- if (*cur_value && (*cur_value)->timestamp() >= ts)
- return;
-
- // if not modifying, delete everything earlier
- if (command != logcmd_modify)
- while (row_type* old_value = *cur_value) {
- if (row_is_delta_marker(old_value)) {
- ti.mark(tc_replay_remove_delta);
- *cur_value = row_get_delta_marker(old_value)->prev_;
- } else
- *cur_value = 0;
- old_value->deallocate(ti);
- }
-
- // actually apply change
- if (command == logcmd_replace)
- *cur_value = row_type::create1(val, ts, ti);
- else if (command != logcmd_modify
- || (*cur_value && (*cur_value)->timestamp() == prev_ts)) {
- lcdf::Json* end_req = parse_changeset(val, jrepo);
- if (command != logcmd_modify)
- *cur_value = row_type::create(jrepo.data(), end_req, ts, ti);
- else {
- row_type* old_value = *cur_value;
- *cur_value = old_value->update(jrepo.data(), end_req, ts, ti);
- if (*cur_value != old_value)
- old_value->deallocate(ti);
- }
- } else {
- // XXX assume that memory exists before saved request -- it does
- // in conventional log replay, but that's an ugly interface
- val.s -= sizeof(row_delta_marker<row_type>);
- val.len += sizeof(row_delta_marker<row_type>);
- row_type* new_value = row_type::create1(val, ts | 1, ti);
- row_delta_marker<row_type>* dm = row_get_delta_marker(new_value, true);
- dm->marker_type_ = row_marker::mt_delta;
- dm->prev_ts_ = prev_ts;
- dm->prev_ = *cur_value;
- *cur_value = new_value;
- ti.mark(tc_replay_create_delta);
- }
-
- // clean up
- while (value && row_is_delta_marker(value)) {
- row_type **prev = 0, **trav = &value;
- while (*trav && row_is_delta_marker(*trav)) {
- prev = trav;
- trav = &row_get_delta_marker(*trav)->prev_;
- }
- if (prev && *trav
- && row_get_delta_marker(*prev)->prev_ts_ == (*trav)->timestamp()) {
- row_type *old_prev = *prev;
- Str req = old_prev->col(0);
- req.s += sizeof(row_delta_marker<row_type>);
- req.len -= sizeof(row_delta_marker<row_type>);
- const lcdf::Json* end_req = parse_changeset(req, jrepo);
- *prev = (*trav)->update(jrepo.data(), end_req, old_prev->timestamp() - 1, ti);
- if (*prev != *trav)
- (*trav)->deallocate(ti);
- old_prev->deallocate(ti);
- ti.mark(tc_replay_remove_delta);
- } else
- break;
- }
-}
-
-
-logreplay::info_type
-logreplay::info() const
-{
- info_type x;
- x.first_epoch = x.last_epoch = x.wake_epoch = x.min_post_quiescent_wake_epoch = 0;
- x.quiescent = true;
-
- const char *buf = buf_, *end = buf_ + size_;
- off_t nr = 0;
- bool log_corrupt = false;
- while (buf + sizeof(logrec_base) <= end) {
- const logrec_base *lr = reinterpret_cast<const logrec_base *>(buf);
- if (unlikely(lr->size_ < sizeof(logrec_base))) {
- log_corrupt = true;
- break;
- } else if (unlikely(buf + lr->size_ > end))
- break;
- x.quiescent = lr->command_ == logcmd_quiesce;
- if (lr->command_ == logcmd_epoch) {
- const logrec_epoch *lre =
- reinterpret_cast<const logrec_epoch *>(buf);
- if (unlikely(lre->size_ < sizeof(*lre))) {
- log_corrupt = true;
- break;
- }
- if (!x.first_epoch)
- x.first_epoch = lre->epoch_;
- x.last_epoch = lre->epoch_;
- if (x.wake_epoch && x.wake_epoch > x.last_epoch) // wrap-around
- x.wake_epoch = 0;
- } else if (lr->command_ == logcmd_wake)
- x.wake_epoch = x.last_epoch;
-#if !NDEBUG
- else if (lr->command_ != logcmd_put
- && lr->command_ != logcmd_replace
- && lr->command_ != logcmd_modify
- && lr->command_ != logcmd_remove
- && lr->command_ != logcmd_quiesce) {
- log_corrupt = true;
- break;
- }
-#endif
- buf += lr->size_;
- ++nr;
- }
-
- fprintf(stderr, "replay %s: %" PRIdOFF_T " records, first %" PRIu64 ", last %" PRIu64 ", wake %" PRIu64 "%s%s @%zu\n",
- filename_.c_str(), nr, x.first_epoch.value(),
- x.last_epoch.value(), x.wake_epoch.value(),
- x.quiescent ? ", quiescent" : "",
- log_corrupt ? ", CORRUPT" : "", buf - buf_);
- return x;
-}
-
-kvepoch_t
-logreplay::min_post_quiescent_wake_epoch(kvepoch_t quiescent_epoch) const
-{
- kvepoch_t e = 0;
- const char *buf = buf_, *end = buf_ + size_;
- bool log_corrupt = false;
- while (buf + sizeof(logrec_base) <= end) {
- const logrec_base *lr = reinterpret_cast<const logrec_base *>(buf);
- if (unlikely(lr->size_ < sizeof(logrec_base))) {
- log_corrupt = true;
- break;
- } else if (unlikely(buf + lr->size_ > end))
- break;
- if (lr->command_ == logcmd_epoch) {
- const logrec_epoch *lre =
- reinterpret_cast<const logrec_epoch *>(buf);
- if (unlikely(lre->size_ < sizeof(*lre))) {
- log_corrupt = true;
- break;
- }
- e = lre->epoch_;
- } else if (lr->command_ == logcmd_wake
- && e
- && e >= quiescent_epoch)
- return e;
- buf += lr->size_;
- }
- (void) log_corrupt;
- return 0;
-}
-
-uint64_t
-logreplay::replayandclean1(kvepoch_t min_epoch, kvepoch_t max_epoch,
- threadinfo *ti)
-{
- uint64_t nr = 0;
- const char *pos = buf_, *end = buf_ + size_;
- const char *repbegin = 0, *repend = 0;
- logrecord lr;
- std::vector<lcdf::Json> jrepo;
-
- // XXX
- while (pos < end) {
- const char *nextpos = lr.extract(pos, end);
- if (lr.command == logcmd_none) {
- fprintf(stderr, "replay %s: %" PRIu64 " entries replayed, CORRUPT @%zu\n",
- filename_.c_str(), nr, pos - buf_);
- break;
- }
- if (lr.command == logcmd_epoch) {
- if ((min_epoch && lr.epoch < min_epoch)
- || (!min_epoch && !repbegin))
- repbegin = pos;
- if (lr.epoch >= max_epoch) {
- always_assert(repbegin);
- repend = nextpos;
- break;
- }
- }
- if (!lr.epoch || (min_epoch && lr.epoch < min_epoch)) {
- pos = nextpos;
- if (repbegin)
- repend = nextpos;
- continue;
- }
- // replay only part of log after checkpoint
- // could replay everything, the if() here tests
- // correctness of checkpoint scheme.
- assert(repbegin);
- repend = nextpos;
- if (lr.key.len) { // skip empty entry
- if (lr.command == logcmd_put
- || lr.command == logcmd_replace
- || lr.command == logcmd_modify
- || lr.command == logcmd_remove)
- lr.run(tree->table(), jrepo, *ti);
- ++nr;
- if (nr % 100000 == 0)
- fprintf(stderr,
- "replay %s: %" PRIu64 " entries replayed\n",
- filename_.c_str(), nr);
- }
- // XXX RCU
- pos = nextpos;
- }
-
- // rewrite portion of log
- if (!repbegin)
- repbegin = repend = buf_;
- else if (!repend) {
- fprintf(stderr, "replay %s: surprise repend\n", filename_.c_str());
- repend = pos;
- }
-
- char tmplog[256];
- int r = snprintf(tmplog, sizeof(tmplog), "%s.tmp", filename_.c_str());
- always_assert(r >= 0 && size_t(r) < sizeof(tmplog));
-
- printf("replay %s: truncate from %" PRIdOFF_T " to %" PRIdSIZE_T " [%" PRIdSIZE_T ",%" PRIdSIZE_T ")\n",
- filename_.c_str(), size_, repend - repbegin,
- repbegin - buf_, repend - buf_);
-
- bool need_copy = repbegin != buf_;
- int fd;
- if (!need_copy)
- fd = replay_truncate(repend - repbegin);
- else
- fd = replay_copy(tmplog, repbegin, repend);
-
- r = fsync(fd);
- always_assert(r == 0);
- r = close(fd);
- always_assert(r == 0);
-
- // replace old log with rewritten log
- if (unmap() != 0)
- abort();
-
- if (need_copy) {
- r = rename(tmplog, filename_.c_str());
- if (r != 0) {
- fprintf(stderr, "replay %s: %s\n", filename_.c_str(), strerror(errno));
- abort();
- }
- }
-
- return nr;
-}
-
-int
-logreplay::replay_truncate(size_t len)
-{
- int fd = open(filename_.c_str(), O_RDWR);
- if (fd < 0) {
- fprintf(stderr, "replay %s: %s\n", filename_.c_str(), strerror(errno));
- abort();
- }
-
- struct stat sb;
- int r = fstat(fd, &sb);
- if (r != 0) {
- fprintf(stderr, "replay %s: %s\n", filename_.c_str(), strerror(errno));
- abort();
- } else if (sb.st_size < off_t(len)) {
- fprintf(stderr, "replay %s: bad length %" PRIdOFF_T "\n", filename_.c_str(), sb.st_size);
- abort();
- }
-
- r = ftruncate(fd, len);
- if (r != 0) {
- fprintf(stderr, "replay %s: truncate: %s\n", filename_.c_str(), strerror(errno));
- abort();
- }
-
- off_t off = lseek(fd, len, SEEK_SET);
- if (off == (off_t) -1) {
- fprintf(stderr, "replay %s: seek: %s\n", filename_.c_str(), strerror(errno));
- abort();
- }
-
- return fd;
-}
-
-int
-logreplay::replay_copy(const char *tmpname, const char *first, const char *last)
-{
- int fd = creat(tmpname, 0666);
- if (fd < 0) {
- fprintf(stderr, "replay %s: create: %s\n", tmpname, strerror(errno));
- abort();
- }
-
- ssize_t w = safe_write(fd, first, last - first);
- always_assert(w >= 0 && w == last - first);
-
- return fd;
-}
-
-void
-logreplay::replay(int which, threadinfo *ti)
-{
- waituntilphase(REC_LOG_TS);
- // find the maximum timestamp of entries in the log
- if (buf_) {
- info_type x = info();
- pthread_mutex_lock(&rec_mu);
- rec_log_infos[which] = x;
- pthread_mutex_unlock(&rec_mu);
- }
- inactive();
-
- waituntilphase(REC_LOG_ANALYZE_WAKE);
- if (buf_) {
- if (rec_replay_min_quiescent_last_epoch
- && rec_replay_min_quiescent_last_epoch <= rec_log_infos[which].wake_epoch)
- rec_log_infos[which].min_post_quiescent_wake_epoch =
- min_post_quiescent_wake_epoch(rec_replay_min_quiescent_last_epoch);
- }
- inactive();
-
- waituntilphase(REC_LOG_REPLAY);
- if (buf_) {
- ti->rcu_start();
- uint64_t nr = replayandclean1(rec_replay_min_epoch, rec_replay_max_epoch, ti);
- ti->rcu_stop();
- printf("recovered %" PRIu64 " records from %s\n", nr, filename_.c_str());
- }
- inactive();
-}
diff --git a/log.hh b/log.hh
deleted file mode 100644
index 3df07fa..0000000
--- a/log.hh
+++ /dev/null
@@ -1,233 +0,0 @@
-/* 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_LOG_HH
-#define MASSTREE_LOG_HH
-#include "kvthread.hh"
-#include "string.hh"
-#include "kvproto.hh"
-#include "str.hh"
-#include <pthread.h>
-class logset;
-using lcdf::Str;
-namespace lcdf { class Json; }
-
-// in-memory log.
-// more than one, to reduce contention on the lock.
-class loginfo {
- public:
- void initialize(const lcdf::String& logfile);
-
- inline void acquire();
- inline void release();
-
- inline kvepoch_t flushed_epoch() const;
- inline bool quiescent() const;
-
- // logging
- struct query_times {
- kvepoch_t epoch;
- kvtimestamp_t ts;
- kvtimestamp_t prev_ts;
- };
- // NB may block!
- void record(int command, const query_times& qt, Str key, Str value);
- void record(int command, const query_times& qt, Str key,
- const lcdf::Json* req, const lcdf::Json* end_req);
-
- private:
- struct waitlist {
- waitlist* next;
- };
- struct front {
- uint32_t lock_;
- waitlist* waiting_;
- lcdf::String::rep_type filename_;
- logset* logset_;
- };
- struct logset_info {
- int32_t size_;
- int allocation_offset_;
- };
-
- front f_;
- char padding1_[CACHE_LINE_SIZE - sizeof(front)];
-
- kvepoch_t log_epoch_; // epoch written to log (non-quiescent)
- kvepoch_t quiescent_epoch_; // epoch we went quiescent
- kvepoch_t wake_epoch_; // epoch for which we recorded a wake command
- kvepoch_t flushed_epoch_; // epoch fsync()ed to disk
-
- union {
- struct {
- char *buf_;
- uint32_t pos_;
- uint32_t len_;
-
- // We have logged all writes up to, but not including,
- // flushed_epoch_.
- // Log is quiesced to disk if quiescent_epoch_ != 0
- // and quiescent_epoch_ == flushed_epoch_.
- // When a log wakes up from quiescence, it sets global_wake_epoch;
- // other threads must record a logcmd_wake in their logs.
- // Invariant: log_epoch_ != quiescent_epoch_ (unless both are 0).
-
- threadinfo *ti_;
- int logindex_;
- };
- struct {
- char cache_line_2_[CACHE_LINE_SIZE - 4 * sizeof(kvepoch_t) - sizeof(logset_info)];
- logset_info lsi_;
- };
- };
-
- loginfo(logset* ls, int logindex);
- ~loginfo();
- void* run();
- static void* trampoline(void*);
-
- friend class logset;
-};
-
-class logset {
- public:
- static logset* make(int size);
- static void free(logset* ls);
-
- inline int size() const;
- inline loginfo& log(int i);
- inline const loginfo& log(int i) const;
-
- private:
- loginfo li_[0];
-};
-
-extern kvepoch_t global_log_epoch;
-extern kvepoch_t global_wake_epoch;
-extern struct timeval log_epoch_interval;
-
-enum logcommand {
- logcmd_none = 0,
- logcmd_put = 0x5455506B, // "kPUT" in little endian
- logcmd_replace = 0x3155506B, // "kPU1"
- logcmd_modify = 0x444F4D6B, // "kMOD"
- logcmd_remove = 0x4D45526B, // "kREM"
- logcmd_epoch = 0x4F50456B, // "kEPO"
- logcmd_quiesce = 0x4955516B, // "kQUI"
- logcmd_wake = 0x4B41576B // "kWAK"
-};
-
-
-class logreplay {
- public:
- logreplay(const lcdf::String &filename);
- ~logreplay();
- int unmap();
-
- struct info_type {
- kvepoch_t first_epoch;
- kvepoch_t last_epoch;
- kvepoch_t wake_epoch;
- kvepoch_t min_post_quiescent_wake_epoch;
- bool quiescent;
- };
- info_type info() const;
- kvepoch_t min_post_quiescent_wake_epoch(kvepoch_t quiescent_epoch) const;
-
- void replay(int i, threadinfo *ti);
-
- private:
- lcdf::String filename_;
- int errno_;
- off_t size_;
- char *buf_;
-
- uint64_t replayandclean1(kvepoch_t min_epoch, kvepoch_t max_epoch,
- threadinfo *ti);
- int replay_truncate(size_t len);
- int replay_copy(const char *tmpname, const char *first, const char *last);
-};
-
-enum { REC_NONE, REC_CKP, REC_LOG_TS, REC_LOG_ANALYZE_WAKE,
- REC_LOG_REPLAY, REC_DONE };
-extern void recphase(int nactive, int state);
-extern void waituntilphase(int phase);
-extern void inactive();
-extern pthread_mutex_t rec_mu;
-extern logreplay::info_type *rec_log_infos;
-extern kvepoch_t rec_ckp_min_epoch;
-extern kvepoch_t rec_ckp_max_epoch;
-extern kvepoch_t rec_replay_min_epoch;
-extern kvepoch_t rec_replay_max_epoch;
-extern kvepoch_t rec_replay_min_quiescent_last_epoch;
-
-
-inline void loginfo::acquire() {
- test_and_set_acquire(&f_.lock_);
-}
-
-inline void loginfo::release() {
- test_and_set_release(&f_.lock_);
-}
-
-inline kvepoch_t loginfo::flushed_epoch() const {
- return flushed_epoch_;
-}
-
-inline bool loginfo::quiescent() const {
- return quiescent_epoch_ && quiescent_epoch_ == flushed_epoch_;
-}
-
-inline int logset::size() const {
- return li_[-1].lsi_.size_;
-}
-
-inline loginfo& logset::log(int i) {
- assert(unsigned(i) < unsigned(size()));
- return li_[i];
-}
-
-inline const loginfo& logset::log(int i) const {
- assert(unsigned(i) < unsigned(size()));
- return li_[i];
-}
-
-
-template <typename R>
-struct row_delta_marker : public row_marker {
- kvtimestamp_t prev_ts_;
- R *prev_;
- char s_[0];
-};
-
-template <typename R>
-inline bool row_is_delta_marker(const R* row) {
- if (row_is_marker(row)) {
- const row_marker* m =
- reinterpret_cast<const row_marker *>(row->col(0).s);
- return m->marker_type_ == m->mt_delta;
- } else
- return false;
-}
-
-template <typename R>
-inline row_delta_marker<R>* row_get_delta_marker(const R* row, bool force = false) {
- (void) force;
- assert(force || row_is_delta_marker(row));
- return reinterpret_cast<row_delta_marker<R>*>
- (const_cast<char*>(row->col(0).s));
-}
-
-#endif
diff --git a/masstree.hh b/masstree.hh
index 3690076..955a5bc 100644
--- a/masstree.hh
+++ b/masstree.hh
@@ -18,10 +18,12 @@
#include "compiler.hh"
#include "str.hh"
#include "ksearch.hh"
+#include "kvthread.hh"
namespace Masstree {
using lcdf::Str;
using lcdf::String;
+typedef void (*destroy_value_cb_func)(void *);
class key_unparse_printable_string;
template <typename T> class value_print;
@@ -31,11 +33,11 @@ template <int LW = 15, int IW = LW> struct nodeparams {
static constexpr int internode_width = IW;
static constexpr bool concurrent = true;
static constexpr bool prefetch = true;
- static constexpr int bound_method = bound_method_binary;
+ static constexpr int bound_method = bound_method_fast;
static constexpr int debug_level = 0;
typedef uint64_t ikey_type;
- typedef uint32_t nodeversion_value_type;
- static constexpr bool need_phantom_epoch = true;
+ typedef uint64_t nodeversion_value_type;
+ static constexpr bool need_phantom_epoch = false;
typedef uint64_t phantom_epoch_type;
static constexpr ssize_t print_max_indent_depth = 12;
typedef key_unparse_printable_string key_unparse_type;
@@ -54,6 +56,8 @@ template <typename P> class basic_table;
template <typename P> class unlocked_tcursor;
template <typename P> class tcursor;
+template <bool CONST_ITERATOR, bool FORWARD, typename P> class MasstreeIterator;
+
template <typename P>
class basic_table {
public:
@@ -64,6 +68,18 @@ class basic_table {
typedef typename P::threadinfo_type threadinfo;
typedef unlocked_tcursor<P> unlocked_cursor_type;
typedef tcursor<P> cursor_type;
+ typedef MasstreeIterator<false, true, P> ForwardIterator;
+ typedef MasstreeIterator<false, false, P> ReverseIterator;
+
+ void find(const uint8_t* key, const uint32_t key_len, void*& output, bool& result, const uint32_t& pid) const;
+
+ void iteratorScan(const char * keybuf, uint32_t keylen, const bool& matchKey, void* const& it, const bool& forwardDirection,
+ bool& result, const uint32_t& pid);
+
+ void *insert(const uint8_t* key, const uint32_t key_len, void* const& entry, bool& result, const uint32_t& pid);
+ void *remove(uint8_t const *const &key, uint32_t length, bool &result, const uint32_t &pid);
+ bool init(const uint16_t keyLength, const std::string& name, destroy_value_cb_func destroyValue_CB = NULL);
+ int getMemtagMaxSize(enum memtag tag);
inline basic_table();
@@ -72,6 +88,7 @@ class basic_table {
inline node_type* root() const;
inline node_type* fix_root();
+ inline node_type** root_ref() { return &root_; }
bool get(Str key, value_type& value, threadinfo& ti) const;
@@ -84,6 +101,13 @@ class basic_table {
private:
node_type* root_;
+ uint16_t keyLength_ = 0;
+ std::string name_;
+ destroy_value_cb_func destroyValue_CB_ = nullptr;
+
+ template<typename H, typename F>
+ int scan(H helper, void const *const &firstKey, unsigned int firstKeyLen,
+ bool matchFirstKey, F &scanner, threadinfo &ti) const;
template <typename H, typename F>
int scan(H helper, Str firstkey, bool matchfirst,
@@ -91,6 +115,13 @@ class basic_table {
friend class unlocked_tcursor<P>;
friend class tcursor<P>;
+
+ friend class MasstreeIterator<true, true, P>;
+ friend class MasstreeIterator<true, false, P>;
+ friend class MasstreeIterator<false, true, P>;
+ friend class MasstreeIterator<false, false, P>;
+
+ DECLARE_CLASS_LOGGER()
};
} // namespace Masstree
diff --git a/masstree_config.h b/masstree_config.h
new file mode 100644
index 0000000..d5a3a17
--- /dev/null
+++ b/masstree_config.h
@@ -0,0 +1,66 @@
+#ifndef MASSTREE_CONFIG_H
+#define MASSTREE_CONFIG_H
+
+#define HAVE_CXX_TEMPLATE_ALIAS 1
+#define HAVE_INT64_T_IS_LONG 1
+#define HAVE_SIZE_T_IS_UNSIGNED_LONG 1
+#define HAVE_STD_HASH 1
+#define HAVE_STD_IS_TRIVIALLY_COPYABLE 1
+#define HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE 1
+#define HAVE_SUPERPAGE 1
+#define HAVE_TYPE_TRAITS 1
+#define HAVE_UNALIGNED_ACCESS 0
+#define HAVE___BUILTIN_CLZ 1
+#define HAVE___BUILTIN_CLZL 1
+#define HAVE___BUILTIN_CLZLL 1
+#define HAVE___BUILTIN_CTZ 1
+#define HAVE___BUILTIN_CTZL 1
+#define HAVE___BUILTIN_CTZLL 1
+#define HAVE___HAS_TRIVIAL_COPY 1
+#define HAVE___HAS_TRIVIAL_DESTRUCTOR 1
+#define HAVE___SYNC_BOOL_COMPARE_AND_SWAP 1
+#define HAVE___SYNC_BOOL_COMPARE_AND_SWAP_8 1
+#define HAVE___SYNC_FETCH_AND_ADD 1
+#define HAVE___SYNC_FETCH_AND_ADD_8 1
+#define HAVE___SYNC_FETCH_AND_OR 1
+#define HAVE___SYNC_FETCH_AND_OR_8 1
+#define HAVE___SYNC_VAL_COMPARE_AND_SWAP 1
+#define HAVE___SYNC_VAL_COMPARE_AND_SWAP_8 1
+
+/* Maximum key length */
+#define MASSTREE_MAXKEYLEN 256U
+
+#define SIZEOF_INT 4
+#define SIZEOF_LONG 8
+#define SIZEOF_LONG_LONG 8
+#define SIZEOF_SHORT 2
+#define WORDS_BIGENDIAN_SET 1
+
+#define MASSTREE_OBSOLETE_CODE 1
+
+#define masstree_invariant(x, ...) assert(x)
+#define masstree_precondition(x, ...) assert(x)
+
+
+
+#ifndef invariant
+#define invariant masstree_invariant
+#endif
+#ifndef precondition
+#define precondition masstree_precondition
+#endif
+
+#ifndef CACHE_LINE_SIZE
+#define CACHE_LINE_SIZE 64
+#endif
+
+#ifndef PRIu64
+#if HAVE_SIZE_T_IS_UNSIGNED_LONG_LONG
+#define PRIu64 "llu"
+#else
+#define PRIu64 "lu"
+#endif
+#endif
+
+
+#endif // MASSTREE_CONFIG_H
diff --git a/masstree_get.hh b/masstree_get.hh
index 819ee8b..54d0f4b 100644
--- a/masstree_get.hh
+++ b/masstree_get.hh
@@ -17,6 +17,8 @@
#define MASSTREE_GET_HH
#include "masstree_tcursor.hh"
#include "masstree_key.hh"
+#include "masstree_remove.hh"
+
namespace Masstree {
template <typename P>
@@ -86,29 +88,39 @@ bool tcursor<P>::find_locked(threadinfo& ti)
fence();
kx_ = leaf<P>::bound_type::lower(ka_, *n_);
if (kx_.p >= 0) {
+ // Key slice (ikey) was found and it is stored in kx_.i
leafvalue<P> lv = n_->lv_[kx_.p];
lv.prefetch(n_->keylenx_[kx_.p]);
state_ = n_->ksuf_matches(kx_.p, ka_);
+ // lv.layer() should be the root of the lower layer but might not be the root anymore. This case handled later in "else if (unlikely(state_ < 0)) {"
if (state_ < 0 && !n_->has_changed(v) && lv.layer()->is_root()) {
+ // Going down to lower layer as the ikey in this layer matches. --> The full key prefix matches (not only this slice) while suffixes don't match
+ // (-state_) == the size of the ikey (in our case, 8 bytes)
ka_.shift_by(-state_);
+ // Change the current cursor root to point to the root of the lower layer and continue the search for the key from there
root = lv.layer();
goto retry;
}
} else
state_ = 0;
+ // n_ now points to the leaf where the key exists or should be added
n_->lock(v, ti.lock_fence(tc_leaf_lock));
if (n_->has_changed(v) || n_->permutation() != perm) {
ti.mark(threadcounter(tc_stable_leaf_insert + n_->simple_has_split(v)));
n_->unlock();
+ // If the node has split, look for the leaf that should hold the key (if exists) by traversing between the leaves in the same layer (using next pointer)
n_ = n_->advance_to_key(ka_, v, ti);
goto forward;
} else if (unlikely(state_ < 0)) {
+ // n_->lv_[kx_.p] is a node in lower layer and should be a root but it is not anymore. It means that it already has a parent in lower layer. So the value is replaced with the lower layer node's parent.
+ // The right thing to do was to replace value with the root of the lower layer. instead, it is done in iterations (level by level)
ka_.shift_by(-state_);
n_->lv_[kx_.p] = root = n_->lv_[kx_.p].layer()->maybe_parent();
n_->unlock();
goto retry;
} else if (unlikely(n_->deleted_layer())) {
+ // Layer was deleted. restart scan from table's root.
ka_.unshift_all();
root = const_cast<node_base<P>*>(root_);
n_->unlock();
diff --git a/masstree_insert.hh b/masstree_insert.hh
index 670f12d..63e1f76 100644
--- a/masstree_insert.hh
+++ b/masstree_insert.hh
@@ -17,24 +17,30 @@
#define MASSTREE_INSERT_HH
#include "masstree_get.hh"
#include "masstree_split.hh"
+
namespace Masstree {
template <typename P>
-bool tcursor<P>::find_insert(threadinfo& ti)
+bool tcursor<P>::find_insert(threadinfo& ti, bool & found)
{
+ found = false;
find_locked(ti);
original_n_ = n_;
original_v_ = n_->full_unlocked_version_value();
// maybe we found it
- if (state_)
+ if (state_) {
+ found = true;
return true;
+ }
// otherwise mark as inserted but not present
state_ = 2;
// maybe we need a new layer
if (kx_.p >= 0)
+ /* Key with same prefix (of size ikey_size * layer height) was found but with different suffix. we need to create at least one new layer that will contain both keys
+ n_.lv[kx_.p] will be replaced with pointer to a new lower layer and n_.keylenx_[kx.p] will be replaced with 128 (defined by layer_keylenx constant) */
return make_new_layer(ti);
// mark insertion if we are changing modification state
@@ -44,17 +50,26 @@ bool tcursor<P>::find_insert(threadinfo& ti)
n_->modstate_ = leaf<P>::modstate_insert;
}
- // try inserting into this node
+ // try inserting into this leaf
if (n_->size() < n_->width) {
kx_.p = permuter_type(n_->permutation_).back();
- // don't inappropriately reuse position 0, which holds the ikey_bound
+ /* don't inappropriately reuse position 0, which holds the ikey_bound. If this is the case, make_split will handle it.
+ Before leaf's first split, ikey0_[0] (ikey_bound) might not contain the lower ikey value in the leaf as it doesn't have a parent yet. After the first split,
+ The new leaf ikey0_[0] will contain the lower ikey value in the right leaf and will be used as a boundary in the parent internode.
+ In case the key in slot 0 will be deleted, the ikey0_[0] ikey value will be kept (to avoid changing the parent's boundary) and entry 0 wont be used anymore.
+ This rule has 2 exceptions:
+ 1. If leaf is the most left leaf in the btree which means ikey0_[0] is not used as a boundary. (!n_->prev_)
+ 2. If a new key, with ikey == ikey0_[0], is added. In this case, we can re-use slot 0 as we won't change the tree's structure. (n_->ikey_bound() == ka_.ikey()) */
if (likely(kx_.p != 0) || !n_->prev_ || n_->ikey_bound() == ka_.ikey()) {
- n_->assign(kx_.p, ka_, ti);
- return false;
+ // if n_->assign fails, we dont have enough space to place the suffix and we failed while allocating larger ksuffix.
+ bool res = n_->assign(kx_.p, ka_, ti);
+ if (!res)
+ ti.set_last_error(MT_MERR_FIND_INSERT_ASSIGN_SUFFIX);
+ return res;
}
}
- // otherwise must split
+ // otherwise we might need to split
return make_split(ti);
}
@@ -65,10 +80,16 @@ bool tcursor<P>::make_new_layer(threadinfo& ti) {
int kcmp = oka.compare(ka_);
// Create a twig of nodes until the suffixes diverge
+ // For each ikey_size bytes (currently 8) that matches in both key's suffixes, we will need to create a new layer
leaf_type* twig_head = n_;
leaf_type* twig_tail = n_;
+ leaf_type* nl = nullptr;
while (kcmp == 0) {
- leaf_type* nl = leaf_type::make_root(0, twig_tail, ti);
+ nl = leaf_type::make_root(0, twig_tail, ti);
+ if (!nl) {
+ ti.set_last_error(MT_MERR_MAKE_NEW_LAYER_LEAF_ALLOC_1);
+ goto make_new_layer_cleanup;
+ }
nl->assign_initialize_for_layer(0, oka);
if (twig_head != n_)
twig_tail->lv_[0] = nl;
@@ -76,9 +97,12 @@ bool tcursor<P>::make_new_layer(threadinfo& ti) {
twig_head = nl;
nl->permutation_ = permuter_type::make_sorted(1);
twig_tail = nl;
+#ifndef MASSTREE_OBSOLETE_CODE
new_nodes_.emplace_back(nl, nl->full_unlocked_version_value());
+#endif
oka.shift();
ka_.shift();
+ // Compare the ikey only. if ikey matches and one or more of the suffixes != 0, compare using suffix size
kcmp = oka.compare(ka_);
}
@@ -90,9 +114,24 @@ bool tcursor<P>::make_new_layer(threadinfo& ti) {
+ n_->iksuf_[0].overhead(n_->width);
else
ksufsize = 0;
- leaf_type *nl = leaf_type::make_root(ksufsize, twig_tail, ti);
- nl->assign_initialize(0, kcmp < 0 ? oka : ka_, ti);
- nl->assign_initialize(1, kcmp < 0 ? ka_ : oka, ti);
+ nl = leaf_type::make_root(ksufsize, twig_tail, ti);
+ if (!nl) {
+ ti.set_last_error(MT_MERR_MAKE_NEW_LAYER_LEAF_ALLOC_2);
+ goto make_new_layer_cleanup;
+ }
+ // Even though the total ksuffix size was already provided to make_root, more memory might be allocated in assign_initialize calls
+ // as leaf internal suffix is bounded by 128 (+ 64 alignment).
+ // We will hit this issue (for sure) if ka_.suffix_length() + oka.suffix_length() > 192, but might hit it also when ka_.suffix_length() + oka.suffix_length() > 128.
+ if (!nl->assign_initialize(0, kcmp < 0 ? oka : ka_, ti)) {
+ ti.set_last_error(MT_MERR_MAKE_NEW_LAYER_KSUFFIX_ALLOC_1);
+ goto make_new_layer_cleanup;
+ }
+
+ if (!nl->assign_initialize(1, kcmp < 0 ? ka_ : oka, ti)) {
+ ti.set_last_error(MT_MERR_MAKE_NEW_LAYER_KSUFFIX_ALLOC_2);
+ goto make_new_layer_cleanup;
+ }
+
nl->lv_[kcmp > 0] = n_->lv_[kx_.p];
nl->lock(*nl, ti.lock_fence(tc_leaf_lock));
if (kcmp < 0)
@@ -122,6 +161,33 @@ bool tcursor<P>::make_new_layer(threadinfo& ti) {
n_->unlock();
n_ = nl;
kx_.i = kx_.p = kcmp < 0;
+ return true;
+
+make_new_layer_cleanup:
+ // n_ was not updated yet. It contains the original key (without any change). it will be unlocked later on (in lp.finish)
+ if (nl) {
+ // nl is not connected yet to twig_tail. handle it seperatly
+ nl->deallocate(ti);
+ nl = nullptr;
+ }
+
+ // Leafs in leaf list (starts from twig_head) has no suffix. In addition, they are not connected to the masstree yet, so we dont need to hold any locks.
+ if (twig_head != n_) {
+ while (twig_head) {
+ masstree_invariant(!twig_head->ksuf_);
+ masstree_invariant(twig_head->size() == 1);
+ masstree_invariant(twig_head->is_layer(0));
+ masstree_invariant(twig_head->stable_annotated(ti.stable_fence()).is_root());
+ leaf_type *next_layer_leaf = (leaf_type *)twig_head->lv_[0].layer();
+ twig_head->lv_[0] = nullptr;
+ // Remove it directly. no need to use rcu.
+ ti.deallocate(twig_head, sizeof(*twig_head) /* Being ignored */, memtag_masstree_leaf);
+ // Stop if we just finished to handle last leaf in list (twig_tail).
+ // Validating that next_layer_leaf != null wont work as twig_tail->lv_[0] == twig_tail.
+ twig_head = (twig_head == twig_tail) ? nullptr : next_layer_leaf;
+ }
+ }
+
return false;
}
@@ -140,15 +206,20 @@ inline void tcursor<P>::finish(int state, threadinfo& ti)
{
if (state < 0 && state_ == 1) {
if (finish_remove(ti))
- return;
+ goto clean_ti;
} else if (state > 0 && state_ == 2)
finish_insert();
// we finally know this!
if (n_ == original_n_)
updated_v_ = n_->full_unlocked_version_value();
+#ifndef MASSTREE_OBSOLETE_CODE
else
new_nodes_.emplace_back(n_, n_->full_unlocked_version_value());
+#endif
n_->unlock();
+
+clean_ti:
+ ti.add_nodes_to_gc();
}
} // namespace Masstree
diff --git a/masstree_key.hh b/masstree_key.hh
index 77fbd1d..72e3ee0 100644
--- a/masstree_key.hh
+++ b/masstree_key.hh
@@ -182,6 +182,9 @@ class key {
int prefix_length() const {
return s_ - first_;
}
+ int full_length() const {
+ return s_ - first_ + len_;
+ }
Str full_string() const {
return Str(first_, s_ + len_);
}
@@ -210,6 +213,12 @@ class key {
void assign_store_length(int len) {
len_ = len;
}
+
+ /* We know that the len_ after the unshift is at least 9 because:
+ 1. Before the unshift, len >=1
+ 2. Unshift size is 8
+ As we didn't find the search key in the current layer, we are going to search for the unshift version of it in the upper layer. It means that the rest of the key can be ignored,because
+ the version key that will be found is our target key */
void unshift() {
masstree_precondition(is_shifted());
s_ -= ikey_size;
diff --git a/masstree_print.hh b/masstree_print.hh
deleted file mode 100644
index fbe924c..0000000
--- a/masstree_print.hh
+++ /dev/null
@@ -1,186 +0,0 @@
-/* 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_PRINT_HH
-#define MASSTREE_PRINT_HH
-#include "masstree_struct.hh"
-#include <stdio.h>
-#include <inttypes.h>
-
-namespace Masstree {
-
-class key_unparse_printable_string {
-public:
- template <typename K>
- static int unparse_key(key<K> key, char* buf, int buflen) {
- String s = key.unparse().printable();
- int cplen = std::min(s.length(), buflen);
- memcpy(buf, s.data(), cplen);
- return cplen;
- }
-};
-
-template <typename T>
-class value_print {
- public:
- static void print(T value, FILE* f, const char* prefix,
- int indent, Str key, kvtimestamp_t initial_timestamp,
- char* suffix) {
- value->print(f, prefix, indent, key, initial_timestamp, suffix);
- }
-};
-
-template <>
-class value_print<unsigned char*> {
- public:
- static void print(unsigned char* value, FILE* f, const char* prefix,
- int indent, Str key, kvtimestamp_t,
- char* suffix) {
- fprintf(f, "%s%*s%.*s = %p%s\n",
- prefix, indent, "", key.len, key.s, value, suffix);
- }
-};
-
-template <>
-class value_print<uint64_t> {
- public:
- static void print(uint64_t value, FILE* f, const char* prefix,
- int indent, Str key, kvtimestamp_t,
- char* suffix) {
- fprintf(f, "%s%*s%.*s = %" PRIu64 "%s\n",
- prefix, indent, "", key.len, key.s, value, suffix);
- }
-};
-
-template <typename P>
-void leaf<P>::print(FILE *f, const char *prefix, int depth, int kdepth) const
-{
- f = f ? f : stderr;
- prefix = prefix ? prefix : "";
- typename node_base<P>::nodeversion_type v;
- permuter_type perm;
- do {
- v = *this;
- fence();
- perm = permutation_;
- } while (this->has_changed(v));
- int indent = 2 * depth;
- if (depth > P::print_max_indent_depth && P::print_max_indent_depth > 0)
- indent = 2 * P::print_max_indent_depth;
-
- {
- char buf[1024];
- int l = 0;
- if (ksuf_ && extrasize64_ < -1)
- l = snprintf(buf, sizeof(buf), " [ksuf i%dx%d]", -extrasize64_ - 1, (int) ksuf_->capacity() / 64);
- else if (ksuf_)
- l = snprintf(buf, sizeof(buf), " [ksuf x%d]", (int) ksuf_->capacity() / 64);
- else if (extrasize64_)
- l = snprintf(buf, sizeof(buf), " [ksuf i%d]", extrasize64_);
- if (P::debug_level > 0) {
- kvtimestamp_t cts = timestamp_sub(created_at_[0], initial_timestamp);
- l += snprintf(&buf[l], sizeof(buf) - l, " @" PRIKVTSPARTS, KVTS_HIGHPART(cts), KVTS_LOWPART(cts));
- }
- static const char* const modstates[] = {"", "-", "D"};
- fprintf(f, "%s%*sleaf %p: %d %s, version %" PRIx64 "%s, permutation %s, parent %p, prev %p, next %p%.*s\n",
- prefix, indent, "", this,
- perm.size(), perm.size() == 1 ? "key" : "keys",
- (uint64_t) v.version_value(),
- modstate_ <= 2 ? modstates[modstate_] : "??",
- perm.unparse().c_str(),
- parent_, prev_, next_.ptr,
- l, buf);
- }
-
- if (v.deleted() || (perm[0] != 0 && prev_))
- fprintf(f, "%s%*s%s = [] #0\n", prefix, indent + 2, "", key_type(ikey_bound()).unparse().c_str());
-
- char keybuf[MASSTREE_MAXKEYLEN];
- char xbuf[15];
- for (int idx = 0; idx < perm.size(); ++idx) {
- int p = perm[idx];
- int l = P::key_unparse_type::unparse_key(this->get_key(p), keybuf, sizeof(keybuf));
- sprintf(xbuf, " #%x/%d", p, keylenx_[p]);
- leafvalue_type lv = lv_[p];
- if (this->has_changed(v)) {
- fprintf(f, "%s%*s[NODE CHANGED]\n", prefix, indent + 2, "");
- break;
- } else if (!lv)
- fprintf(f, "%s%*s%.*s = []%s\n", prefix, indent + 2, "", l, keybuf, xbuf);
- else if (is_layer(p)) {
- fprintf(f, "%s%*s%.*s = SUBTREE%s\n", prefix, indent + 2, "", l, keybuf, xbuf);
- node_base<P> *n = lv.layer();
- while (!n->is_root())
- n = n->maybe_parent();
- n->print(f, prefix, depth + 1, kdepth + key_type::ikey_size);
- } else {
- typename P::value_type tvx = lv.value();
- P::value_print_type::print(tvx, f, prefix, indent + 2, Str(keybuf, l), initial_timestamp, xbuf);
- }
- }
-
- if (v.deleted())
- fprintf(f, "%s%*s[DELETED]\n", prefix, indent + 2, "");
-}
-
-template <typename P>
-void internode<P>::print(FILE* f, const char* prefix, int depth, int kdepth) const
-{
- f = f ? f : stderr;
- prefix = prefix ? prefix : "";
- internode<P> copy(*this);
- for (int i = 0; i < 100 && (copy.has_changed(*this) || this->inserting() || this->splitting()); ++i)
- memcpy(&copy, this, sizeof(copy));
- int indent = 2 * depth;
- if (depth > P::print_max_indent_depth && P::print_max_indent_depth > 0)
- indent = 2 * P::print_max_indent_depth;
-
- {
- char buf[1024];
- int l = 0;
- if (P::debug_level > 0) {
- kvtimestamp_t cts = timestamp_sub(created_at_[0], initial_timestamp);
- l = snprintf(buf, sizeof(buf), " @" PRIKVTSPARTS, KVTS_HIGHPART(cts), KVTS_LOWPART(cts));
- }
- fprintf(f, "%s%*sinternode %p[%u]%s: %d keys, version %" PRIx64 ", parent %p%.*s\n",
- prefix, indent, "", this,
- height_, this->deleted() ? " [DELETED]" : "",
- copy.size(), (uint64_t) copy.version_value(), copy.parent_,
- l, buf);
- }
-
- char keybuf[MASSTREE_MAXKEYLEN];
- for (int p = 0; p < copy.size(); ++p) {
- if (copy.child_[p])
- copy.child_[p]->print(f, prefix, depth + 1, kdepth);
- else
- fprintf(f, "%s%*s[]\n", prefix, indent, "");
- int l = P::key_unparse_type::unparse_key(copy.get_key(p), keybuf, sizeof(keybuf));
- fprintf(f, "%s%*s%p[%u.%d] %.*s\n",
- prefix, indent, "", this, height_, p, l, keybuf);
- }
- if (copy.child_[copy.size()])
- copy.child_[copy.size()]->print(f, prefix, depth + 1, kdepth);
- else
- fprintf(f, "%s%*s[]\n", prefix, indent, "");
-}
-
-template <typename P>
-void basic_table<P>::print(FILE* f) const {
- root_->print(f ? f : stdout, "", 0, 0);
-}
-
-} // namespace Masstree
-#endif
diff --git a/masstree_remove.hh b/masstree_remove.hh
index b1a749d..44c9f4d 100644
--- a/masstree_remove.hh
+++ b/masstree_remove.hh
@@ -120,10 +120,11 @@ struct gc_layer_rcu_callback : public P::threadinfo_type::mrcu_callback {
node_base<P>* root_;
int len_;
char s_[0];
- gc_layer_rcu_callback(node_base<P>* root, Str prefix)
+ gc_layer_rcu_callback(node_base<P>* root, Str prefix, size_t size = 0)
: root_(root), len_(prefix.length()) {
memcpy(s_, prefix.data(), len_);
}
+ size_t operator()(bool drop_index) { return 0; }
void operator()(threadinfo& ti);
size_t size() const {
return len_ + sizeof(*this);
@@ -143,8 +144,9 @@ void gc_layer_rcu_callback<P>::operator()(threadinfo& ti)
if (!do_remove || !lp.finish_remove(ti)) {
lp.n_->unlock();
}
- ti.deallocate(this, size(), memtag_masstree_gc);
}
+ ti.deallocate(this, size(), memtag_masstree_gc);
+ ti.add_nodes_to_gc();
}
template <typename P>
@@ -171,18 +173,20 @@ bool tcursor<P>::finish_remove(threadinfo& ti) {
if (perm.size()) {
return false;
} else {
- return remove_leaf(n_, root_, ka_.prefix_string(), ti);
+ return remove_leaf(n_, root_ref_, ka_.prefix_string(), ti);
}
}
template <typename P>
-bool tcursor<P>::remove_leaf(leaf_type* leaf, node_type* root,
+bool tcursor<P>::remove_leaf(leaf_type* leaf, node_type** root_ref,
Str prefix, threadinfo& ti)
{
if (!leaf->prev_) {
if (!leaf->next_.ptr && !prefix.empty()) {
- gc_layer_rcu_callback<P>::make(root, prefix, ti);
+ // Leaf doesn't hold any keys, not in the highest layer and has no neighbors --> entire layer can be destroyed
+ gc_layer_rcu_callback_ng<P>::make(root_ref, prefix, ti);
}
+ // Leaf has neighbor to the right (next) or leaf in the highest layer. do nothing
return false;
}
@@ -208,12 +212,21 @@ bool tcursor<P>::remove_leaf(leaf_type* leaf, node_type* root,
// Unlink leaf from doubly-linked leaf list
btree_leaflink<leaf_type>::unlink(leaf);
+ // leaf->prev_ != NULL
+ leaf_type *prev = leaf->prev_;
+ if (!prev->prev_ && !prev->next_.ptr && prev->size() == 0 && !prefix.empty() ) {
+ // After removing the leaf, only the most left leaf remains (single leaf). We can remove the layer as the most left leaf
+ // doesn't hold any keys and layer is not the highest one.
+ gc_layer_rcu_callback_ng<P>::make(root_ref, prefix, ti);
+ }
+
// Remove leaf from tree, collapse trivial chains, and rewrite
// ikey bounds.
ikey_type ikey = leaf->ikey_bound();
node_type* n = leaf;
node_type* replacement = nullptr;
+ // Leaf has neighbor to the left (leaf->prev_ != NULL) --> it has a parent
while (true) {
internode_type *p = n->locked_parent(ti);
p->mark_insert();
@@ -228,6 +241,7 @@ bool tcursor<P>::remove_leaf(leaf_type* leaf, node_type* root,
if (replacement) {
replacement->set_parent(p);
} else if (kp > 0) {
+ // No leaf replacement for the removed leaf. Remove the leaf from parent's key boundary and child arrays
p->shift_down(kp - 1, kp, p->nkeys_ - kp);
--p->nkeys_;
}
@@ -282,8 +296,10 @@ struct destroy_rcu_callback : public P::threadinfo_type::mrcu_callback {
typedef typename node_base<P>::internode_type internode_type;
node_base<P>* root_;
int count_;
- destroy_rcu_callback(node_base<P>* root)
- : root_(root), count_(0) {
+ destroy_value_cb_func destroyValueCB_ = nullptr;
+
+ destroy_rcu_callback(node_base<P>* root, destroy_value_cb_func func)
+ : root_(root), count_(0), destroyValueCB_(func) {
}
void operator()(threadinfo& ti);
static void make(node_base<P>* root, Str prefix, threadinfo& ti);
@@ -338,8 +354,12 @@ void destroy_rcu_callback<P>::operator()(threadinfo& ti) {
typename leaf_type::permuter_type perm = l->permutation();
for (int i = 0; i != l->size(); ++i) {
int p = perm[i];
- if (l->is_layer(p))
+ if (l->is_layer(p)) {
enqueue(l->lv_[p].layer(), tailp);
+ } else {
+ if (destroyValueCB_)
+ destroyValueCB_(l->lv_[p].value());
+ }
}
l->deallocate(ti);
} else {
@@ -358,7 +378,7 @@ template <typename P>
void basic_table<P>::destroy(threadinfo& ti) {
if (root_) {
void* data = ti.allocate(sizeof(destroy_rcu_callback<P>), memtag_masstree_gc);
- destroy_rcu_callback<P>* cb = new(data) destroy_rcu_callback<P>(root_);
+ destroy_rcu_callback<P>* cb = new(data) destroy_rcu_callback<P>(root_, destroyValue_CB_);
ti.rcu_register(cb);
root_ = 0;
}
diff --git a/masstree_scan.hh b/masstree_scan.hh
index bccf7b6..b81cd9e 100644
--- a/masstree_scan.hh
+++ b/masstree_scan.hh
@@ -61,13 +61,15 @@ class scanstackelt {
}
template <typename H>
- int find_initial(H& helper, key_type& ka, bool emit_equal,
+ int find_initial(H& helper, key_type& ka, bool emit_equal, bool& found,
leafvalue_type& entry, threadinfo& ti);
template <typename H>
int find_retry(H& helper, key_type& ka, threadinfo& ti);
template <typename H>
int find_next(H& helper, key_type& ka, leafvalue_type& entry);
+ // Return the location of the key in key's related arrays (ikey0_, lv_ and keylenx_)
+ // The unsigned trick is to handle ki_ < 0;
int kp() const {
if (unsigned(ki_) < unsigned(perm_.size()))
return perm_[ki_];
@@ -76,6 +78,7 @@ class scanstackelt {
}
template <typename PX> friend class basic_table;
+ template<bool CONST_ITERATOR, bool FORWARD, typename PX> friend class MasstreeIterator;
};
struct forward_scan_helper {
@@ -85,6 +88,7 @@ struct forward_scan_helper {
template <typename K> bool is_duplicate(const K &k,
typename K::ikey_type ikey,
int keylenx) const {
+ // k.ikey < ikey --> k.compare(ikey, keylenx) < 0
return k.compare(ikey, keylenx) >= 0;
}
template <typename K, typename N> int lower(const K &k, const N *n) const {
@@ -129,17 +133,22 @@ struct reverse_scan_helper {
template <typename K> bool is_duplicate(const K &k,
typename K::ikey_type ikey,
int keylenx) const {
+ // k.ikey < ikey --> k.compare(ikey, keylenx) < 0
return k.compare(ikey, keylenx) <= 0 && !upper_bound_;
}
template <typename K, typename N> int lower(const K &k, const N *n) const {
if (upper_bound_)
return n->size() - 1;
+ // If kx.p < 0, the provided ikey was not found. It means that kx.i is pointing to an index which it's ikey is larger than our ikey.
+ // It also means that the ikey in index (kx.i - 1) (if exists) is the largest ikey in this node that smaller than our key.
key_indexed_position kx = N::bound_type::lower_by(k, *n, *n);
return kx.i - (kx.p < 0);
}
template <typename K, typename N>
key_indexed_position lower_with_position(const K &k, const N *n) const {
key_indexed_position kx = N::bound_type::lower_by(k, *n, *n);
+ // If kx.p < 0, the provided ikey was not found. It means that kx.i is pointing to an index which it's ikey is larger than our ikey.
+ // It also means that the ikey in index (kx.i - 1) (if exists) is the largest ikey in this node that smaller than our key.
kx.i -= kx.p < 0;
return kx;
}
@@ -151,6 +160,7 @@ struct reverse_scan_helper {
}
template <typename N, typename K>
N *advance(const N *n, K &k) const {
+ // Change the ikey of our search key to be the lowest ikey in the leaf. If this is the most left leaf, it could be any of the values that are currently or used to be in this leaf.
k.assign_store_ikey(n->ikey_bound());
k.assign_store_length(0);
return n->prev_;
@@ -178,7 +188,7 @@ struct reverse_scan_helper {
template <typename P> template <typename H>
-int scanstackelt<P>::find_initial(H& helper, key_type& ka, bool emit_equal,
+int scanstackelt<P>::find_initial(H& helper, key_type& ka, bool emit_equal, bool& found,
leafvalue_type& entry, threadinfo& ti)
{
key_indexed_position kx;
@@ -186,27 +196,36 @@ int scanstackelt<P>::find_initial(H& helper, key_type& ka, bool emit_equal,
char suffixbuf[MASSTREE_MAXKEYLEN];
Str suffix;
+ // Goes down to the leaf
retry_root:
n_ = root_->reach_leaf(ka, v_, ti);
retry_node:
if (v_.deleted())
goto retry_root;
+
+ // Finds the key inside the leaf
n_->prefetch();
perm_ = n_->permutation();
kx = helper.lower_with_position(ka, this);
+ // kx.i - index of the key inside the permutation. If the key was not found, index of the closest key (depends on the helper's implementation).
+ // kx.p - position of the given key in the child's array (perm[kx.i]). -1 if was not found
+
+ // If a valid position for the key is found, it is being recorded
if (kx.p >= 0) {
keylenx = n_->keylenx_[kx.p];
fence();
entry = n_->lv_[kx.p];
entry.prefetch(keylenx);
if (n_->keylenx_has_ksuf(keylenx)) {
+ // There is only one key in the tree with our key's prefix
suffix = n_->ksuf(kx.p);
memcpy(suffixbuf, suffix.s, suffix.len);
suffix.s = suffixbuf;
}
}
+ // If the leaf changes we have to find the new correct leaf and retry
if (n_->has_changed(v_)) {
ti.mark(tc_leaf_retry);
n_ = n_->advance_to_key(ka, v_, ti);
@@ -215,20 +234,38 @@ int scanstackelt<P>::find_initial(H& helper, key_type& ka, bool emit_equal,
ki_ = kx.i;
if (kx.p >= 0) {
+ // Matching ikey was found (--> full prefix matches)
+ // We might have to keep going down since we found the subtree we are interested in
if (n_->keylenx_is_layer(keylenx)) {
+ // The ikey was found and it's value pointing to lower layer. keep the current node and the current layer root so we will be able to return back (used in some corner cases)
node_stack_.push_back(root_);
node_stack_.push_back(n_);
+
+ // Change our local search root to point to the lower layer
root_ = entry.layer();
return scan_down;
} else if (n_->keylenx_has_ksuf(keylenx)) {
+ // Key in the tree has suffix (--> this is the only key in the tree with this prefix)
int ksuf_compare = suffix.compare(ka.suffix());
+ // If ksuf_compare > 0 --> suffix > ka.suffix
+ found = (ksuf_compare == 0);
if (helper.initial_ksuf_match(ksuf_compare, emit_equal)) {
+ /* ikey matches and the suffix comparison matches the helper rules:
+ forward - our key is smaller than the tree's key (--> the current key in tree is the closest upper bounder for our key)
+ reverse - our key is larger than the tree's key (--> the current key in tree is the closest lower bounder for our key)
+ In case the suffixes match, if emit_equal is true, both helpers return true and vice versa */
+
+ // Copy the key that was found in the tree to our key, as we are going to return it to the iterator
+ // IDAN: OPTIMIZATION: found is true, our key suffix fully matches the tree's key suffix. we can optimize the code by not copying the data.
int keylen = ka.assign_store_suffix(suffix);
ka.assign_store_length(keylen);
return scan_emit;
}
- } else if (emit_equal)
+ } else if (emit_equal) {
+ // Tree's key has no suffix and does not point to a lower layer --> the tree's key fully matches our key
+ found = true;
return scan_emit;
+ }
// otherwise, this entry must be skipped
ki_ = helper.next(ki_);
}
@@ -260,36 +297,51 @@ int scanstackelt<P>::find_next(H &helper, key_type &ka, leafvalue_type &entry)
retry_entry:
kp = this->kp();
if (kp >= 0) {
+ // After the call to find_initial, ki (index in leaf) points to ikey in the leaf that best match our request (tightest upper or lower boundary, depends on the helper type)
+ // As kp is valid, we should investigate the current ikey in leaf.
+ // If the ikey points to a lower layer, we need to traverse down with it. otherwise, this is our target key.
ikey_type ikey = n_->ikey0_[kp];
int keylenx = n_->keylenx_[kp];
int keylen = keylenx;
fence();
entry = n_->lv_[kp];
entry.prefetch(keylenx);
- if (n_->keylenx_has_ksuf(keylenx))
+ if (n_->keylenx_has_ksuf(keylenx)) {
keylen = ka.assign_store_suffix(n_->ksuf(kp));
+ masstree_invariant(keylen < (int)MASSTREE_MAXKEYLEN);
+ }
if (n_->has_changed(v_))
goto changed;
+ // Verify that the key that we found meets the criteria
else if (helper.is_duplicate(ka, ikey, keylenx)) {
+ /* The current tree's key doesn't meet the criteria:
+ forward - search key is larger or equal to the tree's key
+ reverse - search key is smaller or equal to the tree's key (if upper_bound_ == true, is_duplicate always return false as any key that we find is our target key)
+ * - equal is not good because if an equal key exists, it should have been already reported
+ usually happens when node was changed in previous iteration. */
ki_ = helper.next(ki_);
goto retry_entry;
}
// We know we can emit the data collected above.
+ // Updating the search key with the ikey from the tree's key. we might return our updated key now or continue search with it in lower layer
ka.assign_store_ikey(ikey);
helper.mark_key_complete();
if (n_->keylenx_is_layer(keylenx)) {
+ // The tree's key is in lower layer. save the current layer root and current node (we might need to return back) and continue the search there
node_stack_.push_back(root_);
node_stack_.push_back(n_);
root_ = entry.layer();
return scan_down;
} else {
+ // Key was found. update our search key length with the tree's key (suffix was already copied)
ka.assign_store_length(keylen);
return scan_emit;
}
}
+ // kp is not valid -> ki is no valid. the target key is not in the current node
if (!n_->has_changed(v_)) {
n_ = helper.advance(n_, ka);
if (!n_) {
@@ -308,7 +360,8 @@ int scanstackelt<P>::find_next(H &helper, key_type &ka, leafvalue_type &entry)
template <typename P> template <typename H, typename F>
int basic_table<P>::scan(H helper,
- Str firstkey, bool emit_firstkey,
+ void const *const &firstKey,
+ unsigned int firstKeyLen, bool emit_firstkey,
F& scanner,
threadinfo& ti) const
{
@@ -319,9 +372,9 @@ int basic_table<P>::scan(H helper,
ikey_type x[(MASSTREE_MAXKEYLEN + sizeof(ikey_type) - 1)/sizeof(ikey_type)];
char s[MASSTREE_MAXKEYLEN];
} keybuf;
- masstree_precondition(firstkey.len <= (int) sizeof(keybuf));
- memcpy(keybuf.s, firstkey.s, firstkey.len);
- key_type ka(keybuf.s, firstkey.len);
+ masstree_precondition(firstKeyLen <= (int) sizeof(keybuf));
+ memcpy(keybuf.s, firstKey, firstKeyLen);
+ key_type ka(keybuf.s, firstKeyLen);
typedef scanstackelt<P> mystack_type;
mystack_type stack;
@@ -330,10 +383,13 @@ int basic_table<P>::scan(H helper,
int scancount = 0;
int state;
+ bool foundGiven = false;
while (1) {
- state = stack.find_initial(helper, ka, emit_firstkey, entry, ti);
- scanner.visit_leaf(stack, ka, ti);
+ state = stack.find_initial(helper, ka, emit_firstkey, foundGiven, entry, ti);
+ //If we want to signal that we have visited this leave, we can do it here
+ //like for example range locks
+// scanner.visit_leaf(stack, ka, ti);
if (state != mystack_type::scan_down)
break;
ka.shift();
@@ -343,8 +399,9 @@ int basic_table<P>::scan(H helper,
switch (state) {
case mystack_type::scan_emit:
++scancount;
- if (!scanner.visit_value(ka, entry.value(), ti))
- goto done;
+ // We can check if the value was already visited. currently commented out
+// if (!scanner.visit_value(ka, entry.value(), ti))
+// goto done;
stack.ki_ = helper.next(stack.ki_);
state = stack.find_next(helper, ka, entry);
break;
@@ -352,12 +409,16 @@ int basic_table<P>::scan(H helper,
case mystack_type::scan_find_next:
find_next:
state = stack.find_next(helper, ka, entry);
- if (state != mystack_type::scan_up)
- scanner.visit_leaf(stack, ka, ti);
+ if (state != mystack_type::scan_up) {
+ //If we want to signal that we have visited this leave, we can do it here
+ //like for example range locks
+// scanner.visit_leaf(stack, ka, ti);
+ }
break;
case mystack_type::scan_up:
do {
+ //the scan is finished when the stack is empty
if (stack.node_stack_.empty())
goto done;
stack.n_ = static_cast<leaf<P>*>(stack.node_stack_.back());
diff --git a/masstree_split.hh b/masstree_split.hh
index ac8d97c..4512441 100644
--- a/masstree_split.hh
+++ b/masstree_split.hh
@@ -46,7 +46,7 @@ leaf<P>::ikey_after_insert(const permuter_type& perm, int i,
The split type is 0 if @a ka went into *this, 1 if the @a ka went into
*@a nr, and 2 for the sequential-order optimization (@a ka went into *@a
- nr and no other keys were moved). */
+ nr and no other keys were moved). if -1, split failed due to memory issue */
template <typename P>
int leaf<P>::split_into(leaf<P>* nr, tcursor<P>* cursor,
ikey_type& split_ikey, threadinfo& ti)
@@ -71,7 +71,8 @@ int leaf<P>::split_into(leaf<P>* nr, tcursor<P>* cursor,
int p = cursor->kx_.i;
if (p == 0 && !this->prev_) {
// reverse-sequential optimization
- mid = 1;
+ // We remove this optimization as it can lead us to empty leaf (In case insertion fails)
+ // mid = 1;
} else if (p == width && !this->next_.ptr) {
// sequential optimization
mid = width;
@@ -100,9 +101,16 @@ int leaf<P>::split_into(leaf<P>* nr, tcursor<P>* cursor,
typename permuter_type::value_type pv = perml.value_from(mid - (p < mid));
for (int x = mid; x <= width; ++x) {
if (x == p) {
- nr->assign_initialize(x - mid, cursor->ka_, ti);
+ if (!nr->assign_initialize(x - mid, cursor->ka_, ti)) {
+ ti.set_last_error(MT_MERR_SPLIT_INTO_ASSIGN_INITALIZE_1);
+ return -1;
+ }
+
} else {
- nr->assign_initialize(x - mid, this, pv & 15, ti);
+ if (!nr->assign_initialize(x - mid, this, pv & 15, ti)) {
+ ti.set_last_error(MT_MERR_SPLIT_INTO_ASSIGN_INITALIZE_2);
+ return -1;
+ }
pv >>= 4;
}
}
@@ -174,6 +182,14 @@ int internode<P>::split_into(internode<P>* nr, int p, ikey_type ka,
}
}
+template <typename P>
+void tcursor<P>::release_internodes(internode_type * internodes_array[], int start, int end, threadinfo& ti) {
+ for (int i = start; i < end; i++) {
+ masstree_invariant(internodes_array[i]);
+ ti.deallocate(internodes_array[i], sizeof(*internodes_array[i]) /* Being ignored */, memtag_masstree_internode);
+ internodes_array[i] = nullptr;
+ }
+}
template <typename P>
bool tcursor<P>::make_split(threadinfo& ti)
@@ -189,16 +205,52 @@ bool tcursor<P>::make_split(threadinfo& ti)
if (kx_.p != 0) {
n_->permutation_ = perm.value();
fence();
- n_->assign(kx_.p, ka_, ti);
+ if (n_->assign(kx_.p, ka_, ti)) {
+ return true;
+ }
+ ti.set_last_error(MT_MERR_MAKE_SPLIT_PERM_EXCHANGE);
+ return false;
+ }
+ }
+
+ bool rc = true;
+
+ int reserved_nodes = 2;
+ internode_type * preallocated_internodes[reserved_nodes + 1] = { 0 };
+ int cur_cache_index = 0;
+
+ for (int i = 0; i < reserved_nodes; i++) {
+ preallocated_internodes[i] = (internode_type *)ti.pool_allocate(MAX_MEMTAG_MASSTREE_INTERNODE_ALLOCATION_SIZE,
+ memtag_masstree_internode);
+ if (!preallocated_internodes[i]) {
+ release_internodes(preallocated_internodes, 0, i, ti);
+ ti.set_last_error(MT_MERR_MAKE_SPLIT_PRE_ALLOC);
return false;
}
}
node_type* child = leaf_type::make(n_->ksuf_used_capacity(), n_->phantom_epoch(), ti);
+ if (!child) {
+ release_internodes(preallocated_internodes, 0, reserved_nodes, ti);
+ ti.set_last_error(MT_MERR_MAKE_SPLIT_LEAF_ALLOC);
+ return false;
+ }
child->assign_version(*n_);
+ child->mark_nonroot();
+ // As n_ is locked, child is locked as well.
ikey_type xikey[2];
+ // Add the new key and spread the keys between the 2 leafs. The new key might be inserted to either one of the leafs. Link to parent will be done later.
int split_type = n_->split_into(static_cast<leaf_type*>(child),
this, xikey[0], ti);
+
+ if (split_type < 0) {
+ // Split failed due to ksuffix memory allocation error (child is not connected to n_ at this stage)
+ release_internodes(preallocated_internodes, 0, reserved_nodes, ti);
+ // child is not visiable yet, so we can deallocate without rcu
+ ((leaf_type *)child)->deallocate(ti);
+ child = nullptr;
+ return false;
+ }
unsigned sense = 0;
node_type* n = n_;
uint32_t height = 0;
@@ -206,17 +258,35 @@ bool tcursor<P>::make_split(threadinfo& ti)
while (true) {
masstree_invariant(!n->concurrent || (n->locked() && child->locked() && (n->isleaf() || n->splitting())));
internode_type *next_child = 0;
-
internode_type *p = n->locked_parent(ti);
+ if (cur_cache_index == reserved_nodes) {
+ // Should never happen with pre-allocated internodes (we should have enough reserved nodes). bad flow is not handled
+ ti.set_last_error(MT_MERR_MAKE_SPLIT_INTERNODE_ALLOC_EMPTY_PRE_ALLOC_NOT_EXPECTED);
+ }
+
int kp = -1;
if (n->parent_exists(p)) {
kp = internode_type::bound_type::upper(xikey[sense], *p);
p->mark_insert();
}
- if (kp < 0 || p->height_ > height + 1) {
- internode_type *nn = internode_type::make(height + 1, ti);
+ // If cur_cache_index == 1, reserved internode was used on last loop due to memory allocation failure.
+ // In this case, we have only 1 reserved internode left, so stop climbing and add the new internode in the current layer
+ if (kp < 0 || p->height_ > height + 1 || cur_cache_index == 1) {
+ masstree_invariant(preallocated_internodes[cur_cache_index]);
+ internode_type *nn = internode_type::make(height + 1, ti, nullptr);
+ if (!nn) {
+ ti.set_last_error(MT_MERR_MAKE_INTERNODE_USE_RESERVED);
+ nn = internode_type::make(height + 1, ti, preallocated_internodes[cur_cache_index++]);
+ }
+
+ if (!nn) {
+ // Should never happen with pre-allocated internodes. bad flow is not handled
+ ti.set_last_error(MT_MERR_MAKE_SPLIT_INTERNODE_ALLOC_NOT_EXPECTED);
+ masstree_invariant(false);
+ }
+
nn->child_[0] = n;
nn->assign(0, xikey[sense], child);
nn->nkeys_ = 1;
@@ -230,11 +300,23 @@ bool tcursor<P>::make_split(threadinfo& ti)
n->set_parent(nn);
} else {
if (p->size() >= p->width) {
- next_child = internode_type::make(height + 1, ti);
+ masstree_invariant(preallocated_internodes[cur_cache_index]);
+ next_child = internode_type::make(height + 1, ti, nullptr);
+ if (!next_child) {
+ ti.set_last_error(MT_MERR_MAKE_INTERNODE_USE_RESERVED_2);
+ next_child = internode_type::make(height + 1, ti, preallocated_internodes[cur_cache_index++]);
+ }
+
+ if (!next_child) {
+ // Should never happen with pre-allocated internodes. bad flow is not handled
+ ti.set_last_error(MT_MERR_MAKE_SPLIT_INTERNODE_ALLOC_NOT_EXPECTED_2);
+ masstree_invariant(false);
+ }
+
next_child->assign_version(*p);
next_child->mark_nonroot();
kp = p->split_into(next_child, kp, xikey[sense],
- child, xikey[sense ^ 1], split_type);
+ child, xikey[sense ^ 1], split_type); // No memory allocation
}
if (kp >= 0) {
p->shift_up(kp + 1, kp, p->size() - kp);
@@ -256,16 +338,27 @@ bool tcursor<P>::make_split(threadinfo& ti)
int width = perml.size();
perml.set_size(width - nr->size());
// removed item, if any, must be @ perml.size()
+ int perm_size = perml.size();
+ masstree_invariant(perm_size > 0); // Verify that the leaf is not empty
if (width != nl->width) {
- perml.exchange(perml.size(), nl->width - 1);
+ perml.exchange(perm_size, nl->width - 1);
}
nl->mark_split();
nl->permutation_ = perml.value();
// account for split
if (split_type == 0) {
kx_.p = perml.back();
- nl->assign(kx_.p, ka_, ti);
+
+ // In case the new inserted key should be placed in the origianl leaf (left leaf), memory allocation might be needed for it's ksuffix.
+ // If assign fails (--> memory allocation failure), the flow will continue, but we mark rc as false to indicate that the insertion failed.
+ // In this case, the key wont be exposed in finish_insert(), but the leaf split will be completed successfully.
+ if (!nl->assign(kx_.p, ka_, ti)) {
+ ti.set_last_error(MT_MERR_MAKE_SPLIT_ASSIGN_SUFFIX);
+ rc = false;
+ }
+#ifndef MASSTREE_OBSOLETE_CODE
new_nodes_.emplace_back(nr, nr->full_unlocked_version_value());
+#endif
} else {
kx_.i = kx_.p = kx_.i - perml.size();
n_ = nr;
@@ -293,7 +386,10 @@ bool tcursor<P>::make_split(threadinfo& ti)
}
}
- return false;
+ // Free unused pre-allocated internodes
+ release_internodes(preallocated_internodes, cur_cache_index, reserved_nodes, ti);
+
+ return rc;
}
} // namespace Masstree
diff --git a/masstree_stats.hh b/masstree_stats.hh
deleted file mode 100644
index 36e59d8..0000000
--- a/masstree_stats.hh
+++ /dev/null
@@ -1,130 +0,0 @@
-/* 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_STATS_HH
-#define MASSTREE_STATS_HH
-#include "masstree.hh"
-#include "json.hh"
-
-namespace Masstree {
-
-template <typename P, typename TI>
-void node_json_stats(node_base<P>* n, lcdf::Json& j, int layer, int depth,
- TI& ti)
-{
- if (!n)
- return;
- else if (n->isleaf()) {
- leaf<P>* lf = static_cast<leaf<P>*>(n);
- // track number of leaves by depth and size
- j[&"l1_node_by_depth"[!layer * 3]][depth] += 1;
- j[&"l1_leaf_by_depth"[!layer * 3]][depth] += 1;
- j[&"l1_leaf_by_size"[!layer * 3]][lf->size()] += 1;
-
- // per-key information
- typename leaf<P>::permuter_type perm(lf->permutation_);
- int n = 0, nksuf = 0;
- size_t active_ksuf_len = 0;
- for (int i = 0; i < perm.size(); ++i)
- if (lf->is_layer(perm[i])) {
- lcdf::Json x = j["l1_size"];
- j["l1_size"] = 0;
- node_json_stats(lf->lv_[perm[i]].layer(), j, layer + 1, 0, ti);
- j["l1_size_sum"] += j["l1_size"].to_i();
- j["l1_size"] = x;
- j["l1_count"] += 1;
- } else {
- ++n;
- int l = sizeof(typename P::ikey_type) * layer
- + lf->keylenx_[perm[i]];
- if (lf->has_ksuf(perm[i])) {
- size_t ksuf_len = lf->ksuf(perm[i]).len;
- l += ksuf_len - 1;
- active_ksuf_len += ksuf_len;
- ++nksuf;
- }
- j["key_by_length"][l] += 1;
- }
- j["size"] += n;
- j["l1_size"] += n;
- j["key_by_layer"][layer] += n;
-
- // key suffix information
- if (lf->allocated_size() != lf->min_allocated_size()
- && lf->ksuf_external()) {
- j["overridden_ksuf"] += 1;
- j["overridden_ksuf_capacity"] += lf->allocated_size() - lf->min_allocated_size();
- }
- if (lf->ksuf_capacity()) {
- j["ksuf"] += 1;
- j["ksuf_capacity"] += lf->ksuf_capacity();
- j["ksuf_len"] += active_ksuf_len;
- j["ksuf_by_layer"][layer] += 1;
- if (!active_ksuf_len) {
- j["unused_ksuf_capacity"] += lf->ksuf_capacity();
- j["unused_ksuf_by_layer"][layer] += 1;
- if (lf->ksuf_external())
- j["unused_ksuf_external"] += 1;
- } else
- j["used_ksuf_by_layer"][layer] += 1;
- }
- } else {
- internode<P> *in = static_cast<internode<P> *>(n);
- for (int i = 0; i <= in->size(); ++i)
- if (in->child_[i])
- node_json_stats(in->child_[i], j, layer, depth + 1, ti);
- j[&"l1_node_by_depth"[!layer * 3]][depth] += 1;
- j[&"l1_internode_by_size"[!layer * 3]][in->size()] += 1;
- }
-}
-
-template <typename P, typename TI>
-void json_stats(lcdf::Json& j, basic_table<P>& table, TI& ti)
-{
- using lcdf::Json;
- j["size"] = 0.0;
- j["l1_count"] = 0;
- j["l1_size"] = 0;
- const char* const jarrays[] = {
- "node_by_depth", "internode_by_size", "leaf_by_depth", "leaf_by_size",
- "l1_node_by_depth", "l1_internode_by_size", "l1_leaf_by_depth", "l1_leaf_by_size",
- "key_by_layer", "key_by_length",
- "ksuf_by_layer", "unused_ksuf_by_layer", "used_ksuf_by_layer"
- };
- for (const char* const* x = jarrays; x != jarrays + sizeof(jarrays) / sizeof(*jarrays); ++x)
- j[*x] = Json::make_array();
-
- node_json_stats(table.root(), j, 0, 0, ti);
-
- j.unset("l1_size");
- for (const char* const* x = jarrays; x != jarrays + sizeof(jarrays) / sizeof(*jarrays); ++x) {
- Json& a = j[*x];
- for (Json* it = a.array_data(); it != a.end_array_data(); ++it)
- if (!*it)
- *it = Json((size_t) 0);
- if (a.empty())
- j.unset(*x);
- }
-}
-
-template <typename P, typename TI>
-lcdf::Json json_stats(basic_table<P>& table, TI& ti) {
- lcdf::Json j;
- json_stats(j, table, ti);
- return j;
-}
-
-}
-#endif
diff --git a/masstree_struct.hh b/masstree_struct.hh
index 494dcca..1b5d853 100644
--- a/masstree_struct.hh
+++ b/masstree_struct.hh
@@ -106,9 +106,12 @@ class internode : public node_base<P> {
typedef typename key_bound<width, P::bound_method>::type bound_type;
typedef typename P::threadinfo_type threadinfo;
+ // Number of boundary keys the node currently contains
uint8_t nkeys_;
uint32_t height_;
+ // Boundary keys array
ikey_type ikey0_[width];
+ // Child nodes array (might be leafs or internodes)
node_base<P>* child_[width + 1];
node_base<P>* parent_;
kvtimestamp_t created_at_[P::debug_level > 0];
@@ -117,9 +120,15 @@ class internode : public node_base<P> {
: node_base<P>(false), nkeys_(0), height_(height), parent_() {
}
- static internode<P>* make(uint32_t height, threadinfo& ti) {
- void* ptr = ti.pool_allocate(sizeof(internode<P>),
+ static internode<P>* make(uint32_t height, threadinfo& ti, void * allocated_internode = nullptr) {
+ void* ptr = allocated_internode ?
+ allocated_internode :
+ ti.pool_allocate(MAX_MEMTAG_MASSTREE_INTERNODE_ALLOCATION_SIZE,
memtag_masstree_internode);
+ if (!ptr) {
+ ti.set_last_error(MT_MERR_MAKE_INTERNODE);
+ return nullptr;
+ }
internode<P>* n = new(ptr) internode<P>(height);
assert(n);
if (P::debug_level > 0)
@@ -264,19 +273,38 @@ class leaf : public node_base<P> {
enum {
modstate_insert = 0, modstate_remove = 1, modstate_deleted_layer = 2
};
-
+ /* Indicator for internal suffix (iksuf_) string bag
+ extrasize64_ == 0 --> no iksuf_.
+ extrasize64_ > 0 --> iksuf_ (of size extrasize64_ * 64) exists and is in use
+ extrasize64_ < 0 --> iksuf_ (of size (-extrasize64_ - 1) * 64) exists but not in use anymore (ksuf_ is used instead)
+ */
int8_t extrasize64_;
uint8_t modstate_;
+ /* Key slice (ikey) length array. locations consistent with keys in ikey0_ and lv_ arrays.
+ If key ends in this leaf and has no suffix: length of the key slice is stored
+ If key ends in this leaf and has suffix: ksuf_keylenx (64) is stored
+ If key doesn't end in this leaf (ends in lower layer): layer_keylenx (128) is stored */
uint8_t keylenx_[width];
+ /* Sorted permutation of the ikey's indexes (up to the permutation size). divided into 16 parts of 2 bytes each.
+ First part: the current size of the permutation (number of ikeys in use)
+ Following parts: sorted indexes of the ikeys in ikey0_ array.
+ The permutation always include all values (0-14) but only the first perm.size (which is located in the first part) entries are valid and sorted. */
typename permuter_type::storage_type permutation_;
+ // Key slices (ikey) array. each slice is uint64 and represent 8 bytes of the original key
ikey_type ikey0_[width];
+ // Values array. locations are consistent with keys in ikey0_ and keylenx_ arrays. holds key's value or a link to lower layer
leafvalue_type lv_[width];
+ // Suffixes of keys. Will be used if a key has further suffix but it's prefix is unique.
external_ksuf_type* ksuf_;
+ // Pointer to the next leaf in the same layer
union {
leaf<P>* ptr;
uintptr_t x;
} next_;
+ // Pointer to the previous leaf in the same layer
leaf<P>* prev_;
+
+ // Leaf's parent (might be null if leaf is the root of the btree (trie node)
node_base<P>* parent_;
phantom_epoch_type phantom_epoch_[P::need_phantom_epoch];
kvtimestamp_t created_at_[P::debug_level > 0];
@@ -297,8 +325,12 @@ class leaf : public node_base<P> {
}
static leaf<P>* make(int ksufsize, phantom_epoch_type phantom_epoch, threadinfo& ti) {
- size_t sz = iceil(sizeof(leaf<P>) + std::min(ksufsize, 128), 64);
+ size_t sz = MAX_MEMTAG_MASSTREE_LEAF_ALLOCATION_SIZE; // iceil(sizeof(leaf<P>) + std::min(ksufsize, 128), 64);
void* ptr = ti.pool_allocate(sz, memtag_masstree_leaf);
+ if (!ptr) {
+ ti.set_last_error(MT_MERR_MAKE_LEAF);
+ return nullptr;
+ }
leaf<P>* n = new(ptr) leaf<P>(sz, phantom_epoch);
assert(n);
if (P::debug_level > 0) {
@@ -308,6 +340,10 @@ class leaf : public node_base<P> {
}
static leaf<P>* make_root(int ksufsize, leaf<P>* parent, threadinfo& ti) {
leaf<P>* n = make(ksufsize, parent ? parent->phantom_epoch() : phantom_epoch_type(), ti);
+ if (!n) {
+ ti.set_last_error(MT_MERR_MAKE_ROOT_LEAF);
+ return nullptr;
+ }
n->next_.ptr = n->prev_ = 0;
n->ikey0_[0] = 0; // to avoid undefined behavior
n->make_layer_root();
@@ -391,7 +427,9 @@ class leaf : public node_base<P> {
}
Str ksuf(int p, int keylenx) const {
(void) keylenx;
- masstree_precondition(keylenx_has_ksuf(keylenx));
+ // keylenx might not be equal to ksuf_keylenx as this operation might be called without holding leaf's lock
+ // We allow it, and expect the caller to validate leaf's version and retry.
+ //masstree_precondition(keylenx_has_ksuf(keylenx));
return ksuf_ ? ksuf_->get(p) : iksuf_[0].get(p);
}
Str ksuf(int p) const {
@@ -407,13 +445,17 @@ class leaf : public node_base<P> {
return s.len == ka.suffix().len
&& string_slice<uintptr_t>::equals_sloppy(s.s, ka.suffix().s, s.len);
}
- // Returns 1 if match & not layer, 0 if no match, <0 if match and layer
+ // Returns 1 if match & not layer, 0 if no match, < 0 if match and layer
int ksuf_matches(int p, const key_type& ka) const {
int keylenx = keylenx_[p];
if (keylenx < ksuf_keylenx)
+ // Key does not have extra suffix
return 1;
if (keylenx == layer_keylenx)
+ // Key does not end in this layer. we need to continue looking for it in lower layers.
return -(int) sizeof(ikey_type);
+
+ // Key is stored in this layer and has suffix. lets compare the suffixes.
Str s = ksuf(p, keylenx);
return s.len == ka.suffix().len
&& string_slice<uintptr_t>::equals_sloppy(s.s, ka.suffix().s, s.len);
@@ -494,40 +536,55 @@ class leaf : public node_base<P> {
modstate_ = modstate_deleted_layer;
}
- inline void assign(int p, const key_type& ka, threadinfo& ti) {
+ inline bool assign(int p, const key_type& ka, threadinfo& ti) {
lv_[p] = leafvalue_type::make_empty();
ikey0_[p] = ka.ikey();
if (!ka.has_suffix()) {
keylenx_[p] = ka.length();
} else {
keylenx_[p] = ksuf_keylenx;
- assign_ksuf(p, ka.suffix(), false, ti);
+ if (!assign_ksuf(p, ka.suffix(), false, ti)) {
+ ti.set_last_error(MT_MERR_LEAF_ASSIGN);
+ return false;
+ }
}
+
+ return true;
}
- inline void assign_initialize(int p, const key_type& ka, threadinfo& ti) {
+ inline bool assign_initialize(int p, const key_type& ka, threadinfo& ti) {
lv_[p] = leafvalue_type::make_empty();
ikey0_[p] = ka.ikey();
if (!ka.has_suffix()) {
keylenx_[p] = ka.length();
} else {
keylenx_[p] = ksuf_keylenx;
- assign_ksuf(p, ka.suffix(), true, ti);
+ if (!assign_ksuf(p, ka.suffix(), true, ti)) {
+ ti.set_last_error(MT_MERR_ASSIGN_INITALIZE_1);
+ return false;
+ }
}
+
+ return true;
}
- inline void assign_initialize(int p, leaf<P>* x, int xp, threadinfo& ti) {
+ inline bool assign_initialize(int p, leaf<P>* x, int xp, threadinfo& ti) {
lv_[p] = x->lv_[xp];
ikey0_[p] = x->ikey0_[xp];
keylenx_[p] = x->keylenx_[xp];
if (x->has_ksuf(xp)) {
- assign_ksuf(p, x->ksuf(xp), true, ti);
+ if (!assign_ksuf(p, x->ksuf(xp), true, ti)) {
+ ti.set_last_error(MT_MERR_ASSIGN_INITALIZE_2);
+ return false;
+ }
}
+
+ return true;
}
inline void assign_initialize_for_layer(int p, const key_type& ka) {
assert(ka.has_suffix());
ikey0_[p] = ka.ikey();
keylenx_[p] = layer_keylenx;
}
- void assign_ksuf(int p, Str s, bool initializing, threadinfo& ti);
+ bool assign_ksuf(int p, Str s, bool initializing, threadinfo& ti);
inline ikey_type ikey_after_insert(const permuter_type& perm, int i,
const tcursor<P>* cursor) const;
@@ -606,6 +663,7 @@ leaf<P>::stable_last_key_compare(const key_type& k, nodeversion_type v,
while (true) {
typename leaf<P>::permuter_type perm(permutation_);
int n = perm.size();
+ // Eddie's comment
// If `n == 0`, then this node is empty: it was deleted without ever
// splitting, or it split and then was emptied.
// - It is always safe to return 1, because then the caller will
@@ -616,6 +674,11 @@ leaf<P>::stable_last_key_compare(const key_type& k, nodeversion_type v,
// `perm[0]`. If the node ever had keys in it, then kpermuter ensures
// that slot holds the most recently deleted key, which would belong
// in this leaf. Otherwise, `perm[0]` is 0.
+
+ // Idan's comment
+ // In case the leaf has no keys, perm[-1] will return 0.
+ // This wont work for the most left leaf, as we cannot assume anything about it's ikey.
+ // So we will use the value that was last deleted as an upper boundary (perm[0])
int p = perm[n ? n - 1 : 0];
int cmp = compare_key(k, p);
if (likely(!this->has_changed(v))) {
@@ -644,6 +707,7 @@ inline leaf<P>* node_base<P>::reach_leaf(const key_type& ka,
retry:
sense = 0;
n[sense] = this;
+ // Looking for a local root node up the tree and populate n[0] and v[0] with the founded node\node's version respectively
while (true) {
v[sense] = n[sense]->stable_annotated(ti.stable_fence());
if (v[sense].is_root()) {
@@ -653,10 +717,12 @@ inline leaf<P>* node_base<P>::reach_leaf(const key_type& ka,
n[sense] = n[sense]->maybe_parent();
}
- // Loop over internal nodes.
+ // Traverse over internal nodes until reaching a leaf.
while (!v[sense].isleaf()) {
const internode<P> *in = static_cast<const internode<P>*>(n[sense]);
in->prefetch();
+ // Get the child's node location (inside in->child_) which best match the key in ka
+ // This is done by comparing each one of the boundaries (starting from the lower one) until key in ka is lower than the boundary (linear search)
int kp = internode<P>::bound_type::upper(ka, *in);
n[sense ^ 1] = in->child_[kp];
if (!n[sense ^ 1]) {
@@ -669,8 +735,11 @@ inline leaf<P>* node_base<P>::reach_leaf(const key_type& ka,
continue;
}
+ // Node's version was changed. wait until it is stable again (aka not dirty)
typename node_base<P>::nodeversion_type oldv = v[sense];
v[sense] = in->stable_annotated(ti.stable_fence());
+
+ // Handle the case the node has split (start again from the local root)
if (unlikely(oldv.has_split(v[sense]))
&& in->stable_last_key_compare(ka, v[sense], ti) > 0) {
ti.mark(tc_root_retry);
@@ -725,11 +794,15 @@ leaf<P>* leaf<P>::advance_to_key(const key_type& ka, nodeversion_type& v,
positions [0,p) are ready: keysuffixes in that range are copied. In either
case, the key at position p is NOT copied; it is assigned to @a s. */
template <typename P>
-void leaf<P>::assign_ksuf(int p, Str s, bool initializing, threadinfo& ti) {
+bool leaf<P>::assign_ksuf(int p, Str s, bool initializing, threadinfo& ti) {
if ((ksuf_ && ksuf_->assign(p, s))
|| (extrasize64_ > 0 && iksuf_[0].assign(p, s)))
- return;
-
+ {
+#if !(defined(__x86_64__) || defined(__x86__))
+ fence();
+#endif
+ return true;
+ }
external_ksuf_type* oksuf = ksuf_;
permuter_type perm(permutation_);
@@ -742,20 +815,31 @@ void leaf<P>::assign_ksuf(int p, Str s, bool initializing, threadinfo& ti) {
csz += ksuf(mp).len;
}
- size_t sz = iceil_log2(external_ksuf_type::safe_size(width, csz + s.len));
+ size_t sz;
+ if (likely(!ti.use_pool())) {
+ // We don't use the iceil_log2 because our slab allocator will allocate a buffer in iceil_log2 - 1 size for us.
+ sz = external_ksuf_type::safe_size(width, csz + s.len);
+ } else {
+ sz = iceil_log2(external_ksuf_type::safe_size(width, csz + s.len));
+ }
+
if (oksuf)
sz = std::max(sz, oksuf->capacity());
- void* ptr = ti.allocate(sz, memtag_masstree_ksuffixes);
+ void* ptr = ti.allocate(sz, memtag_masstree_ksuffixes, &sz);
+ if (!ptr) {
+ ti.set_last_error(MT_MERR_ASSIGN_KSUF);
+ return false;
+ }
external_ksuf_type* nksuf = new(ptr) external_ksuf_type(width, sz);
for (int i = 0; i < n; ++i) {
int mp = initializing ? i : perm[i];
if (mp != p && has_ksuf(mp)) {
- bool ok = nksuf->assign(mp, ksuf(mp));
+ bool ok = nksuf->assign(mp, ksuf(mp)); // No memory allocation here
assert(ok); (void) ok;
}
}
- bool ok = nksuf->assign(p, s);
+ bool ok = nksuf->assign(p, s); // No memory allocation here
assert(ok); (void) ok;
fence();
@@ -775,11 +859,12 @@ void leaf<P>::assign_ksuf(int p, Str s, bool initializing, threadinfo& ti) {
if (oksuf)
ti.deallocate_rcu(oksuf, oksuf->capacity(),
memtag_masstree_ksuffixes);
+ return true;
}
template <typename P>
inline basic_table<P>::basic_table()
- : root_(0) {
+ : root_(nullptr) {
}
template <typename P>
diff --git a/masstree_tcursor.hh b/masstree_tcursor.hh
index b3782dd..73231b8 100644
--- a/masstree_tcursor.hh
+++ b/masstree_tcursor.hh
@@ -20,6 +20,7 @@
#include "masstree_struct.hh"
namespace Masstree {
template <typename P> struct gc_layer_rcu_callback;
+template <typename P> struct gc_layer_rcu_callback_ng;
template <typename P>
class unlocked_tcursor {
@@ -105,22 +106,23 @@ class tcursor {
static constexpr int new_nodes_size = 1; // unless we make a new trie newnodes will have at most 1 item
typedef small_vector<std::pair<leaf_type*, nodeversion_value_type>, new_nodes_size> new_nodes_type;
+#ifndef MASSTREE_OBSOLETE_CODE
tcursor(basic_table<P>& table, Str str)
: ka_(str), root_(table.fix_root()) {
}
tcursor(basic_table<P>& table, const char* s, int len)
: ka_(s, len), root_(table.fix_root()) {
}
- tcursor(basic_table<P>& table, const unsigned char* s, int len)
- : ka_(reinterpret_cast<const char*>(s), len), root_(table.fix_root()) {
- }
- tcursor(node_base<P>* root, const char* s, int len)
- : ka_(s, len), root_(root) {
- }
tcursor(node_base<P>* root, const unsigned char* s, int len)
: ka_(reinterpret_cast<const char*>(s), len), root_(root) {
}
-
+#endif
+ tcursor(basic_table<P>& table, const unsigned char* s, int len)
+ : ka_(reinterpret_cast<const char*>(s), len), root_(table.fix_root()), root_ref_(table.root_ref()) {
+ }
+ tcursor(node_base<P>** root_ref, const char* s, int len)
+ : ka_(s, len), root_(*root_ref), root_ref_(root_ref) {
+ }
inline bool has_value() const {
return kx_.p >= 0;
}
@@ -147,13 +149,13 @@ class tcursor {
inline nodeversion_value_type updated_version_value() const {
return updated_v_;
}
-
+#ifndef MASSTREE_OBSOLETE_CODE
inline const new_nodes_type &new_nodes() const {
return new_nodes_;
}
-
+#endif
inline bool find_locked(threadinfo& ti);
- inline bool find_insert(threadinfo& ti);
+ inline bool find_insert(threadinfo& ti, bool & found);
inline void finish(int answer, threadinfo& ti);
@@ -165,13 +167,16 @@ class tcursor {
key_type ka_;
key_indexed_position kx_;
node_base<P>* root_;
+ node_base<P>** root_ref_;
int state_;
- leaf_type* original_n_;
+ leaf_type* original_n_ = nullptr;
nodeversion_value_type original_v_;
nodeversion_value_type updated_v_;
- new_nodes_type new_nodes_;
+#ifndef MASSTREE_OBSOLETE_CODE
+ new_nodes_type new_nodes_;
+#endif
inline node_type* reset_retry() {
ka_.unshift_all();
return root_;
@@ -179,6 +184,7 @@ class tcursor {
bool make_new_layer(threadinfo& ti);
bool make_split(threadinfo& ti);
+ void release_internodes(internode_type * internodes_array[], int start, int end, threadinfo& ti);
friend class leaf<P>;
inline void finish_insert();
inline bool finish_remove(threadinfo& ti);
@@ -190,11 +196,12 @@ class tcursor {
* If removing a leaf in layer 0, @a prefix is empty.
* If removing, for example, the node containing key "01234567ABCDEF" in the layer-1 tree
* rooted at "01234567", then @a prefix should equal "01234567". */
- static bool remove_leaf(leaf_type* leaf, node_type* root,
+ static bool remove_leaf(leaf_type* leaf, node_type** root_ref,
Str prefix, threadinfo& ti);
bool gc_layer(threadinfo& ti);
friend struct gc_layer_rcu_callback<P>;
+ friend struct gc_layer_rcu_callback_ng<P>;
};
template <typename P>
diff --git a/memdebug.cc b/memdebug.cc
deleted file mode 100644
index 15b2d2e..0000000
--- a/memdebug.cc
+++ /dev/null
@@ -1,79 +0,0 @@
-/* Masstree
- * Eddie Kohler, Yandong Mao, Robert Morris
- * Copyright (c) 2012-2016 President and Fellows of Harvard College
- * Copyright (c) 2012-2016 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.
- */
-#include "memdebug.hh"
-#include <stdio.h>
-#include <assert.h>
-
-#if HAVE_MEMDEBUG
-void memdebug::landmark(char* buf, size_t bufsz) const {
- if (this->magic != magic_value && this->magic != magic_free_value)
- snprintf(buf, bufsz, "???");
- else if (this->file)
- snprintf(buf, bufsz, "%s:%d", this->file, this->line);
- else if (this->line)
- snprintf(buf, bufsz, "%d", this->line);
- else
- snprintf(buf, bufsz, "0");
-}
-
-void
-memdebug::hard_free_checks(const memdebug *m, size_t sz, memtag tag,
- int after_rcu, const char *op) {
- char buf[256];
- m->landmark(buf, sizeof(buf));
- if (m->magic == magic_free_value)
- fprintf(stderr, "%s(%p): double free, was @%s\n",
- op, m + 1, buf);
- else if (m->magic != magic_value)
- fprintf(stderr, "%s(%p): freeing unallocated pointer (%x)\n",
- op, m + 1, m->magic);
- assert(m->magic == magic_value);
- if (tag && m->tag != tag)
- fprintf(stderr, "%s(%p): expected type %x, saw %x, "
- "allocated %s\n", op, m + 1, tag, m->tag, buf);
- if (!after_rcu && m->size != sz)
- fprintf(stderr, "%s(%p): expected size %lu, saw %lu, "
- "allocated %s\n", op, m + 1,
- (unsigned long) sz, (unsigned long) m->size, buf);
- if (m->after_rcu != after_rcu)
- fprintf(stderr, "%s(%p): double free after rcu, allocated @%s\n",
- op, m + 1, buf);
- if (tag)
- assert(m->tag == tag);
- if (!after_rcu)
- assert(m->size == sz);
- assert(m->after_rcu == after_rcu);
-}
-
-void
-memdebug::hard_assert_use(const void* ptr, memtag allowed) {
- const memdebug* m = reinterpret_cast<const memdebug*>(ptr) - 1;
- char buf[256];
- m->landmark(buf, sizeof(buf));
- if (m->magic == magic_free_value)
- fprintf(stderr, "%p: use tag %x after free, allocated %s\n",
- m + 1, allowed, buf);
- else if (m->magic != magic_value)
- fprintf(stderr, "%p: pointer is unallocated, not tag %x\n",
- m + 1, allowed);
- assert(m->magic == magic_value);
- if (allowed != 0 && (m->tag ^ allowed) > memtag_pool_mask)
- fprintf(stderr, "%p: expected tag %x, got tag %x, allocated %s\n",
- m + 1, allowed, m->tag, buf);
- if (allowed != 0)
- assert((m->tag ^ allowed) <= memtag_pool_mask);
-}
-#endif
diff --git a/misc.cc b/misc.cc
deleted file mode 100644
index b8a4722..0000000
--- a/misc.cc
+++ /dev/null
@@ -1,40 +0,0 @@
-/* Masstree
- * Eddie Kohler, Yandong Mao, Robert Morris
- * Copyright (c) 2012-2013 President and Fellows of Harvard College
- * Copyright (c) 2012-2013 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.
- */
-#include "misc.hh"
-#include <unistd.h>
-#include "kvthread.hh"
-
-int clp_parse_suffixdouble(Clp_Parser *clp, const char *vstr,
- int complain, void *)
-{
- const char *post;
- if (*vstr == 0 || isspace((unsigned char) *vstr))
- post = vstr;
- else
- clp->val.d = strtod(vstr, (char **) &post);
- if (vstr != post && (*post == 'K' || *post == 'k'))
- clp->val.d *= 1000, ++post;
- else if (vstr != post && (*post == 'M' || *post == 'm'))
- clp->val.d *= 1000000, ++post;
- else if (vstr != post && (*post == 'B' || *post == 'b' || *post == 'G' || *post == 'g'))
- clp->val.d *= 1000000000, ++post;
- if (*vstr != 0 && *post == 0)
- return 1;
- else if (complain)
- return Clp_OptionError(clp, "%<%O%> expects a real number, not %<%s%>", vstr);
- else
- return 0;
-}
diff --git a/misc.hh b/misc.hh
deleted file mode 100644
index 66bcd4c..0000000
--- a/misc.hh
+++ /dev/null
@@ -1,111 +0,0 @@
-/* Masstree
- * Eddie Kohler, Yandong Mao, Robert Morris
- * Copyright (c) 2012-2013 President and Fellows of Harvard College
- * Copyright (c) 2012-2013 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 MISC_HH
-#define MISC_HH
-#include <stdio.h>
-#include <time.h>
-#include <sys/time.h>
-#include <string.h>
-#include <math.h>
-#include "str.hh"
-#include "timestamp.hh"
-#include "clp.h"
-
-inline void xalarm(double d) {
- double ip, fp = modf(d, &ip);
- struct itimerval x;
- timerclear(&x.it_interval);
- x.it_value.tv_sec = (long) ip;
- x.it_value.tv_usec = (long) (fp * 1000000);
- setitimer(ITIMER_REAL, &x, 0);
-}
-
-inline void napms(int n) /* nap n milliseconds */
-{
- int ret;
- struct timespec req, rem;
-
- req.tv_sec = n / 1000;
- req.tv_nsec = (n % 1000) * 1000000;
- ret = nanosleep(&req, &rem);
- if(ret == -1 && errno != EINTR){
- perror("nanosleep");
- exit(EXIT_FAILURE);
- }
-}
-
-struct quick_istr {
- char* bbuf_;
- char buf_[32];
- quick_istr() {
- buf_[sizeof(buf_) - 1] = 0;
- set(0);
- }
- quick_istr(unsigned long x, int minlen = 0) {
- buf_[sizeof(buf_) - 1] = 0;
- set(x, minlen);
- }
- void set(unsigned long x, int minlen = 0) {
- bbuf_ = buf_ + sizeof(buf_) - 1;
- do {
- *--bbuf_ = (x % 10) + '0';
- x /= 10;
- } while (--minlen > 0 || x != 0);
- }
- lcdf::Str string() const {
- return lcdf::Str(bbuf_, buf_ + sizeof(buf_) - 1);
- }
- const char* data() const {
- return bbuf_;
- }
- size_t length() const {
- return (buf_ + sizeof(buf_) - 1) - bbuf_;
- }
- const char* c_str() const {
- return bbuf_;
- }
- bool operator==(lcdf::Str s) const {
- return s.len == int(length()) && memcmp(s.s, data(), s.len) == 0;
- }
- bool operator!=(lcdf::Str s) const {
- return !(*this == s);
- }
- static void increment_from_end(char* ends) {
- while (true) {
- --ends;
- ++*ends;
- if (*ends <= '9') {
- return;
- }
- *ends = '0';
- }
- }
- static void binary_increment_from_end(char* ends) {
- while (true) {
- --ends;
- *ends = (char) ((unsigned char) *ends + 1);
- if (*ends != 0) {
- return;
- }
- }
- }
-};
-
-struct Clp_Parser;
-int clp_parse_suffixdouble(struct Clp_Parser *clp, const char *vstr,
- int complain, void *user_data);
-
-#endif
diff --git a/msgpack.cc b/msgpack.cc
deleted file mode 100644
index 4b8859b..0000000
--- a/msgpack.cc
+++ /dev/null
@@ -1,240 +0,0 @@
-#include "msgpack.hh"
-namespace msgpack {
-
-namespace {
-const uint8_t nbytes[] = {
- /* 0xC0-0xC3 */ 0, 0, 0, 0,
- /* 0xC4-0xC6 fbin8-fbin32 */ 2, 3, 5,
- /* 0xC7-0xC9 fext */ 0, 0, 0,
- /* 0xCA ffloat32 */ 5,
- /* 0xCB ffloat64 */ 9,
- /* 0xCC-0xD3 ints */ 2, 3, 5, 9, 2, 3, 5, 9,
- /* 0xD4-0xD8 ffixext */ 0, 0, 0, 0, 0,
- /* 0xD9-0xDB fstr8-fstr32 */ 2, 3, 5,
- /* 0xDC-0xDD farray16-farray32 */ 3, 5,
- /* 0xDE-0xDF fmap16-fmap32 */ 3, 5
-};
-}
-
-const uint8_t* streaming_parser::consume(const uint8_t* first,
- const uint8_t* last,
- const String& str) {
- using std::swap;
- Json* jx;
- int n = 0;
-
- if (state_ < 0)
- return first;
- if (state_ == st_partial || state_ == st_string) {
- int nneed;
- if (state_ == st_partial)
- nneed = nbytes[str_.udata()[0] - 0xC0];
- else
- nneed = stack_.back().size;
- const uint8_t* next;
- if (last - first < nneed - str_.length())
- next = last;
- else
- next = first + (nneed - str_.length());
- str_.append(first, next);
- if (str_.length() != nneed)
- return next;
- first = next;
- if (state_ == st_string) {
- stack_.pop_back();
- jx = stack_.empty() ? &json_ : stack_.back().jp;
- *jx = str_;
- goto next;
- } else {
- state_ = st_normal;
- consume(str_.ubegin(), str_.uend(), str_);
- if (state_ != st_normal)
- return next;
- }
- }
-
- while (first != last) {
- jx = stack_.empty() ? &json_ : stack_.back().jp;
-
- if (format::is_fixint(*first)) {
- *jx = int(int8_t(*first));
- ++first;
- } else if (*first == format::fnull) {
- *jx = Json();
- ++first;
- } else if (format::is_bool(*first)) {
- *jx = bool(*first - format::ffalse);
- ++first;
- } else if (format::is_fixmap(*first)) {
- n = *first - format::ffixmap;
- ++first;
- map:
- if (jx->is_o())
- jx->clear();
- else
- *jx = Json::make_object();
- } else if (format::is_fixarray(*first)) {
- n = *first - format::ffixarray;
- ++first;
- array:
- if (!jx->is_a())
- *jx = Json::make_array_reserve(n);
- jx->resize(n);
- } else if (format::is_fixstr(*first)) {
- n = *first - format::ffixstr;
- ++first;
- raw:
- if (last - first < n) {
- str_ = String(first, last);
- stack_.push_back(selem{0, n});
- state_ = st_string;
- return last;
- }
- if (first < str.ubegin() || first + n >= str.uend())
- *jx = String(first, n);
- else {
- const char* s = reinterpret_cast<const char*>(first);
- *jx = str.fast_substring(s, s + n);
- }
- first += n;
- } else {
- uint8_t type = *first - format::fnull;
- if (!nbytes[type])
- goto error;
- if (last - first < nbytes[type]) {
- str_ = String(first, last);
- state_ = st_partial;
- return last;
- }
- first += nbytes[type];
- switch (type) {
- case format::ffloat32 - format::fnull:
- *jx = read_in_net_order<float>(first - 4);
- break;
- case format::ffloat64 - format::fnull:
- *jx = read_in_net_order<double>(first - 8);
- break;
- case format::fuint8 - format::fnull:
- *jx = int(first[-1]);
- break;
- case format::fuint16 - format::fnull:
- *jx = read_in_net_order<uint16_t>(first - 2);
- break;
- case format::fuint32 - format::fnull:
- *jx = read_in_net_order<uint32_t>(first - 4);
- break;
- case format::fuint64 - format::fnull:
- *jx = read_in_net_order<uint64_t>(first - 8);
- break;
- case format::fint8 - format::fnull:
- *jx = int8_t(first[-1]);
- break;
- case format::fint16 - format::fnull:
- *jx = read_in_net_order<int16_t>(first - 2);
- break;
- case format::fint32 - format::fnull:
- *jx = read_in_net_order<int32_t>(first - 4);
- break;
- case format::fint64 - format::fnull:
- *jx = read_in_net_order<int64_t>(first - 8);
- break;
- case format::fbin8 - format::fnull:
- case format::fstr8 - format::fnull:
- n = first[-1];
- goto raw;
- case format::fbin16 - format::fnull:
- case format::fstr16 - format::fnull:
- n = read_in_net_order<uint16_t>(first - 2);
- goto raw;
- case format::fbin32 - format::fnull:
- case format::fstr32 - format::fnull:
- n = read_in_net_order<uint32_t>(first - 4);
- goto raw;
- case format::farray16 - format::fnull:
- n = read_in_net_order<uint16_t>(first - 2);
- goto array;
- case format::farray32 - format::fnull:
- n = read_in_net_order<uint32_t>(first - 4);
- goto array;
- case format::fmap16 - format::fnull:
- n = read_in_net_order<uint16_t>(first - 2);
- goto map;
- case format::fmap32 - format::fnull:
- n = read_in_net_order<uint32_t>(first - 4);
- goto map;
- }
- }
-
- // Add it
- next:
- if (jx == &jokey_) {
- // Reading a key for some object Json
- if (!jx->is_s() && !jx->is_i())
- goto error;
- selem* top = &stack_.back();
- Json* jo = (top == stack_.begin() ? &json_ : top[-1].jp);
- top->jp = &jo->get_insert(jx->to_s());
- continue;
- }
-
- if (jx->is_a() && n != 0)
- stack_.push_back(selem{&jx->at_insert(0), n - 1});
- else if (jx->is_o() && n != 0)
- stack_.push_back(selem{&jokey_, n - 1});
- else {
- while (!stack_.empty() && stack_.back().size == 0)
- stack_.pop_back();
- if (stack_.empty()) {
- state_ = st_final;
- return first;
- }
- selem* top = &stack_.back();
- --top->size;
- Json* jo = (top == stack_.begin() ? &json_ : top[-1].jp);
- if (jo->is_a())
- ++top->jp;
- else
- top->jp = &jokey_;
- }
- }
-
- state_ = st_normal;
- return first;
-
- error:
- state_ = st_error;
- return first;
-}
-
-parser& parser::operator>>(Str& x) {
- uint32_t len;
- if ((uint32_t) *s_ - format::ffixstr < format::nfixstr) {
- len = *s_ - format::ffixstr;
- ++s_;
- } else if (*s_ == format::fbin8 || *s_ == format::fstr8) {
- len = s_[1];
- s_ += 2;
- } else if (*s_ == format::fbin16 || *s_ == format::fstr16) {
- len = read_in_net_order<uint16_t>(s_ + 1);
- s_ += 3;
- } else {
- assert(*s_ == format::fbin32 || *s_ == format::fstr32);
- len = read_in_net_order<uint32_t>(s_ + 1);
- s_ += 5;
- }
- x.assign(reinterpret_cast<const char*>(s_), len);
- s_ += len;
- return *this;
-}
-
-parser& parser::operator>>(String& x) {
- Str s;
- *this >> s;
- if (str_)
- x = str_.substring(s.begin(), s.end());
- else
- x.assign(s.begin(), s.end());
- return *this;
-}
-
-} // namespace msgpack
diff --git a/msgpack.hh b/msgpack.hh
deleted file mode 100644
index 8b84e34..0000000
--- a/msgpack.hh
+++ /dev/null
@@ -1,692 +0,0 @@
-#ifndef GSTORE_MSGPACK_HH
-#define GSTORE_MSGPACK_HH
-#include "json.hh"
-#include "small_vector.hh"
-#include "straccum.hh"
-#include <vector>
-namespace msgpack {
-using lcdf::Json;
-using lcdf::Str;
-using lcdf::String;
-using lcdf::StringAccum;
-
-namespace format {
-enum {
- ffixuint = 0x00, nfixuint = 0x80,
- ffixmap = 0x80, nfixmap = 0x10,
- ffixarray = 0x90, nfixarray = 0x10,
- ffixstr = 0xA0, nfixstr = 0x20,
- fnull = 0xC0,
- ffalse = 0xC2, ftrue = 0xC3,
- fbin8 = 0xC4, fbin16 = 0xC5, fbin32 = 0xC6,
- fext8 = 0xC7, fext16 = 0xC8, fext32 = 0xC9,
- ffloat32 = 0xCA, ffloat64 = 0xCB,
- fuint8 = 0xCC, fuint16 = 0xCD, fuint32 = 0xCE, fuint64 = 0xCF,
- fint8 = 0xD0, fint16 = 0xD1, fint32 = 0xD2, fint64 = 0xD3,
- ffixext1 = 0xD4, ffixext2 = 0xD5, ffixext4 = 0xD6,
- ffixext8 = 0xD7, ffixext16 = 0xD8,
- fstr8 = 0xD9, fstr16 = 0xDA, fstr32 = 0xDB,
- farray16 = 0xDC, farray32 = 0xDD,
- fmap16 = 0xDE, fmap32 = 0xDF,
- ffixnegint = 0xE0, nfixnegint = 0x20,
- nfixint = nfixuint + nfixnegint
-};
-
-inline bool in_range(uint8_t x, unsigned low, unsigned n) {
- return (unsigned) x - low < n;
-}
-inline bool in_wrapped_range(uint8_t x, unsigned low, unsigned n) {
- return (unsigned) (int8_t) x - low < n;
-}
-
-inline bool is_fixint(uint8_t x) {
- return in_wrapped_range(x, -nfixnegint, nfixint);
-}
-inline bool is_null_or_bool(uint8_t x) {
- return in_range(x, fnull, 4);
-}
-inline bool is_bool(uint8_t x) {
- return in_range(x, ffalse, 2);
-}
-inline bool is_fixstr(uint8_t x) {
- return in_range(x, ffixstr, nfixstr);
-}
-inline bool is_fixarray(uint8_t x) {
- return in_range(x, ffixarray, nfixarray);
-}
-inline bool is_fixmap(uint8_t x) {
- return in_range(x, ffixmap, nfixmap);
-}
-
-inline char* write_null(char* s) {
- *s++ = fnull;
- return s;
-}
-inline char* write_bool(char* s, bool x) {
- *s++ = ffalse + x;
- return s;
-}
-
-template <size_t s> struct sized_writer {};
-template <> struct sized_writer<4> {
- static inline char* write_unsigned(char* s, uint32_t x) {
- if (x < nfixuint)
- *s++ = x;
- else if (x < 256) {
- *s++ = fuint8;
- *s++ = x;
- } else if (x < 65536) {
- *s++ = fuint16;
- s = write_in_net_order<uint16_t>(s, (uint16_t) x);
- } else {
- *s++ = fuint32;
- s = write_in_net_order<uint32_t>(s, x);
- }
- return s;
- }
- static inline char* write_signed(char* s, int32_t x) {
- if ((uint32_t) x + nfixnegint < nfixint)
- *s++ = x;
- else if ((uint32_t) x + 128 < 256) {
- *s++ = fint8;
- *s++ = x;
- } else if ((uint32_t) x + 32768 < 65536) {
- *s++ = fint16;
- s = write_in_net_order<int16_t>(s, (int16_t) x);
- } else {
- *s++ = fint32;
- s = write_in_net_order<int32_t>(s, x);
- }
- return s;
- }
-};
-template <> struct sized_writer<8> {
- static inline char* write_unsigned(char* s, uint64_t x) {
- if (x < 4294967296ULL)
- return sized_writer<4>::write_unsigned(s, (uint32_t) x);
- else {
- *s++ = fuint64;
- return write_in_net_order<uint64_t>(s, x);
- }
- }
- static inline char* write_signed(char* s, int64_t x) {
- if (x + 2147483648ULL < 4294967296ULL)
- return sized_writer<4>::write_signed(s, (int32_t) x);
- else {
- *s++ = fint64;
- return write_in_net_order<int64_t>(s, x);
- }
- }
-};
-inline char* write_int(char* s, int x) {
- return sized_writer<sizeof(x)>::write_signed(s, x);
-}
-inline char* write_int(char* s, unsigned x) {
- return sized_writer<sizeof(x)>::write_unsigned(s, x);
-}
-inline char* write_int(char* s, long x) {
- return sized_writer<sizeof(x)>::write_signed(s, x);
-}
-inline char* write_int(char* s, unsigned long x) {
- return sized_writer<sizeof(x)>::write_unsigned(s, x);
-}
-#if HAVE_LONG_LONG && SIZEOF_LONG_LONG <= 8
-inline char* write_int(char* s, long long x) {
- return sized_writer<sizeof(x)>::write_signed(s, x);
-}
-inline char* write_int(char* s, unsigned long long x) {
- return sized_writer<sizeof(x)>::write_unsigned(s, x);
-}
-#endif
-inline char* write_wide_int64(char* s, uint64_t x) {
- *s++ = fuint64;
- return write_in_net_order<uint64_t>(s, x);
-}
-inline char* write_wide_int64(char* s, int64_t x) {
- *s++ = fint64;
- return write_in_net_order<int64_t>(s, x);
-}
-inline char* write_float(char* s, float x) {
- *s++ = ffloat32;
- return write_in_net_order<float>(s, x);
-}
-inline char* write_double(char* s, double x) {
- *s++ = ffloat64;
- return write_in_net_order<double>(s, x);
-}
-inline char* write_string(char* s, const char *data, int len) {
- if (len < nfixstr)
- *s++ = 0xA0 + len;
- else if (len < 256) {
- *s++ = fstr8;
- *s++ = len;
- } else if (len < 65536) {
- *s++ = fstr16;
- s = write_in_net_order<uint16_t>(s, (uint16_t) len);
- } else {
- *s++ = fstr32;
- s = write_in_net_order<uint32_t>(s, len);
- }
- memcpy(s, data, len);
- return s + len;
-}
-inline char* write_string(char* s, Str x) {
- return write_string(s, x.data(), x.length());
-}
-template <typename T>
-inline char* write_string(char* s, const lcdf::String_base<T>& x) {
- return write_string(s, x.data(), x.length());
-}
-inline char* write_array_header(char* s, uint32_t size) {
- if (size < nfixarray) {
- *s++ = ffixarray + size;
- return s;
- } else if (size < 65536) {
- *s++ = farray16;
- return write_in_net_order<uint16_t>(s, (uint16_t) size);
- } else {
- *s++ = farray32;
- return write_in_net_order<uint32_t>(s, (uint32_t) size);
- }
-}
-inline char* write_map_header(char* s, uint32_t size) {
- if (size < nfixmap) {
- *s++ = ffixmap + size;
- return s;
- } else if (size < 65536) {
- *s++ = fmap16;
- return write_in_net_order<uint16_t>(s, (uint16_t) size);
- } else {
- *s++ = fmap32;
- return write_in_net_order<uint32_t>(s, (uint32_t) size);
- }
-}
-} // namespace format
-
-struct array_t {
- uint32_t size;
- array_t(uint32_t s)
- : size(s) {
- }
-};
-
-inline array_t array(uint32_t size) {
- return array_t(size);
-}
-
-struct object_t {
- uint32_t size;
- object_t(uint32_t s)
- : size(s) {
- }
-};
-
-inline object_t object(uint32_t size) {
- return object_t(size);
-}
-
-template <typename T>
-class unparser {
- public:
- inline unparser(T& base)
- : base_(base) {
- }
- template <typename X>
- inline unparser(T& base, const X& x)
- : base_(base) {
- *this << x;
- }
-
- inline void clear() {
- base_.clear();
- }
-
- inline unparser<T>& null() {
- base_.append((char) format::fnull);
- return *this;
- }
- inline unparser<T>& operator<<(const Json::null_t&) {
- return null();
- }
- inline unparser<T>& operator<<(int x) {
- char* s = base_.reserve(sizeof(x) + 1);
- base_.set_end(format::write_int(s, x));
- return *this;
- }
- inline unparser<T>& operator<<(unsigned x) {
- char* s = base_.reserve(sizeof(x) + 1);
- base_.set_end(format::write_int(s, x));
- return *this;
- }
- inline unparser<T>& operator<<(long x) {
- char* s = base_.reserve(sizeof(x) + 1);
- base_.set_end(format::write_int(s, x));
- return *this;
- }
- inline unparser<T>& operator<<(unsigned long x) {
- char* s = base_.reserve(sizeof(x) + 1);
- base_.set_end(format::write_int(s, x));
- return *this;
- }
-#if HAVE_LONG_LONG && SIZEOF_LONG_LONG <= 8
- inline unparser<T>& operator<<(long long x) {
- char* s = base_.reserve(sizeof(x) + 1);
- base_.set_end(format::write_int(s, x));
- return *this;
- }
- inline unparser<T>& operator<<(unsigned long long x) {
- char* s = base_.reserve(sizeof(x) + 1);
- base_.set_end(format::write_int(s, x));
- return *this;
- }
-#endif
- inline unparser<T>& write_wide(int64_t x) {
- char* s = base_.reserve(sizeof(x) + 1);
- base_.set_end(format::write_wide_int64(s, x));
- return *this;
- }
- inline unparser<T>& write_wide(uint64_t x) {
- char* s = base_.reserve(sizeof(x) + 1);
- base_.set_end(format::write_wide_int64(s, x));
- return *this;
- }
- inline unparser<T>& operator<<(float x) {
- char* s = base_.reserve(sizeof(x) + 1);
- base_.set_end(format::write_float(s, x));
- return *this;
- }
- inline unparser<T>& operator<<(double x) {
- char* s = base_.reserve(sizeof(x) + 1);
- base_.set_end(format::write_double(s, x));
- return *this;
- }
- inline unparser<T>& operator<<(Str x) {
- char* s = base_.reserve(5 + x.length());
- base_.set_end(format::write_string(s, x.data(), x.length()));
- return *this;
- }
- template <typename X>
- inline unparser<T>& operator<<(const lcdf::String_base<X>& x) {
- char* s = base_.reserve(5 + x.length());
- base_.set_end(format::write_string(s, x.data(), x.length()));
- return *this;
- }
- inline unparser<T>& operator<<(array_t x) {
- char* s = base_.reserve(5);
- base_.set_end(format::write_array_header(s, x.size));
- return *this;
- }
- inline unparser<T>& write_array_header(uint32_t size) {
- char* s = base_.reserve(5);
- base_.set_end(format::write_array_header(s, size));
- return *this;
- }
- inline unparser<T>& operator<<(object_t x) {
- char* s = base_.reserve(5);
- base_.set_end(format::write_map_header(s, x.size));
- return *this;
- }
- unparser<T>& operator<<(const Json& j);
- template <typename X>
- inline unparser<T>& write(const X& x) {
- return *this << x;
- }
-
- private:
- T& base_;
-};
-
-class streaming_parser {
- public:
- inline streaming_parser();
- inline void reset();
-
- inline bool empty() const;
- inline bool done() const;
- inline bool success() const;
- inline bool error() const;
-
- inline size_t consume(const char* first, size_t length,
- const String& str = String());
- inline const char* consume(const char* first, const char* last,
- const String& str = String());
- const uint8_t* consume(const uint8_t* first, const uint8_t* last,
- const String& str = String());
-
- inline Json& result();
- inline const Json& result() const;
-
- private:
- enum {
- st_final = -2, st_error = -1, st_normal = 0, st_partial = 1,
- st_string = 2
- };
- struct selem {
- Json* jp;
- int size;
- };
- int state_;
- small_vector<selem, 2> stack_;
- String str_;
- Json json_;
- Json jokey_;
-};
-
-class parser {
- public:
- explicit inline parser(const char* s)
- : s_(reinterpret_cast<const unsigned char*>(s)), str_() {
- }
- explicit inline parser(const unsigned char* s)
- : s_(s), str_() {
- }
- explicit inline parser(const String& str)
- : s_(reinterpret_cast<const uint8_t*>(str.begin())), str_(str) {
- }
- inline const char* position() const {
- return reinterpret_cast<const char*>(s_);
- }
-
- inline bool try_read_null() {
- if (*s_ == format::fnull) {
- ++s_;
- return true;
- } else
- return false;
- }
- inline int read_tiny_int() {
- assert(format::is_fixint(*s_));
- return (int8_t) *s_++;
- }
- inline parser& read_tiny_int(int& x) {
- x = read_tiny_int();
- return *this;
- }
- template <typename T>
- inline parser& read_int(T& x) {
- if (format::is_fixint(*s_)) {
- x = (int8_t) *s_;
- ++s_;
- } else {
- assert((uint32_t) *s_ - format::fuint8 < 8);
- hard_read_int(x);
- }
- return *this;
- }
- inline parser& operator>>(int& x) {
- return read_int(x);
- }
- inline parser& operator>>(long& x) {
- return read_int(x);
- }
- inline parser& operator>>(long long& x) {
- return read_int(x);
- }
- inline parser& operator>>(unsigned& x) {
- return read_int(x);
- }
- inline parser& operator>>(unsigned long& x) {
- return read_int(x);
- }
- inline parser& operator>>(unsigned long long& x) {
- return read_int(x);
- }
- inline parser& operator>>(bool& x) {
- assert(format::is_bool(*s_));
- x = *s_ - format::ffalse;
- ++s_;
- return *this;
- }
- inline parser& operator>>(double& x) {
- assert(*s_ == format::ffloat64);
- x = read_in_net_order<double>(s_ + 1);
- s_ += 9;
- return *this;
- }
- parser& operator>>(Str& x);
- parser& operator>>(String& x);
- inline parser& read_array_header(unsigned& size) {
- if (format::is_fixarray(*s_)) {
- size = *s_ - format::ffixarray;
- s_ += 1;
- } else if (*s_ == format::farray16) {
- size = read_in_net_order<uint16_t>(s_ + 1);
- s_ += 3;
- } else {
- assert(*s_ == format::farray32);
- size = read_in_net_order<uint32_t>(s_ + 1);
- s_ += 5;
- }
- return *this;
- }
- template <typename T> parser& operator>>(::std::vector<T>& x);
- inline parser& operator>>(Json& j);
-
- inline parser& skip_primitives(unsigned n) {
- for (; n != 0; --n) {
- if (format::is_fixint(*s_) || format::is_null_or_bool(*s_))
- s_ += 1;
- else if (format::is_fixstr(*s_))
- s_ += 1 + (*s_ - format::ffixstr);
- else if (*s_ == format::fuint8 || *s_ == format::fint8)
- s_ += 2;
- else if (*s_ == format::fuint16 || *s_ == format::fint16)
- s_ += 3;
- else if (*s_ == format::fuint32 || *s_ == format::fint32
- || *s_ == format::ffloat32)
- s_ += 5;
- else if (*s_ == format::fstr8 || *s_ == format::fbin8)
- s_ += 2 + s_[1];
- else if (*s_ == format::fstr16 || *s_ == format::fbin16)
- s_ += 3 + read_in_net_order<uint16_t>(s_ + 1);
- else if (*s_ == format::fstr32 || *s_ == format::fbin32)
- s_ += 5 + read_in_net_order<uint32_t>(s_ + 1);
- }
- return *this;
- }
- inline parser& skip_primitive() {
- return skip_primitives(1);
- }
- inline parser& skip_array_size() {
- if (format::is_fixarray(*s_))
- s_ += 1;
- else if (*s_ == format::farray16)
- s_ += 3;
- else {
- assert(*s_ == format::farray32);
- s_ += 5;
- }
- return *this;
- }
- private:
- const uint8_t* s_;
- String str_;
- template <typename T> void hard_read_int(T& x);
-};
-
-template <typename T>
-void parser::hard_read_int(T& x) {
- switch (*s_) {
- case format::fuint8:
- x = s_[1];
- s_ += 2;
- break;
- case format::fuint16:
- x = read_in_net_order<uint16_t>(s_ + 1);
- s_ += 3;
- break;
- case format::fuint32:
- x = read_in_net_order<uint32_t>(s_ + 1);
- s_ += 5;
- break;
- case format::fuint64:
- x = read_in_net_order<uint64_t>(s_ + 1);
- s_ += 9;
- break;
- case format::fint8:
- x = (int8_t) s_[1];
- s_ += 2;
- break;
- case format::fint16:
- x = read_in_net_order<int16_t>(s_ + 1);
- s_ += 3;
- break;
- case format::fint32:
- x = read_in_net_order<int32_t>(s_ + 1);
- s_ += 5;
- break;
- case format::fint64:
- x = read_in_net_order<int64_t>(s_ + 1);
- s_ += 9;
- break;
- }
-}
-
-template <typename T>
-unparser<T>& unparser<T>::operator<<(const Json& j) {
- if (j.is_null())
- base_.append(char(format::fnull));
- else if (j.is_b())
- base_.append(char(format::ffalse + j.as_b()));
- else if (j.is_u()) {
- char* x = base_.reserve(9);
- base_.set_end(format::write_int(x, j.as_u()));
- } else if (j.is_i()) {
- char* x = base_.reserve(9);
- base_.set_end(format::write_int(x, j.as_i()));
- } else if (j.is_d()) {
- char* x = base_.reserve(9);
- base_.set_end(format::write_double(x, j.as_d()));
- } else if (j.is_s()) {
- char* x = base_.reserve(j.as_s().length() + 5);
- base_.set_end(format::write_string(x, j.as_s()));
- } else if (j.is_a()) {
- char* x = base_.reserve(5);
- base_.set_end(format::write_array_header(x, j.size()));
- for (auto it = j.cabegin(); it != j.caend(); ++it)
- *this << *it;
- } else if (j.is_o()) {
- char* x = base_.reserve(5);
- base_.set_end(format::write_map_header(x, j.size()));
- for (auto it = j.cobegin(); it != j.coend(); ++it) {
- char* x = base_.reserve(it.key().length() + 5);
- base_.set_end(format::write_string(x, it.key()));
- *this << it.value();
- }
- } else
- base_.append(char(format::fnull));
- return *this;
-}
-
-inline String unparse(const Json& j) {
- StringAccum sa;
- unparser<StringAccum>(sa, j);
- return sa.take_string();
-}
-template <typename T, typename X>
-inline T& unparse(T& s, const X& x) {
- unparser<T>(s) << x;
- return s;
-}
-template <typename T, typename X>
-inline T& unparse_wide(T& s, const X& x) {
- unparser<T>(s).write_wide(x);
- return s;
-}
-
-template <typename T>
-parser& parser::operator>>(::std::vector<T>& x) {
- uint32_t sz;
- if ((uint32_t) *s_ - format::ffixarray < format::nfixarray) {
- sz = *s_ - format::ffixarray;
- ++s_;
- } else if (*s_ == format::farray16) {
- sz = read_in_net_order<uint16_t>(s_ + 1);
- s_ += 3;
- } else {
- assert(*s_ == format::farray32);
- sz = read_in_net_order<uint32_t>(s_ + 1);
- s_ += 5;
- }
- for (; sz != 0; --sz) {
- x.push_back(T());
- parse(x.back());
- }
- return *this;
-}
-
-inline streaming_parser::streaming_parser()
- : state_(st_normal) {
-}
-
-inline void streaming_parser::reset() {
- state_ = st_normal;
- stack_.clear();
-}
-
-inline bool streaming_parser::empty() const {
- return state_ == st_normal && stack_.empty();
-}
-
-inline bool streaming_parser::done() const {
- return state_ < 0;
-}
-
-inline bool streaming_parser::success() const {
- return state_ == st_final;
-}
-
-inline bool streaming_parser::error() const {
- return state_ == st_error;
-}
-
-inline const char* streaming_parser::consume(const char* first,
- const char* last,
- const String& str) {
- return reinterpret_cast<const char*>
- (consume(reinterpret_cast<const uint8_t*>(first),
- reinterpret_cast<const uint8_t*>(last), str));
-}
-
-inline size_t streaming_parser::consume(const char* first, size_t length,
- const String& str) {
- const uint8_t* ufirst = reinterpret_cast<const uint8_t*>(first);
- return consume(ufirst, ufirst + length, str) - ufirst;
-}
-
-inline Json& streaming_parser::result() {
- return json_;
-}
-
-inline const Json& streaming_parser::result() const {
- return json_;
-}
-
-inline parser& parser::operator>>(Json& j) {
- using std::swap;
- streaming_parser sp;
- while (!sp.done())
- s_ = sp.consume(s_, s_ + 4096, str_);
- if (sp.success())
- swap(j, sp.result());
- return *this;
-}
-
-inline Json parse(const char* first, const char* last) {
- streaming_parser sp;
- first = sp.consume(first, last, String());
- if (sp.success())
- return sp.result();
- return Json();
-}
-
-inline Json parse(const String& str) {
- streaming_parser sp;
- sp.consume(str.begin(), str.end(), str);
- if (sp.success())
- return sp.result();
- return Json();
-}
-
-} // namespace msgpack
-#endif
diff --git a/msgpacktest.cc b/msgpacktest.cc
deleted file mode 100644
index bff1deb..0000000
--- a/msgpacktest.cc
+++ /dev/null
@@ -1,209 +0,0 @@
-#include "msgpack.hh"
-using namespace lcdf;
-
-enum { status_ok, status_error, status_incomplete };
-
-__attribute__((noreturn))
-static void test_error(const char* file, int line,
- const char* data, int len,
- String division, String message) {
- std::cerr << file << ":" << line << " ("
- << String(data, data + len).printable() << ")"
- << (division ? " " : "") << division << ": "
- << message << "\n";
- exit(1);
-}
-
-static void onetest(const char* file, int line,
- const char* data, int len,
- String division, const char* take, int expected_take,
- const char* unparse, int status,
- msgpack::streaming_parser& a) {
- if (expected_take >= 0 && take != data + expected_take)
- test_error(file, line, data, len, division, "accept took " + String(take - data) + " chars, expected " + String(expected_take));
-
- if (status == status_ok && !a.success())
- test_error(file, line, data, len, division, "accept not done");
- if (status == status_error && !a.error())
- test_error(file, line, data, len, division, "accept not error");
- if (status == status_incomplete && a.done())
- test_error(file, line, data, len, division, "accept not incomplete");
-
- if (unparse && a.result().unparse() != unparse)
- test_error(file, line, data, len, division, "result was " + a.result().unparse() + "\n\texpected " + String(unparse));
-}
-
-static void test(const char* file, int line,
- const char* data, int len, int expected_take,
- const char* unparse, int status = status_ok) {
- assert(expected_take <= len);
-
- msgpack::streaming_parser a;
- const char* take;
- take = a.consume(data, data + len);
- onetest(file, line, data, len, "", take, expected_take, unparse, status, a);
-
- if (len > 1) {
- a.reset();
- take = data;
- while (take != data + len) {
- const char* x = a.consume(take, take + 1);
- if (x != take + (take < data + expected_take))
- test_error(file, line, data, len, "by 1s", "accept took unusual amount after " + String(x - data));
- ++take;
- }
- onetest(file, line, data, len, "by 1s", take, -1, unparse, status, a);
- }
-}
-
-#define TEST(...) test(__FILE__, __LINE__, ## __VA_ARGS__)
-
-void check_correctness() {
- TEST("\0", 1, 1, "0");
- TEST("\xFF ", 3, 1, "-1");
- TEST("\xC0 ", 3, 1, "null");
- TEST("\xC2 ", 3, 1, "false");
- TEST("\xC3 ", 3, 1, "true");
- TEST("\xD0\xEE", 2, 2, "-18");
- TEST("\x81\xA7" "compact\xC3", 11, 10, "{\"compact\":true}");
- TEST("\x81\x00\x81\xA7" "compact\xC3", 13, 12, "{\"0\":{\"compact\":true}}");
- TEST("\x82\x00\x81\xA7" "compact\xC3\xA1" "a\xC2", 16, 15, "{\"0\":{\"compact\":true},\"a\":false}");
- TEST("\x93\x00\x01\x02", 5, 4, "[0,1,2]");
- TEST("\x90 ", 5, 1, "[]");
- TEST("\xDC\x00\x00 ", 5, 3, "[]");
- TEST("\224\002\322\000\001\242\321\262p|00356|1000000000\245?!?#*\225\001\322\000\001\242\322\242t|\242t}\332\000Rt|<user_id:5>|<time:10>|<poster_id:5> s|<user_id>|<poster_id> p|<poster_id>|<time>",
- 130, 32, "[2,107217,\"p|00356|1000000000\",\"?!?#*\"]", status_ok);
- TEST("\xCF\x80\0\0\0\0\0\0\0", 9, 9, "9223372036854775808");
-
- {
- msgpack::streaming_parser a;
- Json j = Json::array(0, 0, 0);
- swap(j, a.result());
- a.reset();
- a.consume("\x91\xC2", 2);
- assert(a.success() && a.result().unparse() == "[false]");
- a.reset();
- a.consume("\xC0", 1);
- assert(a.success() && a.result().unparse() == "null");
- a.reset();
- a.consume("\x82\xA7" "compact\xC3\x00\x00", 12);
- assert(a.success() && a.result().unparse() == "{\"compact\":true,\"0\":0}");
- a.reset();
- a.consume("\x82\xA7" "compact\xC3\x00\x00", 12);
- assert(a.success() && a.result().unparse() == "{\"compact\":true,\"0\":0}");
- }
-
- {
- StringAccum sa;
- msgpack::unparser<StringAccum> up(sa);
- up.clear();
- up << -32;
- assert(sa.take_string() == "\xE0");
- up.clear();
- up << -33;
- assert(sa.take_string() == "\xD0\xDF");
- up.clear();
- up << 127;
- assert(sa.take_string() == "\x7F");
- up.clear();
- up << 128;
- assert(sa.take_string() == String("\xD1\x00\x80", 3));
- up << -32768;
- assert(sa.take_string() == String("\xD1\x80\x00", 3));
- up << -32769;
- assert(sa.take_string() == String("\xD2\xFF\xFF\x7F\xFF", 5));
- }
-
- {
- StringAccum sa;
- msgpack::unparser<StringAccum> up(sa);
- up.clear();
- up << msgpack::array(2) << Json((uint64_t) 1 << 63)
- << Json((int64_t) 1 << 63);
- String result = sa.take_string();
- TEST(result.c_str(), result.length(), result.length(),
- "[9223372036854775808,-9223372036854775808]");
- }
-
- std::cout << "All tests pass!\n";
-}
-
-Json __attribute__((noinline)) parse_json(const char* first, const char* last) {
- return Json::parse(first, last);
-}
-
-Json __attribute__((noinline)) parse_json(const String& str) {
- return Json::parse(str);
-}
-
-static const char sample_json[] = "{\"name\": \"Deborah Estrin\", \"email\": \"estrin@usc.edu\", \"affiliation\": \"University of Southern California\", \"roles\": [\"pc\"]}";
-
-static const char sample_msgpack[] = "\204\244name\256Deborah Estrin\245email\256estrin@usc.edu\253affiliation\331!University of Southern California\245roles\221\242pc";
-
-static int parse_json_loop_size = 10000000;
-
-void parse_json_loop_1() {
- int total_size = 0;
- const char* sample_json_end = sample_json + strlen(sample_json);
- for (int i = 0; i != parse_json_loop_size; ++i) {
- Json j = Json::parse(sample_json, sample_json_end);
- total_size += j.size();
- }
- assert(total_size == 4 * parse_json_loop_size);
-}
-
-void parse_json_loop_2() {
- int total_size = 0;
- const char* sample_json_end = sample_json + strlen(sample_json);
- for (int i = 0; i != parse_json_loop_size; ++i) {
- Json j = Json::parse(String(sample_json, sample_json_end));
- total_size += j.size();
- }
- assert(total_size == 4 * parse_json_loop_size);
-}
-
-void parse_json_loop_3() {
- int total_size = 0;
- String sample_json_str(sample_json);
- for (int i = 0; i != parse_json_loop_size; ++i) {
- Json j = Json::parse(sample_json_str);
- total_size += j.size();
- }
- assert(total_size == 4 * parse_json_loop_size);
-}
-
-void parse_msgpack_loop_1() {
- int total_size = 0;
- const char* sample_msgpack_end = sample_msgpack + strlen(sample_msgpack);
- for (int i = 0; i != parse_json_loop_size; ++i) {
- Json j = msgpack::parse(sample_msgpack, sample_msgpack_end);
- total_size += j.size();
- }
- assert(total_size == 4 * parse_json_loop_size);
-}
-
-void parse_msgpack_loop_2() {
- int total_size = 0;
- const char* sample_msgpack_end = sample_msgpack + strlen(sample_msgpack);
- for (int i = 0; i != parse_json_loop_size; ++i) {
- Json j = msgpack::parse(String(sample_msgpack, sample_msgpack_end));
- total_size += j.size();
- }
- assert(total_size == 4 * parse_json_loop_size);
-}
-
-void parse_msgpack_loop_3() {
- int total_size = 0;
- String sample_msgpack_str(sample_msgpack);
- for (int i = 0; i != parse_json_loop_size; ++i) {
- Json j = msgpack::parse(sample_msgpack_str);
- total_size += j.size();
- }
- assert(total_size == 4 * parse_json_loop_size);
-}
-
-int main(int argc, char** argv) {
- (void) argc, (void) argv;
-
- check_correctness();
-}
diff --git a/mtclient.cc b/mtclient.cc
deleted file mode 100644
index dad55eb..0000000
--- a/mtclient.cc
+++ /dev/null
@@ -1,1724 +0,0 @@
-/* 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.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <unistd.h>
-#include <limits.h>
-#include <signal.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <sys/select.h>
-#include <sys/wait.h>
-#include <assert.h>
-#include <string.h>
-#include <pthread.h>
-#include <ctype.h>
-#include <sys/types.h>
-#include <sys/time.h>
-#include <sys/select.h>
-#include <arpa/inet.h>
-#include <math.h>
-#include <fcntl.h>
-#include "kvstats.hh"
-#include "kvio.hh"
-#include "json.hh"
-#include "kvtest.hh"
-#include "mtclient.hh"
-#include "kvrandom.hh"
-#include "clp.h"
-
-const char *serverip = "127.0.0.1";
-static Json test_param;
-
-typedef void (*get_async_cb)(struct child *c, struct async *a,
- bool has_val, const Str &val);
-typedef void (*put_async_cb)(struct child *c, struct async *a,
- int status);
-typedef void (*remove_async_cb)(struct child *c, struct async *a,
- int status);
-
-struct async {
- int cmd; // Cmd_ constant
- unsigned seq;
- union {
- get_async_cb get_fn;
- put_async_cb put_fn;
- remove_async_cb remove_fn;
- };
- char key[16]; // just first 16 bytes
- char wanted[16]; // just first 16 bytes
- int wantedlen;
- int acked;
-};
-#define MAXWINDOW 512
-unsigned window = MAXWINDOW;
-
-struct child {
- int s;
- int udp; // 1 -> udp, 0 -> tcp
- KVConn *conn;
-
- struct async a[MAXWINDOW];
-
- unsigned seq0_;
- unsigned seq1_;
- unsigned long long nsent_;
- int childno;
-
- inline void check_flush();
-};
-
-void checkasync(struct child *c, int force);
-
-inline void child::check_flush() {
- if ((seq1_ & ((window - 1) >> 1)) == 0)
- conn->flush();
- while (seq1_ - seq0_ >= window)
- checkasync(this, 1);
-}
-
-void aget(struct child *, const Str &key, const Str &wanted, get_async_cb fn);
-void aget(struct child *c, long ikey, long iwanted, get_async_cb fn);
-void aget_col(struct child *c, const Str& key, int col, const Str& wanted,
- get_async_cb fn);
-int get(struct child *c, const Str &key, char *val, int max);
-
-void asyncgetcb(struct child *, struct async *a, bool, const Str &val);
-void asyncgetcb_int(struct child *, struct async *a, bool, const Str &val);
-
-void aput(struct child *c, const Str &key, const Str &val,
- put_async_cb fn = 0, const Str &wanted = Str());
-void aput_col(struct child *c, const Str &key, int col, const Str &val,
- put_async_cb fn = 0, const Str &wanted = Str());
-int put(struct child *c, const Str &key, const Str &val);
-void asyncputcb(struct child *, struct async *a, int status);
-
-void aremove(struct child *c, const Str &key, remove_async_cb fn);
-bool remove(struct child *c, const Str &key);
-
-void udp1(struct child *);
-void w1b(struct child *);
-void u1(struct child *);
-void over1(struct child *);
-void over2(struct child *);
-void rec1(struct child *);
-void rec2(struct child *);
-void cpa(struct child *);
-void cpb(struct child *);
-void cpc(struct child *);
-void cpd(struct child *);
-void volt1a(struct child *);
-void volt1b(struct child *);
-void volt2a(struct child *);
-void volt2b(struct child *);
-void scantest(struct child *);
-
-static int children = 1;
-static uint64_t nkeys = 0;
-static int prefixLen = 0;
-static int keylen = 0;
-static uint64_t limit = ~uint64_t(0);
-double duration = 10;
-double duration2 = 0;
-int udpflag = 0;
-int quiet = 0;
-int first_server_port = 2117;
-// Should all child processes connects to the same UDP PORT on server
-bool share_server_port = false;
-volatile bool timeout[2] = {false, false};
-int first_local_port = 0;
-const char *input = NULL;
-static int rsinit_part = 0;
-int kvtest_first_seed = 0;
-static int rscale_partsz = 0;
-static int getratio = -1;
-static int minkeyletter = '0';
-static int maxkeyletter = '9';
-
-
-struct kvtest_client {
- kvtest_client(struct child& c)
- : c_(&c) {
- }
- struct child* child() const {
- return c_;
- }
- int id() const {
- return c_->childno;
- }
- int nthreads() const {
- return ::children;
- }
- char minkeyletter() const {
- return ::minkeyletter;
- }
- char maxkeyletter() const {
- return ::maxkeyletter;
- }
- void register_timeouts(int n) {
- (void) n;
- }
- bool timeout(int which) const {
- return ::timeout[which];
- }
- uint64_t limit() const {
- return ::limit;
- }
- int getratio() const {
- assert(::getratio >= 0);
- return ::getratio;
- }
- uint64_t nkeys() const {
- return ::nkeys;
- }
- int keylen() const {
- return ::keylen;
- }
- int prefixLen() const {
- return ::prefixLen;
- }
- Json param(const String& name, Json default_value = Json()) {
- return test_param.count(name) ? test_param.at(name) : default_value;
- }
- double now() const {
- return ::now();
- }
-
- void get(long ikey, Str *value) {
- quick_istr key(ikey);
- aget(c_, key.string(),
- Str(reinterpret_cast<const char *>(&value), sizeof(value)),
- asyncgetcb);
- }
- void get(const Str &key, int *ivalue) {
- aget(c_, key,
- Str(reinterpret_cast<const char *>(&ivalue), sizeof(ivalue)),
- asyncgetcb_int);
- }
- bool get_sync(long ikey) {
- char got[512];
- quick_istr key(ikey);
- return ::get(c_, key.string(), got, sizeof(got)) >= 0;
- }
- void get_check(long ikey, long iexpected) {
- aget(c_, ikey, iexpected, 0);
- }
- void get_check(const char *key, const char *val) {
- aget(c_, Str(key), Str(val), 0);
- }
- void get_check(const Str &key, const Str &val) {
- aget(c_, key, val, 0);
- }
- void get_check_key8(long ikey, long iexpected) {
- quick_istr key(ikey, 8), expected(iexpected);
- aget(c_, key.string(), expected.string(), 0);
- }
- void get_check_key10(long ikey, long iexpected) {
- quick_istr key(ikey, 10), expected(iexpected);
- aget(c_, key.string(), expected.string(), 0);
- }
- void many_get_check(int, long [], long []) {
- assert(0);
- }
- void get_col_check(const Str &key, int col, const Str &value) {
- aget_col(c_, key, col, value, 0);
- }
- void get_col_check(long ikey, int col, long ivalue) {
- quick_istr key(ikey), value(ivalue);
- get_col_check(key.string(), col, value.string());
- }
- void get_col_check_key10(long ikey, int col, long ivalue) {
- quick_istr key(ikey, 10), value(ivalue);
- get_col_check(key.string(), col, value.string());
- }
- void get_check_sync(long ikey, long iexpected) {
- char key[512], val[512], got[512];
- sprintf(key, "%010ld", ikey);
- sprintf(val, "%ld", iexpected);
- memset(got, 0, sizeof(got));
- ::get(c_, Str(key), got, sizeof(got));
- if (strcmp(val, got)) {
- fprintf(stderr, "key %s, expected %s, got %s\n", key, val, got);
- always_assert(0);
- }
- }
-
- void put(const Str &key, const Str &value) {
- aput(c_, key, value);
- }
- void put(const Str &key, const Str &value, int *status) {
- aput(c_, key, value,
- asyncputcb,
- Str(reinterpret_cast<const char *>(&status), sizeof(status)));
- }
- void put(const char *key, const char *value) {
- aput(c_, Str(key), Str(value));
- }
- void put(const Str &key, long ivalue) {
- quick_istr value(ivalue);
- aput(c_, key, value.string());
- }
- void put(long ikey, long ivalue) {
- quick_istr key(ikey), value(ivalue);
- aput(c_, key.string(), value.string());
- }
- void put_key8(long ikey, long ivalue) {
- quick_istr key(ikey, 8), value(ivalue);
- aput(c_, key.string(), value.string());
- }
- void put_key10(long ikey, long ivalue) {
- quick_istr key(ikey, 10), value(ivalue);
- aput(c_, key.string(), value.string());
- }
- void put_col(const Str &key, int col, const Str &value) {
- aput_col(c_, key, col, value);
- }
- void put_col(long ikey, int col, long ivalue) {
- quick_istr key(ikey), value(ivalue);
- put_col(key.string(), col, value.string());
- }
- void put_col_key10(long ikey, int col, long ivalue) {
- quick_istr key(ikey, 10), value(ivalue);
- put_col(key.string(), col, value.string());
- }
- void put_sync(long ikey, long ivalue) {
- quick_istr key(ikey, 10), value(ivalue);
- ::put(c_, key.string(), value.string());
- }
-
- void remove(const Str &key) {
- aremove(c_, key, 0);
- }
- void remove(long ikey) {
- quick_istr key(ikey);
- remove(key.string());
- }
- bool remove_sync(long ikey) {
- quick_istr key(ikey);
- return ::remove(c_, key.string());
- }
-
- int ruscale_partsz() const {
- return ::rscale_partsz;
- }
- int ruscale_init_part_no() const {
- return ::rsinit_part;
- }
- long nseqkeys() const {
- return 16 * ::rscale_partsz;
- }
- void wait_all() {
- checkasync(c_, 2);
- }
- void puts_done() {
- }
- void rcu_quiesce() {
- }
- void notice(String s) {
- if (!quiet) {
- if (!s.empty() && s.back() == '\n')
- s = s.substr(0, -1);
- if (s.empty() || isspace((unsigned char) s[0]))
- fprintf(stderr, "%d%.*s\n", c_->childno, s.length(), s.data());
- else
- fprintf(stderr, "%d %.*s\n", c_->childno, s.length(), s.data());
- }
- }
- void notice(const char *fmt, ...) {
- if (!quiet) {
- va_list val;
- va_start(val, fmt);
- String x;
- if (!*fmt || isspace((unsigned char) *fmt))
- x = String(c_->childno) + fmt;
- else
- x = String(c_->childno) + String(" ") + fmt;
- vfprintf(stderr, x.c_str(), val);
- va_end(val);
- }
- }
- const Json& report(const Json& x) {
- return report_.merge(x);
- }
- void finish() {
- if (!quiet) {
- lcdf::StringAccum sa;
- double dv;
- if (report_.count("puts"))
- sa << " total " << report_.get("puts");
- if (report_.get("puts_per_sec", dv))
- sa.snprintf(100, " %.0f put/s", dv);
- if (report_.get("gets_per_sec", dv))
- sa.snprintf(100, " %.0f get/s", dv);
- if (!sa.empty())
- notice(sa.take_string());
- }
- printf("%s\n", report_.unparse().c_str());
- }
- kvrandom_random rand;
- struct child *c_;
- Json report_;
-};
-
-
-#define TESTRUNNER_CLIENT_TYPE kvtest_client&
-#include "testrunner.hh"
-
-MAKE_TESTRUNNER(rw1, kvtest_rw1(client));
-MAKE_TESTRUNNER(rw2, kvtest_rw2(client));
-MAKE_TESTRUNNER(rw3, kvtest_rw3(client));
-MAKE_TESTRUNNER(rw4, kvtest_rw4(client));
-MAKE_TESTRUNNER(rw1fixed, kvtest_rw1fixed(client));
-MAKE_TESTRUNNER(rw16, kvtest_rw16(client));
-MAKE_TESTRUNNER(sync_rw1, kvtest_sync_rw1(client));
-MAKE_TESTRUNNER(r1, kvtest_r1_seed(client, kvtest_first_seed + client.id()));
-MAKE_TESTRUNNER(w1, kvtest_w1_seed(client, kvtest_first_seed + client.id()));
-MAKE_TESTRUNNER(w1b, w1b(client.child()));
-MAKE_TESTRUNNER(u1, u1(client.child()));
-MAKE_TESTRUNNER(wd1, kvtest_wd1(10000000, 1, client));
-MAKE_TESTRUNNER(wd1m1, kvtest_wd1(100000000, 1, client));
-MAKE_TESTRUNNER(wd1m2, kvtest_wd1(1000000000, 4, client));
-MAKE_TESTRUNNER(wd1check, kvtest_wd1_check(10000000, 1, client));
-MAKE_TESTRUNNER(wd1m1check, kvtest_wd1_check(100000000, 1, client));
-MAKE_TESTRUNNER(wd1m2check, kvtest_wd1_check(1000000000, 4, client));
-MAKE_TESTRUNNER(wd2, kvtest_wd2(client));
-MAKE_TESTRUNNER(wd2check, kvtest_wd2_check(client));
-MAKE_TESTRUNNER(tri1, kvtest_tri1(10000000, 1, client));
-MAKE_TESTRUNNER(tri1check, kvtest_tri1_check(10000000, 1, client));
-MAKE_TESTRUNNER(same, kvtest_same(client));
-MAKE_TESTRUNNER(wcol1, kvtest_wcol1at(client, client.id() % 24, kvtest_first_seed + client.id() % 48, 5000000));
-MAKE_TESTRUNNER(rcol1, kvtest_rcol1at(client, client.id() % 24, kvtest_first_seed + client.id() % 48, 5000000));
-MAKE_TESTRUNNER(wcol1o1, kvtest_wcol1at(client, (client.id() + 1) % 24, kvtest_first_seed + client.id() % 48, 5000000));
-MAKE_TESTRUNNER(rcol1o1, kvtest_rcol1at(client, (client.id() + 1) % 24, kvtest_first_seed + client.id() % 48, 5000000));
-MAKE_TESTRUNNER(wcol1o2, kvtest_wcol1at(client, (client.id() + 2) % 24, kvtest_first_seed + client.id() % 48, 5000000));
-MAKE_TESTRUNNER(rcol1o2, kvtest_rcol1at(client, (client.id() + 2) % 24, kvtest_first_seed + client.id() % 48, 5000000));
-MAKE_TESTRUNNER(over1, over1(client.child()));
-MAKE_TESTRUNNER(over2, over2(client.child()));
-MAKE_TESTRUNNER(rec1, rec1(client.child()));
-MAKE_TESTRUNNER(rec2, rec2(client.child()));
-MAKE_TESTRUNNER(cpa, cpa(client.child()));
-MAKE_TESTRUNNER(cpb, cpb(client.child()));
-MAKE_TESTRUNNER(cpc, cpc(client.child()));
-MAKE_TESTRUNNER(cpd, cpd(client.child()));
-MAKE_TESTRUNNER(volt1a, volt1a(client.child()));
-MAKE_TESTRUNNER(volt1b, volt1b(client.child()));
-MAKE_TESTRUNNER(volt2a, volt2a(client.child()));
-MAKE_TESTRUNNER(volt2b, volt2b(client.child()));
-MAKE_TESTRUNNER(scantest, scantest(client.child()));
-MAKE_TESTRUNNER(wscale, kvtest_wscale(client));
-MAKE_TESTRUNNER(ruscale_init, kvtest_ruscale_init(client));
-MAKE_TESTRUNNER(rscale, kvtest_rscale(client));
-MAKE_TESTRUNNER(uscale, kvtest_uscale(client));
-MAKE_TESTRUNNER(long_init, kvtest_long_init(client));
-MAKE_TESTRUNNER(long_go, kvtest_long_go(client));
-MAKE_TESTRUNNER(udp1, kvtest_udp1(client));
-
-void run_child(testrunner*, int childno);
-
-
-void
-usage()
-{
- fprintf(stderr, "Usage: mtclient [-s serverip] [-w window] [--udp] "\
- "[-j nchildren] [-d duration] [--ssp] [--flp first_local_port] "\
- "[--fsp first_server_port] [-i json_input]\nTests:\n");
- testrunner::print_names(stderr, 5);
- exit(1);
-}
-
-void
-settimeout(int)
-{
- if (!timeout[0]) {
- timeout[0] = true;
- if (duration2)
- alarm((int) ceil(duration2));
- } else
- timeout[1] = true;
-}
-
-enum { clp_val_suffixdouble = Clp_ValFirstUser };
-enum { opt_threads = 1, opt_threads_deprecated, opt_duration, opt_duration2,
- opt_window, opt_server, opt_first_server_port, opt_quiet, opt_udp,
- opt_first_local_port, opt_share_server_port, opt_input,
- opt_rsinit_part, opt_first_seed, opt_rscale_partsz, opt_keylen,
- opt_limit, opt_prefix_len, opt_nkeys, opt_get_ratio, opt_minkeyletter,
- opt_maxkeyletter, opt_nofork };
-static const Clp_Option options[] = {
- { "threads", 'j', opt_threads, Clp_ValInt, 0 },
- { 0, 'n', opt_threads_deprecated, Clp_ValInt, 0 },
- { "duration", 'd', opt_duration, Clp_ValDouble, 0 },
- { "duration2", 0, opt_duration2, Clp_ValDouble, 0 },
- { "d2", 0, opt_duration2, Clp_ValDouble, 0 },
- { "window", 'w', opt_window, Clp_ValUnsigned, 0 },
- { "server-ip", 's', opt_server, Clp_ValString, 0 },
- { "first-server-port", 0, opt_first_server_port, Clp_ValInt, 0 },
- { "fsp", 0, opt_first_server_port, Clp_ValInt, 0 },
- { "quiet", 'q', opt_quiet, 0, Clp_Negate },
- { "udp", 'u', opt_udp, 0, Clp_Negate },
- { "first-local-port", 0, opt_first_local_port, Clp_ValInt, 0 },
- { "flp", 0, opt_first_local_port, Clp_ValInt, 0 },
- { "share-server-port", 0, opt_share_server_port, 0, Clp_Negate },
- { "ssp", 0, opt_share_server_port, 0, Clp_Negate },
- { "input", 'i', opt_input, Clp_ValString, 0 },
- { "rsinit_part", 0, opt_rsinit_part, Clp_ValInt, 0 },
- { "first_seed", 0, opt_first_seed, Clp_ValInt, 0 },
- { "rscale_partsz", 0, opt_rscale_partsz, Clp_ValInt, 0 },
- { "keylen", 0, opt_keylen, Clp_ValInt, 0 },
- { "limit", 'l', opt_limit, clp_val_suffixdouble, 0 },
- { "prefixLen", 0, opt_prefix_len, Clp_ValInt, 0 },
- { "nkeys", 0, opt_nkeys, Clp_ValInt, 0 },
- { "getratio", 0, opt_get_ratio, Clp_ValInt, 0 },
- { "minkeyletter", 0, opt_minkeyletter, Clp_ValString, 0 },
- { "maxkeyletter", 0, opt_maxkeyletter, Clp_ValString, 0 },
- { "no-fork", 0, opt_nofork, 0, 0 }
-};
-
-int
-main(int argc, char *argv[])
-{
- int i, pid, status;
- testrunner* test = 0;
- int pipes[512];
- int dofork = 1;
-
- Clp_Parser *clp = Clp_NewParser(argc, argv, (int) arraysize(options), options);
- Clp_AddType(clp, clp_val_suffixdouble, Clp_DisallowOptions, clp_parse_suffixdouble, 0);
- int opt;
- while ((opt = Clp_Next(clp)) != Clp_Done) {
- switch (opt) {
- case opt_threads:
- children = clp->val.i;
- break;
- case opt_threads_deprecated:
- Clp_OptionError(clp, "%<%O%> is deprecated, use %<-j%>");
- children = clp->val.i;
- break;
- case opt_duration:
- duration = clp->val.d;
- break;
- case opt_duration2:
- duration2 = clp->val.d;
- break;
- case opt_window:
- window = clp->val.u;
- always_assert(window <= MAXWINDOW);
- always_assert((window & (window - 1)) == 0); // power of 2
- break;
- case opt_server:
- serverip = clp->vstr;
- break;
- case opt_first_server_port:
- first_server_port = clp->val.i;
- break;
- case opt_quiet:
- quiet = !clp->negated;
- break;
- case opt_udp:
- udpflag = !clp->negated;
- break;
- case opt_first_local_port:
- first_local_port = clp->val.i;
- break;
- case opt_share_server_port:
- share_server_port = !clp->negated;
- break;
- case opt_input:
- input = clp->vstr;
- break;
- case opt_rsinit_part:
- rsinit_part = clp->val.i;
- break;
- case opt_first_seed:
- kvtest_first_seed = clp->val.i;
- break;
- case opt_rscale_partsz:
- rscale_partsz = clp->val.i;
- break;
- case opt_keylen:
- keylen = clp->val.i;
- break;
- case opt_limit:
- limit = (uint64_t) clp->val.d;
- break;
- case opt_prefix_len:
- prefixLen = clp->val.i;
- break;
- case opt_nkeys:
- nkeys = clp->val.i;
- break;
- case opt_get_ratio:
- getratio = clp->val.i;
- break;
- case opt_minkeyletter:
- assert(strlen(clp->vstr) == 1);
- minkeyletter = clp->vstr[0];
- break;
- case opt_maxkeyletter:
- assert(strlen(clp->vstr) == 1);
- maxkeyletter = clp->vstr[0];
- break;
- case opt_nofork:
- dofork = !clp->negated;
- break;
- case Clp_NotOption: {
- // check for parameter setting
- if (const char* eqchr = strchr(clp->vstr, '=')) {
- Json& param = test_param[String(clp->vstr, eqchr)];
- const char* end_vstr = clp->vstr + strlen(clp->vstr);
- if (param.assign_parse(eqchr + 1, end_vstr)) {
- // OK, param was valid JSON
- } else if (eqchr[1] != 0) {
- param = String(eqchr + 1, end_vstr);
- } else {
- param = Json();
- }
- } else {
- test = testrunner::find(clp->vstr);
- if (!test) {
- usage();
- }
- }
- break;
- }
- case Clp_BadOption:
- usage();
- break;
- }
- }
- if(children < 1 || (children != 1 && !dofork))
- usage();
- if (!test)
- test = testrunner::first();
-
- printf("%s, w %d, test %s, children %d\n",
- udpflag ? "udp" : "tcp", window,
- test->name().c_str(), children);
-
- fflush(stdout);
-
- if (dofork) {
- for(i = 0; i < children; i++){
- int ptmp[2];
- int r = pipe(ptmp);
- always_assert(r == 0);
- pid = fork();
- if(pid < 0){
- perror("fork");
- exit(1);
- }
- if(pid == 0){
- close(ptmp[0]);
- dup2(ptmp[1], 1);
- close(ptmp[1]);
- signal(SIGALRM, settimeout);
- alarm((int) ceil(duration));
- run_child(test, i);
- exit(0);
- }
- pipes[i] = ptmp[0];
- close(ptmp[1]);
- }
- for(i = 0; i < children; i++){
- if(wait(&status) <= 0){
- perror("wait");
- exit(1);
- }
- if (WIFSIGNALED(status))
- fprintf(stderr, "child %d died by signal %d\n", i, WTERMSIG(status));
- }
- } else {
- int ptmp[2];
- int r = pipe(ptmp);
- always_assert(r == 0);
- pipes[0] = ptmp[0];
- int stdout_fd = dup(STDOUT_FILENO);
- always_assert(stdout_fd > 0);
- r = dup2(ptmp[1], STDOUT_FILENO);
- always_assert(r >= 0);
- close(ptmp[1]);
- signal(SIGALRM, settimeout);
- alarm((int) ceil(duration));
- run_child(test, 0);
- fflush(stdout);
- r = dup2(stdout_fd, STDOUT_FILENO);
- always_assert(r >= 0);
- close(stdout_fd);
- }
-
- long long total = 0;
- kvstats puts, gets, scans, puts_per_sec, gets_per_sec, scans_per_sec;
- for(i = 0; i < children; i++){
- char buf[2048];
- int cc = read(pipes[i], buf, sizeof(buf)-1);
- assert(cc > 0);
- buf[cc] = 0;
- printf("%s", buf);
- Json bufj = Json::parse(buf, buf + cc);
- long long iv;
- double dv;
- if (bufj.to_i(iv))
- total += iv;
- else if (bufj.is_object()) {
- if (bufj.get("ops", iv)
- || bufj.get("total", iv)
- || bufj.get("count", iv))
- total += iv;
- if (bufj.get("puts", iv))
- puts.add(iv);
- if (bufj.get("gets", iv))
- gets.add(iv);
- if (bufj.get("scans", iv))
- scans.add(iv);
- if (bufj.get("puts_per_sec", dv))
- puts_per_sec.add(dv);
- if (bufj.get("gets_per_sec", dv))
- gets_per_sec.add(dv);
- if (bufj.get("scans_per_sec", dv))
- scans_per_sec.add(dv);
- }
- }
-
- printf("total %lld\n", total);
- puts.print_report("puts");
- gets.print_report("gets");
- scans.print_report("scans");
- puts_per_sec.print_report("puts/s");
- gets_per_sec.print_report("gets/s");
- scans_per_sec.print_report("scans/s");
-
- exit(0);
-}
-
-void
-run_child(testrunner* test, int childno)
-{
- struct sockaddr_in sin;
- int ret, yes = 1;
- struct child c;
-
- bzero(&c, sizeof(c));
- c.childno = childno;
-
- if(udpflag){
- c.udp = 1;
- c.s = socket(AF_INET, SOCK_DGRAM, 0);
- } else {
- c.s = socket(AF_INET, SOCK_STREAM, 0);
- }
- if (first_local_port) {
- bzero(&sin, sizeof(sin));
- sin.sin_family = AF_INET;
- sin.sin_port = htons(first_local_port + (childno % 48));
- ret = ::bind(c.s, (struct sockaddr *) &sin, sizeof(sin));
- if (ret < 0) {
- perror("bind");
- exit(1);
- }
- }
-
- assert(c.s >= 0);
- setsockopt(c.s, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes));
-
- bzero(&sin, sizeof(sin));
- sin.sin_family = AF_INET;
- if (udpflag && !share_server_port)
- sin.sin_port = htons(first_server_port + (childno % 48));
- else
- sin.sin_port = htons(first_server_port);
- sin.sin_addr.s_addr = inet_addr(serverip);
- ret = connect(c.s, (struct sockaddr *) &sin, sizeof(sin));
- if(ret < 0){
- perror("connect");
- exit(1);
- }
-
- c.conn = new KVConn(c.s, !udpflag);
- kvtest_client client(c);
-
- test->run(client);
-
- checkasync(&c, 2);
-
- delete c.conn;
- close(c.s);
-}
-
-void KVConn::hard_check(int tryhard) {
- masstree_precondition(inbufpos_ == inbuflen_);
- if (parser_.empty()) {
- inbufpos_ = inbuflen_ = 0;
- for (auto x : oldinbuf_)
- delete[] x;
- oldinbuf_.clear();
- } else if (inbufpos_ == inbufsz) {
- oldinbuf_.push_back(inbuf_);
- inbuf_ = new char[inbufsz];
- inbufpos_ = inbuflen_ = 0;
- }
- if (tryhard == 1) {
- fd_set rfds;
- FD_ZERO(&rfds);
- FD_SET(infd_, &rfds);
- struct timeval tv = {0, 0};
- if (select(infd_ + 1, &rfds, NULL, NULL, &tv) <= 0)
- return;
- } else
- kvflush(out_);
-
- ssize_t r = read(infd_, inbuf_ + inbufpos_, inbufsz - inbufpos_);
- if (r != -1)
- inbuflen_ += r;
-}
-
-int
-get(struct child *c, const Str &key, char *val, int max)
-{
- assert(c->seq0_ == c->seq1_);
-
- unsigned sseq = c->seq1_;
- ++c->seq1_;
- ++c->nsent_;
-
- c->conn->sendgetwhole(key, sseq);
- c->conn->flush();
-
- const Json& result = c->conn->receive();
- always_assert(result && result[0] == sseq);
- ++c->seq0_;
- if (!result[2])
- return -1;
- always_assert(result.size() == 3 && result[2].is_s()
- && result[2].as_s().length() <= max);
- memcpy(val, result[2].as_s().data(), result[2].as_s().length());
- return result[2].as_s().length();
-}
-
-// builtin aget callback: no check
-void
-nocheck(struct child *, struct async *, bool, const Str &)
-{
-}
-
-// builtin aget callback: store string
-void
-asyncgetcb(struct child *, struct async *a, bool, const Str &val)
-{
- Str *sptr;
- assert(a->wantedlen == sizeof(Str *));
- memcpy(&sptr, a->wanted, sizeof(Str *));
- sptr->len = std::min(sptr->len, val.len);
- memcpy(const_cast<char *>(sptr->s), val.s, sptr->len);
-}
-
-// builtin aget callback: store string
-void
-asyncgetcb_int(struct child *, struct async *a, bool, const Str &val)
-{
- int *vptr;
- assert(a->wantedlen == sizeof(int *));
- memcpy(&vptr, a->wanted, sizeof(int *));
- long x = 0;
- if (val.len <= 0)
- x = -1;
- else
- for (int i = 0; i < val.len; ++i)
- if (val.s[i] >= '0' && val.s[i] <= '9')
- x = (x * 10) + (val.s[i] - '0');
- else {
- x = -1;
- break;
- }
- *vptr = x;
-}
-
-// default aget callback: check val against wanted
-void
-defaultget(struct child *, struct async *a, bool have_val, const Str &val)
-{
- // check that we got the expected value
- int wanted_avail = std::min(a->wantedlen, int(sizeof(a->wanted)));
- if (!have_val
- || a->wantedlen != val.len
- || memcmp(val.s, a->wanted, wanted_avail) != 0)
- fprintf(stderr, "oops wanted %.*s(%d) got %.*s(%d)\n",
- wanted_avail, a->wanted, a->wantedlen, val.len, val.s, val.len);
- else {
- always_assert(a->wantedlen == val.len);
- always_assert(memcmp(val.s, a->wanted, wanted_avail) == 0);
- }
-}
-
-// builtin aput/aremove callback: store status
-void
-asyncputcb(struct child *, struct async *a, int status)
-{
- int *sptr;
- assert(a->wantedlen == sizeof(int *));
- memcpy(&sptr, a->wanted, sizeof(int *));
- *sptr = status;
-}
-
-// process any waiting replies to aget() and aput().
-// force=0 means non-blocking check if anything waiting on socket.
-// force=1 means wait for at least one reply.
-// force=2 means wait for all pending (nw-nr) replies.
-void
-checkasync(struct child *c, int force)
-{
- while (c->seq0_ != c->seq1_) {
- if (force)
- c->conn->flush();
- if (c->conn->check(force ? 2 : 1) > 0) {
- const Json& result = c->conn->receive();
- always_assert(result);
-
- // is rseq in the nr..nw window?
- // replies might arrive out of order if UDP
- unsigned rseq = result[0].as_i();
- always_assert(rseq - c->seq0_ < c->seq1_ - c->seq0_);
- struct async *a = &c->a[rseq & (window - 1)];
- always_assert(a->seq == rseq);
-
- // advance the nr..nw window
- always_assert(a->acked == 0);
- a->acked = 1;
- while (c->seq0_ != c->seq1_ && c->a[c->seq0_ & (window - 1)].acked)
- ++c->seq0_;
-
- // might have been the last free slot,
- // don't want to re-use it underfoot.
- struct async tmpa = *a;
-
- if(tmpa.cmd == Cmd_Get){
- // this is a reply to a get
- String s = result.size() > 2 ? result[2].as_s() : String();
- if (tmpa.get_fn)
- (tmpa.get_fn)(c, &tmpa, result.size() > 2, s);
- } else if (tmpa.cmd == Cmd_Put || tmpa.cmd == Cmd_Replace) {
- // this is a reply to a put
- if (tmpa.put_fn)
- (tmpa.put_fn)(c, &tmpa, result[2].as_i());
- } else if(tmpa.cmd == Cmd_Scan){
- // this is a reply to a scan
- always_assert((result.size() - 2) / 2 <= tmpa.wantedlen);
- } else if (tmpa.cmd == Cmd_Remove) {
- // this is a reply to a remove
- if (tmpa.remove_fn)
- (tmpa.remove_fn)(c, &tmpa, result[2].as_i());
- } else {
- always_assert(0);
- }
-
- if (force < 2)
- force = 0;
- } else if (!force)
- break;
- }
-}
-
-// async get, checkasync() will eventually check reply
-// against wanted.
-void
-aget(struct child *c, const Str &key, const Str &wanted, get_async_cb fn)
-{
- c->check_flush();
-
- c->conn->sendgetwhole(key, c->seq1_);
- if (c->udp)
- c->conn->flush();
-
- struct async *a = &c->a[c->seq1_ & (window - 1)];
- a->cmd = Cmd_Get;
- a->seq = c->seq1_;
- a->get_fn = (fn ? fn : defaultget);
- assert(key.len < int(sizeof(a->key)));
- memcpy(a->key, key.s, key.len);
- a->key[key.len] = 0;
- a->wantedlen = wanted.len;
- int wantedavail = std::min(wanted.len, int(sizeof(a->wanted)));
- memcpy(a->wanted, wanted.s, wantedavail);
- a->acked = 0;
-
- ++c->seq1_;
- ++c->nsent_;
-}
-
-void aget_col(struct child *c, const Str& key, int col, const Str& wanted,
- get_async_cb fn)
-{
- c->check_flush();
-
- c->conn->sendgetcol(key, col, c->seq1_);
- if (c->udp)
- c->conn->flush();
-
- struct async *a = &c->a[c->seq1_ & (window - 1)];
- a->cmd = Cmd_Get;
- a->seq = c->seq1_;
- a->get_fn = (fn ? fn : defaultget);
- assert(key.len < int(sizeof(a->key)));
- memcpy(a->key, key.s, key.len);
- a->key[key.len] = 0;
- a->wantedlen = wanted.len;
- int wantedavail = std::min(wanted.len, int(sizeof(a->wanted)));
- memcpy(a->wanted, wanted.s, wantedavail);
- a->acked = 0;
-
- ++c->seq1_;
- ++c->nsent_;
-}
-
-void
-aget(struct child *c, long ikey, long iwanted, get_async_cb fn)
-{
- quick_istr key(ikey), wanted(iwanted);
- aget(c, key.string(), wanted.string(), fn);
-}
-
-int
-put(struct child *c, const Str &key, const Str &val)
-{
- always_assert(c->seq0_ == c->seq1_);
-
- unsigned int sseq = c->seq1_;
- c->conn->sendputwhole(key, val, sseq);
- c->conn->flush();
-
- const Json& result = c->conn->receive();
- always_assert(result && result[0] == sseq);
-
- ++c->seq0_;
- ++c->seq1_;
- ++c->nsent_;
- return 0;
-}
-
-void
-aput(struct child *c, const Str &key, const Str &val,
- put_async_cb fn, const Str &wanted)
-{
- c->check_flush();
-
- c->conn->sendputwhole(key, val, c->seq1_);
- if (c->udp)
- c->conn->flush();
-
- struct async *a = &c->a[c->seq1_ & (window - 1)];
- a->cmd = Cmd_Put;
- a->seq = c->seq1_;
- assert(key.len < int(sizeof(a->key)) - 1);
- memcpy(a->key, key.s, key.len);
- a->key[key.len] = 0;
- a->put_fn = fn;
- if (fn) {
- assert(wanted.len <= int(sizeof(a->wanted)));
- a->wantedlen = wanted.len;
- memcpy(a->wanted, wanted.s, wanted.len);
- } else {
- a->wantedlen = -1;
- a->wanted[0] = 0;
- }
- a->acked = 0;
-
- ++c->seq1_;
- ++c->nsent_;
-}
-
-void aput_col(struct child *c, const Str &key, int col, const Str &val,
- put_async_cb fn, const Str &wanted)
-{
- c->check_flush();
-
- c->conn->sendputcol(key, col, val, c->seq1_);
- if (c->udp)
- c->conn->flush();
-
- struct async *a = &c->a[c->seq1_ & (window - 1)];
- a->cmd = Cmd_Put;
- a->seq = c->seq1_;
- assert(key.len < int(sizeof(a->key)) - 1);
- memcpy(a->key, key.s, key.len);
- a->key[key.len] = 0;
- a->put_fn = fn;
- if (fn) {
- assert(wanted.len <= int(sizeof(a->wanted)));
- a->wantedlen = wanted.len;
- memcpy(a->wanted, wanted.s, wanted.len);
- } else {
- a->wantedlen = -1;
- a->wanted[0] = 0;
- }
- a->acked = 0;
-
- ++c->seq1_;
- ++c->nsent_;
-}
-
-bool remove(struct child *c, const Str &key)
-{
- always_assert(c->seq0_ == c->seq1_);
-
- unsigned int sseq = c->seq1_;
- c->conn->sendremove(key, sseq);
- c->conn->flush();
-
- const Json& result = c->conn->receive();
- always_assert(result && result[0] == sseq);
-
- ++c->seq0_;
- ++c->seq1_;
- ++c->nsent_;
- return result[2].to_b();
-}
-
-void
-aremove(struct child *c, const Str &key, remove_async_cb fn)
-{
- c->check_flush();
-
- c->conn->sendremove(key, c->seq1_);
- if (c->udp)
- c->conn->flush();
-
- struct async *a = &c->a[c->seq1_ & (window - 1)];
- a->cmd = Cmd_Remove;
- a->seq = c->seq1_;
- assert(key.len < int(sizeof(a->key)) - 1);
- memcpy(a->key, key.s, key.len);
- a->key[key.len] = 0;
- a->acked = 0;
- a->remove_fn = fn;
-
- ++c->seq1_;
- ++c->nsent_;
-}
-
-int
-xcompar(const void *xa, const void *xb)
-{
- long *a = (long *) xa;
- long *b = (long *) xb;
- if(*a == *b)
- return 0;
- if(*a < *b)
- return -1;
- return 1;
-}
-
-// like w1, but in a binary-tree-like order that
-// produces a balanced 3-wide tree.
-void
-w1b(struct child *c)
-{
- int n;
- if (limit == ~(uint64_t) 0)
- n = 4000000;
- else
- n = std::min(limit, (uint64_t) INT_MAX);
- long *a = (long *) malloc(sizeof(long) * n);
- always_assert(a);
- char *done = (char *) malloc(n);
-
- srandom(kvtest_first_seed + c->childno);
-
- // insert in an order which causes 3-wide
- // to be balanced
-
- for(int i = 0; i < n; i++){
- a[i] = random();
- done[i] = 0;
- }
-
- qsort(a, n, sizeof(a[0]), xcompar);
- always_assert(a[0] <= a[1] && a[1] <= a[2] && a[2] <= a[3]);
-
- double t0 = now(), t1;
-
- for(int stride = n / 2; stride > 0; stride /= 2){
- for(int i = stride; i < n; i += stride){
- if(done[i] == 0){
- done[i] = 1;
- char key[512], val[512];
- sprintf(key, "%010ld", a[i]);
- sprintf(val, "%ld", a[i] + 1);
- aput(c, Str(key), Str(val));
- }
- }
- }
- for(int i = 0; i < n; i++){
- if(done[i] == 0){
- done[i] = 1;
- char key[512], val[512];
- sprintf(key, "%010ld", a[i]);
- sprintf(val, "%ld", a[i] + 1);
- aput(c, Str(key), Str(val));
- }
- }
-
- checkasync(c, 2);
- t1 = now();
-
- free(done);
- free(a);
- Json result = Json().set("total", (long) (n / (t1 - t0)))
- .set("puts", n)
- .set("puts_per_sec", n / (t1 - t0));
- printf("%s\n", result.unparse().c_str());
-}
-
-// update random keys from a set of 10 million.
-// maybe best to run it twice, first time to
-// populate the database.
-void
-u1(struct child *c)
-{
- int i, n;
- double t0 = now();
-
- srandom(kvtest_first_seed + c->childno);
-
- for(i = 0; i < 10000000; i++){
- char key[512], val[512];
- long x = random() % 10000000;
- sprintf(key, "%ld", x);
- sprintf(val, "%ld", x + 1);
- aput(c, Str(key), Str(val));
- }
- n = i;
-
- checkasync(c, 2);
-
- double t1 = now();
- Json result = Json().set("total", (long) (n / (t1 - t0)))
- .set("puts", n)
- .set("puts_per_sec", n / (t1 - t0));
- printf("%s\n", result.unparse().c_str());
-}
-
-#define CPN 10000000
-
-void
-cpa(struct child *c)
-{
- int i, n;
- double t0 = now();
-
- srandom(kvtest_first_seed + c->childno);
-
- for(i = 0; i < CPN; i++){
- char key[512], val[512];
- long x = random();
- sprintf(key, "%ld", x);
- sprintf(val, "%ld", x + 1);
- aput(c, Str(key), Str(val));
- }
- n = i;
-
- checkasync(c, 2);
-
- double t1 = now();
- Json result = Json().set("total", (long) (n / (t1 - t0)))
- .set("puts", n)
- .set("puts_per_sec", n / (t1 - t0));
- printf("%s\n", result.unparse().c_str());
-
-}
-
-void
-cpc(struct child *c)
-{
- int i, n;
- double t0 = now();
-
- srandom(kvtest_first_seed + c->childno);
-
- for(i = 0; !timeout[0]; i++){
- char key[512], val[512];
- if (i % CPN == 0)
- srandom(kvtest_first_seed + c->childno);
- long x = random();
- sprintf(key, "%ld", x);
- sprintf(val, "%ld", x + 1);
- aget(c, Str(key), Str(val), NULL);
- }
- n = i;
-
- checkasync(c, 2);
-
- double t1 = now();
- Json result = Json().set("total", (long) (n / (t1 - t0)))
- .set("gets", n)
- .set("gets_per_sec", n / (t1 - t0));
- printf("%s\n", result.unparse().c_str());
-}
-
-void
-cpd(struct child *c)
-{
- int i, n;
- double t0 = now();
-
- srandom(kvtest_first_seed + c->childno);
-
- for(i = 0; !timeout[0]; i++){
- char key[512], val[512];
- if (i % CPN == 0)
- srandom(kvtest_first_seed + c->childno);
- long x = random();
- sprintf(key, "%ld", x);
- sprintf(val, "%ld", x + 1);
- aput(c, Str(key), Str(val));
- }
- n = i;
-
- checkasync(c, 2);
-
- double t1 = now();
- Json result = Json().set("total", (long) (n / (t1 - t0)))
- .set("puts", n)
- .set("puts_per_sec", n / (t1 - t0));
- printf("%s\n", result.unparse().c_str());
-}
-
-// multiple threads simultaneously update the same key.
-// keep track of the winning value.
-// use over2 to make sure it's the same after a crash/restart.
-void
-over1(struct child *c)
-{
- int ret, iter;
-
- srandom(kvtest_first_seed + c->childno);
-
- iter = 0;
- while(!timeout[0]){
- char key1[64], key2[64], val1[64], val2[64];
- time_t xt = time(0);
- while(xt == time(0))
- ;
- sprintf(key1, "%d", iter);
- sprintf(val1, "%ld", random());
- put(c, Str(key1), Str(val1));
- napms(500);
- ret = get(c, Str(key1), val2, sizeof(val2));
- always_assert(ret > 0);
- sprintf(key2, "%d-%d", iter, c->childno);
- put(c, Str(key2), Str(val2));
- if(c->childno == 0)
- printf("%d: %s\n", iter, val2);
- iter++;
- }
- checkasync(c, 2);
- printf("0\n");
-}
-
-// check each round of over1()
-void
-over2(struct child *c)
-{
- int iter;
-
- for(iter = 0; ; iter++){
- char key1[64], key2[64], val1[64], val2[64];
- int ret;
- sprintf(key1, "%d", iter);
- ret = get(c, Str(key1), val1, sizeof(val1));
- if(ret == -1)
- break;
- sprintf(key2, "%d-%d", iter, c->childno);
- ret = get(c, Str(key2), val2, sizeof(val2));
- if(ret == -1)
- break;
- if(c->childno == 0)
- printf("%d: %s\n", iter, val2);
- always_assert(strcmp(val1, val2) == 0);
- }
-
- checkasync(c, 2);
- fprintf(stderr, "child %d checked %d\n", c->childno, iter);
- printf("0\n");
-}
-
-// do a bunch of inserts to distinct keys.
-// rec2() checks that a prefix of those inserts are present.
-// meant to be interrupted by a crash/restart.
-void
-rec1(struct child *c)
-{
- int i;
- double t0 = now(), t1;
-
- srandom(kvtest_first_seed + c->childno);
-
- for(i = 0; !timeout[0]; i++){
- char key[512], val[512];
- long x = random();
- sprintf(key, "%ld-%d-%d", x, i, c->childno);
- sprintf(val, "%ld", x);
- aput(c, Str(key), Str(val));
- }
- checkasync(c, 2);
- t1 = now();
-
- fprintf(stderr, "child %d: done %d %.0f put/s\n",
- c->childno,
- i,
- i / (t1 - t0));
- printf("%.0f\n", i / (t1 - t0));
-}
-
-void
-rec2(struct child *c)
-{
- int i;
-
- srandom(kvtest_first_seed + c->childno);
-
- for(i = 0; ; i++){
- char key[512], val[512], wanted[512];
- long x = random();
- sprintf(key, "%ld-%d-%d", x, i, c->childno);
- sprintf(wanted, "%ld", x);
- int ret = get(c, Str(key), val, sizeof(val));
- if(ret == -1)
- break;
- val[ret] = 0;
- if(strcmp(val, wanted) != 0){
- fprintf(stderr, "oops key %s got %s wanted %s\n", key, val, wanted);
- exit(1);
- }
- }
-
- int i0 = i; // first missing record
- for(i = i0+1; i < i0 + 10000; i++){
- char key[512], val[512];
- long x = random();
- sprintf(key, "%ld-%d-%d", x, i, c->childno);
- val[0] = 0;
- int ret = get(c, Str(key), val, sizeof(val));
- if(ret != -1){
- printf("child %d: oops first missing %d but %d present\n",
- c->childno, i0, i);
- exit(1);
- }
- }
- checkasync(c, 2);
-
- fprintf(stderr, "correct prefix of %d records\n", i0);
- printf("0\n");
-}
-
-// ask server to checkpoint
-void
-cpb(struct child *c)
-{
- if (c->childno == 0)
- c->conn->checkpoint(c->childno);
- checkasync(c, 2);
-}
-
-// mimic the first benchmark from the VoltDB blog:
-// https://voltdb.com/blog/key-value-benchmarking
-// https://voltdb.com/blog/key-value-benchmark-faq
-// http://community.voltdb.com/kvbenchdetails
-// svn checkout http://svnmirror.voltdb.com/projects/kvbench/trunk
-// 500,000 items: 50-byte key, 12 KB value
-// volt1a creates the DB.
-// volt1b runs the benchmark.
-#define VOLT1N 500000
-#define VOLT1SIZE (12*1024)
-void
-volt1a(struct child *c)
-{
- int i, j;
- double t0 = now(), t1;
- char *val = (char *) malloc(VOLT1SIZE + 1);
- always_assert(val);
-
- srandom(kvtest_first_seed + c->childno);
-
- for(i = 0; i < VOLT1SIZE; i++)
- val[i] = 'a' + (i % 26);
- val[VOLT1SIZE] = '\0';
-
- // XXX insert the keys in a random order to maintain
- // tree balance.
- int *keys = (int *) malloc(sizeof(int) * VOLT1N);
- always_assert(keys);
- for(i = 0; i < VOLT1N; i++)
- keys[i] = i;
- for(i = 0; i < VOLT1N; i++){
- int x = random() % VOLT1N;
- int tmp = keys[i];
- keys[i] = keys[x];
- keys[x] = tmp;
- }
-
- for(i = 0; i < VOLT1N; i++){
- char key[100];
- sprintf(key, "%-50d", keys[i]);
- for(j = 0; j < 20; j++)
- val[j] = 'a' + (j % 26);
- sprintf(val, ">%d", keys[i]);
- int j = strlen(val);
- val[j] = '<';
- always_assert(strlen(val) == VOLT1SIZE);
- always_assert(strlen(key) == 50);
- always_assert(isdigit(key[0]));
- aput(c, Str(key), Str(val));
- }
- checkasync(c, 2);
- t1 = now();
-
- free(val);
- free(keys);
-
- Json result = Json().set("total", (long) (i / (t1 - t0)));
- printf("%s\n", result.unparse().c_str());
-}
-
-// the actual volt1 benchmark.
-// get or update with equal probability.
-// their client pipelines many requests.
-// they use 8 client threads.
-// blog post says, for one server, VoltDB 17000, Cassandra 9740
-// this benchmark ends up being network or disk limited,
-// due to the huge values.
-void
-volt1b(struct child *c)
-{
- int i, n, j;
- double t0 = now(), t1;
- char *wanted = (char *) malloc(VOLT1SIZE + 1);
- always_assert(wanted);
-
- for(i = 0; i < VOLT1SIZE; i++)
- wanted[i] = 'a' + (i % 26);
- wanted[VOLT1SIZE] = '\0';
-
- srandom(kvtest_first_seed + c->childno);
-
- for(i = 0; !timeout[0]; i++){
- char key[100];
- int x = random() % VOLT1N;
- sprintf(key, "%-50d", x);
- for(j = 0; j < 20; j++)
- wanted[j] = 'a' + (j % 26);
- sprintf(wanted, ">%d", x);
- int j = strlen(wanted);
- wanted[j] = '<';
- if(i > 1)
- checkasync(c, 1); // try to avoid deadlock, only 2 reqs outstanding
- if((random() % 2) == 0)
- aget(c, Str(key, 50), Str(wanted, VOLT1SIZE), 0);
- else
- aput(c, Str(key, 50), Str(wanted, VOLT1SIZE));
- }
- n = i;
-
- checkasync(c, 2);
- t1 = now();
-
- Json result = Json().set("total", (long) (n / (t1 - t0)));
- printf("%s\n", result.unparse().c_str());
-}
-
-// second VoltDB benchmark.
-// 500,000 pairs, 50-byte key, value is 50 32-bit ints.
-// pick a key, read one int, if odd, write a different int (same key).
-// i'm simulating columns by embedding column name in key: rowname-colname.
-// also the read/modify/write is not atomic.
-// volt2a creates the DB.
-// volt2b runs the benchmark.
-#define VOLT2N 500000
-#define VOLT2INTS 50
-void
-volt2a(struct child *c)
-{
- int i, j, n = 0;
- double t0 = now(), t1;
-
- srandom(kvtest_first_seed + c->childno);
-
- // XXX insert the keys in a random order to maintain
- // tree balance.
- int *keys = (int *) malloc(sizeof(int) * VOLT2N);
- always_assert(keys);
- for(i = 0; i < VOLT2N; i++)
- keys[i] = i;
- for(i = 0; i < VOLT2N; i++){
- int x = random() % VOLT2N;
- int tmp = keys[i];
- keys[i] = keys[x];
- keys[x] = tmp;
- }
-
- int subkeys[VOLT2INTS];
- for(i = 0; i < VOLT2INTS; i++)
- subkeys[i] = i;
- for(i = 0; i < VOLT2INTS; i++){
- int x = random() % VOLT2INTS;
- int tmp = subkeys[i];
- subkeys[i] = subkeys[x];
- subkeys[x] = tmp;
- }
-
- for(i = 0; i < VOLT2N; i++){
- for(j = 0; j < VOLT2INTS; j++){
- char val[32], key[100];
- int k;
- sprintf(key, "%d-%d", keys[i], subkeys[j]);
- for(k = strlen(key); k < 50; k++)
- key[k] = ' ';
- key[50] = '\0';
- sprintf(val, "%ld", random());
- aput(c, Str(key), Str(val));
- n++;
- }
- }
- checkasync(c, 2);
- t1 = now();
-
- free(keys);
- Json result = Json().set("total", (long) (n / (t1 - t0)));
- printf("%s\n", result.unparse().c_str());
-}
-
-// get callback
-void
-volt2b1(struct child *c, struct async *a, bool, const Str &val)
-{
- int k = atoi(a->key);
- int v = atoi(val.s);
- if((v % 2) == 1){
- char key[100], val[100];
- sprintf(key, "%d-%ld", k, random() % VOLT2INTS);
- for (int i = strlen(key); i < 50; i++)
- key[i] = ' ';
- sprintf(val, "%ld", random());
- aput(c, Str(key, 50), Str(val));
- }
-}
-
-void
-volt2b(struct child *c)
-{
- int i, n;
- double t0 = now(), t1;
- srandom(kvtest_first_seed + c->childno);
- for(i = 0; !timeout[0]; i++){
- char key[100];
- int x = random() % VOLT2N;
- int y = random() % VOLT2INTS;
- sprintf(key, "%d-%d", x, y);
- int j;
- for(j = strlen(key); j < 50; j++)
- key[j] = ' ';
- aget(c, Str(key, 50), Str(), volt2b1);
- }
- n = i;
-
- checkasync(c, 2);
- t1 = now();
-
- Json result = Json().set("total", (long) (n / (t1 - t0)));
- printf("%s\n", result.unparse().c_str());
-}
-
-using std::vector;
-using std::string;
-
-void
-scantest(struct child *c)
-{
- int i;
-
- srandom(kvtest_first_seed + c->childno);
-
- for(i = 100; i < 200; i++){
- char key[32], val[32];
- int kl = sprintf(key, "k%04d", i);
- sprintf(val, "v%04d", i);
- aput(c, Str(key, kl), Str(val));
- }
-
- checkasync(c, 2);
-
- for(i = 90; i < 210; i++){
- char key[32];
- sprintf(key, "k%04d", i);
- int wanted = random() % 10;
- c->conn->sendscanwhole(key, wanted, 1);
- c->conn->flush();
-
- {
- const Json& result = c->conn->receive();
- always_assert(result && result[0] == 1);
- int n = (result.size() - 2) / 2;
- if(i <= 200 - wanted){
- always_assert(n == wanted);
- } else if(i <= 200){
- always_assert(n == 200 - i);
- } else {
- always_assert(n == 0);
- }
- int k0 = (i < 100 ? 100 : i);
- int j, ki, off = 2;
- for(j = k0, ki = 0; j < k0 + wanted && j < 200; j++, ki++, off += 2){
- char xkey[32], xval[32];
- sprintf(xkey, "k%04d", j);
- sprintf(xval, "v%04d", j);
- if (!result[off].as_s().equals(xkey)) {
- fprintf(stderr, "Assertion failed @%d: strcmp(%s, %s) == 0\n", ki, result[off].as_s().c_str(), xkey);
- always_assert(0);
- }
- always_assert(result[off + 1].as_s().equals(xval));
- }
- }
-
- {
- sprintf(key, "k%04d-a", i);
- c->conn->sendscanwhole(key, 1, 1);
- c->conn->flush();
-
- const Json& result = c->conn->receive();
- always_assert(result && result[0] == 1);
- int n = (result.size() - 2) / 2;
- if(i >= 100 && i < 199){
- always_assert(n == 1);
- sprintf(key, "k%04d", i+1);
- always_assert(result[2].as_s().equals(key));
- }
- }
- }
-
- c->conn->sendscanwhole("k015", 10, 1);
- c->conn->flush();
-
- const Json& result = c->conn->receive();
- always_assert(result && result[0] == 1);
- int n = (result.size() - 2) / 2;
- always_assert(n == 10);
- always_assert(result[2].as_s().equals("k0150"));
- always_assert(result[3].as_s().equals("v0150"));
-
- fprintf(stderr, "scantest OK\n");
- printf("0\n");
-}
diff --git a/mtclient.hh b/mtclient.hh
deleted file mode 100644
index 3413166..0000000
--- a/mtclient.hh
+++ /dev/null
@@ -1,219 +0,0 @@
-/* Masstree
- * Eddie Kohler, Yandong Mao, Robert Morris
- * Copyright (c) 2012-2013 President and Fellows of Harvard College
- * Copyright (c) 2012-2013 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 KVC_HH
-#define KVC_HH 1
-#include "kvproto.hh"
-#include "kvrow.hh"
-#include "json.hh"
-#include "msgpack.hh"
-#include <sys/socket.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <netinet/tcp.h>
-#include <string>
-#include <queue>
-#include <vector>
-
-class KVConn {
- public:
- KVConn(const char *server, int port, int target_core = -1)
- : inbuf_(new char[inbufsz]), inbufpos_(0), inbuflen_(0),
- j_(Json::make_array()) {
- struct hostent *ent = gethostbyname(server);
- always_assert(ent);
- int fd = socket(AF_INET, SOCK_STREAM, 0);
- always_assert(fd > 0);
- fdtoclose_ = fd;
- int yes = 1;
- always_assert(fd >= 0);
- setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes));
-
- struct sockaddr_in sin;
- memset(&sin, 0, sizeof(sin));
- sin.sin_family = AF_INET;
- sin.sin_port = htons(port);
- memcpy(&sin.sin_addr.s_addr, ent->h_addr, ent->h_length);
- int r = connect(fd, (const struct sockaddr *)&sin, sizeof(sin));
- if (r) {
- perror("connect");
- exit(EXIT_FAILURE);
- }
-
- infd_ = fd;
- out_ = new_kvout(fd, 64*1024);
- handshake(target_core);
- }
- KVConn(int fd, bool tcp)
- : inbuf_(new char[inbufsz]), inbufpos_(0), inbuflen_(0), infd_(fd),
- j_(Json::make_array()) {
- out_ = new_kvout(fd, 64*1024);
- fdtoclose_ = -1;
- if (tcp)
- handshake(-1);
- }
- ~KVConn() {
- if (fdtoclose_ >= 0)
- close(fdtoclose_);
- free_kvout(out_);
- delete[] inbuf_;
- for (auto x : oldinbuf_)
- delete[] x;
- }
- void sendgetwhole(Str key, unsigned seq) {
- j_.resize(3);
- j_[0] = seq;
- j_[1] = Cmd_Get;
- j_[2] = String::make_stable(key);
- send();
- }
- void sendgetcol(Str key, int col, unsigned seq) {
- j_.resize(4);
- j_[0] = seq;
- j_[1] = Cmd_Get;
- j_[2] = String::make_stable(key);
- j_[3] = col;
- send();
- }
- void sendget(Str key, const std::vector<unsigned>& f, unsigned seq) {
- j_.resize(4);
- j_[0] = seq;
- j_[1] = Cmd_Get;
- j_[2] = String::make_stable(key);
- j_[3] = Json(f.begin(), f.end());
- send();
- }
-
- void sendputcol(Str key, int col, Str val, unsigned seq) {
- j_.resize(5);
- j_[0] = seq;
- j_[1] = Cmd_Put;
- j_[2] = String::make_stable(key);
- j_[3] = col;
- j_[4] = String::make_stable(val);
- send();
- }
- void sendputwhole(Str key, Str val, unsigned seq) {
- j_.resize(3);
- j_[0] = seq;
- j_[1] = Cmd_Replace;
- j_[2] = String::make_stable(key);
- j_[3] = String::make_stable(val);
- send();
- }
- void sendremove(Str key, unsigned seq) {
- j_.resize(3);
- j_[0] = seq;
- j_[1] = Cmd_Remove;
- j_[2] = String::make_stable(key);
- send();
- }
-
- void sendscanwhole(Str firstkey, int numpairs, unsigned seq) {
- j_.resize(4);
- j_[0] = seq;
- j_[1] = Cmd_Scan;
- j_[2] = String::make_stable(firstkey);
- j_[3] = numpairs;
- send();
- }
- void sendscan(Str firstkey, const std::vector<unsigned>& f,
- int numpairs, unsigned seq) {
- j_.resize(5);
- j_[0] = seq;
- j_[1] = Cmd_Scan;
- j_[2] = String::make_stable(firstkey);
- j_[3] = numpairs;
- j_[4] = Json(f.begin(), f.end());
- send();
- }
-
- void checkpoint(int childno) {
- always_assert(childno == 0);
- fprintf(stderr, "asking for a checkpoint\n");
- j_.resize(2);
- j_[0] = 0;
- j_[1] = Cmd_Checkpoint;
- send();
- flush();
-
- printf("sent\n");
- (void) receive();
- }
-
- void flush() {
- kvflush(out_);
- }
-
- int check(int tryhard) {
- if (inbufpos_ == inbuflen_ && tryhard)
- hard_check(tryhard);
- return inbuflen_ - inbufpos_;
- }
-
- const Json& receive() {
- while (!parser_.done() && check(2))
- inbufpos_ += parser_.consume(inbuf_ + inbufpos_,
- inbuflen_ - inbufpos_,
- String::make_stable(inbuf_, inbufsz));
- if (parser_.success() && parser_.result().is_a())
- parser_.reset();
- else
- parser_.result() = Json();
- return parser_.result();
- }
-
- private:
- enum { inbufsz = 64 * 1024, inbufrefill = 56 * 1024 };
- char* inbuf_;
- int inbufpos_;
- int inbuflen_;
- std::vector<char*> oldinbuf_;
- int infd_;
-
- struct kvout *out_;
-
- Json j_;
- msgpack::streaming_parser parser_;
-
- int fdtoclose_;
- int partition_;
-
- void handshake(int target_core) {
- j_.resize(3);
- j_[0] = 0;
- j_[1] = Cmd_Handshake;
- j_[2] = Json::make_object().set("core", target_core)
- .set("maxkeylen", MASSTREE_MAXKEYLEN);
- send();
- kvflush(out_);
-
- const Json& result = receive();
- if (!result.is_a()
- || result[1] != Cmd_Handshake + 1
- || !result[2]) {
- fprintf(stderr, "Incompatible kvdb protocol\n");
- exit(EXIT_FAILURE);
- }
- partition_ = result[3].as_i();
- }
- inline void send() {
- msgpack::unparse(*out_, j_);
- }
- void hard_check(int tryhard);
-};
-
-#endif
diff --git a/mtd.cc b/mtd.cc
deleted file mode 100644
index a2b725d..0000000
--- a/mtd.cc
+++ /dev/null
@@ -1,1715 +0,0 @@
-/* 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.
- */
-// -*- mode: c++ -*-
-// mtd: key/value server
-//
-
-#include <stdio.h>
-#include <stdarg.h>
-#include <ctype.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <sys/select.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <limits.h>
-#if HAVE_SYS_EPOLL_H
-#include <sys/epoll.h>
-#endif
-#if __linux__
-#include <asm-generic/mman.h>
-#endif
-#include <fcntl.h>
-#include <assert.h>
-#include <string.h>
-#include <pthread.h>
-#include <math.h>
-#include <signal.h>
-#include <errno.h>
-#ifdef __linux__
-#include <malloc.h>
-#endif
-#include "nodeversion.hh"
-#include "kvstats.hh"
-#include "json.hh"
-#include "kvtest.hh"
-#include "kvrandom.hh"
-#include "clp.h"
-#include "log.hh"
-#include "checkpoint.hh"
-#include "file.hh"
-#include "kvproto.hh"
-#include "query_masstree.hh"
-#include "masstree_tcursor.hh"
-#include "masstree_insert.hh"
-#include "masstree_remove.hh"
-#include "masstree_scan.hh"
-#include "msgpack.hh"
-#include <algorithm>
-#include <deque>
-using lcdf::StringAccum;
-
-enum { CKState_Quit, CKState_Uninit, CKState_Ready, CKState_Go };
-
-volatile bool timeout[2] = {false, false};
-double duration[2] = {10, 0};
-
-Masstree::default_table *tree;
-
-// all default to the number of cores
-static int udpthreads = 0;
-static int tcpthreads = 0;
-static int nckthreads = 0;
-static int testthreads = 0;
-static int nlogger = 0;
-static std::vector<int> cores;
-
-static bool logging = true;
-static bool pinthreads = false;
-static bool recovery_only = false;
-volatile uint64_t globalepoch = 1; // global epoch, updated by main thread regularly
-volatile uint64_t active_epoch = 1;
-static int port = 2117;
-static uint64_t test_limit = ~uint64_t(0);
-static int doprint = 0;
-int kvtest_first_seed = 31949;
-
-static volatile sig_atomic_t go_quit = 0;
-static int quit_pipe[2];
-
-static std::vector<const char*> logdirs;
-static std::vector<const char*> ckpdirs;
-
-static logset* logs;
-volatile bool recovering = false; // so don't add log entries, and free old value immediately
-
-static double checkpoint_interval = 1000000;
-static kvepoch_t ckp_gen = 0; // recover from checkpoint
-static ckstate *cks = NULL; // checkpoint status of all checkpointing threads
-static pthread_cond_t rec_cond;
-pthread_mutex_t rec_mu;
-static int rec_nactive;
-static int rec_state = REC_NONE;
-
-kvtimestamp_t initial_timestamp;
-
-static pthread_cond_t checkpoint_cond;
-static pthread_mutex_t checkpoint_mu;
-
-static void prepare_thread(threadinfo *ti);
-static int* tcp_thread_pipes;
-static void* tcp_threadfunc(void* ti);
-static void* udp_threadfunc(void* ti);
-
-static void log_init();
-static void recover(threadinfo*);
-static kvepoch_t read_checkpoint(threadinfo*, const char *path);
-
-static void* conc_checkpointer(void* ti);
-static void recovercheckpoint(threadinfo* ti);
-
-static void *canceling(void *);
-static void catchint(int);
-static void epochinc(int);
-
-/* running local tests */
-void test_timeout(int) {
- size_t n;
- for (n = 0; n < arraysize(timeout) && timeout[n]; ++n)
- /* do nothing */;
- if (n < arraysize(timeout)) {
- timeout[n] = true;
- if (n + 1 < arraysize(timeout) && duration[n + 1])
- xalarm(duration[n + 1]);
- }
-}
-
-struct kvtest_client {
- kvtest_client()
- : checks_(0), kvo_() {
- }
- kvtest_client(const char *testname)
- : testname_(testname), checks_(0), kvo_() {
- }
-
- int id() const {
- return ti_->index();
- }
- int nthreads() const {
- return testthreads;
- }
- void set_thread(threadinfo *ti) {
- ti_ = ti;
- }
- void register_timeouts(int n) {
- always_assert(n <= (int) arraysize(::timeout));
- for (int i = 1; i < n; ++i)
- if (duration[i] == 0)
- duration[i] = 0;//duration[i - 1];
- }
- bool timeout(int which) const {
- return ::timeout[which];
- }
- uint64_t limit() const {
- return test_limit;
- }
- Json param(const String&) const {
- return Json();
- }
- double now() const {
- return ::now();
- }
-
- void get(long ikey, Str *value);
- void get(const Str &key);
- void get(long ikey) {
- quick_istr key(ikey);
- get(key.string());
- }
- void get_check(const Str &key, const Str &expected);
- void get_check(const char *key, const char *expected) {
- get_check(Str(key, strlen(key)), Str(expected, strlen(expected)));
- }
- void get_check(long ikey, long iexpected) {
- quick_istr key(ikey), expected(iexpected);
- get_check(key.string(), expected.string());
- }
- void get_check_key8(long ikey, long iexpected) {
- quick_istr key(ikey, 8), expected(iexpected);
- get_check(key.string(), expected.string());
- }
- void get_check_key10(long ikey, long iexpected) {
- quick_istr key(ikey, 10), expected(iexpected);
- get_check(key.string(), expected.string());
- }
- void get_col_check(const Str &key, int col, const Str &expected);
- void get_col_check_key10(long ikey, int col, long iexpected) {
- quick_istr key(ikey, 10), expected(iexpected);
- get_col_check(key.string(), col, expected.string());
- }
- bool get_sync(long ikey);
-
- void put(const Str &key, const Str &value);
- void put(const char *key, const char *val) {
- put(Str(key, strlen(key)), Str(val, strlen(val)));
- }
- void put(long ikey, long ivalue) {
- quick_istr key(ikey), value(ivalue);
- put(key.string(), value.string());
- }
- void put_key8(long ikey, long ivalue) {
- quick_istr key(ikey, 8), value(ivalue);
- put(key.string(), value.string());
- }
- void put_key10(long ikey, long ivalue) {
- quick_istr key(ikey, 10), value(ivalue);
- put(key.string(), value.string());
- }
- void put_col(const Str &key, int col, const Str &value);
- void put_col_key10(long ikey, int col, long ivalue) {
- quick_istr key(ikey, 10), value(ivalue);
- put_col(key.string(), col, value.string());
- }
-
- bool remove_sync(long ikey);
-
- void puts_done() {
- }
- void wait_all() {
- }
- void rcu_quiesce() {
- }
- String make_message(StringAccum &sa) const;
- void notice(const char *fmt, ...);
- void fail(const char *fmt, ...);
- const Json& report(const Json& x) {
- return report_.merge(x);
- }
- void finish() {
- fprintf(stderr, "%d: %s\n", ti_->index(), report_.unparse().c_str());
- }
- threadinfo *ti_;
- query<row_type> q_[10];
- const char *testname_;
- kvrandom_lcg_nr rand;
- int checks_;
- Json report_;
- struct kvout *kvo_;
- static volatile int failing;
-};
-
-volatile int kvtest_client::failing;
-
-void kvtest_client::get(long ikey, Str *value)
-{
- quick_istr key(ikey);
- if (!q_[0].run_get1(tree->table(), key.string(), 0, *value, *ti_))
- *value = Str();
-}
-
-void kvtest_client::get(const Str &key)
-{
- Str val;
- (void) q_[0].run_get1(tree->table(), key, 0, val, *ti_);
-}
-
-void kvtest_client::get_check(const Str &key, const Str &expected)
-{
- Str val;
- if (!q_[0].run_get1(tree->table(), key, 0, val, *ti_)) {
- fail("get(%.*s) failed (expected %.*s)\n", key.len, key.s, expected.len, expected.s);
- return;
- }
- if (val.len != expected.len || memcmp(val.s, expected.s, val.len) != 0)
- fail("get(%.*s) returned unexpected value %.*s (expected %.*s)\n", key.len, key.s,
- std::min(val.len, 40), val.s, std::min(expected.len, 40), expected.s);
- else
- ++checks_;
-}
-
-void kvtest_client::get_col_check(const Str &key, int col, const Str &expected)
-{
- Str val;
- if (!q_[0].run_get1(tree->table(), key, col, val, *ti_)) {
- fail("get.%d(%.*s) failed (expected %.*s)\n", col, key.len, key.s,
- expected.len, expected.s);
- return;
- }
- if (val.len != expected.len || memcmp(val.s, expected.s, val.len) != 0)
- fail("get.%d(%.*s) returned unexpected value %.*s (expected %.*s)\n",
- col, key.len, key.s, std::min(val.len, 40), val.s,
- std::min(expected.len, 40), expected.s);
- else
- ++checks_;
-}
-
-bool kvtest_client::get_sync(long ikey) {
- quick_istr key(ikey);
- Str val;
- return q_[0].run_get1(tree->table(), key.string(), 0, val, *ti_);
-}
-
-void kvtest_client::put(const Str &key, const Str &value) {
- while (failing)
- /* do nothing */;
- q_[0].run_replace(tree->table(), key, value, *ti_);
- if (ti_->logger()) // NB may block
- ti_->logger()->record(logcmd_replace, q_[0].query_times(), key, value);
-}
-
-void kvtest_client::put_col(const Str &key, int col, const Str &value) {
- while (failing)
- /* do nothing */;
-#if !MASSTREE_ROW_TYPE_STR
- if (!kvo_)
- kvo_ = new_kvout(-1, 2048);
- Json req[2] = {Json(col), Json(String::make_stable(value))};
- (void) q_[0].run_put(tree->table(), key, &req[0], &req[2], *ti_);
- if (ti_->logger()) // NB may block
- ti_->logger()->record(logcmd_put, q_[0].query_times(), key,
- &req[0], &req[2]);
-#else
- (void) key, (void) col, (void) value;
- assert(0);
-#endif
-}
-
-bool kvtest_client::remove_sync(long ikey) {
- quick_istr key(ikey);
- bool removed = q_[0].run_remove(tree->table(), key.string(), *ti_);
- if (removed && ti_->logger()) // NB may block
- ti_->logger()->record(logcmd_remove, q_[0].query_times(), key.string(), Str());
- return removed;
-}
-
-String kvtest_client::make_message(StringAccum &sa) const {
- const char *begin = sa.begin();
- while (begin != sa.end() && isspace((unsigned char) *begin))
- ++begin;
- String s = String(begin, sa.end());
- if (!s.empty() && s.back() != '\n')
- s += '\n';
- return s;
-}
-
-void kvtest_client::notice(const char *fmt, ...) {
- va_list val;
- va_start(val, fmt);
- String m = make_message(StringAccum().vsnprintf(500, fmt, val));
- va_end(val);
- if (m)
- fprintf(stderr, "%d: %s", ti_->index(), m.c_str());
-}
-
-void kvtest_client::fail(const char *fmt, ...) {
- static nodeversion32 failing_lock(false);
- static nodeversion32 fail_message_lock(false);
- static String fail_message;
- failing = 1;
-
- va_list val;
- va_start(val, fmt);
- String m = make_message(StringAccum().vsnprintf(500, fmt, val));
- va_end(val);
- if (!m)
- m = "unknown failure";
-
- fail_message_lock.lock();
- if (fail_message != m) {
- fail_message = m;
- fprintf(stderr, "%d: %s", ti_->index(), m.c_str());
- }
- fail_message_lock.unlock();
-
- if (doprint) {
- failing_lock.lock();
- fprintf(stdout, "%d: %s", ti_->index(), m.c_str());
- tree->print(stdout);
- fflush(stdout);
- }
-
- always_assert(0);
-}
-
-static void* testgo(void* x) {
- kvtest_client *kc = reinterpret_cast<kvtest_client*>(x);
- kc->ti_->pthread() = pthread_self();
- prepare_thread(kc->ti_);
-
- if (strcmp(kc->testname_, "rw1") == 0)
- kvtest_rw1(*kc);
- else if (strcmp(kc->testname_, "rw2") == 0)
- kvtest_rw2(*kc);
- else if (strcmp(kc->testname_, "rw3") == 0)
- kvtest_rw3(*kc);
- else if (strcmp(kc->testname_, "rw4") == 0)
- kvtest_rw4(*kc);
- else if (strcmp(kc->testname_, "rwsmall24") == 0)
- kvtest_rwsmall24(*kc);
- else if (strcmp(kc->testname_, "rwsep24") == 0)
- kvtest_rwsep24(*kc);
- else if (strcmp(kc->testname_, "palma") == 0)
- kvtest_palma(*kc);
- else if (strcmp(kc->testname_, "palmb") == 0)
- kvtest_palmb(*kc);
- else if (strcmp(kc->testname_, "rw16") == 0)
- kvtest_rw16(*kc);
- else if (strcmp(kc->testname_, "rw5") == 0
- || strcmp(kc->testname_, "rw1fixed") == 0)
- kvtest_rw1fixed(*kc);
- else if (strcmp(kc->testname_, "ycsbk") == 0)
- kvtest_ycsbk(*kc);
- else if (strcmp(kc->testname_, "wd1") == 0)
- kvtest_wd1(10000000, 1, *kc);
- else if (strcmp(kc->testname_, "wd1check") == 0)
- kvtest_wd1_check(10000000, 1, *kc);
- else if (strcmp(kc->testname_, "w1") == 0)
- kvtest_w1_seed(*kc, kvtest_first_seed + kc->id());
- else if (strcmp(kc->testname_, "r1") == 0)
- kvtest_r1_seed(*kc, kvtest_first_seed + kc->id());
- else if (strcmp(kc->testname_, "wcol1") == 0)
- kvtest_wcol1at(*kc, kc->id() % 24, kvtest_first_seed + kc->id() % 48, 5000000);
- else if (strcmp(kc->testname_, "rcol1") == 0)
- kvtest_rcol1at(*kc, kc->id() % 24, kvtest_first_seed + kc->id() % 48, 5000000);
- else
- kc->fail("unknown test '%s'", kc->testname_);
- return 0;
-}
-
-static const char * const kvstats_name[] = {
- "ops", "ops_per_sec", "puts", "gets", "scans", "puts_per_sec", "gets_per_sec", "scans_per_sec"
-};
-
-void runtest(const char *testname, int nthreads) {
- std::vector<kvtest_client> clients(nthreads, kvtest_client(testname));
- ::testthreads = nthreads;
- for (int i = 0; i < nthreads; ++i)
- clients[i].set_thread(threadinfo::make(threadinfo::TI_PROCESS, i));
- bzero((void *)timeout, sizeof(timeout));
- signal(SIGALRM, test_timeout);
- if (duration[0])
- xalarm(duration[0]);
- for (int i = 0; i < nthreads; ++i) {
- int r = pthread_create(&clients[i].ti_->pthread(), 0, testgo, &clients[i]);
- always_assert(r == 0);
- }
- for (int i = 0; i < nthreads; ++i)
- pthread_join(clients[i].ti_->pthread(), 0);
-
- kvstats kvs[arraysize(kvstats_name)];
- for (int i = 0; i < nthreads; ++i)
- for (int j = 0; j < (int) arraysize(kvstats_name); ++j)
- if (double x = clients[i].report_.get_d(kvstats_name[j]))
- kvs[j].add(x);
- for (int j = 0; j < (int) arraysize(kvstats_name); ++j)
- kvs[j].print_report(kvstats_name[j]);
-}
-
-
-struct conn {
- int fd;
- enum { inbufsz = 20 * 1024, inbufrefill = 16 * 1024 };
-
- conn(int s)
- : fd(s), inbuf_(new char[inbufsz]),
- inbufpos_(0), inbuflen_(0), kvout(new_kvout(s, 20 * 1024)),
- inbuftotal_(0) {
- }
- ~conn() {
- close(fd);
- free_kvout(kvout);
- delete[] inbuf_;
- for (char* x : oldinbuf_)
- delete[] x;
- }
-
- Json& receive() {
- while (!parser_.done() && check(2))
- inbufpos_ += parser_.consume(inbuf_ + inbufpos_,
- inbuflen_ - inbufpos_,
- String::make_stable(inbuf_, inbufsz));
- if (parser_.success() && parser_.result().is_a())
- parser_.reset();
- else
- parser_.result() = Json();
- return parser_.result();
- }
-
- int check(int tryhard) {
- if (inbufpos_ == inbuflen_ && tryhard)
- hard_check(tryhard);
- return inbuflen_ - inbufpos_;
- }
-
- uint64_t xposition() const {
- return inbuftotal_ + inbufpos_;
- }
- Str recent_string(uint64_t xposition) const {
- if (xposition - inbuftotal_ <= unsigned(inbufpos_))
- return Str(inbuf_ + (xposition - inbuftotal_),
- inbuf_ + inbufpos_);
- else
- return Str();
- }
-
- private:
- char* inbuf_;
- int inbufpos_;
- int inbuflen_;
- std::vector<char*> oldinbuf_;
- msgpack::streaming_parser parser_;
- public:
- struct kvout *kvout;
- private:
- uint64_t inbuftotal_;
-
- void hard_check(int tryhard);
-};
-
-void conn::hard_check(int tryhard) {
- masstree_precondition(inbufpos_ == inbuflen_);
- if (parser_.empty()) {
- inbuftotal_ += inbufpos_;
- inbufpos_ = inbuflen_ = 0;
- for (auto x : oldinbuf_)
- delete[] x;
- oldinbuf_.clear();
- } else if (inbufpos_ == inbufsz) {
- oldinbuf_.push_back(inbuf_);
- inbuf_ = new char[inbufsz];
- inbuftotal_ += inbufpos_;
- inbufpos_ = inbuflen_ = 0;
- }
- if (tryhard == 1) {
- fd_set rfds;
- FD_ZERO(&rfds);
- FD_SET(fd, &rfds);
- struct timeval tv = {0, 0};
- if (select(fd + 1, &rfds, NULL, NULL, &tv) <= 0)
- return;
- } else
- kvflush(kvout);
-
- ssize_t r = read(fd, inbuf_ + inbufpos_, inbufsz - inbufpos_);
- if (r != -1)
- inbuflen_ += r;
-}
-
-struct conninfo {
- int s;
- Json handshake;
-};
-
-
-/* main loop */
-
-enum { clp_val_suffixdouble = Clp_ValFirstUser };
-enum { opt_nolog = 1, opt_pin, opt_logdir, opt_port, opt_ckpdir, opt_duration,
- opt_test, opt_test_name, opt_threads, opt_cores,
- opt_print, opt_norun, opt_checkpoint, opt_limit, opt_epoch_interval };
-static const Clp_Option options[] = {
- { "no-log", 0, opt_nolog, 0, 0 },
- { 0, 'n', opt_nolog, 0, 0 },
- { "no-run", 0, opt_norun, 0, 0 },
- { "pin", 'p', opt_pin, 0, Clp_Negate },
- { "logdir", 0, opt_logdir, Clp_ValString, 0 },
- { "ld", 0, opt_logdir, Clp_ValString, 0 },
- { "checkpoint", 'c', opt_checkpoint, Clp_ValDouble, Clp_Optional | Clp_Negate },
- { "ckp", 0, opt_checkpoint, Clp_ValDouble, Clp_Optional | Clp_Negate },
- { "ckpdir", 0, opt_ckpdir, Clp_ValString, 0 },
- { "ckdir", 0, opt_ckpdir, Clp_ValString, 0 },
- { "cd", 0, opt_ckpdir, Clp_ValString, 0 },
- { "port", 0, opt_port, Clp_ValInt, 0 },
- { "duration", 'd', opt_duration, Clp_ValDouble, 0 },
- { "limit", 'l', opt_limit, clp_val_suffixdouble, 0 },
- { "test", 0, opt_test, Clp_ValString, 0 },
- { "test-rw1", 0, opt_test_name, 0, 0 },
- { "test-rw2", 0, opt_test_name, 0, 0 },
- { "test-rw3", 0, opt_test_name, 0, 0 },
- { "test-rw4", 0, opt_test_name, 0, 0 },
- { "test-rw5", 0, opt_test_name, 0, 0 },
- { "test-rw16", 0, opt_test_name, 0, 0 },
- { "test-palm", 0, opt_test_name, 0, 0 },
- { "test-ycsbk", 0, opt_test_name, 0, 0 },
- { "test-rw1fixed", 0, opt_test_name, 0, 0 },
- { "threads", 'j', opt_threads, Clp_ValInt, 0 },
- { "cores", 0, opt_cores, Clp_ValString, 0 },
- { "print", 0, opt_print, 0, Clp_Negate },
- { "epoch-interval", 0, opt_epoch_interval, Clp_ValDouble, 0 }
-};
-
-int
-main(int argc, char *argv[])
-{
- using std::swap;
- int s, ret, yes = 1, i = 1, firstcore = -1, corestride = 1;
- const char *dotest = 0;
- nlogger = tcpthreads = udpthreads = nckthreads = sysconf(_SC_NPROCESSORS_ONLN);
- Clp_Parser *clp = Clp_NewParser(argc, argv, (int) arraysize(options), options);
- Clp_AddType(clp, clp_val_suffixdouble, Clp_DisallowOptions, clp_parse_suffixdouble, 0);
- int opt;
- double epoch_interval_ms = 1000;
- while ((opt = Clp_Next(clp)) >= 0) {
- switch (opt) {
- case opt_nolog:
- logging = false;
- break;
- case opt_pin:
- pinthreads = !clp->negated;
- break;
- case opt_threads:
- nlogger = tcpthreads = udpthreads = nckthreads = clp->val.i;
- break;
- case opt_logdir: {
- const char *s = strtok((char *) clp->vstr, ",");
- for (; s; s = strtok(NULL, ","))
- logdirs.push_back(s);
- break;
- }
- case opt_ckpdir: {
- const char *s = strtok((char *) clp->vstr, ",");
- for (; s; s = strtok(NULL, ","))
- ckpdirs.push_back(s);
- break;
- }
- case opt_checkpoint:
- if (clp->negated || (clp->have_val && clp->val.d <= 0))
- checkpoint_interval = -1;
- else if (clp->have_val)
- checkpoint_interval = clp->val.d;
- else
- checkpoint_interval = 30;
- break;
- case opt_port:
- port = clp->val.i;
- break;
- case opt_duration:
- duration[0] = clp->val.d;
- break;
- case opt_limit:
- test_limit = (uint64_t) clp->val.d;
- break;
- case opt_test:
- dotest = clp->vstr;
- break;
- case opt_test_name:
- dotest = clp->option->long_name + 5;
- break;
- case opt_print:
- doprint = !clp->negated;
- break;
- case opt_cores:
- if (firstcore >= 0 || cores.size() > 0) {
- Clp_OptionError(clp, "%<%O%> already given");
- exit(EXIT_FAILURE);
- } else {
- const char *plus = strchr(clp->vstr, '+');
- Json ij = Json::parse(clp->vstr),
- aj = Json::parse(String("[") + String(clp->vstr) + String("]")),
- pj1 = Json::parse(plus ? String(clp->vstr, plus) : "x"),
- pj2 = Json::parse(plus ? String(plus + 1) : "x");
- for (int i = 0; aj && i < aj.size(); ++i)
- if (!aj[i].is_int() || aj[i].to_i() < 0)
- aj = Json();
- if (ij && ij.is_int() && ij.to_i() >= 0)
- firstcore = ij.to_i(), corestride = 1;
- else if (pj1 && pj2 && pj1.is_int() && pj1.to_i() >= 0 && pj2.is_int())
- firstcore = pj1.to_i(), corestride = pj2.to_i();
- else if (aj) {
- for (int i = 0; i < aj.size(); ++i)
- cores.push_back(aj[i].to_i());
- } else {
- Clp_OptionError(clp, "bad %<%O%>, expected %<CORE1%>, %<CORE1+STRIDE%>, or %<CORE1,CORE2,...%>");
- exit(EXIT_FAILURE);
- }
- }
- break;
- case opt_norun:
- recovery_only = true;
- break;
- case opt_epoch_interval:
- epoch_interval_ms = clp->val.d;
- break;
- default:
- fprintf(stderr, "Usage: mtd [-np] [--ld dir1[,dir2,...]] [--cd dir1[,dir2,...]]\n");
- exit(EXIT_FAILURE);
- }
- }
- Clp_DeleteParser(clp);
- if (logdirs.empty())
- logdirs.push_back(".");
- if (ckpdirs.empty())
- ckpdirs.push_back(".");
- if (firstcore < 0)
- firstcore = cores.size() ? cores.back() + 1 : 0;
- for (; (int) cores.size() < udpthreads; firstcore += corestride)
- cores.push_back(firstcore);
-
- // for -pg profiling
- signal(SIGINT, catchint);
-
- // log epoch starts at 1
- global_log_epoch = 1;
- global_wake_epoch = 0;
- log_epoch_interval.tv_sec = 0;
- log_epoch_interval.tv_usec = 200000;
-
- // set a timer for incrementing the global epoch
- if (!dotest) {
- if (!epoch_interval_ms) {
- printf("WARNING: epoch interval is 0, it means no GC is executed\n");
- } else {
- signal(SIGALRM, epochinc);
- struct itimerval etimer;
- etimer.it_interval.tv_sec = epoch_interval_ms / 1000;
- etimer.it_interval.tv_usec = fmod(epoch_interval_ms, 1000) * 1000;
- etimer.it_value.tv_sec = epoch_interval_ms / 1000;
- etimer.it_value.tv_usec = fmod(epoch_interval_ms, 1000) * 1000;
- ret = setitimer(ITIMER_REAL, &etimer, NULL);
- always_assert(ret == 0);
- }
- }
-
- // for parallel recovery
- ret = pthread_cond_init(&rec_cond, 0);
- always_assert(ret == 0);
- ret = pthread_mutex_init(&rec_mu, 0);
- always_assert(ret == 0);
-
- // for waking up the checkpoint thread
- ret = pthread_cond_init(&checkpoint_cond, 0);
- always_assert(ret == 0);
- ret = pthread_mutex_init(&checkpoint_mu, 0);
- always_assert(ret == 0);
-
- threadinfo *main_ti = threadinfo::make(threadinfo::TI_MAIN, -1);
- main_ti->pthread() = pthread_self();
-
- initial_timestamp = timestamp();
- tree = new Masstree::default_table;
- tree->initialize(*main_ti);
- printf("%s, %s, pin-threads %s, ", tree->name(), row_type::name(),
- pinthreads ? "enabled" : "disabled");
- if(logging){
- printf("logging enabled\n");
- log_init();
- recover(main_ti);
- } else {
- printf("logging disabled\n");
- }
-
- // UDP threads, each with its own port.
- if (udpthreads == 0)
- printf("0 udp threads\n");
- else if (udpthreads == 1)
- printf("1 udp thread (port %d)\n", port);
- else
- printf("%d udp threads (ports %d-%d)\n", udpthreads, port, port + udpthreads - 1);
- for(i = 0; i < udpthreads; i++){
- threadinfo *ti = threadinfo::make(threadinfo::TI_PROCESS, i);
- ret = pthread_create(&ti->pthread(), 0, udp_threadfunc, ti);
- always_assert(ret == 0);
- }
-
- if (dotest) {
- if (strcmp(dotest, "palm") == 0) {
- runtest("palma", 1);
- runtest("palmb", tcpthreads);
- } else
- runtest(dotest, tcpthreads);
- tree->stats(stderr);
- if (doprint)
- tree->print(stdout);
- exit(0);
- }
-
- // TCP socket and threads
-
- s = socket(AF_INET, SOCK_STREAM, 0);
- always_assert(s >= 0);
- setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
- setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes));
-
- struct sockaddr_in sin;
- sin.sin_family = AF_INET;
- sin.sin_addr.s_addr = INADDR_ANY;
- sin.sin_port = htons(port);
- ret = bind(s, (struct sockaddr *) &sin, sizeof(sin));
- if (ret < 0) {
- perror("bind");
- exit(EXIT_FAILURE);
- }
-
- ret = listen(s, 100);
- if (ret < 0) {
- perror("listen");
- exit(EXIT_FAILURE);
- }
-
- threadinfo **tcpti = new threadinfo *[tcpthreads];
- tcp_thread_pipes = new int[tcpthreads * 2];
- printf("%d tcp threads (port %d)\n", tcpthreads, port);
- for(i = 0; i < tcpthreads; i++){
- threadinfo *ti = threadinfo::make(threadinfo::TI_PROCESS, i);
- ret = pipe(&tcp_thread_pipes[i * 2]);
- always_assert(ret == 0);
- ret = pthread_create(&ti->pthread(), 0, tcp_threadfunc, ti);
- always_assert(ret == 0);
- tcpti[i] = ti;
- }
- // Create a canceling thread.
- ret = pipe(quit_pipe);
- always_assert(ret == 0);
- pthread_t canceling_tid;
- ret = pthread_create(&canceling_tid, NULL, canceling, NULL);
- always_assert(ret == 0);
-
- static int next = 0;
- while(1){
- int s1;
- struct sockaddr_in sin1;
- socklen_t sinlen = sizeof(sin1);
-
- bzero(&sin1, sizeof(sin1));
- s1 = accept(s, (struct sockaddr *) &sin1, &sinlen);
- always_assert(s1 >= 0);
- setsockopt(s1, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes));
-
- // Complete handshake.
- char buf[BUFSIZ];
- ssize_t nr = read(s1, buf, BUFSIZ);
- if (nr == -1) {
- perror("read");
- kill_connection:
- close(s1);
- continue;
- }
-
- msgpack::streaming_parser sp;
- if (nr == 0 || sp.consume(buf, nr) != (size_t) nr
- || !sp.result().is_a() || sp.result().size() < 2
- || !sp.result()[1].is_i() || sp.result()[1].as_i() != Cmd_Handshake) {
- fprintf(stderr, "failed handshake\n");
- goto kill_connection;
- }
-
- int target_core = -1;
- if (sp.result().size() >= 3 && sp.result()[2].is_o()
- && sp.result()[2]["core"].is_i())
- target_core = sp.result()[2]["core"].as_i();
- if (target_core < 0 || target_core >= tcpthreads) {
- target_core = next % tcpthreads;
- ++next;
- }
-
- conninfo* ci = new conninfo;
- ci->s = s1;
- swap(ci->handshake, sp.result());
-
- ssize_t w = write(tcp_thread_pipes[2*target_core + 1], &ci, sizeof(ci));
- always_assert((size_t) w == sizeof(ci));
- }
-}
-
-void
-catchint(int)
-{
- go_quit = 1;
- char cmd = 0;
- // Does not matter if the write fails (when the pipe is full)
- int r = write(quit_pipe[1], &cmd, sizeof(cmd));
- (void)r;
-}
-
-inline const char *threadtype(int type) {
- switch (type) {
- case threadinfo::TI_MAIN:
- return "main";
- case threadinfo::TI_PROCESS:
- return "process";
- case threadinfo::TI_LOG:
- return "log";
- case threadinfo::TI_CHECKPOINT:
- return "checkpoint";
- default:
- always_assert(0 && "Unknown threadtype");
- break;
- };
-}
-
-void *
-canceling(void *)
-{
- char cmd;
- int r = read(quit_pipe[0], &cmd, sizeof(cmd));
- (void) r;
- assert(r == sizeof(cmd) && cmd == 0);
- // Cancel wake up checkpointing threads
- pthread_mutex_lock(&checkpoint_mu);
- pthread_cond_signal(&checkpoint_cond);
- pthread_mutex_unlock(&checkpoint_mu);
-
- fprintf(stderr, "\n");
- // cancel outstanding threads. Checkpointing threads will exit safely
- // when the checkpointing thread 0 sees go_quit, and don't need cancel
- for (threadinfo *ti = threadinfo::allthreads; ti; ti = ti->next())
- if (ti->purpose() != threadinfo::TI_MAIN
- && ti->purpose() != threadinfo::TI_CHECKPOINT) {
- int r = pthread_cancel(ti->pthread());
- always_assert(r == 0);
- }
-
- // join canceled threads
- for (threadinfo *ti = threadinfo::allthreads; ti; ti = ti->next())
- if (ti->purpose() != threadinfo::TI_MAIN) {
- fprintf(stderr, "joining thread %s:%d\n",
- threadtype(ti->purpose()), ti->index());
- int r = pthread_join(ti->pthread(), 0);
- always_assert(r == 0);
- }
- tree->stats(stderr);
- exit(0);
-}
-
-void
-epochinc(int)
-{
- globalepoch += 2;
- active_epoch = threadinfo::min_active_epoch();
-}
-
-// Return 1 if success, -1 if I/O error or protocol unmatch
-int handshake(Json& request, threadinfo& ti) {
- always_assert(request.is_a() && request.size() >= 2
- && request[1].is_i() && request[1].as_i() == Cmd_Handshake
- && (request.size() == 2 || request[2].is_o()));
- if (request.size() >= 2
- && request[2]["maxkeylen"].is_i()
- && request[2]["maxkeylen"].as_i() > MASSTREE_MAXKEYLEN) {
- request[2] = false;
- request[3] = "bad maxkeylen";
- request.resize(4);
- } else {
- request[2] = true;
- request[3] = ti.index();
- request[4] = row_type::name();
- request.resize(5);
- }
- request[1] = Cmd_Handshake + 1;
- return request[2].as_b() ? 1 : -1;
-}
-
-// execute command, return result.
-int onego(query<row_type>& q, Json& request, Str request_str, threadinfo& ti) {
- int command = request[1].as_i();
- if (command == Cmd_Checkpoint) {
- // force checkpoint
- pthread_mutex_lock(&checkpoint_mu);
- pthread_cond_broadcast(&checkpoint_cond);
- pthread_mutex_unlock(&checkpoint_mu);
- request.resize(2);
- } else if (command == Cmd_Get) {
- q.run_get(tree->table(), request, ti);
- } else if (command == Cmd_Put && request.size() > 3
- && (request.size() % 2) == 1) { // insert or update
- Str key(request[2].as_s());
- const Json* req = request.array_data() + 3;
- const Json* end_req = request.end_array_data();
- request[2] = q.run_put(tree->table(), request[2].as_s(),
- req, end_req, ti);
- if (ti.logger() && request_str) {
- // use the client's parsed version of the request
- msgpack::parser mp(request_str.data());
- mp.skip_array_size().skip_primitives(3);
- ti.logger()->record(logcmd_put, q.query_times(), key, Str(mp.position(), request_str.end()));
- } else if (ti.logger())
- ti.logger()->record(logcmd_put, q.query_times(), key, req, end_req);
- request.resize(3);
- } else if (command == Cmd_Replace) { // insert or update
- Str key(request[2].as_s()), value(request[3].as_s());
- request[2] = q.run_replace(tree->table(), key, value, ti);
- if (ti.logger()) // NB may block
- ti.logger()->record(logcmd_replace, q.query_times(), key, value);
- request.resize(3);
- } else if (command == Cmd_Remove) { // remove
- Str key(request[2].as_s());
- bool removed = q.run_remove(tree->table(), key, ti);
- if (removed && ti.logger()) // NB may block
- ti.logger()->record(logcmd_remove, q.query_times(), key, Str());
- request[2] = removed;
- request.resize(3);
- } else if (command == Cmd_Scan) {
- q.run_scan(tree->table(), request, ti);
- } else {
- request[1] = -1;
- request.resize(2);
- return -1;
- }
- request[1] = command + 1;
- return 1;
-}
-
-#if HAVE_SYS_EPOLL_H
-struct tcpfds {
- int epollfd;
-
- tcpfds(int pipefd) {
- epollfd = epoll_create(10);
- if (epollfd < 0) {
- perror("epoll_create");
- exit(EXIT_FAILURE);
- }
- struct epoll_event ev;
- ev.events = EPOLLIN;
- ev.data.ptr = (void *) 1;
- int r = epoll_ctl(epollfd, EPOLL_CTL_ADD, pipefd, &ev);
- always_assert(r == 0);
- }
-
- enum { max_events = 100 };
- typedef struct epoll_event eventset[max_events];
- int wait(eventset &es) {
- return epoll_wait(epollfd, es, max_events, -1);
- }
-
- conn *event_conn(eventset &es, int i) const {
- return (conn *) es[i].data.ptr;
- }
-
- void add(int fd, conn *c) {
- struct epoll_event ev;
- ev.events = EPOLLIN;
- ev.data.ptr = c;
- int r = epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev);
- always_assert(r == 0);
- }
-
- void remove(int fd) {
- int r = epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, NULL);
- always_assert(r == 0);
- }
-};
-#else
-class tcpfds {
- int nfds_;
- fd_set rfds_;
- std::vector<conn *> conns_;
-
- public:
- tcpfds(int pipefd)
- : nfds_(pipefd + 1) {
- always_assert(pipefd < FD_SETSIZE);
- FD_ZERO(&rfds_);
- FD_SET(pipefd, &rfds_);
- conns_.resize(nfds_, 0);
- conns_[pipefd] = (conn *) 1;
- }
-
- typedef fd_set eventset;
- int wait(eventset &es) {
- es = rfds_;
- int r = select(nfds_, &es, 0, 0, 0);
- return r > 0 ? nfds_ : r;
- }
-
- conn *event_conn(eventset &es, int i) const {
- return FD_ISSET(i, &es) ? conns_[i] : 0;
- }
-
- void add(int fd, conn *c) {
- always_assert(fd < FD_SETSIZE);
- FD_SET(fd, &rfds_);
- if (fd >= nfds_) {
- nfds_ = fd + 1;
- conns_.resize(nfds_, 0);
- }
- conns_[fd] = c;
- }
-
- void remove(int fd) {
- always_assert(fd < FD_SETSIZE);
- FD_CLR(fd, &rfds_);
- if (fd == nfds_ - 1) {
- while (nfds_ > 0 && !FD_ISSET(nfds_ - 1, &rfds_))
- --nfds_;
- }
- }
-};
-#endif
-
-void prepare_thread(threadinfo *ti) {
-#if __linux__
- if (pinthreads) {
- cpu_set_t cs;
- CPU_ZERO(&cs);
- CPU_SET(cores[ti->index()], &cs);
- always_assert(sched_setaffinity(0, sizeof(cs), &cs) == 0);
- }
-#else
- always_assert(!pinthreads && "pinthreads not supported\n");
-#endif
- if (logging)
- ti->set_logger(&logs->log(ti->index() % nlogger));
-}
-
-void* tcp_threadfunc(void* x) {
- threadinfo* ti = reinterpret_cast<threadinfo*>(x);
- ti->pthread() = pthread_self();
- prepare_thread(ti);
-
- int myfd = tcp_thread_pipes[2 * ti->index()];
- tcpfds sloop(myfd);
- tcpfds::eventset events;
- std::deque<conn*> ready;
- query<row_type> q;
-
- while (1) {
- int nev = sloop.wait(events);
- for (int i = 0; i < nev; i++)
- if (conn *c = sloop.event_conn(events, i))
- ready.push_back(c);
-
- while (!ready.empty()) {
- conn* c = ready.front();
- ready.pop_front();
-
- if (c == (conn *) 1) {
- // new connections
-#define MAX_NEWCONN 100
- conninfo* ci[MAX_NEWCONN];
- ssize_t len = read(myfd, ci, sizeof(ci));
- always_assert(len > 0 && len % sizeof(int) == 0);
- for (int j = 0; j * sizeof(*ci) < (size_t) len; ++j) {
- struct conn *c = new conn(ci[j]->s);
- sloop.add(c->fd, c);
- int ret = handshake(ci[j]->handshake, *ti);
- msgpack::unparse(*c->kvout, ci[j]->handshake);
- kvflush(c->kvout);
- if (ret < 0) {
- sloop.remove(c->fd);
- delete c;
- }
- delete ci[j];
- }
- } else if (c) {
- // Should not block as suggested by epoll
- uint64_t xposition = c->xposition();
- Json& request = c->receive();
- int ret;
- if (unlikely(!request))
- goto closed;
- ti->rcu_start();
- ret = onego(q, request, c->recent_string(xposition), *ti);
- ti->rcu_stop();
- msgpack::unparse(*c->kvout, request);
- request.clear();
- if (likely(ret >= 0)) {
- if (c->check(0))
- ready.push_back(c);
- else
- kvflush(c->kvout);
- continue;
- }
- printf("socket read error\n");
- closed:
- kvflush(c->kvout);
- sloop.remove(c->fd);
- delete c;
- }
- }
- }
- return 0;
-}
-
-// serve a client udp socket, in a dedicated thread
-void* udp_threadfunc(void* x) {
- threadinfo* ti = reinterpret_cast<threadinfo*>(x);
- ti->pthread() = pthread_self();
- prepare_thread(ti);
-
- struct sockaddr_in sin;
- bzero(&sin, sizeof(sin));
- sin.sin_family = AF_INET;
- sin.sin_port = htons(port + ti->index());
-
- int s = socket(AF_INET, SOCK_DGRAM, 0);
- always_assert(s >= 0);
- int ret = bind(s, (struct sockaddr *) &sin, sizeof(sin));
- always_assert(ret == 0 && "bind failed");
- int sobuflen = 512*1024;
- setsockopt(s, SOL_SOCKET, SO_RCVBUF, &sobuflen, sizeof(sobuflen));
-
- String buf = String::make_uninitialized(4096);
- struct kvout *kvout = new_bufkvout();
- msgpack::streaming_parser parser;
- StringAccum sa;
-
- query<row_type> q;
- while(1){
- struct sockaddr_in sin;
- socklen_t sinlen = sizeof(sin);
- ssize_t cc = recvfrom(s, const_cast<char*>(buf.data()), buf.length(),
- 0, (struct sockaddr *) &sin, &sinlen);
- if(cc < 0){
- perror("udpgo read");
- exit(EXIT_FAILURE);
- }
- kvout_reset(kvout);
-
- parser.reset();
- unsigned consumed = parser.consume(buf.data(), buf.length(), buf);
-
- // Fail if we received a partial request
- if (parser.success() && parser.result().is_a()) {
- ti->rcu_start();
- if (onego(q, parser.result(), Str(buf.data(), consumed), *ti) >= 0) {
- sa.clear();
- msgpack::unparser<StringAccum> cu(sa);
- cu << parser.result();
- cc = sendto(s, sa.data(), sa.length(), 0,
- (struct sockaddr*) &sin, sinlen);
- always_assert(cc == (ssize_t) sa.length());
- }
- ti->rcu_stop();
- } else
- printf("onego failed\n");
- }
- return 0;
-}
-
-static String log_filename(const char* logdir, int logindex) {
- struct stat sb;
- int r = stat(logdir, &sb);
- if (r < 0 && errno == ENOENT) {
- r = mkdir(logdir, 0777);
- if (r < 0) {
- fprintf(stderr, "%s: %s\n", logdir, strerror(errno));
- always_assert(0);
- }
- }
-
- StringAccum sa;
- sa.snprintf(strlen(logdir) + 24, "%s/kvd-log-%d", logdir, logindex);
- return sa.take_string();
-}
-
-void log_init() {
- int ret, i;
-
- logs = logset::make(nlogger);
- for (i = 0; i < nlogger; i++)
- logs->log(i).initialize(log_filename(logdirs[i % logdirs.size()], i));
-
- cks = (ckstate *)malloc(sizeof(ckstate) * nckthreads);
- for (i = 0; i < nckthreads; i++) {
- threadinfo *ti = threadinfo::make(threadinfo::TI_CHECKPOINT, i);
- cks[i].state = CKState_Uninit;
- cks[i].ti = ti;
- ret = pthread_create(&ti->pthread(), 0, conc_checkpointer, ti);
- always_assert(ret == 0);
- }
-}
-
-// read a checkpoint, insert key/value pairs into tree.
-// must be followed by a read of the log!
-// since checkpoint is not consistent
-// with any one point in time.
-// returns the timestamp of the first log record that needs
-// to come from the log.
-kvepoch_t read_checkpoint(threadinfo *ti, const char *path) {
- double t0 = now();
-
- int fd = open(path, 0);
- if(fd < 0){
- printf("no %s\n", path);
- return 0;
- }
- struct stat sb;
- int ret = fstat(fd, &sb);
- always_assert(ret == 0);
- char *p = (char *) mmap(0, sb.st_size, PROT_READ, MAP_FILE|MAP_PRIVATE, fd, 0);
- always_assert(p != MAP_FAILED);
- close(fd);
-
- msgpack::parser par(String::make_stable(p, sb.st_size));
- Json j;
- par >> j;
- std::cerr << j << "\n";
- always_assert(j["generation"].is_i() && j["size"].is_i());
- uint64_t gen = j["generation"].as_i();
- uint64_t n = j["size"].as_i();
- printf("reading checkpoint with %" PRIu64 " nodes\n", n);
-
- // read data
- for (uint64_t i = 0; i != n; ++i)
- ckstate::insert(tree->table(), par, *ti);
-
- munmap(p, sb.st_size);
- double t1 = now();
- printf("%.1f MB, %.2f sec, %.1f MB/sec\n",
- sb.st_size / 1000000.0,
- t1 - t0,
- (sb.st_size / 1000000.0) / (t1 - t0));
- return gen;
-}
-
-void
-waituntilphase(int phase)
-{
- always_assert(pthread_mutex_lock(&rec_mu) == 0);
- while (rec_state != phase)
- always_assert(pthread_cond_wait(&rec_cond, &rec_mu) == 0);
- always_assert(pthread_mutex_unlock(&rec_mu) == 0);
-}
-
-void
-inactive(void)
-{
- always_assert(pthread_mutex_lock(&rec_mu) == 0);
- rec_nactive --;
- always_assert(pthread_cond_broadcast(&rec_cond) == 0);
- always_assert(pthread_mutex_unlock(&rec_mu) == 0);
-}
-
-void recovercheckpoint(threadinfo *ti) {
- waituntilphase(REC_CKP);
- char path[256];
- sprintf(path, "%s/kvd-ckp-%" PRId64 "-%d",
- ckpdirs[ti->index() % ckpdirs.size()],
- ckp_gen.value(), ti->index());
- kvepoch_t gen = read_checkpoint(ti, path);
- always_assert(ckp_gen == gen);
- inactive();
-}
-
-void
-recphase(int nactive, int state)
-{
- rec_nactive = nactive;
- rec_state = state;
- always_assert(pthread_cond_broadcast(&rec_cond) == 0);
- while (rec_nactive)
- always_assert(pthread_cond_wait(&rec_cond, &rec_mu) == 0);
-}
-
-// read the checkpoint file.
-// read each log file.
-// insert will ignore attempts to update with timestamps
-// less than what was in the entry from the checkpoint file.
-// so we don't have to do an explicit merge by time of the log files.
-void
-recover(threadinfo *)
-{
- recovering = true;
- // XXX: discard temporary checkpoint and ckp-gen files generated before crash
-
- // get the generation of the checkpoint from ckp-gen, if any
- char path[256];
- sprintf(path, "%s/kvd-ckp-gen", ckpdirs[0]);
- ckp_gen = 0;
- rec_ckp_min_epoch = rec_ckp_max_epoch = 0;
- int fd = open(path, O_RDONLY);
- if (fd >= 0) {
- Json ckpj = Json::parse(read_file_contents(fd));
- close(fd);
- if (ckpj && ckpj["kvdb_checkpoint"] && ckpj["generation"].is_number()) {
- ckp_gen = ckpj["generation"].to_u64();
- rec_ckp_min_epoch = ckpj["min_epoch"].to_u64();
- rec_ckp_max_epoch = ckpj["max_epoch"].to_u64();
- printf("recover from checkpoint %" PRIu64 " [%" PRIu64 ", %" PRIu64 "]\n", ckp_gen.value(), rec_ckp_min_epoch.value(), rec_ckp_max_epoch.value());
- }
- } else {
- printf("no %s\n", path);
- }
- always_assert(pthread_mutex_lock(&rec_mu) == 0);
-
- // recover from checkpoint, and set timestamp of the checkpoint
- recphase(nckthreads, REC_CKP);
-
- // find minimum maximum timestamp of entries in each log
- rec_log_infos = new logreplay::info_type[nlogger];
- recphase(nlogger, REC_LOG_TS);
-
- // replay log entries, remove inconsistent entries, and append
- // an empty log entry with minimum timestamp
-
- // calculate log range
-
- // Maximum epoch seen in the union of the logs and the checkpoint. (We
- // don't commit a checkpoint until all logs are flushed past the
- // checkpoint's max_epoch.)
- kvepoch_t max_epoch = rec_ckp_max_epoch;
- if (max_epoch)
- max_epoch = max_epoch.next_nonzero();
- for (logreplay::info_type *it = rec_log_infos;
- it != rec_log_infos + nlogger; ++it)
- if (it->last_epoch
- && (!max_epoch || max_epoch < it->last_epoch))
- max_epoch = it->last_epoch;
-
- // Maximum first_epoch seen in the logs. Full log information is not
- // available for epochs before max_first_epoch.
- kvepoch_t max_first_epoch = 0;
- for (logreplay::info_type *it = rec_log_infos;
- it != rec_log_infos + nlogger; ++it)
- if (it->first_epoch
- && (!max_first_epoch || max_first_epoch < it->first_epoch))
- max_first_epoch = it->first_epoch;
-
- // Maximum epoch of all logged wake commands.
- kvepoch_t max_wake_epoch = 0;
- for (logreplay::info_type *it = rec_log_infos;
- it != rec_log_infos + nlogger; ++it)
- if (it->wake_epoch
- && (!max_wake_epoch || max_wake_epoch < it->wake_epoch))
- max_wake_epoch = it->wake_epoch;
-
- // Minimum last_epoch seen in QUIESCENT logs.
- kvepoch_t min_quiescent_last_epoch = 0;
- for (logreplay::info_type *it = rec_log_infos;
- it != rec_log_infos + nlogger; ++it)
- if (it->quiescent
- && (!min_quiescent_last_epoch || min_quiescent_last_epoch > it->last_epoch))
- min_quiescent_last_epoch = it->last_epoch;
-
- // If max_wake_epoch && min_quiescent_last_epoch <= max_wake_epoch, then a
- // wake command was missed by at least one quiescent log. We can't replay
- // anything at or beyond the minimum missed wake epoch. So record, for
- // each log, the minimum wake command that at least one quiescent thread
- // missed.
- if (max_wake_epoch && min_quiescent_last_epoch <= max_wake_epoch)
- rec_replay_min_quiescent_last_epoch = min_quiescent_last_epoch;
- else
- rec_replay_min_quiescent_last_epoch = 0;
- recphase(nlogger, REC_LOG_ANALYZE_WAKE);
-
- // Calculate upper bound of epochs to replay.
- // This is the minimum of min_post_quiescent_wake_epoch (if any) and the
- // last_epoch of all non-quiescent logs.
- rec_replay_max_epoch = max_epoch;
- for (logreplay::info_type *it = rec_log_infos;
- it != rec_log_infos + nlogger; ++it) {
- if (!it->quiescent
- && it->last_epoch
- && it->last_epoch < rec_replay_max_epoch)
- rec_replay_max_epoch = it->last_epoch;
- if (it->min_post_quiescent_wake_epoch
- && it->min_post_quiescent_wake_epoch < rec_replay_max_epoch)
- rec_replay_max_epoch = it->min_post_quiescent_wake_epoch;
- }
-
- // Calculate lower bound of epochs to replay.
- rec_replay_min_epoch = rec_ckp_min_epoch;
- // XXX what about max_first_epoch?
-
- // Checks.
- if (rec_ckp_min_epoch) {
- always_assert(rec_ckp_min_epoch > max_first_epoch);
- always_assert(rec_ckp_min_epoch < rec_replay_max_epoch);
- always_assert(rec_ckp_max_epoch < rec_replay_max_epoch);
- fprintf(stderr, "replay [%" PRIu64 ",%" PRIu64 ") from [%" PRIu64 ",%" PRIu64 ") into ckp [%" PRIu64 ",%" PRIu64 "]\n",
- rec_replay_min_epoch.value(), rec_replay_max_epoch.value(),
- max_first_epoch.value(), max_epoch.value(),
- rec_ckp_min_epoch.value(), rec_ckp_max_epoch.value());
- }
-
- // Actually replay.
- delete[] rec_log_infos;
- rec_log_infos = 0;
- recphase(nlogger, REC_LOG_REPLAY);
-
- // done recovering
- recphase(0, REC_DONE);
-#if !NDEBUG
- // check that all delta markers have been recycled (leaving only remove
- // markers and real values)
- uint64_t deltas_created = 0, deltas_removed = 0;
- for (threadinfo *ti = threadinfo::allthreads; ti; ti = ti->next()) {
- deltas_created += ti->counter(tc_replay_create_delta);
- deltas_removed += ti->counter(tc_replay_remove_delta);
- }
- if (deltas_created)
- fprintf(stderr, "deltas created: %" PRIu64 ", removed: %" PRIu64 "\n", deltas_created, deltas_removed);
- always_assert(deltas_created == deltas_removed);
-#endif
-
- global_log_epoch = rec_replay_max_epoch.next_nonzero();
-
- always_assert(pthread_mutex_unlock(&rec_mu) == 0);
- recovering = false;
- if (recovery_only)
- exit(0);
-}
-
-void
-writecheckpoint(const char *path, ckstate *c, double t0)
-{
- double t1 = now();
- printf("memory phase: %" PRIu64 " nodes, %.2f sec\n", c->count, t1 - t0);
-
- int fd = creat(path, 0666);
- always_assert(fd >= 0);
-
- // checkpoint file format, all msgpack:
- // {"generation": generation, "size": size, ...}
- // then `size` triples of key (string), timestmap (int), value (whatever)
- Json j = Json().set("generation", ckp_gen.value())
- .set("size", c->count)
- .set("firstkey", c->startkey);
- StringAccum sa;
- msgpack::unparse(sa, j);
- checked_write(fd, sa.data(), sa.length());
- checked_write(fd, c->vals->buf, c->vals->n);
-
- int ret = fsync(fd);
- always_assert(ret == 0);
- ret = close(fd);
- always_assert(ret == 0);
-
- double t2 = now();
- c->bytes = c->vals->n;
- printf("file phase (%s): %" PRIu64 " bytes, %.2f sec, %.1f MB/sec\n",
- path,
- c->bytes,
- t2 - t1,
- (c->bytes / 1000000.0) / (t2 - t1));
-}
-
-void
-conc_filecheckpoint(threadinfo *ti)
-{
- ckstate *c = &cks[ti->index()];
- c->vals = new_bufkvout();
- double t0 = now();
- tree->table().scan(c->startkey, true, *c, *ti);
- char path[256];
- sprintf(path, "%s/kvd-ckp-%" PRId64 "-%d",
- ckpdirs[ti->index() % ckpdirs.size()],
- ckp_gen.value(), ti->index());
- writecheckpoint(path, c, t0);
- c->count = 0;
- free(c->vals);
-}
-
-static Json
-prepare_checkpoint(kvepoch_t min_epoch, int nckthreads, const Str *pv)
-{
- Json j;
- j.set("kvdb_checkpoint", true)
- .set("min_epoch", min_epoch.value())
- .set("max_epoch", global_log_epoch.value())
- .set("generation", ckp_gen.value())
- .set("nckthreads", nckthreads);
-
- Json pvj;
- for (int i = 1; i < nckthreads; ++i)
- pvj.push_back(Json::make_string(pv[i].s, pv[i].len));
- j.set("pivots", pvj);
-
- return j;
-}
-
-static void
-commit_checkpoint(Json ckpj)
-{
- // atomically commit a set of checkpoint files by incrementing
- // the checkpoint generation on disk
- char path[256];
- sprintf(path, "%s/kvd-ckp-gen", ckpdirs[0]);
- int r = atomic_write_file_contents(path, ckpj.unparse());
- always_assert(r == 0);
- fprintf(stderr, "kvd-ckp-%" PRIu64 " [%s,%s]: committed\n",
- ckp_gen.value(), ckpj["min_epoch"].to_s().c_str(),
- ckpj["max_epoch"].to_s().c_str());
-
- // delete old checkpoint files
- for (int i = 0; i < nckthreads; i++) {
- char path[256];
- sprintf(path, "%s/kvd-ckp-%" PRId64 "-%d",
- ckpdirs[i % ckpdirs.size()],
- ckp_gen.value() - 1, i);
- unlink(path);
- }
-}
-
-static kvepoch_t
-max_flushed_epoch()
-{
- kvepoch_t mfe = 0, ge = global_log_epoch;
- for (int i = 0; i < nlogger; ++i) {
- loginfo& log = logs->log(i);
- kvepoch_t fe = log.quiescent() ? ge : log.flushed_epoch();
- if (!mfe || fe < mfe)
- mfe = fe;
- }
- return mfe;
-}
-
-// concurrent periodic checkpoint
-void* conc_checkpointer(void* x) {
- threadinfo* ti = reinterpret_cast<threadinfo*>(x);
- ti->pthread() = pthread_self();
- recovercheckpoint(ti);
- ckstate *c = &cks[ti->index()];
- c->count = 0;
- pthread_cond_init(&c->state_cond, NULL);
- c->state = CKState_Ready;
- while (recovering)
- sleep(1);
- if (checkpoint_interval <= 0)
- return 0;
- if (ti->index() == 0) {
- for (int i = 1; i < nckthreads; i++)
- while (cks[i].state != CKState_Ready)
- ;
- Str *pv = new Str[nckthreads + 1];
- Json uncommitted_ckp;
-
- while (1) {
- struct timespec ts;
- set_timespec(ts, now() + (uncommitted_ckp ? 0.25 : checkpoint_interval));
-
- pthread_mutex_lock(&checkpoint_mu);
- if (!go_quit)
- pthread_cond_timedwait(&checkpoint_cond, &checkpoint_mu, &ts);
- if (go_quit) {
- for (int i = 0; i < nckthreads; i++) {
- cks[i].state = CKState_Quit;
- pthread_cond_signal(&cks[i].state_cond);
- }
- pthread_mutex_unlock(&checkpoint_mu);
- break;
- }
- pthread_mutex_unlock(&checkpoint_mu);
-
- if (uncommitted_ckp) {
- kvepoch_t mfe = max_flushed_epoch();
- if (!mfe || mfe > uncommitted_ckp["max_epoch"].to_u64()) {
- commit_checkpoint(uncommitted_ckp);
- uncommitted_ckp = Json();
- }
- continue;
- }
-
- double t0 = now();
- ti->rcu_start();
- for (int i = 0; i < nckthreads + 1; i++)
- pv[i].assign(NULL, 0);
- tree->findpivots(pv, nckthreads + 1);
- ti->rcu_stop();
-
- kvepoch_t min_epoch = global_log_epoch;
- pthread_mutex_lock(&checkpoint_mu);
- ckp_gen = ckp_gen.next_nonzero();
- for (int i = 0; i < nckthreads; i++) {
- cks[i].startkey = pv[i];
- cks[i].endkey = (i == nckthreads - 1 ? Str() : pv[i + 1]);
- cks[i].state = CKState_Go;
- pthread_cond_signal(&cks[i].state_cond);
- }
- pthread_mutex_unlock(&checkpoint_mu);
-
- ti->rcu_start();
- conc_filecheckpoint(ti);
- ti->rcu_stop();
-
- cks[0].state = CKState_Ready;
- uint64_t bytes = cks[0].bytes;
- pthread_mutex_lock(&checkpoint_mu);
- for (int i = 1; i < nckthreads; i++) {
- while (cks[i].state != CKState_Ready)
- pthread_cond_wait(&cks[i].state_cond, &checkpoint_mu);
- bytes += cks[i].bytes;
- }
- pthread_mutex_unlock(&checkpoint_mu);
-
- uncommitted_ckp = prepare_checkpoint(min_epoch, nckthreads, pv);
-
- for (int i = 0; i < nckthreads + 1; i++)
- if (pv[i].s)
- free((void *)pv[i].s);
- double t = now() - t0;
- fprintf(stderr, "kvd-ckp-%" PRIu64 " [%s,%s]: prepared (%.2f sec, %" PRIu64 " MB, %" PRIu64 " MB/sec)\n",
- ckp_gen.value(), uncommitted_ckp["min_epoch"].to_s().c_str(),
- uncommitted_ckp["max_epoch"].to_s().c_str(),
- t, bytes / (1 << 20), (uint64_t)(bytes / t) >> 20);
- }
- } else {
- while(1) {
- pthread_mutex_lock(&checkpoint_mu);
- while (c->state != CKState_Go && c->state != CKState_Quit)
- pthread_cond_wait(&c->state_cond, &checkpoint_mu);
- if (c->state == CKState_Quit) {
- pthread_mutex_unlock(&checkpoint_mu);
- break;
- }
- pthread_mutex_unlock(&checkpoint_mu);
-
- ti->rcu_start();
- conc_filecheckpoint(ti);
- ti->rcu_stop();
-
- pthread_mutex_lock(&checkpoint_mu);
- c->state = CKState_Ready;
- pthread_cond_signal(&c->state_cond);
- pthread_mutex_unlock(&checkpoint_mu);
- }
- }
- return 0;
-}
diff --git a/mttest.cc b/mttest.cc
deleted file mode 100644
index 90623e6..0000000
--- a/mttest.cc
+++ /dev/null
@@ -1,1430 +0,0 @@
-/* Masstree
- * Eddie Kohler, Yandong Mao, Robert Morris
- * Copyright (c) 2012-2013 President and Fellows of Harvard College
- * Copyright (c) 2012-2013 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.
- */
-// -*- mode: c++ -*-
-// mttest: key/value tester
-//
-
-#include <stdio.h>
-#include <stdarg.h>
-#include <ctype.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <sys/select.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <sys/utsname.h>
-#include <limits.h>
-#if HAVE_NUMA_H
-#include <numa.h>
-#endif
-#if HAVE_SYS_EPOLL_H
-#include <sys/epoll.h>
-#endif
-#if HAVE_EXECINFO_H
-#include <execinfo.h>
-#endif
-#if __linux__
-#include <asm-generic/mman.h>
-#endif
-#include <fcntl.h>
-#include <assert.h>
-#include <string.h>
-#include <pthread.h>
-#include <math.h>
-#include <signal.h>
-#include <errno.h>
-#ifdef __linux__
-#include <malloc.h>
-#endif
-#include "nodeversion.hh"
-#include "kvstats.hh"
-#include "query_masstree.hh"
-#include "masstree_tcursor.hh"
-#include "masstree_insert.hh"
-#include "masstree_remove.hh"
-#include "masstree_scan.hh"
-#include "timestamp.hh"
-#include "json.hh"
-#include "kvtest.hh"
-#include "kvrandom.hh"
-#include "kvrow.hh"
-#include "kvio.hh"
-#include "clp.h"
-#include <algorithm>
-#include <numeric>
-
-static std::vector<int> cores;
-volatile bool timeout[2] = {false, false};
-double duration[2] = {10, 0};
-int kvtest_first_seed = 31949;
-uint64_t test_limit = ~uint64_t(0);
-static Json test_param;
-
-bool quiet = false;
-bool print_table = false;
-static const char *gid = NULL;
-
-// all default to the number of cores
-static int udpthreads = 0;
-static int tcpthreads = 0;
-
-static bool tree_stats = false;
-static bool json_stats = false;
-static String gnuplot_yrange;
-static bool pinthreads = false;
-static nodeversion32 global_epoch_lock(false);
-volatile mrcu_epoch_type globalepoch = 1; // global epoch, updated by main thread regularly
-volatile mrcu_epoch_type active_epoch = 1;
-kvepoch_t global_log_epoch = 0;
-static int port = 2117;
-static int rscale_ncores = 0;
-
-#if MEMSTATS && HAVE_NUMA_H && HAVE_LIBNUMA
-struct mttest_numainfo {
- long long free;
- long long size;
-};
-std::vector<mttest_numainfo> numa;
-#endif
-
-volatile bool recovering = false; // so don't add log entries, and free old value immediately
-kvtimestamp_t initial_timestamp;
-
-static const char *threadcounter_names[(int) tc_max];
-
-/* running local tests */
-void test_timeout(int) {
- size_t n;
- for (n = 0; n < arraysize(timeout) && timeout[n]; ++n)
- /* do nothing */;
- if (n < arraysize(timeout)) {
- timeout[n] = true;
- if (n + 1 < arraysize(timeout) && duration[n + 1])
- xalarm(duration[n + 1]);
- }
-}
-
-void set_global_epoch(mrcu_epoch_type e) {
- global_epoch_lock.lock();
- if (mrcu_signed_epoch_type(e - globalepoch) > 0) {
- globalepoch = e;
- active_epoch = threadinfo::min_active_epoch();
- }
- global_epoch_lock.unlock();
-}
-
-template <typename T>
-struct kvtest_client {
- using table_type = T;
-
- kvtest_client()
- : limit_(test_limit), ncores_(udpthreads), kvo_() {
- }
- ~kvtest_client() {
- if (kvo_)
- free_kvout(kvo_);
- }
-
- int nthreads() const {
- return udpthreads;
- }
- int id() const {
- return ti_->index();
- }
- void set_table(T* table, threadinfo *ti) {
- table_ = table;
- ti_ = ti;
- }
- void reset(const String &test, int trial) {
- report_ = Json().set("table", T().name())
- .set("test", test).set("trial", trial)
- .set("thread", ti_->index());
- }
-
- bool timeout(int which) const {
- return ::timeout[which];
- }
- uint64_t limit() const {
- return limit_;
- }
- bool has_param(const String& name) const {
- return test_param.count(name);
- }
- Json param(const String& name, Json default_value = Json()) const {
- return test_param.count(name) ? test_param.at(name) : default_value;
- }
-
- int ncores() const {
- return ncores_;
- }
- double now() const {
- return ::now();
- }
- int ruscale_partsz() const {
- return (140 * 1000000) / 16;
- }
- int ruscale_init_part_no() const {
- return ti_->index();
- }
- long nseqkeys() const {
- return 16 * ruscale_partsz();
- }
-
- void get(long ikey);
- bool get_sync(Str key);
- bool get_sync(Str key, Str& value);
- bool get_sync(long ikey) {
- quick_istr key(ikey);
- return get_sync(key.string());
- }
- bool get_sync_key16(long ikey) {
- quick_istr key(ikey, 16);
- return get_sync(key.string());
- }
- void get_check(Str key, Str expected);
- void get_check(const char *key, const char *expected) {
- get_check(Str(key), Str(expected));
- }
- void get_check(long ikey, long iexpected) {
- quick_istr key(ikey), expected(iexpected);
- get_check(key.string(), expected.string());
- }
- void get_check(Str key, long iexpected) {
- quick_istr expected(iexpected);
- get_check(key, expected.string());
- }
- void get_check_key8(long ikey, long iexpected) {
- quick_istr key(ikey, 8), expected(iexpected);
- get_check(key.string(), expected.string());
- }
- void get_col_check(Str key, int col, Str value);
- void get_col_check(long ikey, int col, long ivalue) {
- quick_istr key(ikey), value(ivalue);
- get_col_check(key.string(), col, value.string());
- }
- void get_col_check_key10(long ikey, int col, long ivalue) {
- quick_istr key(ikey, 10), value(ivalue);
- get_col_check(key.string(), col, value.string());
- }
- void get_check_absent(Str key);
- //void many_get_check(int nk, long ikey[], long iexpected[]);
-
- void scan_sync(Str firstkey, int n,
- std::vector<Str>& keys, std::vector<Str>& values);
- void rscan_sync(Str firstkey, int n,
- std::vector<Str>& keys, std::vector<Str>& values);
- void scan_versions_sync(Str firstkey, int n,
- std::vector<Str>& keys, std::vector<Str>& values);
- const std::vector<uint64_t>& scan_versions() const {
- return scan_versions_;
- }
-
- void put(Str key, Str value);
- void put(const char *key, const char *value) {
- put(Str(key), Str(value));
- }
- void put(long ikey, long ivalue) {
- quick_istr key(ikey), value(ivalue);
- put(key.string(), value.string());
- }
- void put(Str key, long ivalue) {
- quick_istr value(ivalue);
- put(key, value.string());
- }
- void put_key8(long ikey, long ivalue) {
- quick_istr key(ikey, 8), value(ivalue);
- put(key.string(), value.string());
- }
- void put_key16(long ikey, long ivalue) {
- quick_istr key(ikey, 16), value(ivalue);
- put(key.string(), value.string());
- }
- void put_col(Str key, int col, Str value);
- void put_col(long ikey, int col, long ivalue) {
- quick_istr key(ikey), value(ivalue);
- put_col(key.string(), col, value.string());
- }
- void put_col_key10(long ikey, int col, long ivalue) {
- quick_istr key(ikey, 10), value(ivalue);
- put_col(key.string(), col, value.string());
- }
- void insert_check(Str key, Str value);
-
- void remove(Str key);
- void remove(long ikey) {
- quick_istr key(ikey);
- remove(key.string());
- }
- void remove_key8(long ikey) {
- quick_istr key(ikey, 8);
- remove(key.string());
- }
- void remove_key16(long ikey) {
- quick_istr key(ikey, 16);
- remove(key.string());
- }
- bool remove_sync(Str key);
- bool remove_sync(long ikey) {
- quick_istr key(ikey);
- return remove_sync(key.string());
- }
- void remove_check(Str key);
-
- void print() {
- table_->print(stderr);
- }
- void puts_done() {
- }
- void wait_all() {
- }
- void rcu_quiesce() {
- mrcu_epoch_type e = timestamp() >> 16;
- if (e != globalepoch)
- set_global_epoch(e);
- ti_->rcu_quiesce();
- }
- String make_message(lcdf::StringAccum &sa) const;
- void notice(const char *fmt, ...);
- void fail(const char *fmt, ...);
- const Json& report(const Json& x) {
- return report_.merge(x);
- }
- void finish() {
- Json counters;
- for (int i = 0; i < tc_max; ++i) {
- if (uint64_t c = ti_->counter(threadcounter(i)))
- counters.set(threadcounter_names[i], c);
- }
- if (counters) {
- report_.set("counters", counters);
- }
- if (!quiet) {
- fprintf(stderr, "%d: %s\n", ti_->index(), report_.unparse().c_str());
- }
- }
-
- T *table_;
- threadinfo *ti_;
- query<row_type> q_[1];
- kvrandom_lcg_nr rand;
- uint64_t limit_;
- Json report_;
- Json req_;
- std::vector<uint64_t> scan_versions_;
- int ncores_;
- kvout *kvo_;
-
- private:
- void output_scan(const Json& req, std::vector<Str>& keys, std::vector<Str>& values) const;
-};
-
-static volatile int kvtest_printing;
-
-template <typename T> inline void kvtest_print(const T &table, FILE* f, threadinfo *ti) {
- // only print out the tree from the first failure
- while (!bool_cmpxchg((int *) &kvtest_printing, 0, ti->index() + 1)) {
- }
- table.print(f);
-}
-
-template <typename T> inline void kvtest_json_stats(T& table, Json& j, threadinfo& ti) {
- table.json_stats(j, ti);
-}
-
-template <typename T>
-void kvtest_client<T>::get(long ikey) {
- quick_istr key(ikey);
- Str val;
- (void) q_[0].run_get1(table_->table(), key.string(), 0, val, *ti_);
-}
-
-template <typename T>
-bool kvtest_client<T>::get_sync(Str key) {
- Str val;
- return q_[0].run_get1(table_->table(), key, 0, val, *ti_);
-}
-
-template <typename T>
-bool kvtest_client<T>::get_sync(Str key, Str& value) {
- return q_[0].run_get1(table_->table(), key, 0, value, *ti_);
-}
-
-template <typename T>
-void kvtest_client<T>::get_check(Str key, Str expected) {
- Str val;
- if (unlikely(!q_[0].run_get1(table_->table(), key, 0, val, *ti_))) {
- fail("get(%s) failed (expected %s)\n", String(key).printable().c_str(),
- String(expected).printable().c_str());
- } else if (unlikely(expected != val)) {
- fail("get(%s) returned unexpected value %s (expected %s)\n",
- String(key).printable().c_str(),
- String(val).substr(0, 40).printable().c_str(),
- String(expected).substr(0, 40).printable().c_str());
- }
-}
-
-template <typename T>
-void kvtest_client<T>::get_col_check(Str key, int col,
- Str expected) {
- Str val;
- if (unlikely(!q_[0].run_get1(table_->table(), key, col, val, *ti_))) {
- fail("get.%d(%.*s) failed (expected %.*s)\n",
- col, key.len, key.s, expected.len, expected.s);
- } else if (unlikely(expected != val)) {
- fail("get.%d(%.*s) returned unexpected value %.*s (expected %.*s)\n",
- col, key.len, key.s, std::min(val.len, 40), val.s,
- expected.len, expected.s);
- }
-}
-
-template <typename T>
-void kvtest_client<T>::get_check_absent(Str key) {
- Str val;
- if (unlikely(q_[0].run_get1(table_->table(), key, 0, val, *ti_))) {
- fail("get(%s) failed (expected absent key)\n", String(key).printable().c_str());
- }
-}
-
-/*template <typename T>
-void kvtest_client<T>::many_get_check(int nk, long ikey[], long iexpected[]) {
- std::vector<quick_istr> ka(2*nk, quick_istr());
- for(int i = 0; i < nk; i++){
- ka[i].set(ikey[i]);
- ka[i+nk].set(iexpected[i]);
- q_[i].begin_get1(ka[i].string());
- }
- table_->many_get(q_, nk, *ti_);
- for(int i = 0; i < nk; i++){
- Str val = q_[i].get1_value();
- if (ka[i+nk] != val){
- printf("get(%ld) returned unexpected value %.*s (expected %ld)\n",
- ikey[i], std::min(val.len, 40), val.s, iexpected[i]);
- exit(1);
- }
- }
-}*/
-
-template <typename T>
-void kvtest_client<T>::scan_sync(Str firstkey, int n,
- std::vector<Str>& keys,
- std::vector<Str>& values) {
- req_ = Json::array(0, 0, firstkey, n);
- q_[0].run_scan(table_->table(), req_, *ti_);
- output_scan(req_, keys, values);
-}
-
-template <typename T>
-void kvtest_client<T>::rscan_sync(Str firstkey, int n,
- std::vector<Str>& keys,
- std::vector<Str>& values) {
- req_ = Json::array(0, 0, firstkey, n);
- q_[0].run_rscan(table_->table(), req_, *ti_);
- output_scan(req_, keys, values);
-}
-
-template <typename T>
-void kvtest_client<T>::scan_versions_sync(Str firstkey, int n,
- std::vector<Str>& keys,
- std::vector<Str>& values) {
- req_ = Json::array(0, 0, firstkey, n);
- scan_versions_.clear();
- q_[0].run_scan_versions(table_->table(), req_, scan_versions_, *ti_);
- output_scan(req_, keys, values);
-}
-
-template <typename T>
-void kvtest_client<T>::output_scan(const Json& req, std::vector<Str>& keys,
- std::vector<Str>& values) const {
- keys.clear();
- values.clear();
- for (int i = 2; i != req.size(); i += 2) {
- keys.push_back(req[i].as_s());
- values.push_back(req[i + 1].as_s());
- }
-}
-
-template <typename T>
-void kvtest_client<T>::put(Str key, Str value) {
- q_[0].run_replace(table_->table(), key, value, *ti_);
-}
-
-template <typename T>
-void kvtest_client<T>::insert_check(Str key, Str value) {
- if (unlikely(q_[0].run_replace(table_->table(), key, value, *ti_) != Inserted)) {
- fail("insert(%s) did not insert\n", String(key).printable().c_str());
- }
-}
-
-template <typename T>
-void kvtest_client<T>::put_col(Str key, int col, Str value) {
-#if !MASSTREE_ROW_TYPE_STR
- if (!kvo_) {
- kvo_ = new_kvout(-1, 2048);
- }
- Json x[2] = {Json(col), Json(String::make_stable(value))};
- q_[0].run_put(table_->table(), key, &x[0], &x[2], *ti_);
-#else
- (void) key, (void) col, (void) value;
- assert(0);
-#endif
-}
-
-template <typename T> inline bool kvtest_remove(kvtest_client<T> &client, Str key) {
- return client.q_[0].run_remove(client.table_->table(), key, *client.ti_);
-}
-
-template <typename T>
-void kvtest_client<T>::remove(Str key) {
- (void) kvtest_remove(*this, key);
-}
-
-template <typename T>
-bool kvtest_client<T>::remove_sync(Str key) {
- return kvtest_remove(*this, key);
-}
-
-template <typename T>
-void kvtest_client<T>::remove_check(Str key) {
- if (unlikely(!kvtest_remove(*this, key))) {
- fail("remove(%s) did not remove\n", String(key).printable().c_str());
- }
-}
-
-template <typename T>
-String kvtest_client<T>::make_message(lcdf::StringAccum &sa) const {
- const char *begin = sa.begin();
- while (begin != sa.end() && isspace((unsigned char) *begin))
- ++begin;
- String s = String(begin, sa.end());
- if (!s.empty() && s.back() != '\n')
- s += '\n';
- return s;
-}
-
-template <typename T>
-void kvtest_client<T>::notice(const char *fmt, ...) {
- va_list val;
- va_start(val, fmt);
- String m = make_message(lcdf::StringAccum().vsnprintf(500, fmt, val));
- va_end(val);
- if (m && !quiet)
- fprintf(stderr, "%d: %s", ti_->index(), m.c_str());
-}
-
-template <typename T>
-void kvtest_client<T>::fail(const char *fmt, ...) {
- static nodeversion32 failing_lock(false);
- static nodeversion32 fail_message_lock(false);
- static String fail_message;
-
- va_list val;
- va_start(val, fmt);
- String m = make_message(lcdf::StringAccum().vsnprintf(500, fmt, val));
- va_end(val);
- if (!m)
- m = "unknown failure";
-
- fail_message_lock.lock();
- if (fail_message != m) {
- fail_message = m;
- fprintf(stderr, "%d: %s", ti_->index(), m.c_str());
- }
- fail_message_lock.unlock();
-
- failing_lock.lock();
- fprintf(stdout, "%d: %s", ti_->index(), m.c_str());
- kvtest_print(*table_, stdout, ti_);
-
- always_assert(0);
-}
-
-
-static const char *current_test_name;
-static int current_trial;
-static FILE *test_output_file;
-static pthread_mutex_t subtest_mutex;
-static pthread_cond_t subtest_cond;
-
-#define TESTRUNNER_CLIENT_TYPE kvtest_client<Masstree::default_table>&
-#include "testrunner.hh"
-
-MAKE_TESTRUNNER(rw1, kvtest_rw1(client));
-// MAKE_TESTRUNNER(palma, kvtest_palma(client));
-// MAKE_TESTRUNNER(palmb, kvtest_palmb(client));
-MAKE_TESTRUNNER(rw1fixed, kvtest_rw1fixed(client));
-MAKE_TESTRUNNER(rw1long, kvtest_rw1long(client));
-MAKE_TESTRUNNER(rw1puts, kvtest_rw1puts(client));
-MAKE_TESTRUNNER(rw2, kvtest_rw2(client));
-MAKE_TESTRUNNER(rw2fixed, kvtest_rw2fixed(client));
-MAKE_TESTRUNNER(rw2g90, kvtest_rw2g90(client));
-MAKE_TESTRUNNER(rw2fixedg90, kvtest_rw2fixedg90(client));
-MAKE_TESTRUNNER(rw2g98, kvtest_rw2g98(client));
-MAKE_TESTRUNNER(rw2fixedg98, kvtest_rw2fixedg98(client));
-MAKE_TESTRUNNER(rw3, kvtest_rw3(client));
-MAKE_TESTRUNNER(rw4, kvtest_rw4(client));
-MAKE_TESTRUNNER(rw4fixed, kvtest_rw4fixed(client));
-MAKE_TESTRUNNER(wd1, kvtest_wd1(10000000, 1, client));
-MAKE_TESTRUNNER(wd1m1, kvtest_wd1(100000000, 1, client));
-MAKE_TESTRUNNER(wd1m2, kvtest_wd1(1000000000, 4, client));
-MAKE_TESTRUNNER(wd3, kvtest_wd3(client, 70 * client.nthreads()));
-MAKE_TESTRUNNER(same, kvtest_same(client));
-MAKE_TESTRUNNER(rwsmall24, kvtest_rwsmall24(client));
-MAKE_TESTRUNNER(rwsep24, kvtest_rwsep24(client));
-MAKE_TESTRUNNER(wscale, kvtest_wscale(client));
-MAKE_TESTRUNNER(ruscale_init, kvtest_ruscale_init(client));
-MAKE_TESTRUNNER(rscale, if (client.ti_->index() < ::rscale_ncores) kvtest_rscale(client));
-MAKE_TESTRUNNER(uscale, kvtest_uscale(client));
-MAKE_TESTRUNNER(bdb, kvtest_bdb(client));
-MAKE_TESTRUNNER(wcol1, kvtest_wcol1at(client, client.id() % 24, kvtest_first_seed + client.id() % 48, 5000000));
-MAKE_TESTRUNNER(rcol1, kvtest_rcol1at(client, client.id() % 24, kvtest_first_seed + client.id() % 48, 5000000));
-MAKE_TESTRUNNER(wcol1o1, kvtest_wcol1at(client, (client.id() + 1) % 24, kvtest_first_seed + client.id() % 48, 5000000));
-MAKE_TESTRUNNER(rcol1o1, kvtest_rcol1at(client, (client.id() + 1) % 24, kvtest_first_seed + client.id() % 48, 5000000));
-MAKE_TESTRUNNER(wcol1o2, kvtest_wcol1at(client, (client.id() + 2) % 24, kvtest_first_seed + client.id() % 48, 5000000));
-MAKE_TESTRUNNER(rcol1o2, kvtest_rcol1at(client, (client.id() + 2) % 24, kvtest_first_seed + client.id() % 48, 5000000));
-MAKE_TESTRUNNER(scan1, kvtest_scan1(client, 0));
-MAKE_TESTRUNNER(scan1q80, kvtest_scan1(client, 0.8));
-MAKE_TESTRUNNER(rscan1, kvtest_rscan1(client, 0));
-MAKE_TESTRUNNER(rscan1q80, kvtest_rscan1(client, 0.8));
-MAKE_TESTRUNNER(splitremove1, kvtest_splitremove1(client));
-MAKE_TESTRUNNER(url, kvtest_url(client));
-MAKE_TESTRUNNER(conflictscan1, kvtest_conflictscan1(client));
-
-
-enum {
- test_thread_initialize = 1,
- test_thread_destroy = 2,
- test_thread_stats = 3
-};
-
-template <typename T>
-struct test_thread {
- test_thread(threadinfo* ti) {
- client_.set_table(table_, ti);
- client_.ti_->rcu_start();
- }
- ~test_thread() {
- client_.ti_->rcu_stop();
- }
- static void setup(threadinfo* ti, int action) {
- if (action == test_thread_initialize) {
- assert(!table_);
- table_ = new T;
- table_->initialize(*ti);
- } else if (action == test_thread_destroy) {
- assert(table_);
- delete table_;
- table_ = 0;
- } else if (action == test_thread_stats) {
- assert(table_);
- table_->stats(test_output_file);
- }
- }
- static void* go(void* x) {
- threadinfo* ti = reinterpret_cast<threadinfo*>(x);
- ti->pthread() = pthread_self();
- assert(table_);
-#if __linux__
- if (pinthreads) {
- cpu_set_t cs;
- CPU_ZERO(&cs);
- CPU_SET(cores[ti->index()], &cs);
- int r = sched_setaffinity(0, sizeof(cs), &cs);
- always_assert(r == 0);
- }
-#else
- always_assert(!pinthreads && "pinthreads not supported\n");
-#endif
-
- test_thread<T> tt(ti);
- if (fetch_and_add(&active_threads_, 1) == 0)
- tt.ready_timeouts();
- String test = ::current_test_name;
- int subtestno = 0;
- for (int pos = 0; pos < test.length(); ) {
- int comma = test.find_left(',', pos);
- comma = (comma < 0 ? test.length() : comma);
- String subtest = test.substr(pos, comma - pos), tname;
- testrunner* tr = testrunner::find(subtest);
- tname = (subtest == test ? subtest : test + String("@") + String(subtestno));
- tt.client_.reset(tname, ::current_trial);
- if (tr)
- tr->run(tt.client_);
- else
- tt.client_.fail("unknown test %s", subtest.c_str());
- if (comma == test.length())
- break;
- pthread_mutex_lock(&subtest_mutex);
- if (fetch_and_add(&active_threads_, -1) == 1) {
- pthread_cond_broadcast(&subtest_cond);
- tt.ready_timeouts();
- } else
- pthread_cond_wait(&subtest_cond, &subtest_mutex);
- fprintf(test_output_file, "%s\n", tt.client_.report_.unparse().c_str());
- pthread_mutex_unlock(&subtest_mutex);
- fetch_and_add(&active_threads_, 1);
- pos = comma + 1;
- ++subtestno;
- }
- int at = fetch_and_add(&active_threads_, -1);
- if (at == 1 && print_table) {
- kvtest_print(*table_, stdout, tt.client_.ti_);
- }
- if (at == 1 && json_stats) {
- Json j;
- kvtest_json_stats(*table_, j, *tt.client_.ti_);
- if (j) {
- fprintf(stderr, "%s\n", j.unparse(Json::indent_depth(1).tab_width(2).newline_terminator(true)).c_str());
- tt.client_.report_.merge(j);
- }
- }
- fprintf(test_output_file, "%s\n", tt.client_.report_.unparse().c_str());
- return 0;
- }
- void ready_timeouts() {
- for (size_t i = 0; i < arraysize(timeout); ++i) {
- timeout[i] = false;
- }
- if (duration[0]) {
- xalarm(duration[0]);
- }
- }
- static T* table_;
- static unsigned active_threads_;
- kvtest_client<T> client_;
-};
-template <typename T> T* test_thread<T>::table_;
-template <typename T> unsigned test_thread<T>::active_threads_;
-
-typedef test_thread<Masstree::default_table> masstree_test_thread;
-
-static struct {
- const char *treetype;
- void* (*go_func)(void*);
- void (*setup_func)(threadinfo*, int);
-} test_thread_map[] = {
- { "masstree", masstree_test_thread::go, masstree_test_thread::setup },
- { "mass", masstree_test_thread::go, masstree_test_thread::setup },
- { "mbtree", masstree_test_thread::go, masstree_test_thread::setup },
- { "mb", masstree_test_thread::go, masstree_test_thread::setup },
- { "m", masstree_test_thread::go, masstree_test_thread::setup }
-};
-
-
-void runtest(int nthreads, void* (*func)(void*)) {
- std::vector<threadinfo*> tis;
- for (int i = 0; i < nthreads; ++i)
- tis.push_back(threadinfo::make(threadinfo::TI_PROCESS, i));
- signal(SIGALRM, test_timeout);
- for (int i = 0; i < nthreads; ++i) {
- int r = pthread_create(&tis[i]->pthread(), 0, func, tis[i]);
- always_assert(r == 0);
- }
- for (int i = 0; i < nthreads; ++i)
- pthread_join(tis[i]->pthread(), 0);
-}
-
-
-static const char * const kvstats_name[] = {
- "ops_per_sec", "puts_per_sec", "gets_per_sec", "scans_per_sec"
-};
-
-static Json experiment_stats;
-
-void *stat_collector(void *arg) {
- int p = (int) (intptr_t) arg;
- FILE *f = fdopen(p, "r");
- char buf[8192];
- while (fgets(buf, sizeof(buf), f)) {
- Json result = Json::parse(buf);
- if (result && result["table"] && result["test"]) {
- String key = result["test"].to_s() + "/" + result["table"].to_s()
- + "/" + result["trial"].to_s();
- Json &thisex = experiment_stats.get_insert(key);
- thisex[result["thread"].to_i()] = result;
- } else
- fprintf(stderr, "%s\n", buf);
- }
- fclose(f);
- return 0;
-}
-
-
-/* main loop */
-
-enum { clp_val_normalize = Clp_ValFirstUser, clp_val_suffixdouble };
-enum { opt_pin = 1, opt_port, opt_duration,
- opt_test, opt_test_name, opt_threads, opt_trials, opt_quiet, opt_print,
- opt_normalize, opt_limit, opt_notebook, opt_compare, opt_no_run,
- opt_gid, opt_tree_stats, opt_rscale_ncores, opt_cores,
- opt_stats, opt_help, opt_yrange };
-static const Clp_Option options[] = {
- { "pin", 'p', opt_pin, 0, Clp_Negate },
- { "port", 0, opt_port, Clp_ValInt, 0 },
- { "duration", 'd', opt_duration, Clp_ValDouble, 0 },
- { "limit", 'l', opt_limit, clp_val_suffixdouble, 0 },
- { "normalize", 0, opt_normalize, clp_val_normalize, Clp_Negate },
- { "test", 0, opt_test, Clp_ValString, 0 },
- { "rscale_ncores", 'r', opt_rscale_ncores, Clp_ValInt, 0 },
- { "test-rw1", 0, opt_test_name, 0, 0 },
- { "test-rw2", 0, opt_test_name, 0, 0 },
- { "test-rw3", 0, opt_test_name, 0, 0 },
- { "test-rw4", 0, opt_test_name, 0, 0 },
- { "test-rd1", 0, opt_test_name, 0, 0 },
- { "threads", 'j', opt_threads, Clp_ValInt, 0 },
- { "trials", 'T', opt_trials, Clp_ValInt, 0 },
- { "quiet", 'q', opt_quiet, 0, Clp_Negate },
- { "print", 0, opt_print, 0, Clp_Negate },
- { "notebook", 'b', opt_notebook, Clp_ValString, Clp_Negate },
- { "gid", 'g', opt_gid, Clp_ValString, 0 },
- { "tree-stats", 0, opt_tree_stats, 0, 0 },
- { "stats", 0, opt_stats, 0, 0 },
- { "compare", 'c', opt_compare, Clp_ValString, 0 },
- { "cores", 0, opt_cores, Clp_ValString, 0 },
- { "yrange", 0, opt_yrange, Clp_ValString, 0 },
- { "no-run", 'n', opt_no_run, 0, 0 },
- { "help", 0, opt_help, 0, 0 }
-};
-
-static void help() {
- printf("Masstree-beta mttest\n\
-Usage: mttest [-jTHREADS] [OPTIONS] [PARAM=VALUE...] TEST...\n\
- mttest -n -c TESTNAME...\n\
-\n\
-Options:\n\
- -j, --threads=THREADS Run with THREADS threads (default %d).\n\
- -p, --pin Pin each thread to its own core.\n\
- -T, --trials=TRIALS Run each test TRIALS times.\n\
- -q, --quiet Do not generate verbose and Gnuplot output.\n\
- -l, --limit=LIMIT Limit relevant tests to LIMIT operations.\n\
- -d, --duration=TIME Limit relevant tests to TIME seconds.\n\
- -b, --notebook=FILE Record JSON results in FILE (notebook-mttest.json).\n\
- --no-notebook Do not record JSON results.\n\
- --print Print table after test.\n\
-\n\
- -n, --no-run Do not run new tests.\n\
- -c, --compare=EXPERIMENT Generated plot compares to EXPERIMENT.\n\
- --yrange=YRANGE Set Y range for plot.\n\
-\n\
-Known TESTs:\n",
- (int) sysconf(_SC_NPROCESSORS_ONLN));
- testrunner_base::print_names(stdout, 5);
- printf("Or say TEST1,TEST2,... to run several tests in sequence\n\
-on the same tree.\n");
- exit(0);
-}
-
-static void run_one_test(int trial, const char *treetype, const char *test,
- const int *collectorpipe, int nruns);
-enum { normtype_none, normtype_pertest, normtype_firsttest };
-static void print_gnuplot(FILE *f, const char * const *types_begin, const char * const *types_end, std::vector<String> &comparisons, int normalizetype);
-static void update_labnotebook(String notebook);
-
-#if HAVE_EXECINFO_H
-static const int abortable_signals[] = {
- SIGSEGV, SIGBUS, SIGILL, SIGABRT, SIGFPE
-};
-
-static void abortable_signal_handler(int) {
- // reset signals so if a signal recurs, we exit
- for (const int* it = abortable_signals;
- it != abortable_signals + arraysize(abortable_signals); ++it)
- signal(*it, SIG_DFL);
- // dump backtrace to standard error
- void* return_addrs[50];
- int n = backtrace(return_addrs, arraysize(return_addrs));
- backtrace_symbols_fd(return_addrs, n, STDERR_FILENO);
- // re-abort
- abort();
-}
-#endif
-
-int
-main(int argc, char *argv[])
-{
- threadcounter_names[(int) tc_root_retry] = "root_retry";
- threadcounter_names[(int) tc_internode_retry] = "internode_retry";
- threadcounter_names[(int) tc_leaf_retry] = "leaf_retry";
- threadcounter_names[(int) tc_leaf_walk] = "leaf_walk";
- threadcounter_names[(int) tc_stable_internode_insert] = "stable_internode_insert";
- threadcounter_names[(int) tc_stable_internode_split] = "stable_internode_split";
- threadcounter_names[(int) tc_stable_leaf_insert] = "stable_leaf_insert";
- threadcounter_names[(int) tc_stable_leaf_split] = "stable_leaf_split";
- threadcounter_names[(int) tc_internode_lock] = "internode_lock_retry";
- threadcounter_names[(int) tc_leaf_lock] = "leaf_lock_retry";
-
- int ret, ntrials = 1, normtype = normtype_pertest, firstcore = -1, corestride = 1;
- std::vector<const char *> tests, treetypes;
- std::vector<String> comparisons;
- const char *notebook = "notebook-mttest.json";
- tcpthreads = udpthreads = sysconf(_SC_NPROCESSORS_ONLN);
-
- Clp_Parser *clp = Clp_NewParser(argc, argv, (int) arraysize(options), options);
- Clp_AddStringListType(clp, clp_val_normalize, 0,
- "none", (int) normtype_none,
- "pertest", (int) normtype_pertest,
- "test", (int) normtype_pertest,
- "firsttest", (int) normtype_firsttest,
- (const char *) 0);
- Clp_AddType(clp, clp_val_suffixdouble, Clp_DisallowOptions, clp_parse_suffixdouble, 0);
- int opt;
- while ((opt = Clp_Next(clp)) != Clp_Done) {
- switch (opt) {
- case opt_pin:
- pinthreads = !clp->negated;
- break;
- case opt_threads:
- tcpthreads = udpthreads = clp->val.i;
- break;
- case opt_trials:
- ntrials = clp->val.i;
- break;
- case opt_quiet:
- quiet = !clp->negated;
- break;
- case opt_print:
- print_table = !clp->negated;
- break;
- case opt_rscale_ncores:
- rscale_ncores = clp->val.i;
- break;
- case opt_port:
- port = clp->val.i;
- break;
- case opt_duration:
- duration[0] = clp->val.d;
- break;
- case opt_limit:
- test_limit = uint64_t(clp->val.d);
- break;
- case opt_test:
- tests.push_back(clp->vstr);
- break;
- case opt_test_name:
- tests.push_back(clp->option->long_name + 5);
- break;
- case opt_normalize:
- normtype = clp->negated ? normtype_none : clp->val.i;
- break;
- case opt_gid:
- gid = clp->vstr;
- break;
- case opt_tree_stats:
- tree_stats = true;
- break;
- case opt_stats:
- json_stats = true;
- break;
- case opt_yrange:
- gnuplot_yrange = clp->vstr;
- break;
- case opt_notebook:
- if (clp->negated)
- notebook = 0;
- else if (clp->have_val)
- notebook = clp->vstr;
- else
- notebook = "notebook-mttest.json";
- break;
- case opt_compare:
- comparisons.push_back(clp->vstr);
- break;
- case opt_no_run:
- ntrials = 0;
- break;
- case opt_cores:
- if (firstcore >= 0 || cores.size() > 0) {
- Clp_OptionError(clp, "%<%O%> already given");
- exit(EXIT_FAILURE);
- } else {
- const char *plus = strchr(clp->vstr, '+');
- Json ij = Json::parse(clp->vstr),
- aj = Json::parse(String("[") + String(clp->vstr) + String("]")),
- pj1 = Json::parse(plus ? String(clp->vstr, plus) : "x"),
- pj2 = Json::parse(plus ? String(plus + 1) : "x");
- for (int i = 0; aj && i < aj.size(); ++i)
- if (!aj[i].is_int() || aj[i].to_i() < 0)
- aj = Json();
- if (ij && ij.is_int() && ij.to_i() >= 0)
- firstcore = ij.to_i(), corestride = 1;
- else if (pj1 && pj2 && pj1.is_int() && pj1.to_i() >= 0 && pj2.is_int())
- firstcore = pj1.to_i(), corestride = pj2.to_i();
- else if (aj) {
- for (int i = 0; i < aj.size(); ++i)
- cores.push_back(aj[i].to_i());
- } else {
- Clp_OptionError(clp, "bad %<%O%>, expected %<CORE1%>, %<CORE1+STRIDE%>, or %<CORE1,CORE2,...%>");
- exit(EXIT_FAILURE);
- }
- }
- break;
- case opt_help:
- help();
- break;
- case Clp_NotOption:
- // check for parameter setting
- if (const char* eqchr = strchr(clp->vstr, '=')) {
- Json& param = test_param[String(clp->vstr, eqchr)];
- const char* end_vstr = clp->vstr + strlen(clp->vstr);
- if (param.assign_parse(eqchr + 1, end_vstr)) {
- // OK, param was valid JSON
- } else if (eqchr[1] != 0) {
- param = String(eqchr + 1, end_vstr);
- } else {
- param = Json();
- }
- } else {
- // otherwise, tree or test
- bool is_treetype = false;
- for (int i = 0; i < (int) arraysize(test_thread_map) && !is_treetype; ++i) {
- is_treetype = (strcmp(test_thread_map[i].treetype, clp->vstr) == 0);
- }
- (is_treetype ? treetypes.push_back(clp->vstr) : tests.push_back(clp->vstr));
- }
- break;
- default:
- fprintf(stderr, "Usage: mttest [-jN] TESTS...\n\
-Try 'mttest --help' for options.\n");
- exit(EXIT_FAILURE);
- }
- }
- Clp_DeleteParser(clp);
- if (firstcore < 0)
- firstcore = cores.size() ? cores.back() + 1 : 0;
- for (; (int) cores.size() < udpthreads; firstcore += corestride)
- cores.push_back(firstcore);
-
-#if PMC_ENABLED
- always_assert(pinthreads && "Using performance counter requires pinning threads to cores!");
-#endif
-#if MEMSTATS && HAVE_NUMA_H && HAVE_LIBNUMA
- if (numa_available() != -1)
- for (int i = 0; i <= numa_max_node(); i++) {
- numa.push_back(mttest_numainfo());
- numa.back().size = numa_node_size64(i, &numa.back().free);
- }
-#endif
-#if HAVE_EXECINFO_H
- for (const int* it = abortable_signals;
- it != abortable_signals + arraysize(abortable_signals); ++it)
- signal(*it, abortable_signal_handler);
-#endif
-
- if (treetypes.empty())
- treetypes.push_back("m");
- if (tests.empty())
- tests.push_back("rw1");
-
- pthread_mutex_init(&subtest_mutex, 0);
- pthread_cond_init(&subtest_cond, 0);
-
- // pipe for them to write back to us
- int p[2];
- ret = pipe(p);
- always_assert(ret == 0);
- test_output_file = fdopen(p[1], "w");
-
- pthread_t collector;
- ret = pthread_create(&collector, 0, stat_collector, (void *) (intptr_t) p[0]);
- always_assert(ret == 0);
- initial_timestamp = timestamp();
-
- // run tests
- int nruns = ntrials * (int) tests.size() * (int) treetypes.size();
- std::vector<int> runlist(nruns, 0);
- for (int i = 0; i < nruns; ++i)
- runlist[i] = i;
-
- for (int counter = 0; counter < nruns; ++counter) {
- int x = random() % runlist.size();
- int run = runlist[x];
- runlist[x] = runlist.back();
- runlist.pop_back();
-
- int trial = run % ntrials;
- run /= ntrials;
- int t = run % tests.size();
- run /= tests.size();
- int tt = run;
-
- fprintf(stderr, "%d/%u %s/%s%s", counter + 1, (int) (ntrials * tests.size() * treetypes.size()),
- tests[t], treetypes[tt], quiet ? " " : "\n");
-
- run_one_test(trial, treetypes[tt], tests[t], p, nruns);
- struct timeval delay;
- delay.tv_sec = 0;
- delay.tv_usec = 250000;
- (void) select(0, 0, 0, 0, &delay);
-
- if (quiet)
- fprintf(stderr, "\r%60s\r", "");
- }
-
- fclose(test_output_file);
- pthread_join(collector, 0);
-
- // update lab notebook
- if (notebook)
- update_labnotebook(notebook);
-
- // print Gnuplot
- if (ntrials != 0)
- comparisons.insert(comparisons.begin(), "");
- if (!isatty(STDOUT_FILENO) || (ntrials == 0 && comparisons.size()))
- print_gnuplot(stdout, kvstats_name, kvstats_name + arraysize(kvstats_name),
- comparisons, normtype);
-
- return 0;
-}
-
-static void run_one_test_body(int trial, const char *treetype, const char *test) {
- threadinfo *main_ti = threadinfo::make(threadinfo::TI_MAIN, -1);
- main_ti->pthread() = pthread_self();
- globalepoch = active_epoch = timestamp() >> 16;
- for (int i = 0; i < (int) arraysize(test_thread_map); ++i)
- if (strcmp(test_thread_map[i].treetype, treetype) == 0) {
- current_test_name = test;
- current_trial = trial;
- test_thread_map[i].setup_func(main_ti, test_thread_initialize);
- runtest(tcpthreads, test_thread_map[i].go_func);
- if (tree_stats)
- test_thread_map[i].setup_func(main_ti, test_thread_stats);
- test_thread_map[i].setup_func(main_ti, test_thread_destroy);
- break;
- }
-}
-
-static void run_one_test(int trial, const char *treetype, const char *test,
- const int *collectorpipe, int nruns) {
- if (nruns == 1)
- run_one_test_body(trial, treetype, test);
- else {
- pid_t c = fork();
- if (c == 0) {
- close(collectorpipe[0]);
- run_one_test_body(trial, treetype, test);
- exit(0);
- } else
- while (waitpid(c, 0, 0) == -1 && errno == EINTR)
- /* loop */;
- }
-}
-
-static double level(const std::vector<double> &v, double frac) {
- frac *= v.size() - 1;
- int base = (int) frac;
- if (base == frac)
- return v[base];
- else
- return v[base] * (1 - (frac - base)) + v[base + 1] * (frac - base);
-}
-
-static String experiment_test_table_trial(const String &key) {
- const char *l = key.begin(), *r = key.end();
- if (l + 2 < r && l[0] == 'x' && isdigit((unsigned char) l[1])) {
- for (const char *s = l; s != r; ++s)
- if (*s == '/') {
- l = s + 1;
- break;
- }
- }
- return key.substring(l, r);
-}
-
-static String experiment_run_test_table(const String &key) {
- const char *l = key.begin(), *r = key.end();
- for (const char *s = r; s != l; --s)
- if (s[-1] == '/') {
- r = s - 1;
- break;
- } else if (!isdigit((unsigned char) s[-1]))
- break;
- return key.substring(l, r);
-}
-
-static String experiment_test_table(const String &key) {
- return experiment_run_test_table(experiment_test_table_trial(key));
-}
-
-namespace {
-struct gnuplot_info {
- static constexpr double trialdelta = 0.015, treetypedelta = 0.04,
- testdelta = 0.08, typedelta = 0.2;
- double pos;
- double nextdelta;
- double normalization;
- String last_test;
- int normalizetype;
-
- std::vector<lcdf::StringAccum> candlesticks;
- std::vector<lcdf::StringAccum> medians;
- lcdf::StringAccum xtics;
-
- gnuplot_info(int nt)
- : pos(1 - trialdelta), nextdelta(trialdelta), normalization(-1),
- normalizetype(nt) {
- }
- void one(const String &xname, int ti, const String &datatype_name);
- void print(FILE *f, const char * const *types_begin);
-};
-constexpr double gnuplot_info::trialdelta, gnuplot_info::treetypedelta, gnuplot_info::testdelta, gnuplot_info::typedelta;
-
-void gnuplot_info::one(const String &xname, int ti, const String &datatype_name)
-{
- String current_test = experiment_test_table(xname);
- if (current_test != last_test) {
- last_test = current_test;
- if (normalizetype == normtype_pertest)
- normalization = -1;
- if (nextdelta == treetypedelta)
- nextdelta = testdelta;
- }
- double beginpos = pos, firstpos = pos + nextdelta;
-
- std::vector<int> trials;
- for (Json::object_iterator it = experiment_stats.obegin();
- it != experiment_stats.oend(); ++it) {
- String key = it.key();
- if (experiment_run_test_table(key) == xname)
- trials.push_back(strtol(key.c_str() + xname.length() + 1, 0, 0));
- }
- std::sort(trials.begin(), trials.end());
-
- for (std::vector<int>::iterator tit = trials.begin();
- tit != trials.end(); ++tit) {
- Json &this_trial = experiment_stats[xname + "/" + String(*tit)];
- std::vector<double> values;
- for (int jn = 0; jn < this_trial.size(); ++jn)
- if (this_trial[jn].get(datatype_name).is_number())
- values.push_back(this_trial[jn].get(datatype_name).to_d());
- if (values.size()) {
- pos += nextdelta;
- std::sort(values.begin(), values.end());
- if (normalization < 0)
- normalization = normalizetype == normtype_none ? 1 : level(values, 0.5);
- if (int(candlesticks.size()) <= ti) {
- candlesticks.resize(ti + 1);
- medians.resize(ti + 1);
- }
- candlesticks[ti] << pos << " " << level(values, 0)
- << " " << level(values, 0.25)
- << " " << level(values, 0.75)
- << " " << level(values, 1)
- << " " << normalization << "\n";
- medians[ti] << pos << " " << level(values, 0.5) << " " << normalization << "\n";
- nextdelta = trialdelta;
- }
- }
-
- if (pos > beginpos) {
- double middle = (firstpos + pos) / 2;
- xtics << (xtics ? ", " : "") << "\"" << xname << "\" " << middle;
- nextdelta = treetypedelta;
- }
-}
-
-void gnuplot_info::print(FILE *f, const char * const *types_begin) {
- std::vector<int> linetypes(medians.size(), 0);
- int next_linetype = 1;
- for (int i = 0; i < int(medians.size()); ++i)
- if (medians[i])
- linetypes[i] = next_linetype++;
- struct utsname name;
- fprintf(f, "set title \"%s (%d cores)\"\n",
- (uname(&name) == 0 ? name.nodename : "unknown"),
- udpthreads);
- fprintf(f, "set terminal png\n");
- fprintf(f, "set xrange [%g:%g]\n", 1 - treetypedelta, pos + treetypedelta);
- if (gnuplot_yrange)
- fprintf(f, "set yrange [%s]\n", gnuplot_yrange.c_str());
- fprintf(f, "set xtics rotate by 45 right (%s) font \"Verdana,9\"\n", xtics.c_str());
- fprintf(f, "set key top left Left reverse\n");
- if (normalizetype == normtype_none)
- fprintf(f, "set ylabel \"count\"\n");
- else if (normalizetype == normtype_pertest)
- fprintf(f, "set ylabel \"count, normalized per test\"\n");
- else
- fprintf(f, "set ylabel \"normalized count (1=%f)\"\n", normalization);
- const char *sep = "plot ";
- for (int i = 0; i < int(medians.size()); ++i)
- if (medians[i]) {
- fprintf(f, "%s '-' using 1:($3/$6):($2/$6):($5/$6):($4/$6) with candlesticks lt %d title '%s', \\\n",
- sep, linetypes[i], types_begin[i]);
- fprintf(f, " '-' using 1:($2/$3):($2/$3):($2/$3):($2/$3) with candlesticks lt %d notitle", linetypes[i]);
- sep = ", \\\n";
- }
- fprintf(f, "\n");
- for (int i = 0; i < int(medians.size()); ++i)
- if (medians[i]) {
- fwrite(candlesticks[i].begin(), 1, candlesticks[i].length(), f);
- fprintf(f, "e\n");
- fwrite(medians[i].begin(), 1, medians[i].length(), f);
- fprintf(f, "e\n");
- }
-}
-
-}
-
-static void print_gnuplot(FILE *f, const char * const *types_begin, const char * const *types_end,
- std::vector<String> &comparisons, int normalizetype) {
- for (std::vector<String>::iterator cit = comparisons.begin();
- cit != comparisons.end(); ++cit) {
- if (!*cit)
- *cit = "[^x]*";
- else if (cit->length() >= 2 && (*cit)[0] == 'x' && isdigit((unsigned char) (*cit)[1]))
- *cit += String(cit->find_left('/') < 0 ? "/*" : "*");
- else
- *cit = String("x*") + *cit + String("*");
- }
-
- std::vector<String> all_versions, all_experiments;
- for (Json::object_iterator it = experiment_stats.obegin();
- it != experiment_stats.oend(); ++it)
- for (std::vector<String>::const_iterator cit = comparisons.begin();
- cit != comparisons.end(); ++cit)
- if (it.key().glob_match(*cit)) {
- all_experiments.push_back(experiment_run_test_table(it.key()));
- all_versions.push_back(experiment_test_table(it.key()));
- break;
- }
- std::sort(all_experiments.begin(), all_experiments.end());
- all_experiments.erase(std::unique(all_experiments.begin(), all_experiments.end()),
- all_experiments.end());
- std::sort(all_versions.begin(), all_versions.end());
- all_versions.erase(std::unique(all_versions.begin(), all_versions.end()),
- all_versions.end());
-
- int ntypes = (int) (types_end - types_begin);
- gnuplot_info gpinfo(normalizetype);
-
- for (int ti = 0; ti < ntypes; ++ti) {
- double typepos = gpinfo.pos;
- for (std::vector<String>::iterator vit = all_versions.begin();
- vit != all_versions.end(); ++vit) {
- for (std::vector<String>::iterator xit = all_experiments.begin();
- xit != all_experiments.end(); ++xit)
- if (experiment_test_table(*xit) == *vit)
- gpinfo.one(*xit, ti, types_begin[ti]);
- }
- if (gpinfo.pos > typepos)
- gpinfo.nextdelta = gpinfo.typedelta;
- gpinfo.last_test = "";
- }
-
- if (gpinfo.xtics)
- gpinfo.print(f, types_begin);
-}
-
-static String
-read_file(FILE *f, const char *name)
-{
- lcdf::StringAccum sa;
- while (1) {
- size_t x = fread(sa.reserve(4096), 1, 4096, f);
- if (x != 0)
- sa.adjust_length(x);
- else if (ferror(f)) {
- fprintf(stderr, "%s: %s\n", name, strerror(errno));
- return String::make_stable("???", 3);
- } else
- return sa.take_string();
- }
-}
-
-static void
-update_labnotebook(String notebook)
-{
- FILE *f = (notebook == "-" ? stdin : fopen(notebook.c_str(), "r"));
- String previous_text = (f ? read_file(f, notebook.c_str()) : String());
- if (previous_text.out_of_memory())
- return;
- if (f && f != stdin)
- fclose(f);
-
- Json nb = Json::parse(previous_text);
- if (previous_text && (!nb.is_object() || !nb["experiments"])) {
- fprintf(stderr, "%s: unexpected contents, not writing new data\n", notebook.c_str());
- return;
- }
-
- if (!nb)
- nb = Json::make_object();
- if (!nb.get("experiments"))
- nb.set("experiments", Json::make_object());
- if (!nb.get("data"))
- nb.set("data", Json::make_object());
-
- Json old_data = nb["data"];
- if (!experiment_stats) {
- experiment_stats = old_data;
- return;
- }
-
- Json xjson;
-
- FILE *git_info_p = popen("git rev-parse HEAD | tr -d '\n'; git --no-pager diff --exit-code --shortstat HEAD >/dev/null 2>&1 || echo M", "r");
- String git_info = read_file(git_info_p, "<git output>");
- pclose(git_info_p);
- if (git_info)
- xjson.set("git-revision", git_info.trim());
-
- time_t now = time(0);
- xjson.set("time", String(ctime(&now)).trim());
- if (gid)
- xjson.set("gid", String(gid));
-
- struct utsname name;
- if (uname(&name) == 0)
- xjson.set("machine", name.nodename);
-
- xjson.set("cores", udpthreads);
-
- Json &runs = xjson.get_insert("runs");
- String xname = "x" + String(nb["experiments"].size());
- for (Json::const_iterator it = experiment_stats.begin();
- it != experiment_stats.end(); ++it) {
- String xkey = xname + "/" + it.key();
- runs.push_back(xkey);
- nb["data"][xkey] = it.value();
- }
- xjson.set("runs", runs);
-
- nb["experiments"][xname] = xjson;
-
- String new_text = nb.unparse(Json::indent_depth(4).tab_width(2).newline_terminator(true));
- f = (notebook == "-" ? stdout : fopen((notebook + "~").c_str(), "w"));
- if (!f) {
- fprintf(stderr, "%s~: %s\n", notebook.c_str(), strerror(errno));
- return;
- }
- size_t written = fwrite(new_text.data(), 1, new_text.length(), f);
- if (written != size_t(new_text.length())) {
- fprintf(stderr, "%s~: %s\n", notebook.c_str(), strerror(errno));
- fclose(f);
- return;
- }
- if (f != stdout) {
- fclose(f);
- if (rename((notebook + "~").c_str(), notebook.c_str()) != 0)
- fprintf(stderr, "%s: %s\n", notebook.c_str(), strerror(errno));
- }
-
- fprintf(stderr, "EXPERIMENT %s\n", xname.c_str());
- experiment_stats.merge(old_data);
-}
diff --git a/nodeversion.hh b/nodeversion.hh
index 4d3bdb0..9db3aa4 100644
--- a/nodeversion.hh
+++ b/nodeversion.hh
@@ -46,6 +46,8 @@ class nodeversion {
acquire_fence();
return x;
}
+
+ // Spin while node is in dirty state
template <typename SF>
nodeversion<P> stable_annotated(SF spin_function) const {
value_type x = v_;
@@ -133,8 +135,10 @@ class nodeversion {
masstree_invariant((fence(), x.v_ == v_));
masstree_invariant(x.v_ & P::lock_bit);
if (x.v_ & P::splitting_bit)
+ // Increasing the vsplit version counter, clean all lower bits (inserting, splitting, lock ...) and zeroing the vinsert version counter
x.v_ = (x.v_ + P::vsplit_lowbit) & P::split_unlock_mask;
else
+ // Clean all lower bits (inserting, splitting, lock ...). If P::inserting_bit == 1, also increasing the vinsert version counter
x.v_ = (x.v_ + ((x.v_ & P::inserting_bit) << 2)) & P::unlock_mask;
release_fence();
v_ = x.v_;
diff --git a/perfstat.cc b/perfstat.cc
deleted file mode 100644
index 85e93a4..0000000
--- a/perfstat.cc
+++ /dev/null
@@ -1,216 +0,0 @@
-/* Masstree
- * Eddie Kohler, Yandong Mao, Robert Morris
- * Copyright (c) 2012-2013 President and Fellows of Harvard College
- * Copyright (c) 2012-2013 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.
- */
-#include "perfstat.hh"
-#include "compiler.hh"
-#include "kvstats.hh"
-#if HAVE_NUMA_H
-#include <numa.h>
-#endif
-
-enum { MaxCores = 48 }; // Maximum number of cores kvdb statistics support
-enum { MaxNumaNode = 8 }; // Maximum number of Numa node kvdb statistics support
-enum { CoresPerChip = MaxCores / MaxNumaNode };
-
-namespace Perf {
-
-#if MEMSTATS && HAVE_NUMA_H && HAVE_LIBNUMA
-static struct {
- long long free;
- long long size;
-} numa[MaxNumaNode];
-#endif
-
-void
-stat::initmain(bool pinthreads) {
- (void) pinthreads;
-#if PMC_ENABLED
- always_assert(pinthreads && "Using performance counter requires pinning threads to cores!");
-#endif
-#if MEMSTATS && HAVE_NUMA_H && HAVE_LIBNUMA
- if (numa_available() != -1) {
- always_assert(numa_max_node() <= MaxNumaNode);
- for (int i = 0; i <= numa_max_node(); i++)
- numa[i].size = numa_node_size64(i, &numa[i].free);
- }
-#endif
-}
-
-template <typename T>
-kvstats
-sum_all_cores(const stat **s, int n, const int offset) {
- kvstats sum;
- for (int i = 0; i < n; i++) {
- if (!s[i])
- continue;
- T v = *reinterpret_cast<const T *>(reinterpret_cast<const char *>(s[i]) + offset);
- sum.add(v);
- }
- return sum;
-}
-
-template <typename T>
-kvstats
-sum_one_chip(const stat **s, int n, const int offset, const int chipidx) {
- kvstats sum;
- for (int i = 0; i < n; i++) {
- if (!s[i] || s[i]->cid / (MaxCores / MaxNumaNode) != chipidx)
- continue;
- T v = *reinterpret_cast<const T *>(reinterpret_cast<const char *>(s[i]) + offset);
- sum.add(v);
- }
- return sum;
-}
-
-template <typename T>
-kvstats
-sum_all_per_chip(const stat **s, int n, const int offset) {
- kvstats per_chip[MaxNumaNode];
- for (int i = 0; i < n; i++) {
- if (!s[i])
- continue;
- T v = *reinterpret_cast<const T *>(reinterpret_cast<const char *>(s[i]) + offset);
- per_chip[i / CoresPerChip].add(v);
- }
- kvstats sum;
- for (int i = 0; i < MaxNumaNode; i++)
- if (per_chip[i].count)
- sum.add(per_chip[i].avg());
- return sum;
-}
-
-void
-stat::print(const stat **s, int n) {
- (void)n;
- (void)s;
-#define sum_all_cores_of(field) \
- sum_all_cores<typeof(s[0]->field)>(s, n, offsetof(Perf::stat, field))
-#define sum_one_chip_of(field, c) \
- sum_one_chip<typeof(s[0]->field)>(s, n, offsetof(Perf::stat, field), c)
-#define sum_all_per_chip_of(field) \
- sum_all_per_chip<typeof(s[0]->field)>(s, n, offsetof(Perf::stat, field))
-
-#define sum_all_cores_of_array(field, oa) \
- sum_all_cores<typeof(s[0]->field[0])>(s, n, offsetof(Perf::stat, field) + \
- sizeof(s[0]->field[0]) * oa)
-#define sum_one_chip_of_array(field, oa, c) \
- sum_one_chip<typeof(s[0]->field[0])>(s, n, offsetof(Perf::stat, field) + \
- sizeof(s[0]->field[0]) * oa, c)
-#define sum_all_per_chip_of_array(field, oa) \
- sum_all_per_chip<typeof(s[0]->field[0])>(s, n, offsetof(Perf::stat, field) + \
- sizeof(s[0]->field[0]) * oa)
-
-#if GETSTATS && 0
- for (int i = 0; i < n; i++)
- if (s[i]->ngets < 1000) {
- s[i] = NULL;
- continue;
- }
- kvstats ngets = sum_all_cores_of(ngets);
- kvstats ntsc = sum_all_cores_of(ntsc);
- kvstats np = sum_all_cores_of(nprobe);
- if (np.sum >= 1)
- fprintf(stderr, "Total probe %.0f, probe/get %.2f\n", np.sum, np.sum / ngets.sum);
-#if PMC_ENABLED
- fprintf(stderr, "(Inaccurate because PMC is Enabled!)");
-#endif
- fprintf(stderr, "Cycles/get (between mark_get_begin and mark_get_end): %.0f\n",
- ntsc.sum / ngets.sum);
-#if PMC_ENABLED
- for (int i = 0; i < n; i++) {
- if (!s[i])
- continue;
- fprintf(stderr, "Core %d:\n", i);
- for (int pi = 0; pi < 4; pi++) {
- fprintf(stderr, "\tpmc[%d]: %016" PRIx64 "->%016" PRIx64 "\n",
- pi, s[i]->pmc_firstget[pi], s[i]->pmc_start[pi]);
- always_assert(s[i]->pmc_start[pi] >= s[i]->pmc_firstget[pi]);
- always_assert(s[i]->t1_lastget >= s[i]->t0_firstget);
- }
- }
- // Compute the start and end time of get phase
- kvstats getstart = sum_all_cores_of(t0_firstget);
- kvstats getend = sum_all_cores_of(t1_lastget);
- getstart.print_report("time of first get");
- getend.print_report("time of last get");
-
- // Compute per-chip pmc during the whole get phase
- double pcpmc_phase[MaxNumaNode][4];
- for (int i = 0; i < MaxNumaNode; i++)
- for (int pi = 0; pi < 4; pi++)
- pcpmc_phase[i][pi] = sum_one_chip_of_array(pmc_start, pi, i).avg() -
- sum_one_chip_of_array(pmc_firstget, pi, i).avg();
-
- // Compute cputime and realtime during get phase
- kvstats t_firstget = sum_all_cores_of(t0_firstget);
- kvstats t_lastget = sum_all_cores_of(t1_lastget);
- double realtime = t_lastget.avg() - t_firstget.avg();
-
- for (int pi = 0; pi < 4; pi++) {
- fprintf(stderr, "DRAM access to node (pmc %d)\n", pi);
- double sum = 0;
- for (int i = 0; i < MaxNumaNode; i++) {
- fprintf(stderr, "\tFrom chip %2d: %8.1f GB/s\n", i,
- pcpmc_phase[i][pi] * 64 / (realtime * (1 << 30)));
- sum += pcpmc_phase[i][pi];
- }
- fprintf(stderr, "\tSum: %8.1f GB/s\n",
- sum * 64 / (realtime * (1 << 30)));
- }
- // Print per-get pmc_lookup
- fprintf(stderr, "Per get statistics (counted between mark_get_begin and mark_get_end):\n");
- for (int pi = 0; (ngets.sum > 0) && pi < 4; pi ++) {
- kvstats pmc_lookup = sum_all_cores_of_array(pmc_lookup, pi);
- kvstats pcpmc_lookup = sum_all_per_chip_of_array(pmc_lookup, pi);
- fprintf(stderr, "\tpmc%d/get: %6.1f, per_chip_pmc%d/get: %6.1f\n",
- pi, (double) pmc_lookup.sum / ngets.sum, pi,
- (double) pcpmc_lookup.sum / ngets.sum);
- }
-#endif
-#endif
-
-#if MEMSTATS && HAVE_NUMA_H && HAVE_LIBNUMA && 0
- // collect tree memory
- kvstats tree_mem = sum_all_cores_of(tree_mem);
- kvstats tree_keys = sum_all_cores_of(tree_keys);
- fprintf(stderr, "Memory statistics\n");
- fprintf(stderr, "\tAllocated per key: %.0f bytes, %.0f\n", tree_mem.sum / tree_keys.sum, tree_keys.sum);
- if (numa_available() != -1) {
- unsigned long total_alloc = 0;
- for (int i = 0; i <= numa_max_node(); i++) {
- kvstats chip = sum_one_chip_of(tree_mem, i);
- long long nowfree;
- long long size = numa_node_size64(i, &nowfree);
- total_alloc += numa[i].free - nowfree;
- fprintf(stderr, "\tNode %d (MB): size %6lld, allocated = %6lld - "
- "%6lld = %6lld, tree_mem %6.0f\n",
- i, size >> 20, numa[i].free >> 20, nowfree >> 20,
- (numa[i].free - nowfree) / (1 << 20),
- chip.sum / (1 << 20));
- }
- fprintf(stderr, "Total allocated memory %ld MB\n", total_alloc >> 20);
- }
-#endif
-
-#if GCSTATS
- // collect memory used by epoch based garbage collector
- kvstats gc_nfree = sum_all_cores_of(gc_nfree);
- kvstats gc_nalloc = sum_all_cores_of(gc_nalloc);
- fprintf(stderr, "reuse per gc slot: %.0f, freed: %.0f, allocated: %.0f\n",
- gc_nfree.sum / gc_nalloc.sum, gc_nfree.sum, gc_nalloc.sum);
-#endif
-}
-
-}
diff --git a/perfstat.hh b/perfstat.hh
deleted file mode 100644
index fe37ea5..0000000
--- a/perfstat.hh
+++ /dev/null
@@ -1,38 +0,0 @@
-/* Masstree
- * Eddie Kohler, Yandong Mao, Robert Morris
- * Copyright (c) 2012-2013 President and Fellows of Harvard College
- * Copyright (c) 2012-2013 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 PERF_STAT_HH
-#define PERF_STAT_HH 1
-#include "compiler.hh"
-#include "misc.hh"
-#include <stdlib.h>
-#include <inttypes.h>
-
-namespace Perf {
-struct stat {
- /** @brief An initialization call from main function
- */
- static void initmain(bool pinthreads);
-#if GCSTATS
- int gc_nfree;
-#endif
- void initialize(int cid) {
- this->cid = cid;
- }
- static void print(const stat **s, int n);
- int cid; // core index
-};
-}
-#endif
diff --git a/query_masstree.cc b/query_masstree.cc
deleted file mode 100644
index d514d9a..0000000
--- a/query_masstree.cc
+++ /dev/null
@@ -1,342 +0,0 @@
-/* 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.
- */
-#include "masstree.hh"
-#include "masstree_key.hh"
-#include "masstree_struct.hh"
-#include "masstree_tcursor.hh"
-#include "masstree_get.hh"
-#include "masstree_insert.hh"
-#include "masstree_split.hh"
-#include "masstree_remove.hh"
-#include "masstree_scan.hh"
-#include "masstree_stats.hh"
-#include "masstree_print.hh"
-#include "query_masstree.hh"
-#include "string_slice.hh"
-#include "kpermuter.hh"
-#include "ksearch.hh"
-#include "stringbag.hh"
-#include "json.hh"
-#include "kvrow.hh"
-
-namespace Masstree {
-
-static uint64_t heightcounts[300], fillcounts[100];
-
-template <typename P>
-static void treestats1(node_base<P>* n, unsigned height) {
- if (!n)
- return;
- int sz;
- if (n->isleaf()) {
- assert(height < arraysize(heightcounts));
- if (n->deleted())
- return;
- leaf<P> *lf = (leaf<P> *)n;
- typename leaf<P>::permuter_type perm = lf->permutation_;
- sz = perm.size();
- for (int idx = 0; idx < sz; ++idx) {
- int p = perm[idx];
- typename leaf<P>::leafvalue_type lv = lf->lv_[p];
- if (!lv || !lf->is_layer(p))
- ++heightcounts[height];
- else {
- node_base<P>* layer = lv.layer();
- while (!layer->is_root())
- layer = layer->maybe_parent();
- treestats1(layer, height + 1);
- }
- }
- } else {
- internode<P> *in = (internode<P> *) n;
- sz = in->size();
- for (int i = 0; i <= sz; ++i)
- if (in->child_[i])
- treestats1(in->child_[i], height + 1);
- }
- assert((size_t) sz < arraysize(fillcounts));
- fillcounts[sz] += 1;
-}
-
-template <typename P>
-void query_table<P>::stats(FILE* f) {
- memset(heightcounts, 0, sizeof(heightcounts));
- memset(fillcounts, 0, sizeof(fillcounts));
- treestats1(table_.root(), 0);
- fprintf(f, " heights:");
- for (unsigned i = 0; i < arraysize(heightcounts); ++i)
- if (heightcounts[i])
- fprintf(f, " %d=%" PRIu64, i, heightcounts[i]);
- fprintf(f, "\n node counts:");
- for (unsigned i = 0; i < arraysize(fillcounts); ++i)
- if (fillcounts[i])
- fprintf(f, " %d=%" PRIu64, i, fillcounts[i]);
- fprintf(f, "\n");
-}
-
-template <typename P>
-void query_table<P>::json_stats(lcdf::Json& j, threadinfo& ti) {
- Masstree::json_stats(j, table_, ti);
-}
-
-template <typename N>
-static Str findpv(N *n, int pvi, int npv)
-{
- // XXX assumes that most keys differ in the low bytes
- // XXX will fail badly if all keys have the same prefix
- // XXX not clear what to do then
- int nbranch = 1, branchid = 0;
- typedef typename N::internode_type internode_type;
- typedef typename N::leaf_type leaf_type;
-
- while (!n->is_root())
- n = n->maybe_parent();
-
- while (1) {
- typename N::nodeversion_type v = n->stable();
- int size;
- if (n->isleaf())
- size = static_cast<leaf_type*>(n)->size();
- else
- size = static_cast<internode_type*>(n)->size() + 1;
- if (size == 0)
- return Str();
-
- int total_nkeys_estimate = nbranch * size;
- int first_pv_in_node = branchid * size;
- int pv_offset = pvi * total_nkeys_estimate / npv - first_pv_in_node;
-
- if (!n->isleaf() && total_nkeys_estimate < npv) {
- internode_type *in = static_cast<internode_type *>(n);
- pv_offset = std::min(std::max(pv_offset, 0), size - 1);
- N *next = in->child_[pv_offset];
- if (!n->has_changed(v)) {
- nbranch = total_nkeys_estimate;
- branchid = first_pv_in_node + pv_offset;
- n = next;
- }
- continue;
- }
-
- pv_offset = std::min(std::max(pv_offset, 0), size - 1 - !n->isleaf());
- typename N::ikey_type ikey0;
- if (n->isleaf()) {
- leaf_type *l = static_cast<leaf_type *>(n);
- typename leaf_type::permuter_type perm = l->permutation();
- ikey0 = l->ikey0_[perm[pv_offset]];
- } else {
- internode_type *in = static_cast<internode_type *>(n);
- ikey0 = in->ikey0_[pv_offset];
- }
-
- if (!n->has_changed(v)) {
- char *x = (char *) malloc(sizeof(ikey0));
- int len = string_slice<typename N::ikey_type>::unparse_comparable(x, sizeof(ikey0), ikey0);
- return Str(x, len);
- }
- }
-}
-
-// findpivots should allocate memory for pv[i]->s, which will be
-// freed by the caller.
-template <typename P>
-void query_table<P>::findpivots(Str *pv, int npv) const
-{
- pv[0].assign(NULL, 0);
- char *cmaxk = (char *)malloc(MASSTREE_MAXKEYLEN);
- memset(cmaxk, 255, MASSTREE_MAXKEYLEN);
- pv[npv - 1].assign(cmaxk, MASSTREE_MAXKEYLEN);
- for (int i = 1; i < npv - 1; i++)
- pv[i] = findpv(table_.root(), i, npv - 1);
-}
-
-namespace {
-struct scan_tester {
- const char * const *vbegin_, * const *vend_;
- char key_[32];
- int keylen_;
- bool reverse_;
- bool first_;
- scan_tester(const char * const *vbegin, const char * const *vend,
- bool reverse = false)
- : vbegin_(vbegin), vend_(vend), keylen_(0), reverse_(reverse),
- first_(true) {
- if (reverse_) {
- memset(key_, 255, sizeof(key_));
- keylen_ = sizeof(key_);
- }
- }
- template <typename SS, typename K>
- void visit_leaf(const SS&, const K&, threadinfo&) {
- }
- bool visit_value(Str key, row_type*, threadinfo&) {
- memcpy(key_, key.s, key.len);
- keylen_ = key.len;
- const char *pos = (reverse_ ? vend_[-1] : vbegin_[0]);
- if ((int) strlen(pos) != key.len || memcmp(pos, key.s, key.len) != 0) {
- fprintf(stderr, "%sscan encountered %.*s, expected %s\n", reverse_ ? "r" : "", key.len, key.s, pos);
- assert((int) strlen(pos) == key.len && memcmp(pos, key.s, key.len) == 0);
- }
- fprintf(stderr, "%sscan %.*s\n", reverse_ ? "r" : "", key.len, key.s);
- (reverse_ ? --vend_ : ++vbegin_);
- first_ = false;
- return vbegin_ != vend_;
- }
- template <typename T>
- int scan(T& table, threadinfo& ti) {
- return table.table().scan(Str(key_, keylen_), first_, *this, ti);
- }
- template <typename T>
- int rscan(T& table, threadinfo& ti) {
- return table.table().rscan(Str(key_, keylen_), first_, *this, ti);
- }
-};
-}
-
-template <typename P>
-void query_table<P>::test(threadinfo& ti) {
- query_table<P> t;
- t.initialize(ti);
- query<row_type> 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 * const *end_values = values + arraysize(values);
- 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];
- }
-
- t.table_.print();
- printf("\n");
-
- scan_tester scanner(values, values + 3);
- while (scanner.scan(t, ti)) {
- scanner.vend_ = std::min(scanner.vend_ + 3, end_values);
- fprintf(stderr, "-scanbreak-\n");
- }
-
- scanner = scan_tester(values, values + 8);
- while (scanner.scan(t, ti)) {
- scanner.vend_ = std::min(scanner.vend_ + 8, end_values);
- fprintf(stderr, "-scanbreak-\n");
- }
-
- scanner = scan_tester(values + 10, values + 11);
- int r = t.table_.scan(Str(values[10]), true, scanner, ti);
- always_assert(r == 1);
-
- scanner = scan_tester(values + 10, values + 11);
- r = t.table_.scan(Str(values[10] + 1), true, scanner, ti);
- always_assert(r == 1);
-
- scanner = scan_tester(values + 11, values + 12);
- r = t.table_.scan(Str(values[10]), false, scanner, ti);
- always_assert(r == 1);
-
- scanner = scan_tester(values + 10, values + 11);
- r = t.table_.scan(Str("aaaaaaaaaaaaaaaaaaaaaaaaaZ"), true, scanner, ti);
- always_assert(r == 1);
-
- scanner = scan_tester(values + 11, values + 12);
- r = t.table_.scan(Str(values[11]), true, scanner, ti);
- always_assert(r == 1);
-
- scanner = scan_tester(values + 12, values + 13);
- r = t.table_.scan(Str(values[11]), false, scanner, ti);
- always_assert(r == 1);
-
-
- scanner = scan_tester(end_values - 3, end_values, true);
- while (scanner.rscan(t, ti)) {
- scanner.vbegin_ = std::max(scanner.vbegin_ - 3, (const char * const *) values);
- fprintf(stderr, "-scanbreak-\n");
- }
-
- scanner = scan_tester(end_values - 2, end_values, true);
- r = scanner.rscan(t, ti);
- always_assert(r == 2);
- scanner.vbegin_ = std::max(scanner.vbegin_ - 2, (const char * const *) values);
- fprintf(stderr, "-scanbreak-\n");
- r = scanner.rscan(t, ti);
- always_assert(r == 2);
-
- scanner = scan_tester(end_values - 8, end_values, true);
- while (scanner.rscan(t, ti)) {
- scanner.vbegin_ = std::max(scanner.vbegin_ - 8, (const char * const *) values);
- fprintf(stderr, "-scanbreak-\n");
- }
-
- scanner = scan_tester(values + 10, values + 11);
- r = t.table_.rscan(Str(values[10]), true, scanner, ti);
- always_assert(r == 1);
-
- scanner = scan_tester(values + 10, values + 11);
- r = t.table_.rscan(Str("aaaaaaaaaaaaaaaaaaaaaaaaab"), true, scanner, ti);
- always_assert(r == 1);
-
- scanner = scan_tester(values + 9, values + 10);
- r = t.table_.rscan(Str(values[10]), false, scanner, ti);
- always_assert(r == 1);
-
- scanner = scan_tester(values + 10, values + 11);
- r = t.table_.rscan(Str("aaaaaaaaaaaaaaaaaaaaaaaaab"), true, scanner, ti);
- always_assert(r == 1);
-
- scanner = scan_tester(values + 11, values + 12);
- r = t.table_.rscan(Str(values[11]), true, scanner, ti);
- always_assert(r == 1);
-
- scanner = scan_tester(values + 10, values + 11);
- r = t.table_.rscan(Str(values[11]), false, scanner, ti);
- always_assert(r == 1);
-
-
- Str pv[10];
- t.findpivots(pv, 10);
- for (int i = 0; i < 10; ++i) {
- fprintf(stderr, "%d >%.*s<\n", i, std::min(pv[i].len, 10), pv[i].s);
- free((char *)pv[i].s);
- }
- t.findpivots(pv, 4);
- for (int i = 0; i < 4; ++i) {
- fprintf(stderr, "%d >%.*s<\n", i, std::min(pv[i].len, 10), pv[i].s);
- free((char *)pv[i].s);
- }
-
- // XXX destroy tree
-}
-
-template <typename P>
-void query_table<P>::print(FILE* f) const {
- table_.print(f);
-}
-
-template class basic_table<default_table::parameters_type>;
-template class query_table<default_table::parameters_type>;
-
-}
diff --git a/query_masstree.hh b/query_masstree.hh
deleted file mode 100644
index 74baee7..0000000
--- a/query_masstree.hh
+++ /dev/null
@@ -1,83 +0,0 @@
-/* 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 QUERY_MASSTREE_HH
-#define QUERY_MASSTREE_HH 1
-#include "masstree.hh"
-#include "kvrow.hh"
-class threadinfo;
-namespace lcdf { class Json; }
-
-namespace Masstree {
-
-template <typename P>
-class query_table {
- public:
- typedef P parameters_type;
- typedef node_base<P> node_type;
- typedef leaf<P> leaf_type;
- typedef typename P::threadinfo_type threadinfo;
- typedef unlocked_tcursor<P> unlocked_cursor_type;
- typedef tcursor<P> cursor_type;
-
- query_table() {
- }
-
- const basic_table<P>& table() const {
- return table_;
- }
- basic_table<P>& table() {
- return table_;
- }
-
- void initialize(threadinfo& ti) {
- table_.initialize(ti);
- }
- void destroy(threadinfo& ti) {
- table_.destroy(ti);
- }
-
- void findpivots(Str* pv, int npv) const;
-
- void stats(FILE* f);
- void json_stats(lcdf::Json& j, threadinfo& ti);
- inline lcdf::Json json_stats(threadinfo& ti) {
- lcdf::Json j;
- json_stats(j, ti);
- return j;
- }
-
- void print(FILE* f) const;
-
- static void test(threadinfo& ti);
-
- static const char* name() {
- return "mb";
- }
-
- private:
- basic_table<P> table_;
-};
-
-struct default_query_table_params : public nodeparams<15, 15> {
- typedef row_type* value_type;
- typedef value_print<value_type> value_print_type;
- typedef ::threadinfo threadinfo_type;
-};
-
-typedef query_table<default_query_table_params> default_table;
-
-} // namespace Masstree
-#endif
diff --git a/scantest.cc b/scantest.cc
deleted file mode 100644
index be2d6f9..0000000
--- a/scantest.cc
+++ /dev/null
@@ -1,18 +0,0 @@
-#include "query_masstree.hh"
-
-using namespace Masstree;
-
-kvepoch_t global_log_epoch = 0;
-volatile mrcu_epoch_type globalepoch = 1; // global epoch, updated by main thread regularly
-volatile bool recovering = false; // so don't add log entries, and free old value immediately
-kvtimestamp_t initial_timestamp;
-
-int
-main(int argc, char *argv[])
-{
- (void) argc;
- (void) argv;
-
- threadinfo* ti = threadinfo::make(threadinfo::TI_MAIN, -1);
- default_table::test(*ti);
-}
diff --git a/str.cc b/str.cc
deleted file mode 100644
index 123fb1c..0000000
--- a/str.cc
+++ /dev/null
@@ -1,53 +0,0 @@
-/* Masstree
- * Eddie Kohler, Yandong Mao, Robert Morris
- * Copyright (c) 2012-2013 President and Fellows of Harvard College
- * Copyright (c) 2012-2013 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.
- */
-#include "str.hh"
-namespace lcdf {
-
-const Str Str::maxkey("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
- "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
- "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
- "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
- "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
- "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
- "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
- "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
- "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
- "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
- "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
- "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
- "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
- "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
- "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
- "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
- "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
- "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
- "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
- "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
- "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
- "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
- "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
- "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
- "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
- "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
- "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
- "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
- "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
- "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
- "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
- "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
- "\xFF", 257);
-
-} // namespace lcdf
diff --git a/str.hh b/str.hh
index 773ddb7..0f4b5a1 100644
--- a/str.hh
+++ b/str.hh
@@ -18,6 +18,7 @@
#include "string_base.hh"
#include <stdarg.h>
#include <stdio.h>
+
namespace lcdf {
struct Str : public String_base<Str> {
@@ -129,8 +130,7 @@ struct Str : public String_base<Str> {
x = (x * 10) + s[p] - '0';
return p == len && p != 0 ? x : -1;
}
-
- static Str snprintf(char *buf, size_t size, const char *fmt, ...) {
+ static Str snprintf(char *buf, size_t size, const char *fmt, ...) LCDF_SNPRINTF_ATTR {
va_list val;
va_start(val, fmt);
int n = vsnprintf(buf, size, fmt, val);
diff --git a/straccum.cc b/straccum.cpp
similarity index 97%
rename from straccum.cc
rename to straccum.cpp
index f82c153..7261076 100644
--- a/straccum.cc
+++ b/straccum.cpp
@@ -266,7 +266,7 @@ StringAccum &
operator<<(StringAccum &sa, long i)
{
if (char *x = sa.reserve(24)) {
- int len = sprintf(x, "%ld", i);
+ int len = snprintf(x, 24, "%ld", i);
sa.adjust_length(len);
}
return sa;
@@ -279,7 +279,7 @@ StringAccum &
operator<<(StringAccum &sa, unsigned long u)
{
if (char *x = sa.reserve(24)) {
- int len = sprintf(x, "%lu", u);
+ int len = snprintf(x, 24, "%lu", u);
sa.adjust_length(len);
}
return sa;
@@ -292,7 +292,7 @@ StringAccum &
operator<<(StringAccum &sa, long long i)
{
if (char *x = sa.reserve(24)) {
- int len = sprintf(x, "%lld", i);
+ int len = snprintf(x, 24, "%lld", i);
sa.adjust_length(len);
}
return sa;
@@ -305,7 +305,7 @@ StringAccum &
operator<<(StringAccum &sa, unsigned long long u)
{
if (char *x = sa.reserve(24)) {
- int len = sprintf(x, "%llu", u);
+ int len = snprintf(x, 24, "%llu", u);
sa.adjust_length(len);
}
return sa;
@@ -315,7 +315,7 @@ StringAccum &
operator<<(StringAccum &sa, double d)
{
if (char *x = sa.reserve(256)) {
- int len = sprintf(x, "%.12g", d);
+ int len = snprintf(x, 256, "%.12g", d);
sa.adjust_length(len);
}
return sa;
@@ -334,7 +334,7 @@ StringAccum::vsnprintf(int n, const char *format, va_list val)
#if HAVE_VSNPRINTF
int len = ::vsnprintf(x, n + 1, format, val);
#else
- int len = vsprintf(x, format, val);
+ int len = ::vsnprintf(x, n + 1, format, val);
assert(len <= n);
#endif
adjust_length(len);
diff --git a/straccum.hh b/straccum.hh
index d6d110a..f71c81c 100644
--- a/straccum.hh
+++ b/straccum.hh
@@ -19,11 +19,6 @@
#include <assert.h>
#include <stdarg.h>
#include "string.hh"
-#if __GNUC__ > 4
-# define LCDF_SNPRINTF_ATTR __attribute__((__format__(__printf__, 3, 4)))
-#else
-# define LCDF_SNPRINTF_ATTR /* nothing */
-#endif
namespace lcdf {
/** @file <lcdf/straccum.hh>
@@ -121,7 +116,7 @@ class StringAccum { public:
void append_break_lines(const String& text, int linelen, const String& leftmargin = String());
StringAccum& snprintf(int n, const char* format, ...) LCDF_SNPRINTF_ATTR;
- StringAccum& vsnprintf(int n, const char* format, va_list val);
+ StringAccum& vsnprintf(int n, const char* format, va_list val) LCDF_VSNPRINTF_ATTR;
String take_string();
@@ -724,5 +719,4 @@ inline void swap(StringAccum& a, StringAccum& b) {
}
} // namespace lcdf
-#undef LCDF_SNPRINTF_ATTR
#endif
diff --git a/string.cc b/string.cpp
similarity index 99%
rename from string.cc
rename to string.cpp
index e098f8b..1ddaf58 100644
--- a/string.cc
+++ b/string.cpp
@@ -584,7 +584,7 @@ String::String(int x)
_r.assign(int_data + 2 * x, 1, 0);
else {
char buf[128];
- sprintf(buf, "%d", x);
+ snprintf(buf, 128, "%d", x);
assign(buf, -1, false);
}
}
@@ -596,7 +596,7 @@ String::String(unsigned x)
_r.assign(int_data + 2 * x, 1, 0);
else {
char buf[128];
- sprintf(buf, "%u", x);
+ snprintf(buf, 128, "%u", x);
assign(buf, -1, false);
}
}
@@ -608,7 +608,7 @@ String::String(long x)
_r.assign(int_data + 2 * x, 1, 0);
else {
char buf[128];
- sprintf(buf, "%ld", x);
+ snprintf(buf, 128, "%ld", x);
assign(buf, -1, false);
}
}
@@ -620,7 +620,7 @@ String::String(unsigned long x)
_r.assign(int_data + 2 * x, 1, 0);
else {
char buf[128];
- sprintf(buf, "%lu", x);
+ snprintf(buf, 128, "%lu", x);
assign(buf, -1, false);
}
}
@@ -632,7 +632,7 @@ String::String(long long x)
_r.assign(int_data + 2 * x, 1, 0);
else {
char buf[128];
- sprintf(buf, "%lld", x);
+ snprintf(buf, 128, "%lld", x);
assign(buf, -1, false);
}
}
@@ -644,7 +644,7 @@ String::String(unsigned long long x)
_r.assign(int_data + 2 * x, 1, 0);
else {
char buf[128];
- sprintf(buf, "%llu", x);
+ snprintf(buf, 128, "%llu", x);
assign(buf, -1, false);
}
}
@@ -652,7 +652,7 @@ String::String(unsigned long long x)
String::String(double x)
{
char buf[128];
- int len = sprintf(buf, "%.12g", x);
+ int len = snprintf(buf, 128, "%.12g", x);
assign(buf, len, false);
}
@@ -975,11 +975,11 @@ hard_printable(const String &s, int pos, int type)
if (x[pos] >= 9 && x[pos] <= 13)
sa << '\\' << ("tnvfr"[x[pos] - 9]);
else if (char *buf = sa.extend(4, 1))
- sprintf(buf, "\\%03o", x[pos]);
+ snprintf(buf, 5, "\\%03o", x[pos]);
} else if (x[pos] < 32 && type != 1)
sa << '^' << (unsigned char)(x[pos] + 64);
else if (char *buf = sa.extend(4, 1))
- sprintf(buf, "\\%03o", x[pos]);
+ snprintf(buf, 5, "\\%03o", x[pos]);
}
return sa.take_string();
}
diff --git a/string_base.hh b/string_base.hh
index bf1e010..adcd717 100644
--- a/string_base.hh
+++ b/string_base.hh
@@ -26,6 +26,14 @@ namespace lcdf {
class StringAccum;
#define LCDF_CONSTANT_CSTR(cstr) ((cstr) && __builtin_constant_p(strlen((cstr))))
+#if __GNUC__ > 4
+# define LCDF_SNPRINTF_ATTR __attribute__((__format__(__printf__, 3, 4)))
+# define LCDF_VSNPRINTF_ATTR __attribute__((__format__(__printf__, 3, 0)))
+#else
+# define LCDF_SNPRINTF_ATTR /* nothing */
+# define LCDF_VSNPRINTF_ATTR /* nothing */
+#endif
+
class String_generic {
public:
static const char empty_data[1];
diff --git a/string_slice.cc b/string_slice.cc
deleted file mode 100644
index 2334912..0000000
--- a/string_slice.cc
+++ /dev/null
@@ -1,16 +0,0 @@
-/* Masstree
- * Eddie Kohler, Yandong Mao, Robert Morris
- * Copyright (c) 2012-2013 President and Fellows of Harvard College
- * Copyright (c) 2012-2013 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.
- */
-#include "string_slice.hh"
diff --git a/test_atomics.cc b/test_atomics.cc
deleted file mode 100644
index ed32c04..0000000
--- a/test_atomics.cc
+++ /dev/null
@@ -1,468 +0,0 @@
-/* Masstree
- * Eddie Kohler, Yandong Mao, Robert Morris
- * Copyright (c) 2012-2013 President and Fellows of Harvard College
- * Copyright (c) 2012-2013 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.
- */
-#define FORCE_ENABLE_ASSERTIONS 1
-#undef NDEBUG
-#include "compiler.hh"
-#include <stdlib.h>
-#include <algorithm>
-#include <sys/time.h>
-#include <assert.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <pthread.h>
-#include <unistd.h>
-#include "kvrandom.hh"
-#include "string_slice.hh"
-#include "kpermuter.hh"
-#include "value_bag.hh"
-#include "value_string.hh"
-#include "json.hh"
-using namespace lcdf;
-
-uint8_t xb[100];
-uint16_t xw[100];
-uint32_t xl[100];
-
-struct fake_threadinfo {
- static void* allocate(size_t sz, memtag = memtag_none) {
- return new char[sz];
- }
- static void deallocate(void* p, size_t, memtag = memtag_none) {
- delete[] reinterpret_cast<char*>(p);
- }
-};
-
-// also check bitfield layout
-union myunion {
- uint32_t x;
- struct {
- unsigned one : 1;
- unsigned two : 2;
- unsigned three : 3;
- };
-} xunion;
-
-void test_atomics() {
- uint32_t x;
-
- xunion.one = 1;
- assert(xunion.x == 1);
-
- x = xchg(&xb[0], 2);
- assert(x == 0 && xb[0] == 2 && xb[1] == 0 && xb[2] == 0 && xb[3] == 0);
- x = xchg(&xb[0], 3);
- assert(x == 2 && xb[0] == 3 && xb[1] == 0 && xb[2] == 0 && xb[3] == 0);
-
- x = xchg(&xw[0], 1);
- assert(x == 0);
-
- x = fetch_and_add(&xl[0], 100);
- assert(x == 0 && xl[0] == 100);
- x = cmpxchg(&xl[0], 100, 200);
- assert(x == 100 && xl[0] == 200);
- if (bool_cmpxchg(&xl[0], 200, 300))
- xl[1] = 400;
- assert(xl[0] == 300 && xl[1] == 400);
- if (bool_cmpxchg(&xl[0], 400, 100))
- xl[1] = 500;
- assert(xl[0] == 300 && xl[1] == 400);
-}
-
-void test_psdes_nr() {
- kvrandom_psdes_nr r;
- union {
- uint32_t u;
- float f;
- } u;
-
- r.seed(1);
- u.u = r();
- assert(u.u == 0x509C0C23U);
- u.u = r[99];
- assert(u.u == 0xA66CB41A);
-
- r.seed(99);
- u.u = r();
- assert(u.u == 0x64300984);
- u.u = r[99];
- assert(u.u == 0x59BA89EB);
-}
-
-template <typename T>
-void time_random() {
- T r;
- uint32_t x = 0;
- for (int i = 0; i < 1000000000; ++i)
- x ^= r();
- assert(x != 0);
-}
-
-template <typename T>
-void time_keyslice() {
- char *b = (char *) malloc(4096);
- FILE *f = fopen("/dev/urandom", "rb");
- ssize_t rr = fread(b, 1, 4096, f);
- assert(rr == 4096);
- fclose(f);
- T x = 0;
- kvrandom_lcg_nr r;
- x ^= string_slice<T>::make(b, 1);
- for (int i = 0; i < 1000000000; ++i)
- x ^= string_slice<T>::make(b + r() % 2048, r() % 16);
- assert(x);
-}
-
-void test_kpermuter() {
- typedef kpermuter<15> kpermuter_type;
- kpermuter_type k = kpermuter_type::make_empty();
- int i = k.insert_from_back(0);
- assert(k.size() == 1 && i == 0 && k[0] == 0);
- i = k.insert_from_back(0);
- assert(k.size() == 2 && i == 1 && k[0] == 1 && k[1] == 0);
- i = k.insert_from_back(2);
- assert(k.size() == 3 && i == 2 && k[0] == 1 && k[1] == 0 && k[2] == 2);
- k.remove(1);
- assert(k.size() == 2 && k[0] == 1 && k[1] == 2 && k[2] == 0);
- k.remove(1);
- assert(k.size() == 1 && k[0] == 1 && k[1] == 2 && k[2] == 0 && k[3] == 14);
- i = k.insert_from_back(0);
- assert(k.size() == 2 && i == 3 && k[0] == 3 && k[1] == 1 && k[2] == 2 && k[3] == 0 && k[4] == 14);
- k.insert_selected(0, 3);
- assert(k.size() == 3 && k[0] == 0 && k[1] == 3 && k[2] == 1 && k[3] == 2 && k[4] == 14);
- k.insert_selected(2, 11);
- assert(k.size() == 4 && k[0] == 0 && k[1] == 3 && k[2] == 7 && k[3] == 1 && k[4] == 2
- && k[5] == 14 && k[11] == 8 && k[12] == 6);
- k.insert_selected(2, 14);
- assert(k.size() == 5 && k[0] == 0 && k[1] == 3 && k[2] == 4 && k[3] == 7 && k[4] == 1 && k[5] == 2
- && k[6] == 14 && k[12] == 8 && k[13] == 6);
- k.exchange(0, 1);
- assert(k.size() == 5 && k[0] == 3 && k[1] == 0 && k[2] == 4 && k[3] == 7 && k[4] == 1 && k[5] == 2
- && k[6] == 14 && k[12] == 8 && k[13] == 6);
- k.remove_to_back(2);
- assert(k.size() == 4 && k[0] == 3 && k[1] == 0 && k[2] == 7 && k[3] == 1 && k[4] == 2
- && k[5] == 14 && k[11] == 8 && k[12] == 6 && k[14] == 4);
- assert(k.back() == 4);
- i = k.insert_from_back(2);
- assert(k.size() == 5 && k[0] == 3 && k[1] == 0 && k[2] == 4 && k[3] == 7 && k[4] == 1 && k[5] == 2
- && k[6] == 14 && k[12] == 8 && k[13] == 6 && i == 4);
- k.exchange(0, 0);
- assert(k.size() == 5 && k[0] == 3 && k[1] == 0 && k[2] == 4 && k[3] == 7 && k[4] == 1 && k[5] == 2
- && k[6] == 14 && k[12] == 8 && k[13] == 6 && i == 4);
-
- assert(find_lowest_zero_nibble(0x0120U) == 0);
- assert(find_lowest_zero_nibble(0x0123U) == 3);
- assert(find_lowest_zero_nibble(0x12345678U) == -1);
-
- kpermuter<14> ka = kpermuter<14>::make_empty();
- i = ka.insert_from_back(0);
- assert(ka.size() == 1 && i == 0 && ka[0] == 0);
- i = ka.insert_from_back(0);
- assert(ka.size() == 2 && i == 1 && ka[0] == 1 && ka[1] == 0);
- i = ka.insert_from_back(2);
- assert(ka.size() == 3 && i == 2 && ka[0] == 1 && ka[1] == 0 && ka[2] == 2);
- ka.remove_to_back(1);
- assert(ka.size() == 2 && ka[0] == 1 && ka[1] == 2 && ka.back() == 0);
-}
-
-void test_string_slice() {
- typedef string_slice<uint32_t> ss_type;
- assert(ss_type::make("a", 1) == ss_type::make("aaa", 1));
- assert(ss_type::make_sloppy("0123abcdef" + 4, 1)
- == ss_type::make_sloppy("bcdea01293" + 4, 1));
- assert(ss_type::make_comparable("a", 1) < ss_type::make_comparable("b", 1));
- assert(ss_type::equals_sloppy("0123abcdef" + 4, "abcdea02345" + 5, 1));
- assert(ss_type::make_comparable("abcd", 4) < ss_type::make_comparable("abce", 4));
- assert(ss_type::make_comparable("abce", 4) > ss_type::make_comparable("abcd", 4));
- assert(ss_type::equals_sloppy("0123abcdef" + 4, "abcdeabcd5" + 5, 4));
- assert(!ss_type::equals_sloppy("0123abcdef" + 4, "abcdeabcd5" + 5, 5));
- assert(String("12345").find_right("") == 5);
- assert(String("12345").find_right("5") == 4);
- assert(String("12345").find_right("23") == 1);
- assert(String("12345", 0).find_right("23") == -1);
-}
-
-void test_string_bag() {
- fake_threadinfo ti;
- typedef value_bag<uint16_t> bag_t;
- bag_t eb;
- if (eb.size() > sizeof(bag_t))
- fprintf(stderr, "sizes are off: %zu vs. %zu\n", eb.size(), sizeof(bag_t));
- assert(eb.size() <= sizeof(bag_t));
- bag_t* b = eb.update(0, Str("A", 1), 1, ti);
- assert(b->row_string() == Str("\001\000\006\000\007\000A", 7));
- assert(b->ncol() == 1);
- assert(b->col(0) == Str("A", 1));
-
- bag_t* bx = bag_t::create1(Str("A", 1), 1, ti);
- assert(bx->row_string() == b->row_string());
- bx->deallocate(ti);
-
- bag_t *bb = b->update(1, Str("BB", 2), 2, ti);
- b->deallocate(ti);
- b = bb;
- assert(b->row_string() == Str("\002\000"
- "\010\000\011\000\013\000"
- "ABB", 013));
- assert(b->ncol() == 2);
- assert(b->col(0) == Str("A", 1));
- assert(b->col(1) == Str("BB", 2));
-
- bb = b->update(3, Str("CCC", 3), 3, ti);
- b->deallocate(ti);
- b = bb;
- assert(b->row_string() == Str("\004\000"
- "\014\000\015\000\017\000\017\000\022\000"
- "ABBCCC", 022));
- assert(b->ncol() == 4);
- assert(b->col(0) == Str("A", 1));
- assert(b->col(1) == Str("BB", 2));
- assert(b->col(2) == Str("", 0));
- assert(b->col(3) == Str("CCC", 3));
-
- bb = b->update(1, Str("bbb", 3), 4, ti);
- b->deallocate(ti);
- b = bb;
- assert(b->row_string() == Str("\004\000"
- "\014\000\015\000\020\000\020\000\023\000"
- "AbbbCCC", 023));
- assert(b->ncol() == 4);
- assert(b->col(0) == Str("A", 1));
- assert(b->col(1) == Str("bbb", 3));
- assert(b->col(2) == Str("", 0));
- assert(b->col(3) == Str("CCC", 3));
-
- bb = b->update(0, Str("a", 1), 4, ti);
- b->deallocate(ti);
- b = bb;
- assert(b->row_string() == Str("\004\000"
- "\014\000\015\000\020\000\020\000\023\000"
- "abbbCCC", 023));
- assert(b->ncol() == 4);
- assert(b->col(0) == Str("a", 1));
- assert(b->col(1) == Str("bbb", 3));
- assert(b->col(2) == Str("", 0));
- assert(b->col(3) == Str("CCC", 3));
-
- bb = b->update(1, Str("", 0), 4, ti);
- b->deallocate(ti);
- b = bb;
- assert(b->row_string() == Str("\004\000"
- "\014\000\015\000\015\000\015\000\020\000"
- "aCCC", 020));
- assert(b->ncol() == 4);
- assert(b->col(0) == Str("a", 1));
- assert(b->col(1) == Str("", 0));
- assert(b->col(2) == Str("", 0));
- assert(b->col(3) == Str("CCC", 3));
-
- b->deallocate(ti);
-}
-
-void test_json()
-{
- Json j;
- assert(j.empty());
- assert(!j);
-
- j = Json::make_object();
- assert(j.empty());
- assert(j);
-
- j.set("foo", "bar");
- assert(j["foo"]);
- assert(j["foo"].to_s() == "bar");
- assert(j.size() == 1);
-
- j.set("baz", "flim");
- assert(j.size() == 2);
- assert(j.unparse() == "{\"foo\":\"bar\",\"baz\":\"flim\"}");
-
- j.erase("foo");
- assert(j.size() == 1);
-
- j.assign_parse("2");
- assert(j == 2);
-
- j.assign_parse("null");
- assert(j == Json());
-
- j.assign_parse("\"a\"");
- assert(j == "a");
-
- j.assign_parse("[1,2]");
- assert(j.unparse() == "[1,2]");
-
- j.assign_parse("[[[]],{\"a\":{}}]");
- assert(j.unparse() == "[[[]],{\"a\":{}}]");
-
- j = Json::parse("{\"x22\":{\n\
- \"git-revision\":\"ebbd3d4767847300f552b181a10bda57a926f554M\",\n\
- \"time\":\"Tue Feb 7 20:20:33 2012\",\n\
- \"machine\":\"rtshanks-laptop\",\n\
- \"cores\":2,\n\
- \"runs\":[\"x22\\/rw2\\/mb\\/0\"]\n\
- },\n\
- \"x23\":{\n\
- \"git-revision\":\"ebbd3d4767847300f552b181a10bda57a926f554M\",\n\
- \"time\":\"Tue Feb 7 20:31:05 2012\",\n\
- \"machine\":\"rtshanks-laptop\",\n\
- \"cores\":2,\n\
- \"runs\":[\"x23\\/rw2\\/mb\\/0\",\"x23\\/rw1\\/mb\\/0\",\"x23\\/rw3\\/mb\\/0\"]\n\
- },\n\
- \"x24\":{\n\
- \"git-revision\":\"62e9970ca8ae9c6eebf2d71b7065ea694fb25282M\",\n\
- \"time\":\"Sat Feb 11 15:54:01 2012\",\n\
- \"machine\":\"rtshanks-laptop\",\n\
- \"cores\":2,\n\
- \"runs\":[\"x24\\/rw1\\/b\\/0\"]\n\
- },\"b\":\"c\",\"c\":\"d\"}");
- assert(j["x22"]["time"] == "Tue Feb 7 20:20:33 2012");
- assert(j["x22"]["cores"] == 2);
- {
- Json::object_iterator it = j.obegin();
- assert(it.key() == "x22");
- ++it;
- assert(it.key() == "x23");
- ++it;
- assert(it.key() == "x24");
- ++it;
- assert(it.key() == "b");
- ++it;
- assert(it.key() == "c");
- }
-
- {
- Json jcopy = j;
- assert(j.size() == 5);
- int count = 0;
- for (Json::iterator it = jcopy.begin(); it != jcopy.end(); ++it) {
- it->second = Json();
- ++count;
- }
- assert(!jcopy["x22"]);
- assert(j["x22"]["cores"] == 2);
- assert(count == jcopy.size());
- }
-
- assert(!j["x49"]);
- assert(j.size() == 5);
- assert(!j["x49"]["45"][2]["a"]);
- assert(j.size() == 5);
- j["x49"]["45"][2]["a"] = 1;
- assert(j.size() == 6);
- assert(j["x22"].is_object() && j.get("x22").is_object());
- assert(j["x23"].is_object() && j.get("x23").is_object());
- assert(j["b"].is_string() && j.get("b").is_string());
- assert(j["x49"].is_object() && j.get("x49").is_object());
- assert(j["x49"]["45"].is_array());
- assert(j["x49"]["45"].size() == 3 && j["x49"]["45"][2].is_object());
-
- j = Json::make_object();
- j["a"] = j["b"];
- assert(j.size() == 1);
- assert(j["a"].is_null());
- assert(j.count("a") == 1);
-
- j = Json::make_object();
- Json k = Json::make_array();
- j["a"] = k[2];
- assert(j.size() == 1);
- assert(k.size() == 0);
- assert(j["a"].is_null());
- assert(j.count("a") == 1);
-
- j = Json(1);
- assert(j.get("a").is_null());
-
- j.assign_parse("{\"a\":1,\"b\":true,\"c\":\"\"}");
- {
- int i = 0;
- bool b = false;
- String s;
- assert(j.get("a", i));
- assert(i == 1);
- assert(j.get("a", i).get("b", b));
- assert(b == true);
- assert(!j.get("a", s).status());
- assert(!j.get("a", s).status(b));
- assert(b == false);
- assert(j.get("a", k));
- assert(k == Json(1));
- assert(!j.get("cc", k));
- }
-
- j["a"] = Json(5);
- j.set("a", Json(5));
- j["b"] = Json::parse("[]");
-}
-
-void test_value_updates() {
- fake_threadinfo ti;
- typedef value_bag<uint16_t> bag_t;
- typedef value_string vstr_t;
- bag_t* eb = new(ti.allocate(sizeof(bag_t))) bag_t;
- vstr_t* strb = new(ti.allocate(sizeof(vstr_t))) vstr_t;
-
- Json bagupdate = Json::array(0, "ABC", 1, "def", 2, "EGHIJ", 3, "klm");
-
- Json strupdate = Json::array(vstr_t::make_index(0, 3), "ABC",
- vstr_t::make_index(3, 3), "def",
- vstr_t::make_index(6, 5), "EGHIJ",
- vstr_t::make_index(11, 3), "klm");
-
- {
- bag_t* eb2 = eb->update(bagupdate.array_data(), bagupdate.end_array_data(), 1, ti);
- eb->deallocate(ti);
- eb = eb2;
- assert(eb->col(0) == Str("ABC"));
- assert(eb->col(1) == Str("def"));
- assert(eb->col(2) == Str("EGHIJ"));
- assert(eb->col(3) == Str("klm"));
- }
-
- {
- vstr_t* strb2 = strb->update(strupdate.array_data(), strupdate.end_array_data(), 1, ti);
- strb->deallocate(ti);
- strb = strb2;
- assert(strb->col(vstr_t::make_index(0, 3)) == Str("ABC"));
- assert(strb->col(vstr_t::make_index(3, 3)) == Str("def"));
- assert(strb->col(vstr_t::make_index(6, 5)) == Str("EGHIJ"));
- assert(strb->col(vstr_t::make_index(11, 3)) == Str("klm"));
- assert(strb->col(0) == Str("ABCdefEGHIJklm"));
- }
-
- eb->deallocate(ti);
- strb->deallocate(ti);
-}
-
-int main(int, char *[])
-{
- //test_atomics();
- //test_psdes_nr();
- //time_random<kvrandom_lcg_nr>();
- assert(iceil_log2(2) == 2);
- assert(iceil_log2(3) == 4);
- assert(ifloor_log2(2) == 2);
- assert(ifloor_log2(3) == 2);
- //time_keyslice<uint64_t>();
- test_kpermuter();
- test_string_slice();
- test_string_bag();
- test_json();
- test_value_updates();
- std::cout << "Tests complete!\n";
- return 0;
-}
diff --git a/test_string.cc b/test_string.cc
deleted file mode 100644
index 1e418ec..0000000
--- a/test_string.cc
+++ /dev/null
@@ -1,89 +0,0 @@
-/* Masstree
- * Eddie Kohler, Yandong Mao, Robert Morris
- * Copyright (c) 2012-2013 President and Fellows of Harvard College
- * Copyright (c) 2012-2013 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.
- */
-#include "string.hh"
-#include <stdio.h>
-#include <assert.h>
-#include <string.h>
-#include <stdlib.h>
-#include "straccum.hh"
-
-template <typename T>
-static bool
-check_straccum_utf8(StringAccum &sa, const char *in, int inlen,
- const char *out, int outlen)
-{
- sa.clear();
- Encoding::UTF8Encoder<T> encoder;
- sa.append_encoded(encoder, in, in + inlen);
- return sa.length() == outlen && memcmp(sa.begin(), out, sa.length()) == 0;
-}
-
-template <typename T>
-static bool
-check_straccum2_utf8(StringAccum &sa, const char *in, int inlen,
- const char *out, int outlen)
-{
- sa.clear();
- memcpy(sa.reserve(inlen), in, inlen);
- Encoding::UTF8Encoder<T> encoder;
- sa.append_encoded(encoder, sa.begin(), sa.begin() + inlen);
- return sa.length() == outlen && memcmp(sa.begin(), out, sa.length()) == 0;
-}
-
-int
-main(int argc, char *argv[])
-{
- assert(String("abc").to_utf8() == "abc");
- assert(String("").to_utf8() == "");
- assert(String("ab\000cd", 5).to_utf8() == "abcd");
- assert(String("\xc3\x9dHi!").to_utf8() == "\xc3\x9dHi!");
- assert(String("\xddHi!").to_utf8() == "\xc3\x9dHi!");
- assert(String("\xc3\x9dHi!\x9c").to_utf8() == "\xc3\x9dHi!\xc5\x93");
- assert(String("ab\000c\x9c", 5).to_utf8() == "abc\xc5\x93");
- assert(String("\xc3\x9dXY\000c\x9c", 7).to_utf8() == "\xc3\x9dXYc\xc5\x93");
-
- StringAccum sa;
- check_straccum_utf8<Encoding::UTF8>(sa, "abc", 3, "abc", 3);
- check_straccum_utf8<Encoding::UTF8>(sa, "", 0, "", 0);
- check_straccum_utf8<Encoding::UTF8>(sa, "ab\000cd", 5, "ab\000cd", 5);
- check_straccum_utf8<Encoding::UTF8NoNul>(sa, "ab\000cd", 5, "abcd", 4);
- check_straccum_utf8<Encoding::UTF8>(sa, "\xc3\x9dHi!", 5, "\xc3\x9dHi!", 5);
- check_straccum_utf8<Encoding::Windows1252>(sa, "\xddHi!", 4, "\xc3\x9dHi!", 5);
-
- check_straccum2_utf8<Encoding::UTF8>(sa, "abc", 3, "abc", 3);
- check_straccum2_utf8<Encoding::UTF8>(sa, "", 0, "", 0);
- check_straccum2_utf8<Encoding::UTF8>(sa, "ab\000cd", 5, "ab\000cd", 5);
- check_straccum2_utf8<Encoding::UTF8NoNul>(sa, "ab\000cd", 5, "abcd", 4);
- check_straccum2_utf8<Encoding::UTF8>(sa, "\xc3\x9dHi!", 5, "\xc3\x9dHi!", 5);
- check_straccum2_utf8<Encoding::Windows1252>(sa, "\xddHi!", 4, "\xc3\x9dHi!", 5);
-
- if (argc == 2) {
- FILE *f;
- if (strcmp(argv[1], "-") == 0)
- f = stdin;
- else if (!(f = fopen(argv[1], "rb"))) {
- perror("test_string");
- exit(1);
- }
- StringAccum sa;
- while (!feof(f)) {
- size_t x = fread(sa.reserve(1024), 1, 1024, f);
- sa.adjust_length(x);
- }
- String s = sa.take_string().to_utf8(String::utf_strip_bom);
- fwrite(s.data(), 1, s.length(), stdout);
- }
-}
diff --git a/testrunner.cc b/testrunner.cc
deleted file mode 100644
index 67da559..0000000
--- a/testrunner.cc
+++ /dev/null
@@ -1,54 +0,0 @@
-/* Masstree
- * Eddie Kohler, Yandong Mao, Robert Morris
- * Copyright (c) 2012-2013 President and Fellows of Harvard College
- * Copyright (c) 2012-2013 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.
- */
-#include "testrunner.hh"
-#include <algorithm>
-#include <numeric>
-#include <vector>
-
-testrunner_base* testrunner_base::thehead;
-testrunner_base* testrunner_base::thetail;
-
-void testrunner_base::print_names(FILE* stream, int ncol) {
- masstree_precondition(ncol >= 1);
-
- std::vector<lcdf::String> names;
- for (testrunner_base* tr = thehead; tr; tr = tr->next_)
- names.push_back(tr->name());
-
- size_t percol;
- std::vector<int> colwidth;
- while (1) {
- percol = (names.size() + ncol - 1) / ncol;
- colwidth.assign(ncol, 0);
- for (size_t i = 0; i != names.size(); ++i)
- colwidth[i/percol] = std::max(colwidth[i/percol], names[i].length());
- if (ncol == 1
- || std::accumulate(colwidth.begin(), colwidth.end(), 0)
- + ncol * 3 <= 78)
- break;
- --ncol;
- }
-
- for (size_t row = 0; row != percol; ++row) {
- size_t off = row;
- for (int col = 0; col != ncol; ++col, off += percol)
- if (off < names.size())
- fprintf(stream, "%*s %s",
- (int) (col ? colwidth[col-1] - names[off-percol].length() : 0), "",
- names[off].c_str());
- fprintf(stream, "\n");
- }
-}
diff --git a/testrunner.hh b/testrunner.hh
deleted file mode 100644
index ac7fdd9..0000000
--- a/testrunner.hh
+++ /dev/null
@@ -1,60 +0,0 @@
-#ifndef MASSTREE_TESTRUNNER_HH
-#define MASSTREE_TESTRUNNER_HH
-#include "string.hh"
-#include <stdio.h>
-
-class testrunner_base {
-public:
- testrunner_base(const lcdf::String& name)
- : name_(name), next_(0) {
- thehead ? thetail->next_ = this : thehead = this;
- thetail = this;
- }
- virtual ~testrunner_base() {
- }
- const lcdf::String& name() const {
- return name_;
- }
- static testrunner_base* first() {
- return thehead;
- }
- static testrunner_base* find(const lcdf::String& name) {
- testrunner_base* t = thehead;
- while (t && t->name_ != name)
- t = t->next_;
- return t;
- }
- static void print_names(FILE* stream, int maxcol);
-private:
- static testrunner_base* thehead;
- static testrunner_base* thetail;
- lcdf::String name_;
- testrunner_base* next_;
-};
-
-#ifdef TESTRUNNER_CLIENT_TYPE
-
-class testrunner : public testrunner_base {
-public:
- inline testrunner(const lcdf::String& name)
- : testrunner_base(name) {
- }
- static testrunner* first() {
- return static_cast<testrunner*>(testrunner_base::first());
- }
- static testrunner* find(const lcdf::String& name) {
- return static_cast<testrunner*>(testrunner_base::find(name));
- }
- virtual void run(TESTRUNNER_CLIENT_TYPE) = 0;
-};
-
-#define MAKE_TESTRUNNER(name, text) \
- namespace { \
- class testrunner_##name : public testrunner { \
- public: \
- testrunner_##name() : testrunner(#name) {} \
- void run(TESTRUNNER_CLIENT_TYPE client) { text; client.finish(); } \
- }; static testrunner_##name testrunner_##name##_instance; }
-
-#endif
-#endif
diff --git a/unit-mt.cc b/unit-mt.cc
deleted file mode 100644
index 39a824e..0000000
--- a/unit-mt.cc
+++ /dev/null
@@ -1,190 +0,0 @@
-#include <iostream>
-#include <random>
-#include <vector>
-#include <thread>
-
-#include <pthread.h>
-
-#include "config.h"
-#include "compiler.hh"
-
-#include "masstree.hh"
-#include "kvthread.hh"
-#include "masstree_tcursor.hh"
-#include "masstree_insert.hh"
-#include "masstree_print.hh"
-#include "masstree_remove.hh"
-#include "masstree_scan.hh"
-#include "masstree_stats.hh"
-#include "string.hh"
-
-#define NUM_THREADS 64
-
-class key_unparse_unsigned {
-public:
- static int unparse_key(Masstree::key<uint64_t> key, char* buf, int buflen) {
- return snprintf(buf, buflen, "%" PRIu64, key.ikey());
- }
-};
-
-class MasstreeWrapper {
-public:
- static constexpr uint64_t insert_bound = 0xfffff; //0xffffff;
- struct table_params : public Masstree::nodeparams<15,15> {
- typedef uint64_t value_type;
- typedef Masstree::value_print<value_type> value_print_type;
- typedef threadinfo threadinfo_type;
- typedef key_unparse_unsigned key_unparse_type;
- static constexpr ssize_t print_max_indent_depth = 12;
- };
-
- typedef Masstree::Str Str;
- typedef Masstree::basic_table<table_params> table_type;
- typedef Masstree::unlocked_tcursor<table_params> unlocked_cursor_type;
- typedef Masstree::tcursor<table_params> cursor_type;
- typedef Masstree::leaf<table_params> leaf_type;
- typedef Masstree::internode<table_params> internode_type;
-
- typedef typename table_type::node_type node_type;
- typedef typename unlocked_cursor_type::nodeversion_value_type nodeversion_value_type;
-
- static __thread typename table_params::threadinfo_type *ti;
-
- MasstreeWrapper() {
- this->table_init();
- }
-
- void table_init() {
- if (ti == nullptr)
- ti = threadinfo::make(threadinfo::TI_MAIN, -1);
- table_.initialize(*ti);
- key_gen_ = 0;
- }
-
- void keygen_reset() {
- key_gen_ = 0;
- }
-
- static void thread_init(int thread_id) {
- if (ti == nullptr)
- ti = threadinfo::make(threadinfo::TI_PROCESS, thread_id);
- }
-
- void insert_test() {
- while (1) {
- auto int_key = fetch_and_add(&key_gen_, 1);
- uint64_t key_buf;
- if (int_key > insert_bound)
- break;
- Str key = make_key(int_key, key_buf);
-
- cursor_type lp(table_, key);
- bool found = lp.find_insert(*ti);
- always_assert(!found, "keys should all be unique");
-
- lp.value() = int_key;
-
- fence();
- lp.finish(1, *ti);
- }
- }
-
- void remove_test() {
- while (1) {
- auto int_key = fetch_and_add(&key_gen_, 1);
- uint64_t key_buf;
- if (int_key > insert_bound)
- break;
- Str key = make_key(int_key, key_buf);
-
- cursor_type lp(table_, key);
- bool found = lp.find_locked(*ti);
- always_assert(found, "keys must all exist");
- lp.finish(-1, *ti);
- }
- }
-
- void insert_remove_test(int thread_id) {
- std::mt19937 gen(thread_id);
- std::uniform_int_distribution<int> dist(1, 6);
- uint64_t int_key = 0;
- bool need_print = true;
- while (!stopping) {
- int_key = fetch_and_add(&key_gen_, 1);
- uint64_t key_buf;
- if (int_key > insert_bound)
- break;
- Str key = make_key(int_key, key_buf);
-
- cursor_type lp(table_, key);
- bool found = lp.find_insert(*ti);
- always_assert(!found, "keys should all be unique 1");
-
- lp.value() = int_key;
- fence();
- lp.finish(1, *ti);
-
- if (dist(gen) <= 2) {
- cursor_type lp1(table_, key);
- bool found1 = lp1.find_locked(*ti);
- if (!found1) {
- stopping = true;
- lp1.finish(0, *ti);
- printf("failed at key %" PRIu64 ", lp1 got %p\n", int_key, lp1.node());
- need_print = true;
- break;
- always_assert(found1, "this is my key!");
- } else {
- lp1.finish(-1, *ti);
- }
- }
- }
- printf("stopped at key %" PRIu64 "\n", int_key);
- if (need_print && fetch_and_add(&printing, 1) == 0) {
- table_.print(stdout);
- fflush(stdout);
- fprintf(stdout, "Stats: %s\n",
- Masstree::json_stats(table_, ti).unparse(lcdf::Json::indent_depth(1000)).c_str());
- }
- }
-
-private:
- table_type table_;
- uint64_t key_gen_;
- static bool stopping;
- static uint32_t printing;
-
- static inline Str make_key(uint64_t int_key, uint64_t& key_buf) {
- key_buf = __builtin_bswap64(int_key);
- return Str((const char *)&key_buf, sizeof(key_buf));
- }
-};
-
-__thread typename MasstreeWrapper::table_params::threadinfo_type* MasstreeWrapper::ti = nullptr;
-bool MasstreeWrapper::stopping = false;
-uint32_t MasstreeWrapper::printing = 0;
-
-volatile mrcu_epoch_type active_epoch = 1;
-volatile uint64_t globalepoch = 1;
-volatile bool recovering = false;
-
-void test_thread(MasstreeWrapper* mt, int thread_id) {
- mt->thread_init(thread_id);
- mt->insert_remove_test(thread_id);
-}
-
-int main() {
- auto mt = new MasstreeWrapper();
- mt->keygen_reset();
- std::cout << "insert_remove_test..." << std::endl;
-
- std::vector<std::thread> ths;
-
- for (int i = 0; i < NUM_THREADS; ++i)
- ths.emplace_back(test_thread, mt, i);
- for (auto& t : ths)
- t.join();
-
- std::cout << "test pass." << std::endl;
- return 0;
-}
diff --git a/value_array.cc b/value_array.cc
deleted file mode 100644
index 879f0d1..0000000
--- a/value_array.cc
+++ /dev/null
@@ -1,66 +0,0 @@
-/* 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.
- */
-#include "kvrow.hh"
-#include "value_array.hh"
-#include <string.h>
-
-value_array* value_array::make_sized_row(int ncol, kvtimestamp_t ts,
- threadinfo& ti) {
- value_array *tv;
- tv = (value_array *) ti.allocate(shallow_size(ncol), memtag_value);
- tv->ts_ = ts;
- tv->ncol_ = ncol;
- memset(tv->cols_, 0, sizeof(tv->cols_[0]) * ncol);
- return tv;
-}
-
-value_array* value_array::update(const Json* first, const Json* last,
- kvtimestamp_t ts, threadinfo& ti) const {
- masstree_precondition(ts >= ts_);
- unsigned ncol = std::max(int(ncol_), int(last[-2].as_i()) + 1);
- value_array* row = (value_array*) ti.allocate(shallow_size(ncol), memtag_value);
- row->ts_ = ts;
- row->ncol_ = ncol;
- memcpy(row->cols_, cols_, ncol_ * sizeof(cols_[0]));
- memset(row->cols_ + ncol_, 0, (ncol - ncol_) * sizeof(cols_[0]));
- for (; first != last; first += 2)
- row->cols_[first[0].as_u()] = make_column(first[1].as_s(), ti);
- return row;
-}
-
-void value_array::deallocate(threadinfo& ti) {
- for (short i = 0; i < ncol_; ++i)
- deallocate_column(cols_[i], ti);
- ti.deallocate(this, shallow_size(), memtag_value);
-}
-
-void value_array::deallocate_rcu(threadinfo& ti) {
- for (short i = 0; i < ncol_; ++i)
- deallocate_column_rcu(cols_[i], ti);
- ti.deallocate_rcu(this, shallow_size(), memtag_value);
-}
-
-void value_array::deallocate_rcu_after_update(const Json* first, const Json* last, threadinfo& ti) {
- for (; first != last && first[0].as_u() < unsigned(ncol_); first += 2)
- deallocate_column_rcu(cols_[first[0].as_u()], ti);
- ti.deallocate_rcu(this, shallow_size(), memtag_value);
-}
-
-void value_array::deallocate_after_failed_update(const Json* first, const Json* last, threadinfo& ti) {
- for (; first != last; first += 2)
- deallocate_column(cols_[first[0].as_u()], ti);
- ti.deallocate(this, shallow_size(), memtag_value);
-}
diff --git a/value_array.hh b/value_array.hh
deleted file mode 100644
index 5191027..0000000
--- a/value_array.hh
+++ /dev/null
@@ -1,156 +0,0 @@
-/* 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 VALUE_ARRAY_HH
-#define VALUE_ARRAY_HH
-#include "compiler.hh"
-#include "json.hh"
-
-class value_array {
- public:
- typedef short index_type;
- static const char *name() { return "Array"; }
-
- typedef lcdf::Json Json;
-
- inline value_array();
-
- inline kvtimestamp_t timestamp() const;
- inline int ncol() const;
- inline Str col(int i) const;
-
- void deallocate(threadinfo &ti);
- void deallocate_rcu(threadinfo &ti);
-
- value_array* update(const Json* first, const Json* last, kvtimestamp_t ts, threadinfo& ti) const;
- static value_array* create(const Json* first, const Json* last, kvtimestamp_t ts, threadinfo& ti);
- static inline value_array* create1(Str value, kvtimestamp_t ts, threadinfo& ti);
- void deallocate_rcu_after_update(const Json* first, const Json* last, threadinfo& ti);
- void deallocate_after_failed_update(const Json* first, const Json* last, threadinfo& ti);
-
- template <typename PARSER>
- static value_array* checkpoint_read(PARSER& par, kvtimestamp_t ts,
- threadinfo& ti);
- template <typename UNPARSER>
- void checkpoint_write(UNPARSER& unpar) const;
-
- void print(FILE* f, const char* prefix, int indent, Str key,
- kvtimestamp_t initial_ts, const char* suffix = "") {
- kvtimestamp_t adj_ts = timestamp_sub(ts_, initial_ts);
- fprintf(f, "%s%*s%.*s = ### @" PRIKVTSPARTS "%s\n", prefix, indent, "",
- key.len, key.s, KVTS_HIGHPART(adj_ts), KVTS_LOWPART(adj_ts), suffix);
- }
-
- static inline lcdf::inline_string* make_column(Str str, threadinfo& ti);
- static void deallocate_column(lcdf::inline_string* col, threadinfo& ti);
- static void deallocate_column_rcu(lcdf::inline_string* col, threadinfo& ti);
-
- private:
- kvtimestamp_t ts_;
- short ncol_;
- lcdf::inline_string* cols_[0];
-
- static inline size_t shallow_size(int ncol);
- inline size_t shallow_size() const;
- static value_array* make_sized_row(int ncol, kvtimestamp_t ts, threadinfo& ti);
-};
-
-inline value_array::value_array()
- : ts_(0), ncol_(0) {
-}
-
-inline kvtimestamp_t value_array::timestamp() const {
- return ts_;
-}
-
-inline int value_array::ncol() const {
- return ncol_;
-}
-
-inline Str value_array::col(int i) const {
- if (unsigned(i) < unsigned(ncol_) && cols_[i])
- return Str(cols_[i]->s, cols_[i]->len);
- else
- return Str();
-}
-
-inline size_t value_array::shallow_size(int ncol) {
- return sizeof(value_array) + sizeof(lcdf::inline_string*) * ncol;
-}
-
-inline size_t value_array::shallow_size() const {
- return shallow_size(ncol_);
-}
-
-inline lcdf::inline_string* value_array::make_column(Str str, threadinfo& ti) {
- using lcdf::inline_string;
- if (str) {
- inline_string* col = (inline_string*) ti.allocate(inline_string::size(str.length()), memtag_value);
- col->len = str.length();
- memcpy(col->s, str.data(), str.length());
- return col;
- } else
- return 0;
-}
-
-inline void value_array::deallocate_column(lcdf::inline_string* col,
- threadinfo& ti) {
- if (col)
- ti.deallocate(col, col->size(), memtag_value);
-}
-
-inline void value_array::deallocate_column_rcu(lcdf::inline_string* col,
- threadinfo& ti) {
- if (col)
- ti.deallocate_rcu(col, col->size(), memtag_value);
-}
-
-inline value_array* value_array::create(const Json* first, const Json* last,
- kvtimestamp_t ts, threadinfo& ti) {
- value_array empty;
- return empty.update(first, last, ts, ti);
-}
-
-inline value_array* value_array::create1(Str value, kvtimestamp_t ts, threadinfo& ti) {
- value_array* row = (value_array*) ti.allocate(shallow_size(1), memtag_value);
- row->ts_ = ts;
- row->ncol_ = 1;
- row->cols_[0] = make_column(value, ti);
- return row;
-}
-
-template <typename PARSER>
-value_array* value_array::checkpoint_read(PARSER& par, kvtimestamp_t ts,
- threadinfo& ti) {
- unsigned ncol;
- par.read_array_header(ncol);
- value_array* row = make_sized_row(ncol, ts, ti);
- Str col;
- for (unsigned i = 0; i != ncol; i++) {
- par >> col;
- if (col)
- row->cols_[i] = make_column(col, ti);
- }
- return row;
-}
-
-template <typename UNPARSER>
-void value_array::checkpoint_write(UNPARSER& unpar) const {
- unpar.write_array_header(ncol_);
- for (short i = 0; i != ncol_; i++)
- unpar << col(i);
-}
-
-#endif
diff --git a/value_bag.hh b/value_bag.hh
deleted file mode 100644
index 2dec89d..0000000
--- a/value_bag.hh
+++ /dev/null
@@ -1,285 +0,0 @@
-/* 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 VALUE_BAG_HH
-#define VALUE_BAG_HH
-#include "kvthread.hh"
-#include "json.hh"
-
-template <typename O>
-class value_bag {
- public:
- typedef short index_type;
- typedef O offset_type;
- typedef lcdf::Str Str;
- typedef lcdf::Json Json;
-
- private:
- union bagdata {
- struct {
- offset_type ncol_;
- offset_type pos_[1];
- };
- char s_[0];
- };
-
- public:
- static const char *name() { return "Bag"; }
-
- inline value_bag();
-
- inline kvtimestamp_t timestamp() const;
- inline size_t size() const;
- inline int ncol() const;
- inline O column_length(int i) const;
- inline Str col(int i) const;
-
- inline Str row_string() const;
-
- template <typename ALLOC> inline void deallocate(ALLOC& ti);
- template <typename ALLOC> inline void deallocate_rcu(ALLOC& ti);
-
- template <typename ALLOC>
- value_bag<O>* update(const Json* first, const Json* last, kvtimestamp_t ts,
- ALLOC& ti) const;
- template <typename ALLOC>
- inline value_bag<O>* update(int col, Str value,
- kvtimestamp_t ts, ALLOC& ti) const;
- template <typename ALLOC>
- static value_bag<O>* create(const Json* first, const Json* last,
- kvtimestamp_t ts, ALLOC& ti);
- template <typename ALLOC>
- static value_bag<O>* create1(Str value, kvtimestamp_t ts, ALLOC& ti);
- template <typename ALLOC>
- inline void deallocate_rcu_after_update(const Json* first, const Json* last, ALLOC& ti);
- template <typename ALLOC>
- inline void deallocate_after_failed_update(const Json* first, const Json* last, ALLOC& ti);
-
- template <typename PARSER, typename ALLOC>
- static value_bag<O>* checkpoint_read(PARSER& par, kvtimestamp_t ts,
- ALLOC& ti);
- template <typename UNPARSER>
- inline void checkpoint_write(UNPARSER& unpar) const;
-
- void print(FILE* f, const char* prefix, int indent, Str key,
- kvtimestamp_t initial_ts, const char* suffix = "");
-
- private:
- kvtimestamp_t ts_;
- bagdata d_;
-};
-
-
-template <typename O>
-inline value_bag<O>::value_bag()
- : ts_(0) {
- d_.ncol_ = 0;
- d_.pos_[0] = sizeof(bagdata);
-}
-
-template <typename O>
-inline kvtimestamp_t value_bag<O>::timestamp() const {
- return ts_;
-}
-
-template <typename O>
-inline size_t value_bag<O>::size() const {
- return sizeof(kvtimestamp_t) + d_.pos_[d_.ncol_];
-}
-
-template <typename O>
-inline int value_bag<O>::ncol() const {
- return d_.ncol_;
-}
-
-template <typename O>
-inline O value_bag<O>::column_length(int i) const {
- return d_.pos_[i + 1] - d_.pos_[i];
-}
-
-template <typename O>
-inline lcdf::Str value_bag<O>::col(int i) const {
- if (unsigned(i) < unsigned(d_.ncol_))
- return Str(d_.s_ + d_.pos_[i], column_length(i));
- else
- return Str();
-}
-
-template <typename O>
-inline lcdf::Str value_bag<O>::row_string() const {
- return Str(d_.s_, d_.pos_[d_.ncol_]);
-}
-
-template <typename O> template <typename ALLOC>
-inline void value_bag<O>::deallocate(ALLOC& ti) {
- ti.deallocate(this, size(), memtag_value);
-}
-
-template <typename O> template <typename ALLOC>
-inline void value_bag<O>::deallocate_rcu(ALLOC& ti) {
- ti.deallocate_rcu(this, size(), memtag_value);
-}
-
-// prerequisite: [first, last) is an array [column, value, column, value, ...]
-// each column is unsigned; the columns are strictly increasing;
-// each value is a string
-template <typename O> template <typename ALLOC>
-value_bag<O>* value_bag<O>::update(const Json* first, const Json* last,
- kvtimestamp_t ts, ALLOC& ti) const
-{
- size_t sz = size();
- unsigned ncol = d_.ncol_;
- for (auto it = first; it != last; it += 2) {
- unsigned idx = it[0].as_u();
- sz += it[1].as_s().length();
- if (idx < d_.ncol_)
- sz -= column_length(idx);
- else
- ncol = idx + 1;
- }
- if (ncol > d_.ncol_)
- sz += (ncol - d_.ncol_) * sizeof(offset_type);
-
- value_bag<O>* row = (value_bag<O>*) ti.allocate(sz, memtag_value);
- row->ts_ = ts;
-
- // Minor optimization: Replacing one small column without changing length
- if (ncol == d_.ncol_ && sz == size() && first + 2 == last
- && first[1].as_s().length() <= 16) {
- memcpy(row->d_.s_, d_.s_, sz - sizeof(kvtimestamp_t));
- memcpy(row->d_.s_ + d_.pos_[first[0].as_u()],
- first[1].as_s().data(), first[1].as_s().length());
- return row;
- }
-
- // Otherwise need to do more work
- row->d_.ncol_ = ncol;
- sz = sizeof(bagdata) + ncol * sizeof(offset_type);
- unsigned col = 0;
- while (1) {
- unsigned this_col = (first != last ? first[0].as_u() : ncol);
-
- // copy data from old row
- if (col != this_col && col < d_.ncol_) {
- unsigned end_col = std::min(this_col, unsigned(d_.ncol_));
- ssize_t delta = sz - d_.pos_[col];
- if (delta == 0)
- memcpy(row->d_.pos_ + col, d_.pos_ + col,
- sizeof(offset_type) * (end_col - col));
- else
- for (unsigned i = col; i < end_col; ++i)
- row->d_.pos_[i] = d_.pos_[i] + delta;
- size_t amt = d_.pos_[end_col] - d_.pos_[col];
- memcpy(row->d_.s_ + sz, d_.s_ + d_.pos_[col], amt);
- sz += amt;
- col = end_col;
- }
-
- // mark empty columns if we're extending
- while (col != this_col) {
- row->d_.pos_[col] = sz;
- ++col;
- }
-
- if (col == ncol)
- break;
-
- // copy data from change
- row->d_.pos_[col] = sz;
- Str val = first[1].as_s();
- memcpy(row->d_.s_ + sz, val.data(), val.length());
- sz += val.length();
- first += 2;
- ++col;
- }
- row->d_.pos_[ncol] = sz;
- return row;
-}
-
-template <typename O> template <typename ALLOC>
-inline value_bag<O>* value_bag<O>::update(int col, Str value, kvtimestamp_t ts,
- ALLOC& ti) const {
- Json change[2] = {Json(col), Json(value)};
- return update(&change[0], &change[2], ts, ti);
-}
-
-template <typename O> template <typename ALLOC>
-inline value_bag<O>* value_bag<O>::create(const Json* first, const Json* last,
- kvtimestamp_t ts, ALLOC& ti) {
- value_bag<O> empty;
- return empty.update(first, last, ts, ti);
-}
-
-template <typename O> template <typename ALLOC>
-inline value_bag<O>* value_bag<O>::create1(Str str, kvtimestamp_t ts,
- ALLOC& ti) {
- value_bag<O>* row = (value_bag<O>*) ti.allocate(sizeof(kvtimestamp_t) + sizeof(bagdata) + sizeof(O) + str.length(), memtag_value);
- row->ts_ = ts;
- row->d_.ncol_ = 1;
- row->d_.pos_[0] = sizeof(bagdata) + sizeof(O);
- row->d_.pos_[1] = sizeof(bagdata) + sizeof(O) + str.length();
- memcpy(row->d_.s_ + row->d_.pos_[0], str.data(), str.length());
- return row;
-}
-
-template <typename O> template <typename ALLOC>
-inline void value_bag<O>::deallocate_rcu_after_update(const Json*, const Json*, ALLOC& ti) {
- deallocate_rcu(ti);
-}
-
-template <typename O> template <typename ALLOC>
-inline void value_bag<O>::deallocate_after_failed_update(const Json*, const Json*, ALLOC& ti) {
- deallocate(ti);
-}
-
-template <typename O> template <typename PARSER, typename ALLOC>
-inline value_bag<O>* value_bag<O>::checkpoint_read(PARSER& par,
- kvtimestamp_t ts,
- ALLOC& ti) {
- Str value;
- par >> value;
- value_bag<O>* row = (value_bag<O>*) ti.allocate(sizeof(kvtimestamp_t) + value.length(), memtag_value);
- row->ts_ = ts;
- memcpy(row->d_.s_, value.data(), value.length());
- return row;
-}
-
-template <typename O> template <typename UNPARSER>
-inline void value_bag<O>::checkpoint_write(UNPARSER& unpar) const {
- unpar << Str(d_.s_, d_.pos_[d_.ncol_]);
-}
-
-template <typename O>
-void value_bag<O>::print(FILE *f, const char *prefix, int indent,
- Str key, kvtimestamp_t initial_ts,
- const char *suffix)
-{
- kvtimestamp_t adj_ts = timestamp_sub(ts_, initial_ts);
- if (d_.ncol_ == 1)
- fprintf(f, "%s%*s%.*s = %.*s @" PRIKVTSPARTS "%s\n", prefix, indent, "",
- key.len, key.s, d_.pos_[1] - d_.pos_[0], d_.s_ + d_.pos_[0],
- KVTS_HIGHPART(adj_ts), KVTS_LOWPART(adj_ts), suffix);
- else {
- fprintf(f, "%s%*s%.*s = [", prefix, indent, "", key.len, key.s);
- for (int col = 0; col < d_.ncol_; ++col) {
- int pos = d_.pos_[col], len = std::min(40, d_.pos_[col + 1] - pos);
- fprintf(f, col ? "|%.*s" : "%.*s", len, d_.s_ + pos);
- }
- fprintf(f, "] @" PRIKVTSPARTS "%s\n",
- KVTS_HIGHPART(adj_ts), KVTS_LOWPART(adj_ts), suffix);
- }
-}
-
-#endif
diff --git a/value_string.hh b/value_string.hh
deleted file mode 100644
index dc35c40..0000000
--- a/value_string.hh
+++ /dev/null
@@ -1,193 +0,0 @@
-/* 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 VALUE_STRING_HH
-#define VALUE_STRING_HH
-#include "compiler.hh"
-#include "json.hh"
-
-class value_string {
- public:
- typedef unsigned index_type;
- static const char *name() { return "String"; }
-
- typedef lcdf::Str Str;
- typedef lcdf::Json Json;
-
- inline value_string();
-
- inline kvtimestamp_t timestamp() const;
- inline size_t size() const;
- inline int ncol() const;
- inline Str col(index_type idx) const;
-
- template <typename ALLOC>
- inline void deallocate(ALLOC& ti);
- inline void deallocate_rcu(threadinfo& ti);
-
- template <typename ALLOC>
- value_string* update(const Json* first, const Json* last, kvtimestamp_t ts, ALLOC& ti) const;
- static inline value_string* create(const Json* first, const Json* last, kvtimestamp_t ts, threadinfo& ti);
- static inline value_string* create1(Str value, kvtimestamp_t ts, threadinfo& ti);
- inline void deallocate_rcu_after_update(const Json* first, const Json* last, threadinfo& ti);
- inline void deallocate_after_failed_update(const Json* first, const Json* last, threadinfo& ti);
-
- template <typename PARSER>
- static inline value_string* checkpoint_read(PARSER& par, kvtimestamp_t ts,
- threadinfo& ti);
- template <typename UNPARSER>
- inline void checkpoint_write(UNPARSER& unpar) const;
-
- void print(FILE* f, const char* prefix, int indent, Str key,
- kvtimestamp_t initial_ts, const char* suffix = "") {
- kvtimestamp_t adj_ts = timestamp_sub(ts_, initial_ts);
- fprintf(f, "%s%*s%.*s = %.*s @" PRIKVTSPARTS "%s\n", prefix, indent, "",
- key.len, key.s, std::min(40U, vallen_), s_,
- KVTS_HIGHPART(adj_ts), KVTS_LOWPART(adj_ts), suffix);
- }
-
- static inline index_type make_index(unsigned offset, unsigned length);
- static inline unsigned index_offset(index_type idx);
- static inline unsigned index_length(index_type idx);
-
- private:
- kvtimestamp_t ts_;
- unsigned vallen_;
- char s_[0];
-
- static inline unsigned index_last_offset(index_type idx);
- static inline size_t shallow_size(int vallen);
- inline size_t shallow_size() const;
-};
-
-inline value_string::index_type value_string::make_index(unsigned offset, unsigned length) {
- return offset + (length << 16);
-}
-
-inline unsigned value_string::index_offset(index_type idx) {
- return idx & 0xFFFF;
-}
-
-inline unsigned value_string::index_length(index_type idx) {
- return idx >> 16;
-}
-
-inline value_string::value_string()
- : ts_(0), vallen_(0) {
-}
-
-inline kvtimestamp_t value_string::timestamp() const {
- return ts_;
-}
-
-inline size_t value_string::size() const {
- return sizeof(value_string) + vallen_;
-}
-
-inline int value_string::ncol() const {
- return 1;
-}
-
-inline unsigned value_string::index_last_offset(index_type idx) {
- return index_offset(idx) + index_length(idx);
-}
-
-inline lcdf::Str value_string::col(index_type idx) const {
- if (idx == 0)
- return Str(s_, vallen_);
- else {
- unsigned off = std::min(vallen_, index_offset(idx));
- return Str(s_ + off, std::min(vallen_ - off, index_length(idx)));
- }
-}
-
-template <typename ALLOC>
-inline void value_string::deallocate(ALLOC& ti) {
- ti.deallocate(this, size(), memtag_value);
-}
-
-inline void value_string::deallocate_rcu(threadinfo& ti) {
- ti.deallocate_rcu(this, size(), memtag_value);
-}
-
-inline size_t value_string::shallow_size(int vallen) {
- return sizeof(value_string) + vallen;
-}
-
-inline size_t value_string::shallow_size() const {
- return shallow_size(vallen_);
-}
-
-template <typename ALLOC>
-value_string* value_string::update(const Json* first, const Json* last,
- kvtimestamp_t ts, ALLOC& ti) const {
- unsigned vallen = 0, cut = vallen_;
- for (auto it = first; it != last; it += 2) {
- unsigned idx = it[0].as_u(), length = it[1].as_s().length();
- if (idx == 0)
- cut = length;
- vallen = std::max(vallen, index_offset(idx) + length);
- }
- vallen = std::max(vallen, cut);
- value_string* row = (value_string*) ti.allocate(shallow_size(vallen), memtag_value);
- row->ts_ = ts;
- row->vallen_ = vallen;
- memcpy(row->s_, s_, cut);
- for (; first != last; first += 2) {
- Str val = first[1].as_s();
- memcpy(row->s_ + index_offset(first[0].as_u()), val.data(), val.length());
- }
- return row;
-}
-
-inline value_string* value_string::create(const Json* first, const Json* last,
- kvtimestamp_t ts, threadinfo& ti) {
- value_string empty;
- return empty.update(first, last, ts, ti);
-}
-
-inline value_string* value_string::create1(Str value,
- kvtimestamp_t ts,
- threadinfo& ti) {
- value_string* row = (value_string*) ti.allocate(shallow_size(value.length()), memtag_value);
- row->ts_ = ts;
- row->vallen_ = value.length();
- memcpy(row->s_, value.data(), value.length());
- return row;
-}
-
-inline void value_string::deallocate_rcu_after_update(const Json*, const Json*, threadinfo& ti) {
- deallocate_rcu(ti);
-}
-
-inline void value_string::deallocate_after_failed_update(const Json*, const Json*, threadinfo& ti) {
- deallocate(ti);
-}
-
-template <typename PARSER>
-inline value_string* value_string::checkpoint_read(PARSER& par,
- kvtimestamp_t ts,
- threadinfo& ti) {
- Str str;
- par >> str;
- return create1(str, ts, ti);
-}
-
-template <typename UNPARSER>
-inline void value_string::checkpoint_write(UNPARSER& unpar) const {
- unpar << Str(s_, vallen_);
-}
-
-#endif
diff --git a/value_versioned_array.cc b/value_versioned_array.cc
deleted file mode 100644
index dadd352..0000000
--- a/value_versioned_array.cc
+++ /dev/null
@@ -1,97 +0,0 @@
-/* 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.
- */
-#include "kvrow.hh"
-#include "value_versioned_array.hh"
-#include <string.h>
-
-value_versioned_array* value_versioned_array::make_sized_row(int ncol, kvtimestamp_t ts, threadinfo& ti) {
- value_versioned_array* row = (value_versioned_array*) ti.allocate(shallow_size(ncol), memtag_value);
- row->ts_ = ts;
- row->ver_ = rowversion();
- row->ncol_ = row->ncol_cap_ = ncol;
- memset(row->cols_, 0, sizeof(row->cols_[0]) * ncol);
- return row;
-}
-
-void value_versioned_array::snapshot(value_versioned_array*& storage,
- const std::vector<index_type>& f, threadinfo& ti) const {
- if (!storage || storage->ncol_cap_ < ncol_) {
- if (storage)
- storage->deallocate(ti);
- storage = make_sized_row(ncol_, ts_, ti);
- }
- storage->ncol_ = ncol_;
- rowversion v1 = ver_.stable();
- while (1) {
- if (f.size() == 1)
- storage->cols_[f[0]] = cols_[f[0]];
- else
- memcpy(storage->cols_, cols_, sizeof(cols_[0]) * storage->ncol_);
- rowversion v2 = ver_.stable();
- if (!v1.has_changed(v2))
- break;
- v1 = v2;
- }
-}
-
-value_versioned_array*
-value_versioned_array::update(const Json* first, const Json* last,
- kvtimestamp_t ts, threadinfo& ti,
- bool always_copy) {
- int ncol = last[-2].as_u() + 1;
- value_versioned_array* row;
- if (ncol > ncol_cap_ || always_copy) {
- row = (value_versioned_array*) ti.allocate(shallow_size(ncol), memtag_value);
- row->ts_ = ts;
- row->ver_ = rowversion();
- row->ncol_ = row->ncol_cap_ = ncol;
- memcpy(row->cols_, cols_, sizeof(cols_[0]) * ncol_);
- } else
- row = this;
- if (ncol > ncol_)
- memset(row->cols_ + ncol_, 0, sizeof(cols_[0]) * (ncol - ncol_));
-
- if (row == this) {
- ver_.setdirty();
- fence();
- }
- if (row->ncol_ < ncol)
- row->ncol_ = ncol;
-
- for (; first != last; first += 2) {
- unsigned idx = first[0].as_u();
- value_array::deallocate_column_rcu(row->cols_[idx], ti);
- row->cols_[idx] = value_array::make_column(first[1].as_s(), ti);
- }
-
- if (row == this) {
- fence();
- ver_.clearandbump();
- }
- return row;
-}
-
-void value_versioned_array::deallocate(threadinfo &ti) {
- for (short i = 0; i < ncol_; ++i)
- value_array::deallocate_column(cols_[i], ti);
- ti.deallocate(this, shallow_size(), memtag_value);
-}
-
-void value_versioned_array::deallocate_rcu(threadinfo &ti) {
- for (short i = 0; i < ncol_; ++i)
- value_array::deallocate_column_rcu(cols_[i], ti);
- ti.deallocate_rcu(this, shallow_size(), memtag_value);
-}
diff --git a/value_versioned_array.hh b/value_versioned_array.hh
deleted file mode 100644
index 9f7a3b9..0000000
--- a/value_versioned_array.hh
+++ /dev/null
@@ -1,205 +0,0 @@
-/* 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 VALUE_VERSIONED_ARRAY_HH
-#define VALUE_VERSIONED_ARRAY_HH
-#include "compiler.hh"
-#include "value_array.hh"
-
-struct rowversion {
- rowversion() {
- v_.u = 0;
- }
- bool dirty() {
- return v_.dirty;
- }
- void setdirty() {
- v_.u = v_.u | 0x80000000;
- }
- void clear() {
- v_.u = v_.u & 0x7fffffff;
- }
- void clearandbump() {
- v_.u = (v_.u + 1) & 0x7fffffff;
- }
- rowversion stable() const {
- value_t x = v_;
- while (x.dirty) {
- relax_fence();
- x = v_;
- }
- acquire_fence();
- return x;
- }
- bool has_changed(rowversion x) const {
- fence();
- return x.v_.ctr != v_.ctr;
- }
- private:
- union value_t {
- struct {
- uint32_t ctr:31;
- uint32_t dirty:1;
- };
- uint32_t u;
- };
- value_t v_;
-
- rowversion(value_t v)
- : v_(v) {
- }
-
-};
-
-class value_versioned_array {
- public:
- typedef value_array::index_type index_type;
- static const char *name() { return "ArrayVersion"; }
-
- typedef lcdf::Json Json;
-
- inline value_versioned_array();
-
- inline kvtimestamp_t timestamp() const;
- inline int ncol() const;
- inline Str col(int i) const;
-
- void deallocate(threadinfo &ti);
- void deallocate_rcu(threadinfo &ti);
-
- void snapshot(value_versioned_array*& storage,
- const std::vector<index_type>& f, threadinfo& ti) const;
-
- value_versioned_array* update(const Json* first, const Json* last,
- kvtimestamp_t ts, threadinfo& ti,
- bool always_copy = false);
- static value_versioned_array* create(const Json* first, const Json* last,
- kvtimestamp_t ts, threadinfo& ti);
- static value_versioned_array* create1(Str value, kvtimestamp_t ts, threadinfo& ti);
- inline void deallocate_rcu_after_update(const Json* first, const Json* last, threadinfo& ti);
- inline void deallocate_after_failed_update(const Json* first, const Json* last, threadinfo& ti);
-
- template <typename PARSER>
- static value_versioned_array* checkpoint_read(PARSER& par, kvtimestamp_t ts,
- threadinfo& ti);
- template <typename UNPARSER>
- void checkpoint_write(UNPARSER& unpar) const;
-
- void print(FILE *f, const char *prefix, int indent, Str key,
- kvtimestamp_t initial_ts, const char *suffix = "") {
- kvtimestamp_t adj_ts = timestamp_sub(ts_, initial_ts);
- fprintf(f, "%s%*s%.*s = ### @" PRIKVTSPARTS "%s\n", prefix, indent, "",
- key.len, key.s, KVTS_HIGHPART(adj_ts), KVTS_LOWPART(adj_ts), suffix);
- }
-
- private:
- kvtimestamp_t ts_;
- rowversion ver_;
- short ncol_;
- short ncol_cap_;
- lcdf::inline_string* cols_[0];
-
- static inline size_t shallow_size(int ncol);
- inline size_t shallow_size() const;
- static value_versioned_array* make_sized_row(int ncol, kvtimestamp_t ts, threadinfo& ti);
-};
-
-template <>
-struct query_helper<value_versioned_array> {
- value_versioned_array* snapshot_;
-
- query_helper()
- : snapshot_() {
- }
- inline const value_versioned_array* snapshot(const value_versioned_array* row,
- const std::vector<value_versioned_array::index_type>& f,
- threadinfo& ti) {
- row->snapshot(snapshot_, f, ti);
- return snapshot_;
- }
-};
-
-inline value_versioned_array::value_versioned_array()
- : ts_(0), ncol_(0), ncol_cap_(0) {
-}
-
-inline kvtimestamp_t value_versioned_array::timestamp() const {
- return ts_;
-}
-
-inline int value_versioned_array::ncol() const {
- return ncol_;
-}
-
-inline Str value_versioned_array::col(int i) const {
- if (unsigned(i) < unsigned(ncol_) && cols_[i])
- return Str(cols_[i]->s, cols_[i]->len);
- else
- return Str();
-}
-
-inline size_t value_versioned_array::shallow_size(int ncol) {
- return sizeof(value_versioned_array) + ncol * sizeof(lcdf::inline_string*);
-}
-
-inline size_t value_versioned_array::shallow_size() const {
- return shallow_size(ncol_);
-}
-
-inline value_versioned_array* value_versioned_array::create(const Json* first, const Json* last, kvtimestamp_t ts, threadinfo& ti) {
- value_versioned_array empty;
- return empty.update(first, last, ts, ti, true);
-}
-
-inline value_versioned_array* value_versioned_array::create1(Str value, kvtimestamp_t ts, threadinfo& ti) {
- value_versioned_array* row = (value_versioned_array*) ti.allocate(shallow_size(1), memtag_value);
- row->ts_ = ts;
- row->ver_ = rowversion();
- row->ncol_ = row->ncol_cap_ = 1;
- row->cols_[0] = value_array::make_column(value, ti);
- return row;
-}
-
-inline void value_versioned_array::deallocate_rcu_after_update(const Json*, const Json*, threadinfo& ti) {
- ti.deallocate_rcu(this, shallow_size(), memtag_value);
-}
-
-inline void value_versioned_array::deallocate_after_failed_update(const Json*, const Json*, threadinfo&) {
- always_assert(0);
-}
-
-template <typename PARSER>
-value_versioned_array*
-value_versioned_array::checkpoint_read(PARSER& par, kvtimestamp_t ts,
- threadinfo& ti) {
- unsigned ncol;
- par.read_array_header(ncol);
- value_versioned_array* row = make_sized_row(ncol, ts, ti);
- Str col;
- for (unsigned i = 0; i != ncol; i++) {
- par >> col;
- row->cols_[i] = value_array::make_column(col, ti);
- }
- return row;
-}
-
-template <typename UNPARSER>
-void value_versioned_array::checkpoint_write(UNPARSER& unpar) const {
- unpar.write_array_header(ncol_);
- for (short i = 0; i != ncol_; ++i)
- unpar << col(i);
-}
-
-#endif
--
2.23.0