From c4f1e38566d811d5a4de1c6ac30ed2175cc343cf Mon Sep 17 00:00:00 2001 From: zhengshaoyu 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 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 - void visit_leaf(const SS&, const K&, threadinfo&) { - } - bool visit_value(Str key, const row_type* value, threadinfo& ti); - - template - static void insert(T& table, msgpack::parser& par, threadinfo& ti); -}; - -template -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 -#include - -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 -#define __STDC_FORMAT_MACROS -#include #include #if HAVE_TYPE_TRAITS #include @@ -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 struct sized_compiler_operations; template 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 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 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 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 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 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 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 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 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 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(reinterpret_cast(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 inline int compare(T a, T b) { if (a == b) @@ -996,24 +1030,6 @@ template struct type_synonym { typedef T type; }; - -#if HAVE_CXX_TEMPLATE_ALIAS && HAVE_TYPE_TRAITS -template -using integral_constant = std::integral_constant; -typedef std::true_type true_type; -typedef std::false_type false_type; -#else -template -struct integral_constant { - typedef integral_constant type; - typedef T value_type; - static constexpr T value = V; -}; -template constexpr T integral_constant::value; -typedef integral_constant true_type; -typedef integral_constant false_type; -#endif - #if HAVE_CXX_TEMPLATE_ALIAS && HAVE_TYPE_TRAITS template using conditional = std::conditional; @@ -1158,36 +1174,6 @@ template constexpr bool fast_argument::is_reference; } - -template -struct has_fast_int_multiply : public mass::false_type { - // enum { check_t_integral = mass::integer_traits::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 : 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 : 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 : 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 - int64_t f(int64_t* x) { return __sync_fetch_and_add(x, (int64_t) 2); }]]) - -KVDB_CHECK_BUILTIN([__sync_add_and_fetch_8], - [[#include - 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 - int64_t f(int64_t* x) { return __sync_fetch_and_or(x, (int64_t) 2); }]]) - -KVDB_CHECK_BUILTIN([__sync_or_and_fetch_8], - [[#include - 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 - 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 - 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 struct X { typedef T type; }; template using Y = X; int f(int x) { return x; }]], [[return f(Y::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 -#include ], - [[std::hash 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 ], [[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 ], [[return std::is_trivially_copyable::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 ], [[return std::is_trivially_destructible::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 ], [[return std::is_rvalue_reference::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 ]) -KVDB_CHECK_SAME_TYPE([off_t], [long long], [#include ]) -KVDB_CHECK_SAME_TYPE([int64_t], [long], [#include ]) -KVDB_CHECK_SAME_TYPE([int64_t], [long long], [#include ]) -KVDB_CHECK_SAME_TYPE([size_t], [unsigned], [#include ]) -KVDB_CHECK_SAME_TYPE([size_t], [unsigned long], [#include ]) -KVDB_CHECK_SAME_TYPE([size_t], [unsigned long long], [#include ]) - -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 -#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 -#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 -#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 \(tt\) 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 - -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 -#include -#include -#include -#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(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(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 inline void -checked_write(int fd, const T *x) -{ - checked_write(fd, reinterpret_cast(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 -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: - - - 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}"); - - - Compare this with the Javascript code: - - - var j1 = {}, j2 = {}; - j1.a = j2; // stores a REFERENCE to j2 in j1 - j2.b = 1; - assert(JSON.stringify(j1) == "{\"a\":{\"b\":1}}"); - - - 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 as_ and at functions, rather than - the liberal to_, get, and operator[] - 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(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(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(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(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 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(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(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(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(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(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(¤t()->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 -#include -#include -namespace lcdf { - -template class Json_proxy_base; -template class Json_object_proxy; -template class Json_object_str_proxy; -template class Json_array_proxy; -class Json_get_proxy; - -template -struct Json_rep_item; -template struct Json_rep_item { - T x; - int type; -}; -template struct Json_rep_item { - 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 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 inline Json(const Json_proxy_base

& 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 inline Json(const std::vector& x); - template 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 - static inline Json array(Args&&... rest); - static inline Json make_object(); - template - 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 operator[](const String& key); - inline Json_object_str_proxy operator[](const std::string& key); - inline Json_object_str_proxy operator[](Str key); - inline Json_object_str_proxy 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 - inline Json& set(const String& key, const Json_proxy_base

& value); - inline Json& unset(Str key); - - inline Json& set_list(); - template - inline Json& set_list(const String& key, T value, Args&&... rest); - - inline std::pair 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 inline Json& merge(const Json_proxy_base

& 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 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 inline Json& push_back(const Json_proxy_base

& x); - inline void pop_back(); - - inline Json& push_back_list(); - template - inline Json& push_back_list(T first, Args&&... rest); - - inline array_iterator insert(array_iterator position, Json x); - template - inline array_iterator insert(array_iterator position, const Json_proxy_base

& 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 inline Json& operator=(const Json_proxy_base

& 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 i; - Json_rep_item u; - Json_rep_item d; - String::rep_type str; - Json_rep_item a; - Json_rep_item o; - Json_rep_item 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 inline Json& add(T x); - inline Json& subtract(double x); - template 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 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 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(this); - else - ArrayJson::destroy(static_cast(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 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(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(j_)->uniqueify_array(false, 0); - return j_->ajson()->a[i_]; - } - Json& operator[](difference_type i) const { - const_cast(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 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(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(j_)->hard_uniqueify_object(false); - else - const_cast(j_)->hard_uniqueify_array(false, 0); - const_cast(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 -class Json_proxy_base { - public: - const Json& cvalue() const { - return static_cast(this)->cvalue(); - } - Json& value() { - return static_cast

(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

operator[](const String& key) { - return Json_object_proxy

(*static_cast(this), key); - } - Json_object_str_proxy

operator[](std::string key) { - return Json_object_str_proxy

(*static_cast(this), Str(key.data(), key.length())); - } - Json_object_str_proxy

operator[](Str key) { - return Json_object_str_proxy

(*static_cast(this), key); - } - Json_object_str_proxy

operator[](const char* key) { - return Json_object_str_proxy

(*static_cast(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 - inline Json& set(const String& key, const Json_proxy_base& value) { - return this->value().set(key, value); - } - Json& unset(Str key) { - return value().unset(key); - } - template - inline Json& set_list(Args&&... args) { - return value().set_list(std::forward(args)...); - } - std::pair 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 Json& merge(const Json_proxy_base& 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

operator[](int key) { - return Json_array_proxy

(*static_cast(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 Json& push_back(const Json_proxy_base& x) { - return value().push_back(x); - } - void pop_back() { - value().pop_back(); - } - template Json& push_back_list(Args&&... args) { - return value().push_back_list(std::forward(args)...); - } - Json::array_iterator insert(Json::array_iterator position, Json x) { - return value().insert(position, std::move(x)); - } - template Json::array_iterator insert(Json::array_iterator position, const Json_proxy_base& 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 -class Json_object_proxy : public Json_proxy_base > { - 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& x) { - return value() = x.cvalue(); - } - template Json& operator=(const Json_proxy_base

& x) { - return value() = x.cvalue(); - } - Json_object_proxy(T& ref, const String& key) - : base_(ref), key_(key) { - } - T &base_; - String key_; -}; - -template -class Json_object_str_proxy : public Json_proxy_base > { - 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& x) { - return value() = x.cvalue(); - } - template Json& operator=(const Json_proxy_base

& x) { - return value() = x.cvalue(); - } - Json_object_str_proxy(T& ref, Str key) - : base_(ref), key_(key) { - } - T &base_; - Str key_; -}; - -template -class Json_array_proxy : public Json_proxy_base > { - 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& x) { - return value() = x.cvalue(); - } - template Json& operator=(const Json_proxy_base

& 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 { - 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 -inline const Json_get_proxy Json_proxy_base::get(Str key, Json& x) const { - return cvalue().get(key, x); -} -template -inline const Json_get_proxy Json_proxy_base::get(Str key, int& x) const { - return cvalue().get(key, x); -} -template -inline const Json_get_proxy Json_proxy_base::get(Str key, unsigned& x) const { - return cvalue().get(key, x); -} -template -inline const Json_get_proxy Json_proxy_base::get(Str key, long& x) const { - return cvalue().get(key, x); -} -template -inline const Json_get_proxy Json_proxy_base::get(Str key, unsigned long& x) const { - return cvalue().get(key, x); -} -template -inline const Json_get_proxy Json_proxy_base::get(Str key, long long& x) const { - return cvalue().get(key, x); -} -template -inline const Json_get_proxy Json_proxy_base::get(Str key, unsigned long long& x) const { - return cvalue().get(key, x); -} -template -inline const Json_get_proxy Json_proxy_base::get(Str key, double& x) const { - return cvalue().get(key, x); -} -template -inline const Json_get_proxy Json_proxy_base::get(Str key, bool& x) const { - return cvalue().get(key, x); -} -template -inline const Json_get_proxy Json_proxy_base::get(Str key, Str& x) const { - return cvalue().get(key, x); -} -template -inline const Json_get_proxy Json_proxy_base::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 inline Json::Json(const Json_proxy_base

& 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 -inline Json::Json(const std::vector &x) { - u_.a.type = j_array; - u_.a.x = ArrayJson::make(int(x.size())); - for (typename std::vector::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 struct Json_iterator_initializer { - template - static inline void run(Json& j, I first, I last) { - for (j = Json::make_array(); first != last; ++first) - j.push_back(Json(*first)); - } -}; -template -struct Json_iterator_initializer > { - template - 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 -inline Json::Json(T first, T last) { - u_.a.type = 0; - Json_iterator_initializer::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 -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)...); - 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 -inline Json Json::object(Args&&... rest) { - Json j; - j.u_.o.type = j_object; - j.set_list(std::forward(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(u_.str); -} - -/** @overload */ -inline String& Json::as_s() { - precondition(u_.x.type <= 0 && u_.x.x); - return reinterpret_cast(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 i, then returns get(i). 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: - - - 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); - - - 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: - - - 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); - */ -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::operator[](const String& key) { - return Json_object_proxy(*this, key); -} - -/** @overload */ -inline Json_object_str_proxy Json::operator[](const std::string& key) { - return Json_object_str_proxy(*this, Str(key.data(), key.length())); -} - -/** @overload */ -inline Json_object_str_proxy Json::operator[](Str key) { - return Json_object_str_proxy(*this, key); -} - -/** @overload */ -inline Json_object_str_proxy Json::operator[](const char* key) { - return Json_object_str_proxy(*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 -inline Json& Json::set(const String& key, const Json_proxy_base

& 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 -inline Json& Json::set_list(const String& key, T value, Args&&... rest) { - set(key, std::move(value)); - set_list(std::forward(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::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 -inline Json& Json::merge(const Json_proxy_base& 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::operator[](size_type x) { - return Json_array_proxy(*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 inline Json& Json::push_back(const Json_proxy_base

& 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 -inline Json& Json::push_back_list(T first, Args&&... rest) { - push_back(std::move(first)); - push_back_list(std::forward(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 inline Json::array_iterator Json::insert(array_iterator position, const Json_proxy_base

& 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 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(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 - (consume(reinterpret_cast(first), - reinterpret_cast(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 -inline Json& Json::operator=(const Json_proxy_base &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 -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 -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 -inline StringAccum &operator<<(StringAccum &sa, const Json_proxy_base

&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 -inline std::ostream &operator<<(std::ostream& f, const Json_proxy_base

& json) { - return (f << json.cvalue()); -} - -bool operator==(const Json& a, const Json& b); - -template -inline bool operator==(const Json_proxy_base& a, const Json& b) { - return a.cvalue() == b; -} - -template -inline bool operator==(const Json& a, const Json_proxy_base& b) { - return a == b.cvalue(); -} - -template -inline bool operator==(const Json_proxy_base& a, - const Json_proxy_base& b) { - return a.cvalue() == b.cvalue(); -} - -inline bool operator!=(const Json& a, const Json& b) { - return !(a == b); -} - -template -inline bool operator!=(const Json_proxy_base& a, const Json& b) { - return !(a == b); -} - -template -inline bool operator!=(const Json& a, const Json_proxy_base& b) { - return !(a == b); -} - -template -inline bool operator!=(const Json_proxy_base& a, - const Json_proxy_base& 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 -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 void incr(T& x) __attribute__((noinline)); -template -void incr(T& x) { - ++x; -} -#endif - -void benchmark_parse() { - std::vector 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("\"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("\"\\\"\\\\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("\"\\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("\"\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("{}"); - r = jsp.consume(x, x + 2, String()); - CHECK(r == x + 2); - CHECK(jsp.success()); - CHECK(jsp.result().unparse() == "{}"); - - jsp.reset(); - x = reinterpret_cast("\"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("{\"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("[]"); - r = jsp.consume(x, x + 2, String()); - CHECK(r == x + 2); - CHECK(jsp.success()); - CHECK(jsp.result().unparse() == "[]"); - - jsp.reset(); - x = reinterpret_cast("[\"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("[[\"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("[[],[[]]]"); - r = jsp.consume(x, x + 9, String()); - CHECK(r == x + 9); - CHECK(jsp.success()); - CHECK(jsp.result().unparse() == "[[],[[]]]"); - - jsp.reset(); - x = reinterpret_cast("{\"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("{\"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("{\"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("{\"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("{\"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("{\"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("[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 v = {1, 2, 3, 4, 5}; - Json j(v.begin(), v.end()); - CHECK(j.unparse() == "[1,2,3,4,5]"); - } - - { - std::unordered_map 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 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 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()); } +// Binary search template 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()); } +/* 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 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 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 -#include -#include -#include -#include -#include -#include -#include -#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 -#include -#include -#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 -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 - -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 -#include -#include - -// 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 -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 - 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 - -#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 row_type; -#endif - -template -struct query_helper { - inline const R* snapshot(const R* row, const std::vector&, threadinfo&) { - return row; - } -}; - -template class query_json_scanner; - -template -class query { - public: - typedef lcdf::Json Json; - - template - void run_get(T& table, Json& req, threadinfo& ti); - template - bool run_get1(T& table, Str key, int col, Str& value, threadinfo& ti); - - template - result_t run_put(T& table, Str key, - const Json* firstreq, const Json* lastreq, threadinfo& ti); - template - result_t run_replace(T& table, Str key, Str value, threadinfo& ti); - template - bool run_remove(T& table, Str key, threadinfo& ti); - - template - void run_scan(T& table, Json& request, threadinfo& ti); - template - void run_scan_versions(T& table, Json& request, std::vector& scan_versions, threadinfo& ti); - template - void run_rscan(T& table, Json& request, threadinfo& ti); - - const loginfo::query_times& query_times() const { - return qtimes_; - } - - private: - std::vector f_; - loginfo::query_times qtimes_; - query_helper 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 friend class query_json_scanner; -}; - - -template -void query::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 -void query::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 template -void query::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 template -bool query::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 -inline void query::assign_timestamp(threadinfo& ti) { - qtimes_.ts = ti.update_timestamp(); - qtimes_.prev_ts = 0; -} - -template -inline void query::assign_timestamp(threadinfo& ti, kvtimestamp_t min_ts) { - qtimes_.ts = ti.update_timestamp(min_ts); - qtimes_.prev_ts = min_ts; -} - - -template template -result_t query::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 -inline bool query::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 template -result_t query::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 -inline bool query::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 template -bool query::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 -inline void query::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::less_equal(node_ts, qtimes_.ts)) { - node_ts = qtimes_.ts + 2; - } - old_value->deallocate_rcu(ti); -} - - -template -class query_json_scanner { - public: - query_json_scanner(query& q, lcdf::Json& request, std::vector* 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 - void visit_leaf(const SS& scanstack, const K&, threadinfo&) { - if (scan_versions_) { - scan_versions_->push_back(reinterpret_cast(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(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& q_; - int nleft_; - lcdf::Json& request_; - lcdf::String firstkey_; - std::vector* scan_versions_; -}; - -template template -void query::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 scanf(*this, request, nullptr); - table.scan(scanf.firstkey(), true, scanf, ti); -} - -template template -void query::run_scan_versions(T& table, Json& request, - std::vector& 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 scanf(*this, request, &scan_versions); - table.scan(scanf.firstkey(), true, scanf, ti); -} - -template template -void query::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 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 - -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 -#include -#include - -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 -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 -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 -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 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 -void kvtest_sync_rw1(C &client) -{ - kvtest_sync_rw1_seed(client, kvtest_first_seed + client.id() % 48); -} - -template -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 -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 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 -void kvtest_rw1puts(C &client) -{ - kvtest_rw1puts_seed(client, kvtest_first_seed + client.id() % 48); -} - -template -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 -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 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 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 -void kvtest_rw1long(C &client) -{ - kvtest_rw1long_seed(client, kvtest_first_seed + client.id() % 48); -} - -// interleave inserts and gets for random keys. -template -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 -void kvtest_rw2(C &client) -{ - kvtest_rw2_seed(client, kvtest_first_seed + client.id() % 48, 0.5); -} - -template -void kvtest_rw2g90(C &client) -{ - kvtest_rw2_seed(client, kvtest_first_seed + client.id() % 48, 0.9); -} - -template -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 -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 -void kvtest_rw2fixed(C &client) -{ - kvtest_rw2fixed_seed(client, kvtest_first_seed + client.id() % 48, 0.5); -} - -template -void kvtest_rw2fixedg90(C &client) -{ - kvtest_rw2fixed_seed(client, kvtest_first_seed + client.id() % 48, 0.9); -} - -template -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 -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 -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 -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 -void kvtest_same_seed(C &client, int seed) -{ - client.rand.seed(seed); - - double t0 = client.now(); - unsigned n; - kvrandom_uniform_int_distribution 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 -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 -void kvtest_rwsmall_seed(C &client, int nkeys, int seed) -{ - client.rand.seed(seed); - - double t0 = client.now(); - unsigned n; - kvrandom_uniform_int_distribution 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 -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 -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 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 -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 -void kvtest_rw1fixed_seed(C &client, int seed) -{ - client.rand.seed(seed); - double tp0 = client.now(); - unsigned n; - kvrandom_uniform_int_distribution 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 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 -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 -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 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 -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 -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 -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 -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 -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 -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 -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 ntd(1, client.nthreads() - 1); - std::vector 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(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 -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 -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 -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 -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 -void kvtest_palmb(C &client) -{ - kvtest_palmb_seed(client, kvtest_first_seed + client.id() % 48); -} - -template -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 -void kvtest_ycsbk(C &client) -{ - kvtest_ycsbk_seed(client, kvtest_first_seed + client.id() % 48); -} - -template -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 -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 -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 -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 -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 -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 -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 -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 -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 -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 -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 -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 -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 -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 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 -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 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 -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 -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 -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 -#include -#include -#include -#include -#if HAVE_SUPERPAGE && !NOSUPERPAGE -#include -#include -#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(pool); - void** nextptr = reinterpret_cast(p); - for (size_t off = unit; off + unit <= sz; off += unit) { - *nextptr = p + off; - nextptr = reinterpret_cast(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(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 +#include +#include +#include +#include +#if HAVE_SUPERPAGE && !NOSUPERPAGE +#include +#include +#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 #include #include +#include + +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

) + 128, 64) +#define MAX_MEMTAG_MASSTREE_INTERNODE_ALLOCATION_SIZE sizeof(internode

) +#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

::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 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(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 (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(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 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 -#include -#include -#include -#include -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(buf); - lr->command_ = command; - lr->size_ = sizeof(*lr); - return sizeof(*lr); - } - static bool check(const char *buf) { - const logrec_base *lr = reinterpret_cast(buf); - return lr->size_ >= sizeof(*lr); - } - static uint32_t command(const char *buf) { - const logrec_base *lr = reinterpret_cast(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(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(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(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(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(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(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(ls_pos) % CACHE_LINE_SIZE; - if (left) - ls_pos += CACHE_LINE_SIZE - left; - logset* ls = reinterpret_cast(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(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(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 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 - void run(T& table, std::vector& jrepo, threadinfo& ti); - - private: - inline void apply(row_type*& value, bool found, - std::vector& jrepo, threadinfo& ti); -}; - -const char * -logrecord::extract(const char *buf, const char *end) -{ - const logrec_base *lr = reinterpret_cast(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(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(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(buf); - if (unlikely(lre->size_ < logrec_epoch::size())) - goto fail; - epoch = lre->epoch_; - } - - return buf + lr->size_; -} - -template -void logrecord::run(T& table, std::vector& 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& 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& 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); - val.len += sizeof(row_delta_marker); - row_type* new_value = row_type::create1(val, ts | 1, ti); - row_delta_marker* 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); - req.len -= sizeof(row_delta_marker); - 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(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(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(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(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 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 -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 -struct row_delta_marker : public row_marker { - kvtimestamp_t prev_ts_; - R *prev_; - char s_[0]; -}; - -template -inline bool row_is_delta_marker(const R* row) { - if (row_is_marker(row)) { - const row_marker* m = - reinterpret_cast(row->col(0).s); - return m->marker_type_ == m->mt_delta; - } else - return false; -} - -template -inline row_delta_marker* row_get_delta_marker(const R* row, bool force = false) { - (void) force; - assert(force || row_is_delta_marker(row)); - return reinterpret_cast*> - (const_cast(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 class value_print; @@ -31,11 +33,11 @@ template 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 class basic_table; template class unlocked_tcursor; template class tcursor; +template class MasstreeIterator; + template class basic_table { public: @@ -64,6 +68,18 @@ class basic_table { typedef typename P::threadinfo_type threadinfo; typedef unlocked_tcursor

unlocked_cursor_type; typedef tcursor

cursor_type; + typedef MasstreeIterator ForwardIterator; + typedef MasstreeIterator 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 + int scan(H helper, void const *const &firstKey, unsigned int firstKeyLen, + bool matchFirstKey, F &scanner, threadinfo &ti) const; template int scan(H helper, Str firstkey, bool matchfirst, @@ -91,6 +115,13 @@ class basic_table { friend class unlocked_tcursor

; friend class tcursor

; + + friend class MasstreeIterator; + friend class MasstreeIterator; + friend class MasstreeIterator; + friend class MasstreeIterator; + + 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 @@ -86,29 +88,39 @@ bool tcursor

::find_locked(threadinfo& ti) fence(); kx_ = leaf

::bound_type::lower(ka_, *n_); if (kx_.p >= 0) { + // Key slice (ikey) was found and it is stored in kx_.i leafvalue

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*>(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 -bool tcursor

::find_insert(threadinfo& ti) +bool tcursor

::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

::find_insert(threadinfo& ti) n_->modstate_ = leaf

::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

::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

::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

::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

::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

::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 -#include - -namespace Masstree { - -class key_unparse_printable_string { -public: - template - static int unparse_key(key 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 -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 { - 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 { - 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 -void leaf

::print(FILE *f, const char *prefix, int depth, int kdepth) const -{ - f = f ? f : stderr; - prefix = prefix ? prefix : ""; - typename node_base

::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

*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 -void internode

::print(FILE* f, const char* prefix, int depth, int kdepth) const -{ - f = f ? f : stderr; - prefix = prefix ? prefix : ""; - internode

copy(*this); - for (int i = 0; i < 100 && (copy.has_changed(*this) || this->inserting() || this->splitting()); ++i) - memcpy(©, 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 -void basic_table

::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

* root_; int len_; char s_[0]; - gc_layer_rcu_callback(node_base

* root, Str prefix) + gc_layer_rcu_callback(node_base

* 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

::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 @@ -171,18 +173,20 @@ bool tcursor

::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 -bool tcursor

::remove_leaf(leaf_type* leaf, node_type* root, +bool tcursor

::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

::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

::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

::remove_leaf(leaf_type* leaf, node_type* root, // Unlink leaf from doubly-linked leaf list btree_leaflink::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

::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

::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

::internode_type internode_type; node_base

* root_; int count_; - destroy_rcu_callback(node_base

* root) - : root_(root), count_(0) { + destroy_value_cb_func destroyValueCB_ = nullptr; + + destroy_rcu_callback(node_base

* root, destroy_value_cb_func func) + : root_(root), count_(0), destroyValueCB_(func) { } void operator()(threadinfo& ti); static void make(node_base

* root, Str prefix, threadinfo& ti); @@ -338,8 +354,12 @@ void destroy_rcu_callback

::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 void basic_table

::destroy(threadinfo& ti) { if (root_) { void* data = ti.allocate(sizeof(destroy_rcu_callback

), memtag_masstree_gc); - destroy_rcu_callback

* cb = new(data) destroy_rcu_callback

(root_); + destroy_rcu_callback

* cb = new(data) destroy_rcu_callback

(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 - 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 int find_retry(H& helper, key_type& ka, threadinfo& ti); template 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 friend class basic_table; + template friend class MasstreeIterator; }; struct forward_scan_helper { @@ -85,6 +88,7 @@ struct forward_scan_helper { template 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 int lower(const K &k, const N *n) const { @@ -129,17 +133,22 @@ struct reverse_scan_helper { template 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 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 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 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 template -int scanstackelt

::find_initial(H& helper, key_type& ka, bool emit_equal, +int scanstackelt

::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

::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

::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

::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

::find_next(H &helper, key_type &ka, leafvalue_type &entry) template template int basic_table

::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

::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

mystack_type; mystack_type stack; @@ -330,10 +383,13 @@ int basic_table

::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

::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

::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*>(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

::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 int leaf

::split_into(leaf

* nr, tcursor

* cursor, ikey_type& split_ikey, threadinfo& ti) @@ -71,7 +71,8 @@ int leaf

::split_into(leaf

* nr, tcursor

* 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

::split_into(leaf

* nr, tcursor

* 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

::split_into(internode

* nr, int p, ikey_type ka, } } +template +void tcursor

::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 bool tcursor

::make_split(threadinfo& ti) @@ -189,16 +205,52 @@ bool tcursor

::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(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

::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

::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

::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

::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 -void node_json_stats(node_base

* n, lcdf::Json& j, int layer, int depth, - TI& ti) -{ - if (!n) - return; - else if (n->isleaf()) { - leaf

* lf = static_cast*>(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

::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

*in = static_cast *>(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 -void json_stats(lcdf::Json& j, basic_table

& 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 -lcdf::Json json_stats(basic_table

& 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

{ typedef typename key_bound::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

* child_[width + 1]; node_base

* parent_; kvtimestamp_t created_at_[P::debug_level > 0]; @@ -117,9 +120,15 @@ class internode : public node_base

{ : node_base

(false), nkeys_(0), height_(height), parent_() { } - static internode

* make(uint32_t height, threadinfo& ti) { - void* ptr = ti.pool_allocate(sizeof(internode

), + static internode

* 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

* n = new(ptr) internode

(height); assert(n); if (P::debug_level > 0) @@ -264,19 +273,38 @@ class leaf : public node_base

{ 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

* ptr; uintptr_t x; } next_; + // Pointer to the previous leaf in the same layer leaf

* prev_; + + // Leaf's parent (might be null if leaf is the root of the btree (trie node) node_base

* 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

{ } static leaf

* make(int ksufsize, phantom_epoch_type phantom_epoch, threadinfo& ti) { - size_t sz = iceil(sizeof(leaf

) + std::min(ksufsize, 128), 64); + size_t sz = MAX_MEMTAG_MASSTREE_LEAF_ALLOCATION_SIZE; // iceil(sizeof(leaf

) + 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

* n = new(ptr) leaf

(sz, phantom_epoch); assert(n); if (P::debug_level > 0) { @@ -308,6 +340,10 @@ class leaf : public node_base

{ } static leaf

* make_root(int ksufsize, leaf

* parent, threadinfo& ti) { leaf

* 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

{ } 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

{ return s.len == ka.suffix().len && string_slice::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::equals_sloppy(s.s, ka.suffix().s, s.len); @@ -494,40 +536,55 @@ class leaf : public node_base

{ 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

* x, int xp, threadinfo& ti) { + inline bool assign_initialize(int p, leaf

* 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

* cursor) const; @@ -606,6 +663,7 @@ leaf

::stable_last_key_compare(const key_type& k, nodeversion_type v, while (true) { typename leaf

::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

::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

* node_base

::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

* node_base

::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

*in = static_cast*>(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

::bound_type::upper(ka, *in); n[sense ^ 1] = in->child_[kp]; if (!n[sense ^ 1]) { @@ -669,8 +735,11 @@ inline leaf

* node_base

::reach_leaf(const key_type& ka, continue; } + // Node's version was changed. wait until it is stable again (aka not dirty) typename node_base

::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

* leaf

::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 -void leaf

::assign_ksuf(int p, Str s, bool initializing, threadinfo& ti) { +bool leaf

::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

::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

::assign_ksuf(int p, Str s, bool initializing, threadinfo& ti) { if (oksuf) ti.deallocate_rcu(oksuf, oksuf->capacity(), memtag_masstree_ksuffixes); + return true; } template inline basic_table

::basic_table() - : root_(0) { + : root_(nullptr) { } template 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 struct gc_layer_rcu_callback; +template struct gc_layer_rcu_callback_ng; template 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, new_nodes_size> new_nodes_type; +#ifndef MASSTREE_OBSOLETE_CODE tcursor(basic_table

& table, Str str) : ka_(str), root_(table.fix_root()) { } tcursor(basic_table

& table, const char* s, int len) : ka_(s, len), root_(table.fix_root()) { } - tcursor(basic_table

& table, const unsigned char* s, int len) - : ka_(reinterpret_cast(s), len), root_(table.fix_root()) { - } - tcursor(node_base

* root, const char* s, int len) - : ka_(s, len), root_(root) { - } tcursor(node_base

* root, const unsigned char* s, int len) : ka_(reinterpret_cast(s), len), root_(root) { } - +#endif + tcursor(basic_table

& table, const unsigned char* s, int len) + : ka_(reinterpret_cast(s), len), root_(table.fix_root()), root_ref_(table.root_ref()) { + } + tcursor(node_base

** 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

* root_; + node_base

** 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

; 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

; + friend struct gc_layer_rcu_callback_ng

; }; template 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 -#include - -#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(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 -#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 -#include -#include -#include -#include -#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(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(first - 4); - break; - case format::ffloat64 - format::fnull: - *jx = read_in_net_order(first - 8); - break; - case format::fuint8 - format::fnull: - *jx = int(first[-1]); - break; - case format::fuint16 - format::fnull: - *jx = read_in_net_order(first - 2); - break; - case format::fuint32 - format::fnull: - *jx = read_in_net_order(first - 4); - break; - case format::fuint64 - format::fnull: - *jx = read_in_net_order(first - 8); - break; - case format::fint8 - format::fnull: - *jx = int8_t(first[-1]); - break; - case format::fint16 - format::fnull: - *jx = read_in_net_order(first - 2); - break; - case format::fint32 - format::fnull: - *jx = read_in_net_order(first - 4); - break; - case format::fint64 - format::fnull: - *jx = read_in_net_order(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(first - 2); - goto raw; - case format::fbin32 - format::fnull: - case format::fstr32 - format::fnull: - n = read_in_net_order(first - 4); - goto raw; - case format::farray16 - format::fnull: - n = read_in_net_order(first - 2); - goto array; - case format::farray32 - format::fnull: - n = read_in_net_order(first - 4); - goto array; - case format::fmap16 - format::fnull: - n = read_in_net_order(first - 2); - goto map; - case format::fmap32 - format::fnull: - n = read_in_net_order(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(s_ + 1); - s_ += 3; - } else { - assert(*s_ == format::fbin32 || *s_ == format::fstr32); - len = read_in_net_order(s_ + 1); - s_ += 5; - } - x.assign(reinterpret_cast(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 -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 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(s, (uint16_t) x); - } else { - *s++ = fuint32; - s = write_in_net_order(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(s, (int16_t) x); - } else { - *s++ = fint32; - s = write_in_net_order(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(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(s, x); - } - } -}; -inline char* write_int(char* s, int x) { - return sized_writer::write_signed(s, x); -} -inline char* write_int(char* s, unsigned x) { - return sized_writer::write_unsigned(s, x); -} -inline char* write_int(char* s, long x) { - return sized_writer::write_signed(s, x); -} -inline char* write_int(char* s, unsigned long x) { - return sized_writer::write_unsigned(s, x); -} -#if HAVE_LONG_LONG && SIZEOF_LONG_LONG <= 8 -inline char* write_int(char* s, long long x) { - return sized_writer::write_signed(s, x); -} -inline char* write_int(char* s, unsigned long long x) { - return sized_writer::write_unsigned(s, x); -} -#endif -inline char* write_wide_int64(char* s, uint64_t x) { - *s++ = fuint64; - return write_in_net_order(s, x); -} -inline char* write_wide_int64(char* s, int64_t x) { - *s++ = fint64; - return write_in_net_order(s, x); -} -inline char* write_float(char* s, float x) { - *s++ = ffloat32; - return write_in_net_order(s, x); -} -inline char* write_double(char* s, double x) { - *s++ = ffloat64; - return write_in_net_order(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(s, (uint16_t) len); - } else { - *s++ = fstr32; - s = write_in_net_order(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 -inline char* write_string(char* s, const lcdf::String_base& 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(s, (uint16_t) size); - } else { - *s++ = farray32; - return write_in_net_order(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(s, (uint16_t) size); - } else { - *s++ = fmap32; - return write_in_net_order(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 -class unparser { - public: - inline unparser(T& base) - : base_(base) { - } - template - inline unparser(T& base, const X& x) - : base_(base) { - *this << x; - } - - inline void clear() { - base_.clear(); - } - - inline unparser& null() { - base_.append((char) format::fnull); - return *this; - } - inline unparser& operator<<(const Json::null_t&) { - return null(); - } - inline unparser& operator<<(int x) { - char* s = base_.reserve(sizeof(x) + 1); - base_.set_end(format::write_int(s, x)); - return *this; - } - inline unparser& operator<<(unsigned x) { - char* s = base_.reserve(sizeof(x) + 1); - base_.set_end(format::write_int(s, x)); - return *this; - } - inline unparser& operator<<(long x) { - char* s = base_.reserve(sizeof(x) + 1); - base_.set_end(format::write_int(s, x)); - return *this; - } - inline unparser& 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& operator<<(long long x) { - char* s = base_.reserve(sizeof(x) + 1); - base_.set_end(format::write_int(s, x)); - return *this; - } - inline unparser& 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& 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& 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& operator<<(float x) { - char* s = base_.reserve(sizeof(x) + 1); - base_.set_end(format::write_float(s, x)); - return *this; - } - inline unparser& operator<<(double x) { - char* s = base_.reserve(sizeof(x) + 1); - base_.set_end(format::write_double(s, x)); - return *this; - } - inline unparser& operator<<(Str x) { - char* s = base_.reserve(5 + x.length()); - base_.set_end(format::write_string(s, x.data(), x.length())); - return *this; - } - template - inline unparser& operator<<(const lcdf::String_base& x) { - char* s = base_.reserve(5 + x.length()); - base_.set_end(format::write_string(s, x.data(), x.length())); - return *this; - } - inline unparser& operator<<(array_t x) { - char* s = base_.reserve(5); - base_.set_end(format::write_array_header(s, x.size)); - return *this; - } - inline unparser& write_array_header(uint32_t size) { - char* s = base_.reserve(5); - base_.set_end(format::write_array_header(s, size)); - return *this; - } - inline unparser& operator<<(object_t x) { - char* s = base_.reserve(5); - base_.set_end(format::write_map_header(s, x.size)); - return *this; - } - unparser& operator<<(const Json& j); - template - inline unparser& 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 stack_; - String str_; - Json json_; - Json jokey_; -}; - -class parser { - public: - explicit inline parser(const char* s) - : s_(reinterpret_cast(s)), str_() { - } - explicit inline parser(const unsigned char* s) - : s_(s), str_() { - } - explicit inline parser(const String& str) - : s_(reinterpret_cast(str.begin())), str_(str) { - } - inline const char* position() const { - return reinterpret_cast(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 - 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(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(s_ + 1); - s_ += 3; - } else { - assert(*s_ == format::farray32); - size = read_in_net_order(s_ + 1); - s_ += 5; - } - return *this; - } - template parser& operator>>(::std::vector& 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(s_ + 1); - else if (*s_ == format::fstr32 || *s_ == format::fbin32) - s_ += 5 + read_in_net_order(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 void hard_read_int(T& x); -}; - -template -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(s_ + 1); - s_ += 3; - break; - case format::fuint32: - x = read_in_net_order(s_ + 1); - s_ += 5; - break; - case format::fuint64: - x = read_in_net_order(s_ + 1); - s_ += 9; - break; - case format::fint8: - x = (int8_t) s_[1]; - s_ += 2; - break; - case format::fint16: - x = read_in_net_order(s_ + 1); - s_ += 3; - break; - case format::fint32: - x = read_in_net_order(s_ + 1); - s_ += 5; - break; - case format::fint64: - x = read_in_net_order(s_ + 1); - s_ += 9; - break; - } -} - -template -unparser& unparser::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(sa, j); - return sa.take_string(); -} -template -inline T& unparse(T& s, const X& x) { - unparser(s) << x; - return s; -} -template -inline T& unparse_wide(T& s, const X& x) { - unparser(s).write_wide(x); - return s; -} - -template -parser& parser::operator>>(::std::vector& 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(s_ + 1); - s_ += 3; - } else { - assert(*s_ == format::farray32); - sz = read_in_net_order(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 - (consume(reinterpret_cast(first), - reinterpret_cast(last), str)); -} - -inline size_t streaming_parser::consume(const char* first, size_t length, - const String& str) { - const uint8_t* ufirst = reinterpret_cast(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||| s|| 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 -#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 -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(reinterpret_cast(s[i]) + offset); - sum.add(v); - } - return sum; -} - -template -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(reinterpret_cast(s[i]) + offset); - sum.add(v); - } - return sum; -} - -template -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(reinterpret_cast(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_coresfield)>(s, n, offsetof(Perf::stat, field)) -#define sum_one_chip_of(field, c) \ - sum_one_chipfield)>(s, n, offsetof(Perf::stat, field), c) -#define sum_all_per_chip_of(field) \ - sum_all_per_chipfield)>(s, n, offsetof(Perf::stat, field)) - -#define sum_all_cores_of_array(field, oa) \ - sum_all_coresfield[0])>(s, n, offsetof(Perf::stat, field) + \ - sizeof(s[0]->field[0]) * oa) -#define sum_one_chip_of_array(field, oa, c) \ - sum_one_chipfield[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_chipfield[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 -#include - -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 -static void treestats1(node_base

* n, unsigned height) { - if (!n) - return; - int sz; - if (n->isleaf()) { - assert(height < arraysize(heightcounts)); - if (n->deleted()) - return; - leaf

*lf = (leaf

*)n; - typename leaf

::permuter_type perm = lf->permutation_; - sz = perm.size(); - for (int idx = 0; idx < sz; ++idx) { - int p = perm[idx]; - typename leaf

::leafvalue_type lv = lf->lv_[p]; - if (!lv || !lf->is_layer(p)) - ++heightcounts[height]; - else { - node_base

* layer = lv.layer(); - while (!layer->is_root()) - layer = layer->maybe_parent(); - treestats1(layer, height + 1); - } - } - } else { - internode

*in = (internode

*) 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 -void query_table

::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 -void query_table

::json_stats(lcdf::Json& j, threadinfo& ti) { - Masstree::json_stats(j, table_, ti); -} - -template -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(n)->size(); - else - size = static_cast(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(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(n); - typename leaf_type::permuter_type perm = l->permutation(); - ikey0 = l->ikey0_[perm[pv_offset]]; - } else { - internode_type *in = static_cast(n); - ikey0 = in->ikey0_[pv_offset]; - } - - if (!n->has_changed(v)) { - char *x = (char *) malloc(sizeof(ikey0)); - int len = string_slice::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 -void query_table

::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 - 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 - int scan(T& table, threadinfo& ti) { - return table.table().scan(Str(key_, keylen_), first_, *this, ti); - } - template - int rscan(T& table, threadinfo& ti) { - return table.table().rscan(Str(key_, keylen_), first_, *this, ti); - } -}; -} - -template -void query_table

::test(threadinfo& ti) { - query_table

t; - t.initialize(ti); - query q; - - const char * const values[] = { - "", "0", "1", "10", "100000000", // 0-4 - "1000000001", "1000000002", "2", "20", "200000000", // 5-9 - "aaaaaaaaaaaaaaaaaaaaaaaaaa", // 10 - "aaaaaaaaaaaaaaabbbb", "aaaaaaaaaaaaaaabbbc", "aaaaaaaaaxaaaaabbbc", "b", "c", "d", "e", "f", "g", "h", "i", "j", - "kkkkkkkk\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" "a", - "kkkkkkkk\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" "b", - "xxxxxxxxy" - }; - const char * 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 -void query_table

::print(FILE* f) const { - table_.print(f); -} - -template class basic_table; -template class query_table; - -} 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 -class query_table { - public: - typedef P parameters_type; - typedef node_base

node_type; - typedef leaf

leaf_type; - typedef typename P::threadinfo_type threadinfo; - typedef unlocked_tcursor

unlocked_cursor_type; - typedef tcursor

cursor_type; - - query_table() { - } - - const basic_table

& table() const { - return table_; - } - basic_table

& 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

table_; -}; - -struct default_query_table_params : public nodeparams<15, 15> { - typedef row_type* value_type; - typedef value_print value_print_type; - typedef ::threadinfo threadinfo_type; -}; - -typedef query_table 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 #include + namespace lcdf { struct Str : public String_base { @@ -129,8 +130,7 @@ struct Str : public String_base { 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 #include #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 @@ -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 -#include -#include -#include -#include -#include -#include -#include -#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(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 -void time_random() { - T r; - uint32_t x = 0; - for (int i = 0; i < 1000000000; ++i) - x ^= r(); - assert(x != 0); -} - -template -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::make(b, 1); - for (int i = 0; i < 1000000000; ++i) - x ^= string_slice::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 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 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 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(); - assert(iceil_log2(2) == 2); - assert(iceil_log2(3) == 4); - assert(ifloor_log2(2) == 2); - assert(ifloor_log2(3) == 2); - //time_keyslice(); - 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 -#include -#include -#include -#include "straccum.hh" - -template -static bool -check_straccum_utf8(StringAccum &sa, const char *in, int inlen, - const char *out, int outlen) -{ - sa.clear(); - Encoding::UTF8Encoder encoder; - sa.append_encoded(encoder, in, in + inlen); - return sa.length() == outlen && memcmp(sa.begin(), out, sa.length()) == 0; -} - -template -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 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(sa, "abc", 3, "abc", 3); - check_straccum_utf8(sa, "", 0, "", 0); - check_straccum_utf8(sa, "ab\000cd", 5, "ab\000cd", 5); - check_straccum_utf8(sa, "ab\000cd", 5, "abcd", 4); - check_straccum_utf8(sa, "\xc3\x9dHi!", 5, "\xc3\x9dHi!", 5); - check_straccum_utf8(sa, "\xddHi!", 4, "\xc3\x9dHi!", 5); - - check_straccum2_utf8(sa, "abc", 3, "abc", 3); - check_straccum2_utf8(sa, "", 0, "", 0); - check_straccum2_utf8(sa, "ab\000cd", 5, "ab\000cd", 5); - check_straccum2_utf8(sa, "ab\000cd", 5, "abcd", 4); - check_straccum2_utf8(sa, "\xc3\x9dHi!", 5, "\xc3\x9dHi!", 5); - check_straccum2_utf8(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 -#include -#include - -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 names; - for (testrunner_base* tr = thehead; tr; tr = tr->next_) - names.push_back(tr->name()); - - size_t percol; - std::vector 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 - -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_base::first()); - } - static testrunner* find(const lcdf::String& name) { - return static_cast(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 -#include -#include -#include - -#include - -#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 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_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_type; - typedef Masstree::unlocked_tcursor unlocked_cursor_type; - typedef Masstree::tcursor cursor_type; - typedef Masstree::leaf leaf_type; - typedef Masstree::internode 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 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 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 - -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 - static value_array* checkpoint_read(PARSER& par, kvtimestamp_t ts, - threadinfo& ti); - template - 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 -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 -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 -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 inline void deallocate(ALLOC& ti); - template inline void deallocate_rcu(ALLOC& ti); - - template - value_bag* update(const Json* first, const Json* last, kvtimestamp_t ts, - ALLOC& ti) const; - template - inline value_bag* update(int col, Str value, - kvtimestamp_t ts, ALLOC& ti) const; - template - static value_bag* create(const Json* first, const Json* last, - kvtimestamp_t ts, ALLOC& ti); - template - static value_bag* create1(Str value, kvtimestamp_t ts, ALLOC& ti); - template - inline void deallocate_rcu_after_update(const Json* first, const Json* last, ALLOC& ti); - template - inline void deallocate_after_failed_update(const Json* first, const Json* last, ALLOC& ti); - - template - static value_bag* checkpoint_read(PARSER& par, kvtimestamp_t ts, - ALLOC& ti); - template - 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 -inline value_bag::value_bag() - : ts_(0) { - d_.ncol_ = 0; - d_.pos_[0] = sizeof(bagdata); -} - -template -inline kvtimestamp_t value_bag::timestamp() const { - return ts_; -} - -template -inline size_t value_bag::size() const { - return sizeof(kvtimestamp_t) + d_.pos_[d_.ncol_]; -} - -template -inline int value_bag::ncol() const { - return d_.ncol_; -} - -template -inline O value_bag::column_length(int i) const { - return d_.pos_[i + 1] - d_.pos_[i]; -} - -template -inline lcdf::Str value_bag::col(int i) const { - if (unsigned(i) < unsigned(d_.ncol_)) - return Str(d_.s_ + d_.pos_[i], column_length(i)); - else - return Str(); -} - -template -inline lcdf::Str value_bag::row_string() const { - return Str(d_.s_, d_.pos_[d_.ncol_]); -} - -template template -inline void value_bag::deallocate(ALLOC& ti) { - ti.deallocate(this, size(), memtag_value); -} - -template template -inline void value_bag::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 template -value_bag* value_bag::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* row = (value_bag*) 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 template -inline value_bag* value_bag::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 template -inline value_bag* value_bag::create(const Json* first, const Json* last, - kvtimestamp_t ts, ALLOC& ti) { - value_bag empty; - return empty.update(first, last, ts, ti); -} - -template template -inline value_bag* value_bag::create1(Str str, kvtimestamp_t ts, - ALLOC& ti) { - value_bag* row = (value_bag*) 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 template -inline void value_bag::deallocate_rcu_after_update(const Json*, const Json*, ALLOC& ti) { - deallocate_rcu(ti); -} - -template template -inline void value_bag::deallocate_after_failed_update(const Json*, const Json*, ALLOC& ti) { - deallocate(ti); -} - -template template -inline value_bag* value_bag::checkpoint_read(PARSER& par, - kvtimestamp_t ts, - ALLOC& ti) { - Str value; - par >> value; - value_bag* row = (value_bag*) ti.allocate(sizeof(kvtimestamp_t) + value.length(), memtag_value); - row->ts_ = ts; - memcpy(row->d_.s_, value.data(), value.length()); - return row; -} - -template template -inline void value_bag::checkpoint_write(UNPARSER& unpar) const { - unpar << Str(d_.s_, d_.pos_[d_.ncol_]); -} - -template -void value_bag::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 - inline void deallocate(ALLOC& ti); - inline void deallocate_rcu(threadinfo& ti); - - template - 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 - static inline value_string* checkpoint_read(PARSER& par, kvtimestamp_t ts, - threadinfo& ti); - template - 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 -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 -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 -inline value_string* value_string::checkpoint_read(PARSER& par, - kvtimestamp_t ts, - threadinfo& ti) { - Str str; - par >> str; - return create1(str, ts, ti); -} - -template -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 - -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& 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& 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 - static value_versioned_array* checkpoint_read(PARSER& par, kvtimestamp_t ts, - threadinfo& ti); - template - 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* snapshot_; - - query_helper() - : snapshot_() { - } - inline const value_versioned_array* snapshot(const value_versioned_array* row, - const std::vector& 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 -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 -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