summaryrefslogtreecommitdiffstats
path: root/include
diff options
context:
space:
mode:
Diffstat (limited to 'include')
-rw-r--r--include/Makefile.am2
-rw-r--r--include/Makefile.in638
-rw-r--r--include/mdds/Makefile.am28
-rw-r--r--include/mdds/Makefile.in720
-rw-r--r--include/mdds/flat_segment_tree.hpp885
-rw-r--r--include/mdds/flat_segment_tree_def.inl889
-rw-r--r--include/mdds/flat_segment_tree_itr.hpp348
-rw-r--r--include/mdds/global.hpp216
-rw-r--r--include/mdds/multi_type_matrix.hpp844
-rw-r--r--include/mdds/multi_type_matrix_def.inl814
-rw-r--r--include/mdds/multi_type_vector.hpp44
-rw-r--r--include/mdds/multi_type_vector/Makefile.am16
-rw-r--r--include/mdds/multi_type_vector/Makefile.in707
-rw-r--r--include/mdds/multi_type_vector/aos/Makefile.am8
-rw-r--r--include/mdds/multi_type_vector/aos/Makefile.in585
-rw-r--r--include/mdds/multi_type_vector/aos/block_util.hpp241
-rw-r--r--include/mdds/multi_type_vector/aos/iterator.hpp303
-rw-r--r--include/mdds/multi_type_vector/aos/main.hpp1335
-rw-r--r--include/mdds/multi_type_vector/aos/main_def.inl4837
-rw-r--r--include/mdds/multi_type_vector/block_funcs.hpp233
-rw-r--r--include/mdds/multi_type_vector/collection.hpp280
-rw-r--r--include/mdds/multi_type_vector/collection_def.inl305
-rw-r--r--include/mdds/multi_type_vector/env.hpp35
-rw-r--r--include/mdds/multi_type_vector/iterator_node.hpp127
-rw-r--r--include/mdds/multi_type_vector/macro.hpp207
-rw-r--r--include/mdds/multi_type_vector/soa/Makefile.am8
-rw-r--r--include/mdds/multi_type_vector/soa/Makefile.in585
-rw-r--r--include/mdds/multi_type_vector/soa/block_util.hpp621
-rw-r--r--include/mdds/multi_type_vector/soa/iterator.hpp414
-rw-r--r--include/mdds/multi_type_vector/soa/main.hpp1449
-rw-r--r--include/mdds/multi_type_vector/soa/main_def.inl5238
-rw-r--r--include/mdds/multi_type_vector/standard_element_blocks.hpp86
-rw-r--r--include/mdds/multi_type_vector/types.hpp1011
-rw-r--r--include/mdds/multi_type_vector/types_util.hpp165
-rw-r--r--include/mdds/multi_type_vector/util.hpp264
-rw-r--r--include/mdds/multi_type_vector_itr.hpp36
-rw-r--r--include/mdds/multi_type_vector_macro.hpp33
-rw-r--r--include/mdds/multi_type_vector_types.hpp33
-rw-r--r--include/mdds/node.hpp490
-rw-r--r--include/mdds/point_quad_tree.hpp1576
-rw-r--r--include/mdds/quad_node.hpp375
-rw-r--r--include/mdds/ref_pair.hpp80
-rw-r--r--include/mdds/rtree.hpp830
-rw-r--r--include/mdds/rtree_def.inl2625
-rw-r--r--include/mdds/segment_tree.hpp704
-rw-r--r--include/mdds/segment_tree_def.inl644
-rw-r--r--include/mdds/sorted_string_map.hpp125
-rw-r--r--include/mdds/sorted_string_map_def.inl167
-rw-r--r--include/mdds/trie_map.hpp713
-rw-r--r--include/mdds/trie_map_def.inl1735
-rw-r--r--include/mdds/trie_map_itr.hpp889
51 files changed, 35543 insertions, 0 deletions
diff --git a/include/Makefile.am b/include/Makefile.am
new file mode 100644
index 0000000..7b38e42
--- /dev/null
+++ b/include/Makefile.am
@@ -0,0 +1,2 @@
+SUBDIRS = mdds
+
diff --git a/include/Makefile.in b/include/Makefile.in
new file mode 100644
index 0000000..6a605b2
--- /dev/null
+++ b/include/Makefile.in
@@ -0,0 +1,638 @@
+# Makefile.in generated by automake 1.16.5 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2021 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+subdir = include
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4/ax_cxx_compile_stdcxx_17.m4 \
+ $(top_srcdir)/m4/m4_ax_valgrind_check.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+SOURCES =
+DIST_SOURCES =
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+ ctags-recursive dvi-recursive html-recursive info-recursive \
+ install-data-recursive install-dvi-recursive \
+ install-exec-recursive install-html-recursive \
+ install-info-recursive install-pdf-recursive \
+ install-ps-recursive install-recursive installcheck-recursive \
+ installdirs-recursive pdf-recursive ps-recursive \
+ tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+ $(RECURSIVE_TARGETS) \
+ $(RECURSIVE_CLEAN_TARGETS) \
+ $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+ distdir distdir-am
+am__extra_recursive_targets = check-valgrind-recursive \
+ check-valgrind-memcheck-recursive \
+ check-valgrind-helgrind-recursive check-valgrind-drd-recursive \
+ check-valgrind-sgcheck-recursive
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+DIST_SUBDIRS = $(SUBDIRS)
+am__DIST_COMMON = $(srcdir)/Makefile.in
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+ dir0=`pwd`; \
+ sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+ sed_rest='s,^[^/]*/*,,'; \
+ sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+ sed_butlast='s,/*[^/]*$$,,'; \
+ while test -n "$$dir1"; do \
+ first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+ if test "$$first" != "."; then \
+ if test "$$first" = ".."; then \
+ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+ else \
+ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+ if test "$$first2" = "$$first"; then \
+ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+ else \
+ dir2="../$$dir2"; \
+ fi; \
+ dir0="$$dir0"/"$$first"; \
+ fi; \
+ fi; \
+ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+ done; \
+ reldir="$$dir2"
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+API_VERSION = @API_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_NODEBUG = @CPPFLAGS_NODEBUG@
+CSCOPE = @CSCOPE@
+CTAGS = @CTAGS@
+CXX = @CXX@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CXXFLAGS_UNITTESTS = @CXXFLAGS_UNITTESTS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DOXYGEN = @DOXYGEN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+ENABLE_VALGRIND_drd = @ENABLE_VALGRIND_drd@
+ENABLE_VALGRIND_helgrind = @ENABLE_VALGRIND_helgrind@
+ENABLE_VALGRIND_memcheck = @ENABLE_VALGRIND_memcheck@
+ENABLE_VALGRIND_sgcheck = @ENABLE_VALGRIND_sgcheck@
+ETAGS = @ETAGS@
+EXEEXT = @EXEEXT@
+EXPECT = @EXPECT@
+GDB = @GDB@
+HAVE_CXX17 = @HAVE_CXX17@
+INCDIR = @INCDIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MISCDIR = @MISCDIR@
+MKDIR_P = @MKDIR_P@
+OBJDIR = @OBJDIR@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+QUICKCHECKDIR = @QUICKCHECKDIR@
+RUNTEST_BIN = @RUNTEST_BIN@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPHINX = @SPHINX@
+STRIP = @STRIP@
+VALGRIND = @VALGRIND@
+VALGRIND_ENABLED = @VALGRIND_ENABLED@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build_alias = @build_alias@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host_alias = @host_alias@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+valgrind_enabled_tools = @valgrind_enabled_tools@
+valgrind_tools = @valgrind_tools@
+SUBDIRS = mdds
+all: all-recursive
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign include/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign include/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+# (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+ @fail=; \
+ if $(am__make_keepgoing); then \
+ failcom='fail=yes'; \
+ else \
+ failcom='exit 1'; \
+ fi; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+check-valgrind-local:
+check-valgrind-memcheck-local:
+check-valgrind-helgrind-local:
+check-valgrind-drd-local:
+check-valgrind-sgcheck-local:
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ $(am__make_dryrun) \
+ || test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+ $(am__relativize); \
+ new_distdir=$$reldir; \
+ dir1=$$subdir; dir2="$(top_distdir)"; \
+ $(am__relativize); \
+ new_top_distdir=$$reldir; \
+ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+ ($(am__cd) $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$new_top_distdir" \
+ distdir="$$new_distdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ am__skip_mode_fix=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-recursive
+all-am: Makefile
+installdirs: installdirs-recursive
+installdirs-am:
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+check-valgrind: check-valgrind-recursive
+
+check-valgrind-am: check-valgrind-local
+
+check-valgrind-drd: check-valgrind-drd-recursive
+
+check-valgrind-drd-am: check-valgrind-drd-local
+
+check-valgrind-helgrind: check-valgrind-helgrind-recursive
+
+check-valgrind-helgrind-am: check-valgrind-helgrind-local
+
+check-valgrind-memcheck: check-valgrind-memcheck-recursive
+
+check-valgrind-memcheck-am: check-valgrind-memcheck-local
+
+check-valgrind-sgcheck: check-valgrind-sgcheck-recursive
+
+check-valgrind-sgcheck-am: check-valgrind-sgcheck-local
+
+clean: clean-recursive
+
+clean-am: clean-generic mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-generic
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: $(am__recursive_targets) install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \
+ check-am check-valgrind-am check-valgrind-drd-am \
+ check-valgrind-drd-local check-valgrind-helgrind-am \
+ check-valgrind-helgrind-local check-valgrind-local \
+ check-valgrind-memcheck-am check-valgrind-memcheck-local \
+ check-valgrind-sgcheck-am check-valgrind-sgcheck-local clean \
+ clean-generic cscopelist-am ctags ctags-am distclean \
+ distclean-generic distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-man install-pdf install-pdf-am \
+ install-ps install-ps-am install-strip installcheck \
+ installcheck-am installdirs installdirs-am maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-generic pdf \
+ pdf-am ps ps-am tags tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/include/mdds/Makefile.am b/include/mdds/Makefile.am
new file mode 100644
index 0000000..7784656
--- /dev/null
+++ b/include/mdds/Makefile.am
@@ -0,0 +1,28 @@
+SUBDIRS = multi_type_vector
+
+headersdir = $(includedir)/mdds-@API_VERSION@/mdds
+
+headers_HEADERS = \
+ flat_segment_tree_def.inl \
+ flat_segment_tree.hpp \
+ flat_segment_tree_itr.hpp \
+ global.hpp \
+ multi_type_matrix_def.inl \
+ multi_type_matrix.hpp \
+ multi_type_vector.hpp \
+ multi_type_vector_itr.hpp \
+ multi_type_vector_macro.hpp \
+ multi_type_vector_types.hpp \
+ node.hpp \
+ point_quad_tree.hpp \
+ quad_node.hpp \
+ ref_pair.hpp \
+ rtree_def.inl \
+ rtree.hpp \
+ segment_tree_def.inl \
+ segment_tree.hpp \
+ sorted_string_map_def.inl \
+ sorted_string_map.hpp \
+ trie_map_def.inl \
+ trie_map.hpp \
+ trie_map_itr.hpp
diff --git a/include/mdds/Makefile.in b/include/mdds/Makefile.in
new file mode 100644
index 0000000..4d6d934
--- /dev/null
+++ b/include/mdds/Makefile.in
@@ -0,0 +1,720 @@
+# Makefile.in generated by automake 1.16.5 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2021 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+subdir = include/mdds
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4/ax_cxx_compile_stdcxx_17.m4 \
+ $(top_srcdir)/m4/m4_ax_valgrind_check.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(headers_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+SOURCES =
+DIST_SOURCES =
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+ ctags-recursive dvi-recursive html-recursive info-recursive \
+ install-data-recursive install-dvi-recursive \
+ install-exec-recursive install-html-recursive \
+ install-info-recursive install-pdf-recursive \
+ install-ps-recursive install-recursive installcheck-recursive \
+ installdirs-recursive pdf-recursive ps-recursive \
+ tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(headersdir)"
+HEADERS = $(headers_HEADERS)
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+ $(RECURSIVE_TARGETS) \
+ $(RECURSIVE_CLEAN_TARGETS) \
+ $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+ distdir distdir-am
+am__extra_recursive_targets = check-valgrind-recursive \
+ check-valgrind-memcheck-recursive \
+ check-valgrind-helgrind-recursive check-valgrind-drd-recursive \
+ check-valgrind-sgcheck-recursive
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+DIST_SUBDIRS = $(SUBDIRS)
+am__DIST_COMMON = $(srcdir)/Makefile.in
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+ dir0=`pwd`; \
+ sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+ sed_rest='s,^[^/]*/*,,'; \
+ sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+ sed_butlast='s,/*[^/]*$$,,'; \
+ while test -n "$$dir1"; do \
+ first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+ if test "$$first" != "."; then \
+ if test "$$first" = ".."; then \
+ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+ else \
+ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+ if test "$$first2" = "$$first"; then \
+ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+ else \
+ dir2="../$$dir2"; \
+ fi; \
+ dir0="$$dir0"/"$$first"; \
+ fi; \
+ fi; \
+ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+ done; \
+ reldir="$$dir2"
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+API_VERSION = @API_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_NODEBUG = @CPPFLAGS_NODEBUG@
+CSCOPE = @CSCOPE@
+CTAGS = @CTAGS@
+CXX = @CXX@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CXXFLAGS_UNITTESTS = @CXXFLAGS_UNITTESTS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DOXYGEN = @DOXYGEN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+ENABLE_VALGRIND_drd = @ENABLE_VALGRIND_drd@
+ENABLE_VALGRIND_helgrind = @ENABLE_VALGRIND_helgrind@
+ENABLE_VALGRIND_memcheck = @ENABLE_VALGRIND_memcheck@
+ENABLE_VALGRIND_sgcheck = @ENABLE_VALGRIND_sgcheck@
+ETAGS = @ETAGS@
+EXEEXT = @EXEEXT@
+EXPECT = @EXPECT@
+GDB = @GDB@
+HAVE_CXX17 = @HAVE_CXX17@
+INCDIR = @INCDIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MISCDIR = @MISCDIR@
+MKDIR_P = @MKDIR_P@
+OBJDIR = @OBJDIR@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+QUICKCHECKDIR = @QUICKCHECKDIR@
+RUNTEST_BIN = @RUNTEST_BIN@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPHINX = @SPHINX@
+STRIP = @STRIP@
+VALGRIND = @VALGRIND@
+VALGRIND_ENABLED = @VALGRIND_ENABLED@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build_alias = @build_alias@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host_alias = @host_alias@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+valgrind_enabled_tools = @valgrind_enabled_tools@
+valgrind_tools = @valgrind_tools@
+SUBDIRS = multi_type_vector
+headersdir = $(includedir)/mdds-@API_VERSION@/mdds
+headers_HEADERS = \
+ flat_segment_tree_def.inl \
+ flat_segment_tree.hpp \
+ flat_segment_tree_itr.hpp \
+ global.hpp \
+ multi_type_matrix_def.inl \
+ multi_type_matrix.hpp \
+ multi_type_vector.hpp \
+ multi_type_vector_itr.hpp \
+ multi_type_vector_macro.hpp \
+ multi_type_vector_types.hpp \
+ node.hpp \
+ point_quad_tree.hpp \
+ quad_node.hpp \
+ ref_pair.hpp \
+ rtree_def.inl \
+ rtree.hpp \
+ segment_tree_def.inl \
+ segment_tree.hpp \
+ sorted_string_map_def.inl \
+ sorted_string_map.hpp \
+ trie_map_def.inl \
+ trie_map.hpp \
+ trie_map_itr.hpp
+
+all: all-recursive
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign include/mdds/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign include/mdds/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-headersHEADERS: $(headers_HEADERS)
+ @$(NORMAL_INSTALL)
+ @list='$(headers_HEADERS)'; test -n "$(headersdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(headersdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(headersdir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(headersdir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(headersdir)" || exit $$?; \
+ done
+
+uninstall-headersHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(headers_HEADERS)'; test -n "$(headersdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(headersdir)'; $(am__uninstall_files_from_dir)
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+# (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+ @fail=; \
+ if $(am__make_keepgoing); then \
+ failcom='fail=yes'; \
+ else \
+ failcom='exit 1'; \
+ fi; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+check-valgrind-local:
+check-valgrind-memcheck-local:
+check-valgrind-helgrind-local:
+check-valgrind-drd-local:
+check-valgrind-sgcheck-local:
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ $(am__make_dryrun) \
+ || test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+ $(am__relativize); \
+ new_distdir=$$reldir; \
+ dir1=$$subdir; dir2="$(top_distdir)"; \
+ $(am__relativize); \
+ new_top_distdir=$$reldir; \
+ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+ ($(am__cd) $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$new_top_distdir" \
+ distdir="$$new_distdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ am__skip_mode_fix=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-recursive
+all-am: Makefile $(HEADERS)
+installdirs: installdirs-recursive
+installdirs-am:
+ for dir in "$(DESTDIR)$(headersdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+check-valgrind: check-valgrind-recursive
+
+check-valgrind-am: check-valgrind-local
+
+check-valgrind-drd: check-valgrind-drd-recursive
+
+check-valgrind-drd-am: check-valgrind-drd-local
+
+check-valgrind-helgrind: check-valgrind-helgrind-recursive
+
+check-valgrind-helgrind-am: check-valgrind-helgrind-local
+
+check-valgrind-memcheck: check-valgrind-memcheck-recursive
+
+check-valgrind-memcheck-am: check-valgrind-memcheck-local
+
+check-valgrind-sgcheck: check-valgrind-sgcheck-recursive
+
+check-valgrind-sgcheck-am: check-valgrind-sgcheck-local
+
+clean: clean-recursive
+
+clean-am: clean-generic mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am: install-headersHEADERS
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-generic
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am: uninstall-headersHEADERS
+
+.MAKE: $(am__recursive_targets) install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \
+ check-am check-valgrind-am check-valgrind-drd-am \
+ check-valgrind-drd-local check-valgrind-helgrind-am \
+ check-valgrind-helgrind-local check-valgrind-local \
+ check-valgrind-memcheck-am check-valgrind-memcheck-local \
+ check-valgrind-sgcheck-am check-valgrind-sgcheck-local clean \
+ clean-generic cscopelist-am ctags ctags-am distclean \
+ distclean-generic distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-headersHEADERS install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ installdirs-am maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-generic pdf pdf-am ps ps-am tags \
+ tags-am uninstall uninstall-am uninstall-headersHEADERS
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/include/mdds/flat_segment_tree.hpp b/include/mdds/flat_segment_tree.hpp
new file mode 100644
index 0000000..51c6732
--- /dev/null
+++ b/include/mdds/flat_segment_tree.hpp
@@ -0,0 +1,885 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * Copyright (c) 2008-2023 Kohei Yoshida
+ *
+ * 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, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ ************************************************************************/
+
+#ifndef INCLUDED_MDDS_FLAT_SEGMENT_TREE_HPP
+#define INCLUDED_MDDS_FLAT_SEGMENT_TREE_HPP
+
+#include <iostream>
+#include <sstream>
+#include <utility>
+#include <cassert>
+
+#include "mdds/node.hpp"
+#include "mdds/flat_segment_tree_itr.hpp"
+#include "mdds/global.hpp"
+
+#ifdef MDDS_UNIT_TEST
+#include <cstdio>
+#include <vector>
+#endif
+
+namespace mdds {
+
+template<typename Key, typename Value>
+class flat_segment_tree
+{
+public:
+ typedef Key key_type;
+ typedef Value value_type;
+ typedef size_t size_type;
+
+ struct nonleaf_value_type
+ {
+ key_type low; /// low range value (inclusive)
+ key_type high; /// high range value (non-inclusive)
+
+ bool operator==(const nonleaf_value_type& r) const
+ {
+ return low == r.low && high == r.high;
+ }
+
+ nonleaf_value_type() : low{}, high{}
+ {}
+ };
+
+ struct leaf_value_type
+ {
+ key_type key;
+ value_type value;
+
+ bool operator==(const leaf_value_type& r) const
+ {
+ return key == r.key && value == r.value;
+ }
+
+ leaf_value_type() : key{}, value{}
+ {}
+ };
+
+ // Handlers required by the node template class.
+ struct fill_nonleaf_value_handler;
+ struct init_handler;
+ struct dispose_handler;
+#ifdef MDDS_UNIT_TEST
+ struct to_string_handler;
+#endif
+
+ typedef __st::node<flat_segment_tree> node;
+ typedef typename node::node_ptr node_ptr;
+
+ typedef __st::nonleaf_node<flat_segment_tree> nonleaf_node;
+
+ struct fill_nonleaf_value_handler
+ {
+ void operator()(
+ __st::nonleaf_node<flat_segment_tree>& _self, const __st::node_base* left_node,
+ const __st::node_base* right_node)
+ {
+ // Parent node should carry the range of all of its child nodes.
+ if (left_node)
+ {
+ _self.value_nonleaf.low = left_node->is_leaf
+ ? static_cast<const node*>(left_node)->value_leaf.key
+ : static_cast<const nonleaf_node*>(left_node)->value_nonleaf.low;
+ }
+ else
+ {
+ // Having a left node is prerequisite.
+ throw general_error(
+ "flat_segment_tree::fill_nonleaf_value_handler: Having a left node is prerequisite.");
+ }
+
+ if (right_node)
+ {
+ if (right_node->is_leaf)
+ {
+ // When the child nodes are leaf nodes, the upper bound
+ // must be the value of the node that comes after the
+ // right leaf node (if such node exists).
+ const node* p = static_cast<const node*>(right_node);
+ if (p->next)
+ _self.value_nonleaf.high = p->next->value_leaf.key;
+ else
+ _self.value_nonleaf.high = p->value_leaf.key;
+ }
+ else
+ {
+ _self.value_nonleaf.high = static_cast<const nonleaf_node*>(right_node)->value_nonleaf.high;
+ }
+ }
+ else
+ {
+ _self.value_nonleaf.high = left_node->is_leaf
+ ? static_cast<const node*>(left_node)->value_leaf.key
+ : static_cast<const nonleaf_node*>(left_node)->value_nonleaf.high;
+ }
+ }
+ };
+
+#ifdef MDDS_UNIT_TEST
+ struct to_string_handler
+ {
+ std::string operator()(const node& _self) const
+ {
+ std::ostringstream os;
+ os << "(" << _self.value_leaf.key << ") ";
+ return os.str();
+ }
+
+ std::string operator()(const mdds::__st::nonleaf_node<flat_segment_tree>& _self) const
+ {
+ std::ostringstream os;
+ os << "(" << _self.value_nonleaf.low << "-" << _self.value_nonleaf.high << ") ";
+ return os.str();
+ }
+ };
+#endif
+
+ struct init_handler
+ {
+ void operator()(node& /*_self*/)
+ {}
+ void operator()(__st::nonleaf_node<flat_segment_tree>& /*_self*/)
+ {}
+ };
+
+ struct dispose_handler
+ {
+ void operator()(node& /*_self*/)
+ {}
+ void operator()(__st::nonleaf_node<flat_segment_tree>& /*_self*/)
+ {}
+ };
+
+private:
+ friend struct ::mdds::fst::detail::forward_itr_handler<flat_segment_tree>;
+ friend struct ::mdds::fst::detail::reverse_itr_handler<flat_segment_tree>;
+
+public:
+ using const_segment_iterator = mdds::fst::detail::const_segment_iterator<flat_segment_tree>;
+
+ class const_iterator : public ::mdds::fst::detail::const_iterator_base<
+ flat_segment_tree, ::mdds::fst::detail::forward_itr_handler<flat_segment_tree>>
+ {
+ typedef ::mdds::fst::detail::const_iterator_base<
+ flat_segment_tree, ::mdds::fst::detail::forward_itr_handler<flat_segment_tree>>
+ base_type;
+ friend class flat_segment_tree;
+
+ using base_type::get_parent;
+ using base_type::get_pos;
+ using base_type::is_end_pos;
+
+ public:
+ const_iterator() : base_type(nullptr, false)
+ {}
+
+ /**
+ * Create a segment iterator that references the same segment the source
+ * iterator references the start key of.
+ */
+ const_segment_iterator to_segment() const;
+
+ private:
+ explicit const_iterator(const typename base_type::fst_type* _db, bool _end) : base_type(_db, _end)
+ {}
+
+ explicit const_iterator(const typename base_type::fst_type* _db, const node* p) : base_type(_db, p)
+ {}
+ };
+
+ class const_reverse_iterator : public ::mdds::fst::detail::const_iterator_base<
+ flat_segment_tree, ::mdds::fst::detail::reverse_itr_handler<flat_segment_tree>>
+ {
+ typedef ::mdds::fst::detail::const_iterator_base<
+ flat_segment_tree, ::mdds::fst::detail::reverse_itr_handler<flat_segment_tree>>
+ base_type;
+ friend class flat_segment_tree;
+
+ public:
+ const_reverse_iterator() : base_type(nullptr, false)
+ {}
+
+ private:
+ explicit const_reverse_iterator(const typename base_type::fst_type* _db, bool _end) : base_type(_db, _end)
+ {}
+ };
+
+ class const_segment_range_type
+ {
+ node_ptr m_left_leaf;
+ node_ptr m_right_leaf;
+
+ public:
+ const_segment_range_type(node_ptr left_leaf, node_ptr right_leaf);
+
+ const_segment_iterator begin() const;
+ const_segment_iterator end() const;
+ };
+
+ /**
+ * Return an iterator that points to the first leaf node that correspondes
+ * with the start position of the first segment.
+ *
+ * @return immutable iterator that points to the first leaf node that
+ * corresponds with the start position of the first segment.
+ */
+ const_iterator begin() const
+ {
+ return const_iterator(this, false);
+ }
+
+ /**
+ * Return an iterator that points to the position past the last leaf node
+ * that corresponds with the end position of the last segment.
+ *
+ * @return immutable iterator that points to the position past last leaf
+ * node that corresponds with the end position of the last
+ * segment.
+ */
+ const_iterator end() const
+ {
+ return const_iterator(this, true);
+ }
+
+ /**
+ * Return an iterator that points to the last leaf node that correspondes
+ * with the end position of the last segment. This iterator moves in the
+ * reverse direction of a normal iterator.
+ *
+ * @return immutable reverse iterator that points to the last leaf node
+ * that corresponds with the end position of the last segment.
+ */
+ const_reverse_iterator rbegin() const
+ {
+ return const_reverse_iterator(this, false);
+ }
+
+ /**
+ * Return an iterator that points to the position past the first leaf node
+ * that corresponds with the start position of the first segment. This
+ * iterator moves in the reverse direction of a normal iterator.
+ *
+ * @return immutable reverse iterator that points to the position past
+ * first leaf node that corresponds with the start position of the
+ * first segment.
+ */
+ const_reverse_iterator rend() const
+ {
+ return const_reverse_iterator(this, true);
+ }
+
+ /**
+ * Return an immutable iterator that points to the first segment stored in
+ * the tree. It iterates through the segments one segment at a time.
+ * Each iterator value consists of <code>start</code>, <code>end</code>,
+ * and <code>value</code> members that correspond with the start and end
+ * positions of a segment and the value of that segment, respectively.
+ *
+ * @return immutable iterator that points to the first segment stored in
+ * the tree.
+ */
+ const_segment_iterator begin_segment() const;
+
+ /**
+ * Return an immutable iterator that points to the position past the last
+ * segment stored in the tree. It iterates through the segments one
+ * segment at a time. Each iterator value consists of <code>start</code>,
+ * <code>end</code>, and <code>value</code> members that correspond with
+ * the start and end positions of a segment and the value of that segment,
+ * respectively.
+ *
+ * @return immutable iterator that points to the position past the last
+ * segment stored in the tree.
+ */
+ const_segment_iterator end_segment() const;
+
+ /**
+ * Return a range object that provides a begin iterator and an end sentinel.
+ */
+ const_segment_range_type segment_range() const;
+
+ flat_segment_tree() = delete;
+
+ /**
+ * Constructor that takes minimum and maximum keys and the value to be
+ * used for the initial segment.
+ *
+ * @param min_val minimum allowed key value for the entire series of
+ * segments.
+ * @param max_val maximum allowed key value for the entires series of
+ * segments.
+ * @param init_val value to be used for the initial segment. This value
+ * will also be used for empty segments.
+ */
+ flat_segment_tree(key_type min_val, key_type max_val, value_type init_val);
+
+ /**
+ * Copy constructor only copies the leaf nodes.
+ */
+ flat_segment_tree(const flat_segment_tree& r);
+
+ /**
+ * Move constructor.
+ *
+ * @warning The source instance will not be usable after the move
+ * construction.
+ */
+ flat_segment_tree(flat_segment_tree&& other);
+
+ ~flat_segment_tree();
+
+ /**
+ * Copy assignment operator.
+ *
+ * @param other Source instance to copy from.
+ *
+ * @note It only copies the leaf nodes.
+ */
+ flat_segment_tree<Key, Value>& operator=(const flat_segment_tree& other);
+
+ /**
+ * Move assignment operator.
+ *
+ * @param other Source instance to move from.
+ */
+ flat_segment_tree<Key, Value>& operator=(flat_segment_tree&& other);
+
+ /**
+ * Swap the content of the tree with another instance.
+ *
+ * @param other instance of flat_segment_tree to swap content with.
+ */
+ void swap(flat_segment_tree& other);
+
+ /**
+ * Remove all stored segments except for the initial segment. The minimum
+ * and maximum keys and the default value will be retained after the call
+ * returns. This call will also remove the tree.
+ */
+ void clear();
+
+ /**
+ * Insert a new segment into the tree. It searches for the point of
+ * insertion from the first leaf node.
+ *
+ * @param start_key start value of the segment being inserted. The value
+ * is inclusive.
+ * @param end_key end value of the segment being inserted. The value is
+ * not inclusive.
+ * @param val value associated with this segment.
+ *
+ * @return pair of const_iterator corresponding to the start position of
+ * the inserted segment, and a boolean value indicating whether or
+ * not the insertion has modified the tree.
+ */
+ std::pair<const_iterator, bool> insert_front(key_type start_key, key_type end_key, value_type val)
+ {
+ return insert_segment_impl(std::move(start_key), std::move(end_key), std::move(val), true);
+ }
+
+ /**
+ * Insert a new segment into the tree. Unlike the insert_front()
+ * counterpart, this method searches for the point of insertion from the
+ * last leaf node toward the first.
+ *
+ * @param start_key start value of the segment being inserted. The value
+ * is inclusive.
+ * @param end_key end value of the segment being inserted. The value is
+ * not inclusive.
+ * @param val value associated with this segment.
+ *
+ * @return pair of const_iterator corresponding to the start position of
+ * the inserted segment, and a boolean value indicating whether or
+ * not the insertion has modified the tree.
+ */
+ std::pair<const_iterator, bool> insert_back(key_type start_key, key_type end_key, value_type val)
+ {
+ return insert_segment_impl(std::move(start_key), std::move(end_key), std::move(val), false);
+ }
+
+ /**
+ * Insert a new segment into the tree at or after specified point of
+ * insertion.
+ *
+ * @param pos specified insertion point
+ * @param start_key start value of the segment being inserted. The value
+ * is inclusive.
+ * @param end_key end value of the segment being inserted. The value is
+ * not inclusive.
+ * @param val value associated with this segment.
+ *
+ * @return pair of const_iterator corresponding to the start position of
+ * the inserted segment, and a boolean value indicating whether or
+ * not the insertion has modified the tree.
+ */
+ std::pair<const_iterator, bool> insert(
+ const const_iterator& pos, key_type start_key, key_type end_key, value_type val);
+
+ /**
+ * Remove a segment specified by the start and end key values, and shift
+ * the remaining segments (i.e. those segments that come after the removed
+ * segment) to left. Note that the start and end positions of the segment
+ * being removed <b>must</b> be within the base segment span.
+ *
+ * @param start_key start position of the segment being removed.
+ * @param end_key end position of the segment being removed.
+ */
+ void shift_left(key_type start_key, key_type end_key);
+
+ /**
+ * Shift all segments that occur at or after the specified start position
+ * to right by the size specified.
+ *
+ * @param pos position where the right-shift occurs.
+ * @param size amount of shift (must be greater than 0)
+ * @param skip_start_node if true, and the specified position is at an
+ * existing node position, that node will
+ * <i>not</i> be shifted. This argument has no
+ * effect if the position specified does not
+ * coincide with any of the existing nodes.
+ */
+ void shift_right(key_type pos, key_type size, bool skip_start_node);
+
+ /**
+ * Perform leaf-node search for a value associated with a key.
+ *
+ * @param key key value
+ * @param value value associated with key specified gets stored upon
+ * successful search.
+ * @param start_key pointer to a variable where the start key value of the
+ * segment that contains the key gets stored upon
+ * successful search.
+ * @param end_key pointer to a varaible where the end key value of the
+ * segment that contains the key gets stored upon
+ * successful search.
+ * @return a pair of const_iterator corresponding to the start position of
+ * the segment containing the key, and a boolean value indicating
+ * whether or not the search has been successful.
+ *
+ */
+ std::pair<const_iterator, bool> search(
+ key_type key, value_type& value, key_type* start_key = nullptr, key_type* end_key = nullptr) const;
+
+ /**
+ * Perform leaf-node search for a value associated with a key.
+ *
+ * @param pos position from which the search should start. When the
+ * position is invalid, it falls back to the normal search.
+ * @param key key value.
+ * @param value value associated with key specified gets stored upon
+ * successful search.
+ * @param start_key pointer to a variable where the start key value of the
+ * segment that contains the key gets stored upon
+ * successful search.
+ * @param end_key pointer to a varaible where the end key value of the
+ * segment that contains the key gets stored upon
+ * successful search.
+ * @return a pair of const_iterator corresponding to the start position of
+ * the segment containing the key, and a boolean value indicating
+ * whether or not the search has been successful.
+ */
+ std::pair<const_iterator, bool> search(
+ const const_iterator& pos, key_type key, value_type& value, key_type* start_key = nullptr,
+ key_type* end_key = nullptr) const;
+
+ /**
+ * Perform leaf-node search for a value associated with a key.
+ *
+ * @param key Key value to perform search for.
+ *
+ * @return Iterator position associated with the start position of the
+ * segment containing the key, or end iterator position upon search
+ * failure.
+ */
+ const_iterator search(key_type key) const;
+
+ /**
+ * Perform leaf-node search for a value associated with a key.
+ *
+ * @param pos Position from which the search should start if valid. In case
+ * of an invalid position, it falls back to a search starting
+ * with the first position.
+ * @param key Key value to perform search for.
+ *
+ * @return Iterator position associated with the start position of the
+ * segment containing the key, or end iterator position if the
+ * search has failed.
+ */
+ const_iterator search(const const_iterator& pos, key_type key) const;
+
+ /**
+ * Perform tree search for a value associated with a key. This method
+ * assumes that the tree is valid. Call is_tree_valid() to find out
+ * whether the tree is valid, and build_tree() to build a new tree in case
+ * it's not.
+ *
+ * @param key key value
+ * @param value value associated with key specified gets stored upon
+ * successful search.
+ * @param start_key pointer to a variable where the start key value of the
+ * segment that contains the key gets stored upon
+ * successful search.
+ * @param end_key pointer to a varaible where the end key value of the
+ * segment that contains the key gets stored upon
+ * successful search.
+ * @return a pair of const_iterator corresponding to the start position of
+ * the segment containing the key, and a boolean value indicating
+ * whether or not the search has been successful.
+ */
+ std::pair<const_iterator, bool> search_tree(
+ key_type key, value_type& value, key_type* start_key = nullptr, key_type* end_key = nullptr) const;
+
+ /**
+ * Perform tree search for a value associated with a key. The tree must be
+ * valid before performing the search, else the search will fail.
+ *
+ * @param key Key value to perform search for.
+ *
+ * @return Iterator position associated with the start position of the
+ * segment containing the key, or end iterator position if the
+ * search has failed.
+ */
+ const_iterator search_tree(key_type key) const;
+
+ /**
+ * Build a tree of non-leaf nodes based on the values stored in the leaf
+ * nodes. The tree must be valid before you can call the search_tree()
+ * method.
+ */
+ void build_tree();
+
+ /**
+ * @return true if the tree is valid, otherwise false. The tree must be
+ * valid before you can call the search_tree() method.
+ */
+ bool is_tree_valid() const
+ {
+ return m_valid_tree;
+ }
+
+ /**
+ * Equality between two flat_segment_tree instances is evaluated by
+ * comparing the keys and the values of the leaf nodes only. Neither the
+ * non-leaf nodes nor the validity of the tree is evaluated.
+ */
+ bool operator==(const flat_segment_tree& other) const;
+
+ bool operator!=(const flat_segment_tree& other) const
+ {
+ return !operator==(other);
+ }
+
+ key_type min_key() const
+ {
+ return m_left_leaf->value_leaf.key;
+ }
+
+ key_type max_key() const
+ {
+ return m_right_leaf->value_leaf.key;
+ }
+
+ value_type default_value() const
+ {
+ return m_init_val;
+ }
+
+ /**
+ * Return the number of leaf nodes.
+ *
+ * @return number of leaf nodes.
+ */
+ size_type leaf_size() const;
+
+#ifdef MDDS_UNIT_TEST
+ const nonleaf_node* get_root_node() const
+ {
+ return m_root_node;
+ }
+
+ void dump_tree() const
+ {
+ using ::std::cout;
+ using ::std::endl;
+
+ if (!m_valid_tree)
+ assert(!"attempted to dump an invalid tree!");
+
+ size_t node_count = mdds::__st::tree_dumper<node, nonleaf_node>::dump(m_root_node);
+ size_t node_instance_count = node::get_instance_count();
+ size_t leaf_count = leaf_size();
+
+ cout << "tree node count = " << node_count << "; node instance count = " << node_instance_count
+ << "; leaf node count = " << leaf_count << endl;
+
+ assert(leaf_count == node_instance_count);
+ }
+
+ void dump_leaf_nodes() const
+ {
+ using ::std::cout;
+ using ::std::endl;
+
+ cout << "------------------------------------------" << endl;
+
+ node_ptr cur_node = m_left_leaf;
+ long node_id = 0;
+ while (cur_node)
+ {
+ cout << " node " << node_id++ << ": key = " << cur_node->value_leaf.key
+ << "; value = " << cur_node->value_leaf.value << endl;
+ cur_node = cur_node->next;
+ }
+ cout << endl << " node instance count = " << node::get_instance_count() << endl;
+ }
+
+ /**
+ * Verify keys in the leaf nodes.
+ *
+ * @param key_values vector containing key values in the left-to-right
+ * order, including the key value of the rightmost leaf
+ * node.
+ */
+ bool verify_keys(const ::std::vector<key_type>& key_values) const
+ {
+ {
+ // Start from the left-most node, and traverse right.
+ node* cur_node = m_left_leaf.get();
+ typename ::std::vector<key_type>::const_iterator itr = key_values.begin(), itr_end = key_values.end();
+ for (; itr != itr_end; ++itr)
+ {
+ if (!cur_node)
+ // Position past the right-mode node. Invalid.
+ return false;
+
+ if (cur_node->value_leaf.key != *itr)
+ // Key values differ.
+ return false;
+
+ cur_node = cur_node->next.get();
+ }
+
+ if (cur_node)
+ // At this point, we expect the current node to be at the position
+ // past the right-most node, which is nullptr.
+ return false;
+ }
+
+ {
+ // Start from the right-most node, and traverse left.
+ node* cur_node = m_right_leaf.get();
+ typename ::std::vector<key_type>::const_reverse_iterator itr = key_values.rbegin(),
+ itr_end = key_values.rend();
+ for (; itr != itr_end; ++itr)
+ {
+ if (!cur_node)
+ // Position past the left-mode node. Invalid.
+ return false;
+
+ if (cur_node->value_leaf.key != *itr)
+ // Key values differ.
+ return false;
+
+ cur_node = cur_node->prev.get();
+ }
+
+ if (cur_node)
+ // Likewise, we expect the current position to be past the
+ // left-most node, in which case the node value is nullptr.
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Verify values in the leaf nodes.
+ *
+ * @param values vector containing values to verify against, in the
+ * left-to-right order, <i>not</i> including the value of
+ * the rightmost leaf node.
+ */
+ bool verify_values(const ::std::vector<value_type>& values) const
+ {
+ node* cur_node = m_left_leaf.get();
+ node* end_node = m_right_leaf.get();
+ typename ::std::vector<value_type>::const_iterator itr = values.begin(), itr_end = values.end();
+ for (; itr != itr_end; ++itr)
+ {
+ if (cur_node == end_node || !cur_node)
+ return false;
+
+ if (cur_node->value_leaf.value != *itr)
+ // Key values differ.
+ return false;
+
+ cur_node = cur_node->next.get();
+ }
+
+ if (cur_node != end_node)
+ // At this point, we expect the current node to be at the end of
+ // range.
+ return false;
+
+ return true;
+ }
+#endif
+
+private:
+ const_iterator search_by_key_impl(const node* start_pos, key_type key) const;
+
+ const node* search_tree_for_leaf_node(key_type key) const;
+
+ void append_new_segment(key_type start_key)
+ {
+ if (m_right_leaf->prev->value_leaf.key == start_key)
+ {
+ m_right_leaf->prev->value_leaf.value = m_init_val;
+ return;
+ }
+
+#ifdef MDDS_UNIT_TEST
+ // The start position must come after the position of the last node
+ // before the right-most node.
+ assert(m_right_leaf->prev->value_leaf.key < start_key);
+#endif
+
+ if (m_right_leaf->prev->value_leaf.value == m_init_val)
+ // The existing segment has the same value. No need to insert a
+ // new segment.
+ return;
+
+ node_ptr new_node(new node);
+ new_node->value_leaf.key = start_key;
+ new_node->value_leaf.value = m_init_val;
+ new_node->prev = m_right_leaf->prev;
+ new_node->next = m_right_leaf;
+ m_right_leaf->prev->next = new_node;
+ m_right_leaf->prev = new_node;
+ m_valid_tree = false;
+ }
+
+ ::std::pair<const_iterator, bool> insert_segment_impl(
+ key_type start_key, key_type end_key, value_type val, bool forward);
+
+ ::std::pair<const_iterator, bool> insert_to_pos(
+ node_ptr start_pos, key_type start_key, key_type end_key, value_type val);
+
+ ::std::pair<const_iterator, bool> search_impl(
+ const node* pos, key_type key, value_type& value, key_type* start_key, key_type* end_key) const;
+
+ const node* get_insertion_pos_leaf_reverse(const key_type& key, const node* start_pos) const;
+
+ /**
+ * Find a node with the largest value whose key equals or less than a
+ * specified key, starting with a specific node.
+ *
+ * @pre The caller must ensure that the key equals or greater than the min
+ * key.
+ *
+ * @note If the key exceeds the max key value, it will return
+ * <code>nullptr</code>.
+ */
+ const node* get_insertion_pos_leaf(const key_type& key, const node* start_pos) const;
+
+ static void shift_leaf_key_left(node_ptr& begin_node, node_ptr& end_node, key_type shift_value)
+ {
+ node* cur_node_p = begin_node.get();
+ node* end_node_p = end_node.get();
+ while (cur_node_p != end_node_p)
+ {
+ cur_node_p->value_leaf.key -= shift_value;
+ cur_node_p = cur_node_p->next.get();
+ }
+ }
+
+ static void shift_leaf_key_right(node_ptr& cur_node, node_ptr& end_node, key_type shift_value)
+ {
+ key_type end_node_key = end_node->value_leaf.key;
+ while (cur_node.get() != end_node.get())
+ {
+ cur_node->value_leaf.key += shift_value;
+ if (cur_node->value_leaf.key < end_node_key)
+ {
+ // The node is still in-bound. Keep shifting.
+ cur_node = cur_node->next;
+ continue;
+ }
+
+ // This node has been pushed outside the end node position.
+ // Remove all nodes that follows, and connect the previous node
+ // with the end node.
+
+ node_ptr last_node = cur_node->prev;
+ while (cur_node.get() != end_node.get())
+ {
+ node_ptr next_node = cur_node->next;
+ disconnect_all_nodes(cur_node.get());
+ cur_node = next_node;
+ }
+ last_node->next = end_node;
+ end_node->prev = last_node;
+ return;
+ }
+ }
+
+ void destroy();
+
+ /**
+ * Check and optionally adjust the start and end key values if one of them
+ * is out-of-bound.
+ *
+ * @return true if the start and end key values are valid, either with or
+ * without adjustments, otherwise false.
+ */
+ bool adjust_segment_range(key_type& start_key, key_type& end_key) const;
+
+private:
+ std::vector<nonleaf_node> m_nonleaf_node_pool;
+
+ const nonleaf_node* m_root_node;
+ node_ptr m_left_leaf;
+ node_ptr m_right_leaf;
+ value_type m_init_val;
+ bool m_valid_tree;
+};
+
+template<typename Key, typename Value>
+void swap(flat_segment_tree<Key, Value>& left, flat_segment_tree<Key, Value>& right)
+{
+ left.swap(right);
+}
+
+} // namespace mdds
+
+#include "flat_segment_tree_def.inl"
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/mdds/flat_segment_tree_def.inl b/include/mdds/flat_segment_tree_def.inl
new file mode 100644
index 0000000..5398b06
--- /dev/null
+++ b/include/mdds/flat_segment_tree_def.inl
@@ -0,0 +1,889 @@
+/*************************************************************************
+ *
+ * Copyright (c) 2010-2023 Kohei Yoshida
+ *
+ * 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, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ ************************************************************************/
+
+namespace mdds {
+
+template<typename Key, typename Value>
+typename flat_segment_tree<Key, Value>::const_segment_iterator flat_segment_tree<
+ Key, Value>::const_iterator::to_segment() const
+{
+ const node* pos = get_pos();
+ const auto* parent = get_parent();
+ if (!parent || is_end_pos() || !pos || !pos->next)
+ return parent->end_segment();
+
+ return const_segment_iterator(pos, pos->next.get());
+}
+
+template<typename Key, typename Value>
+flat_segment_tree<Key, Value>::const_segment_range_type::const_segment_range_type(
+ node_ptr left_leaf, node_ptr right_leaf)
+ : m_left_leaf(left_leaf), m_right_leaf(right_leaf)
+{}
+
+template<typename Key, typename Value>
+typename flat_segment_tree<Key, Value>::const_segment_iterator flat_segment_tree<
+ Key, Value>::const_segment_range_type::begin() const
+{
+ return const_segment_iterator(m_left_leaf.get(), m_left_leaf->next.get());
+}
+
+template<typename Key, typename Value>
+typename flat_segment_tree<Key, Value>::const_segment_iterator flat_segment_tree<
+ Key, Value>::const_segment_range_type::end() const
+{
+ return const_segment_iterator(m_right_leaf.get(), nullptr);
+}
+
+template<typename Key, typename Value>
+typename flat_segment_tree<Key, Value>::const_segment_iterator flat_segment_tree<Key, Value>::begin_segment() const
+{
+ return const_segment_iterator(m_left_leaf.get(), m_left_leaf->next.get());
+}
+
+template<typename Key, typename Value>
+typename flat_segment_tree<Key, Value>::const_segment_iterator flat_segment_tree<Key, Value>::end_segment() const
+{
+ return const_segment_iterator(m_right_leaf.get(), nullptr);
+}
+
+template<typename Key, typename Value>
+typename flat_segment_tree<Key, Value>::const_segment_range_type flat_segment_tree<Key, Value>::segment_range() const
+{
+ return const_segment_range_type(m_left_leaf, m_right_leaf);
+}
+
+template<typename Key, typename Value>
+flat_segment_tree<Key, Value>::flat_segment_tree(key_type min_val, key_type max_val, value_type init_val)
+ : m_root_node(nullptr), m_left_leaf(new node), m_right_leaf(new node), m_init_val(std::move(init_val)),
+ m_valid_tree(false)
+{
+ // we need to create two end nodes during initialization.
+ m_left_leaf->value_leaf.key = std::move(min_val);
+ m_left_leaf->value_leaf.value = m_init_val; // copy
+ m_left_leaf->next = m_right_leaf;
+
+ m_right_leaf->value_leaf.key = std::move(max_val);
+ m_right_leaf->prev = m_left_leaf;
+
+ // We don't ever use the value of the right leaf node, but we need the
+ // value to be always the same, to make it easier to check for
+ // equality.
+ m_right_leaf->value_leaf.value = value_type{};
+}
+
+template<typename Key, typename Value>
+flat_segment_tree<Key, Value>::flat_segment_tree(const flat_segment_tree& r)
+ : m_root_node(nullptr), m_left_leaf(new node(*r.m_left_leaf)), m_right_leaf(), m_init_val(r.m_init_val),
+ m_valid_tree(false) // tree is invalid because we only copy the leaf nodes.
+{
+ // Copy all the leaf nodes from the original instance.
+ node* src_node = r.m_left_leaf.get();
+ node_ptr dest_node = m_left_leaf;
+ while (true)
+ {
+ dest_node->next.reset(new node(*src_node->next));
+
+ // Move on to the next source node.
+ src_node = src_node->next.get();
+
+ // Move on to the next destination node, and have the next node point
+ // back to the previous node.
+ node_ptr old_node = dest_node;
+ dest_node = dest_node->next;
+ dest_node->prev = old_node;
+
+ if (src_node == r.m_right_leaf.get())
+ {
+ // Reached the right most leaf node. We can stop here.
+ m_right_leaf = dest_node;
+ break;
+ }
+ }
+}
+
+template<typename Key, typename Value>
+flat_segment_tree<Key, Value>::flat_segment_tree(flat_segment_tree&& other)
+ : m_nonleaf_node_pool(std::move(other.m_nonleaf_node_pool)), m_root_node(other.m_root_node),
+ m_left_leaf(other.m_left_leaf), m_right_leaf(other.m_right_leaf), m_init_val(other.m_init_val),
+ m_valid_tree(other.m_valid_tree)
+{
+ // NB: boost::intrusive_ptr doesn't have move constructor
+ other.m_left_leaf.reset();
+ other.m_right_leaf.reset();
+ other.m_root_node = nullptr;
+ other.m_valid_tree = false;
+}
+
+template<typename Key, typename Value>
+flat_segment_tree<Key, Value>::~flat_segment_tree()
+{
+ destroy();
+}
+
+template<typename Key, typename Value>
+flat_segment_tree<Key, Value>& flat_segment_tree<Key, Value>::operator=(const flat_segment_tree& other)
+{
+ flat_segment_tree copy(other);
+ swap(copy);
+ return *this;
+}
+
+template<typename Key, typename Value>
+flat_segment_tree<Key, Value>& flat_segment_tree<Key, Value>::operator=(flat_segment_tree&& other)
+{
+ flat_segment_tree moved(std::move(other));
+ swap(moved);
+ return *this;
+}
+
+template<typename Key, typename Value>
+void flat_segment_tree<Key, Value>::swap(flat_segment_tree& other)
+{
+ m_nonleaf_node_pool.swap(other.m_nonleaf_node_pool);
+ std::swap(m_root_node, other.m_root_node);
+ std::swap(m_left_leaf, other.m_left_leaf);
+ std::swap(m_right_leaf, other.m_right_leaf);
+ std::swap(m_init_val, other.m_init_val);
+ std::swap(m_valid_tree, other.m_valid_tree);
+}
+
+template<typename Key, typename Value>
+void flat_segment_tree<Key, Value>::clear()
+{
+ // the border nodes should not be destroyed--add a ref to keep them alive
+ node_ptr left(m_left_leaf);
+ node_ptr right(m_right_leaf);
+
+ // destroy the tree
+ destroy();
+
+ // and construct the default tree
+ __st::link_nodes<flat_segment_tree>(m_left_leaf, m_right_leaf);
+ m_left_leaf->value_leaf.value = m_init_val;
+ m_valid_tree = false;
+}
+
+template<typename Key, typename Value>
+::std::pair<typename flat_segment_tree<Key, Value>::const_iterator, bool> flat_segment_tree<
+ Key, Value>::insert_segment_impl(key_type start_key, key_type end_key, value_type val, bool forward)
+{
+ typedef std::pair<typename flat_segment_tree<Key, Value>::const_iterator, bool> ret_type;
+
+ if (!adjust_segment_range(start_key, end_key))
+ return ret_type(const_iterator(this, true), false);
+
+ // Find the node with value that either equals or is greater than the
+ // start value.
+
+ node_ptr start_pos;
+ if (forward)
+ {
+ const node* p = get_insertion_pos_leaf(start_key, m_left_leaf.get());
+ start_pos.reset(const_cast<node*>(p));
+ }
+ else
+ {
+ const node* p = get_insertion_pos_leaf_reverse(start_key, m_right_leaf.get());
+ if (p)
+ start_pos = p->next;
+ else
+ start_pos = m_left_leaf;
+ }
+ if (!start_pos)
+ {
+ // Insertion position not found. Bail out.
+ assert(!"Insertion position not found. Bail out");
+ return ret_type(const_iterator(this, true), false);
+ }
+
+ return insert_to_pos(start_pos, std::move(start_key), std::move(end_key), std::move(val));
+}
+
+template<typename Key, typename Value>
+::std::pair<typename flat_segment_tree<Key, Value>::const_iterator, bool> flat_segment_tree<Key, Value>::insert_to_pos(
+ node_ptr start_pos, key_type start_key, key_type end_key, value_type val)
+{
+ node_ptr end_pos;
+ {
+ const node* p = get_insertion_pos_leaf(end_key, start_pos.get());
+ end_pos.reset(const_cast<node*>(p));
+ }
+ if (!end_pos)
+ end_pos = m_right_leaf;
+
+ node_ptr new_start_node;
+ value_type old_value;
+
+ // Set the start node.
+
+ bool changed = false;
+
+ if (start_pos->value_leaf.key == start_key)
+ {
+ // Re-use the existing node, but save the old value for later.
+
+ if (start_pos->prev && start_pos->prev->value_leaf.value == val)
+ {
+ // Extend the existing segment.
+ old_value = start_pos->value_leaf.value;
+ new_start_node = start_pos->prev;
+ }
+ else
+ {
+ // Update the value of the existing node.
+ old_value = start_pos->value_leaf.value;
+ start_pos->value_leaf.value = val;
+ new_start_node = start_pos;
+
+ changed = (old_value != val);
+ }
+ }
+ else if (start_pos->prev->value_leaf.value == val)
+ {
+ // Extend the existing segment.
+ old_value = start_pos->prev->value_leaf.value;
+ new_start_node = start_pos->prev;
+ }
+ else
+ {
+ // Insert a new node before the insertion position node.
+ node_ptr new_node(new node);
+ new_node->value_leaf.key = std::move(start_key);
+ new_node->value_leaf.value = val;
+ new_start_node = new_node;
+
+ node_ptr left_node = start_pos->prev;
+ old_value = left_node->value_leaf.value;
+
+ // Link to the left node.
+ __st::link_nodes<flat_segment_tree>(left_node, new_node);
+
+ // Link to the right node.
+ __st::link_nodes<flat_segment_tree>(new_node, start_pos);
+ changed = true;
+ }
+
+ node_ptr cur_node = new_start_node->next;
+ while (cur_node != end_pos)
+ {
+ // Disconnect the link between the current node and the previous node.
+ cur_node->prev->next.reset();
+ cur_node->prev.reset();
+ old_value = cur_node->value_leaf.value;
+
+ cur_node = cur_node->next;
+ changed = true;
+ }
+
+ // Set the end node.
+
+ if (end_pos->value_leaf.key == end_key)
+ {
+ // The new segment ends exactly at the end node position.
+
+ if (end_pos->next && end_pos->value_leaf.value == val)
+ {
+ // Remove this node, and connect the new start node with the
+ // node that comes after this node.
+ new_start_node->next = end_pos->next;
+ if (end_pos->next)
+ end_pos->next->prev = new_start_node;
+ disconnect_all_nodes(end_pos.get());
+ changed = true;
+ }
+ else if (new_start_node->next != end_pos)
+ {
+ // Just link the new segment to this node.
+ new_start_node->next = end_pos;
+ end_pos->prev = new_start_node;
+ changed = true;
+ }
+ }
+ else if (old_value == val)
+ {
+ if (new_start_node->next != end_pos)
+ {
+ __st::link_nodes<flat_segment_tree>(new_start_node, end_pos);
+ changed = true;
+ }
+ }
+ else
+ {
+ // Insert a new node before the insertion position node.
+ node_ptr new_node(new node);
+ new_node->value_leaf.key = std::move(end_key);
+ new_node->value_leaf.value = old_value;
+
+ // Link to the left node.
+ __st::link_nodes<flat_segment_tree>(new_start_node, new_node);
+
+ // Link to the right node.
+ __st::link_nodes<flat_segment_tree>(new_node, end_pos);
+ changed = true;
+ }
+
+ if (changed)
+ m_valid_tree = false;
+
+ return ::std::pair<const_iterator, bool>(const_iterator(this, new_start_node.get()), changed);
+}
+
+template<typename Key, typename Value>
+::std::pair<typename flat_segment_tree<Key, Value>::const_iterator, bool> flat_segment_tree<Key, Value>::insert(
+ const const_iterator& pos, key_type start_key, key_type end_key, value_type val)
+{
+ const node* p = pos.get_pos();
+ if (!p || this != pos.get_parent())
+ {
+ // Switch to normal insert.
+ return insert_front(start_key, end_key, val);
+ }
+
+ assert(p->is_leaf);
+
+ if (start_key < p->value_leaf.key)
+ {
+ // Specified position is already past the start key position. Not good.
+ return insert_front(start_key, end_key, val);
+ }
+
+ if (!adjust_segment_range(start_key, end_key))
+ {
+ typedef std::pair<typename flat_segment_tree<Key, Value>::const_iterator, bool> ret_type;
+ return ret_type(const_iterator(this, true), false);
+ }
+
+ p = get_insertion_pos_leaf(start_key, p);
+ node_ptr start_pos(const_cast<node*>(p));
+ return insert_to_pos(start_pos, start_key, end_key, val);
+}
+
+template<typename Key, typename Value>
+void flat_segment_tree<Key, Value>::shift_left(key_type start_key, key_type end_key)
+{
+ if (start_key >= end_key)
+ return;
+
+ key_type left_leaf_key = m_left_leaf->value_leaf.key;
+ key_type right_leaf_key = m_right_leaf->value_leaf.key;
+ if (start_key < left_leaf_key || end_key < left_leaf_key)
+ // invalid key value
+ return;
+
+ if (start_key > right_leaf_key || end_key > right_leaf_key)
+ // invalid key value.
+ return;
+
+ node_ptr node_pos;
+ if (left_leaf_key == start_key)
+ node_pos = m_left_leaf;
+ else
+ {
+ // Get the first node with a key value equal to or greater than the
+ // start key value. But we want to skip the leftmost node.
+ const node* p = get_insertion_pos_leaf(start_key, m_left_leaf->next.get());
+ node_pos.reset(const_cast<node*>(p));
+ }
+
+ if (!node_pos)
+ return;
+
+ key_type segment_size = end_key - start_key;
+
+ if (node_pos == m_right_leaf)
+ {
+ // The segment being removed begins after the last node before the
+ // right-most node.
+
+ if (right_leaf_key <= end_key)
+ {
+ // The end position equals or is past the right-most node.
+ append_new_segment(start_key);
+ }
+ else
+ {
+ // The end position stops before the right-most node. Simply
+ // append the blank segment to the end.
+ append_new_segment(right_leaf_key - segment_size);
+ }
+ return;
+ }
+
+ if (end_key < node_pos->value_leaf.key)
+ {
+ // The removed segment does not overlap with any nodes. Simply
+ // shift the key values of those nodes that come after the removed
+ // segment.
+ shift_leaf_key_left(node_pos, m_right_leaf, segment_size);
+ append_new_segment(right_leaf_key - segment_size);
+ m_valid_tree = false;
+ return;
+ }
+
+ // Move the first node to the starting position, and from there search
+ // for the first node whose key value is greater than the end value.
+ node_pos->value_leaf.key = start_key;
+ node_ptr start_pos = node_pos;
+ node_pos = node_pos->next;
+ value_type last_seg_value = start_pos->value_leaf.value;
+ while (node_pos.get() != m_right_leaf.get() && node_pos->value_leaf.key <= end_key)
+ {
+ last_seg_value = node_pos->value_leaf.value;
+ node_ptr next = node_pos->next;
+ disconnect_all_nodes(node_pos.get());
+ node_pos = next;
+ }
+
+ start_pos->value_leaf.value = last_seg_value;
+ start_pos->next = node_pos;
+ node_pos->prev = start_pos;
+ if (start_pos->prev && start_pos->prev->value_leaf.value == start_pos->value_leaf.value)
+ {
+ // Removing a segment resulted in two consecutive segments with
+ // identical value. Combine them by removing the 2nd redundant
+ // node.
+ start_pos->prev->next = start_pos->next;
+ start_pos->next->prev = start_pos->prev;
+ disconnect_all_nodes(start_pos.get());
+ }
+
+ shift_leaf_key_left(node_pos, m_right_leaf, segment_size);
+ m_valid_tree = false;
+
+ // Insert at the end a new segment with the initial base value, for
+ // the length of the removed segment.
+ append_new_segment(right_leaf_key - segment_size);
+}
+
+template<typename Key, typename Value>
+void flat_segment_tree<Key, Value>::shift_right(key_type pos, key_type size, bool skip_start_node)
+{
+ if (size <= 0)
+ return;
+
+ if (pos < m_left_leaf->value_leaf.key || m_right_leaf->value_leaf.key <= pos)
+ // specified position is out-of-bound
+ return;
+
+ if (m_left_leaf->value_leaf.key == pos)
+ {
+ // Position is at the leftmost node. Shift all the other nodes,
+ // and insert a new node at (pos + size) position.
+ node_ptr cur_node = m_left_leaf->next;
+ shift_leaf_key_right(cur_node, m_right_leaf, size);
+
+ if (m_left_leaf->value_leaf.value != m_init_val && !skip_start_node)
+ {
+ if (size < m_right_leaf->value_leaf.key - m_left_leaf->value_leaf.key)
+ {
+ // The leftmost leaf node has a non-initial value. We need to
+ // insert a new node to carry that value after the shift.
+ node_ptr new_node(new node);
+ new_node->value_leaf.key = pos + size;
+ new_node->value_leaf.value = m_left_leaf->value_leaf.value;
+ m_left_leaf->value_leaf.value = m_init_val;
+ new_node->prev = m_left_leaf;
+ new_node->next = m_left_leaf->next;
+ m_left_leaf->next->prev = new_node;
+ m_left_leaf->next = new_node;
+ }
+ else
+ {
+ // We shifted out the whole range, so there would be no new
+ // node inserted. Just set default value.
+ m_left_leaf->value_leaf.value = m_init_val;
+ }
+ }
+
+ m_valid_tree = false;
+ return;
+ }
+
+ // Get the first node with a key value equal to or greater than the
+ // start key value. But we want to skip the leftmost node.
+ const node* p = get_insertion_pos_leaf(pos, m_left_leaf->next.get());
+ node_ptr cur_node(const_cast<node*>(p));
+
+ // If the point of insertion is at an existing node position, don't
+ // shift that node but start with the one after it if that's
+ // requested.
+ if (skip_start_node && cur_node && cur_node->value_leaf.key == pos)
+ cur_node = cur_node->next;
+
+ if (!cur_node)
+ return;
+
+ shift_leaf_key_right(cur_node, m_right_leaf, size);
+ m_valid_tree = false;
+}
+
+template<typename Key, typename Value>
+::std::pair<typename flat_segment_tree<Key, Value>::const_iterator, bool> flat_segment_tree<Key, Value>::search_impl(
+ const node* pos, key_type key, value_type& value, key_type* start_key, key_type* end_key) const
+{
+ typedef ::std::pair<const_iterator, bool> ret_type;
+
+ if (pos->value_leaf.key == key)
+ {
+ value = pos->value_leaf.value;
+ if (start_key)
+ *start_key = pos->value_leaf.key;
+ if (end_key && pos->next)
+ *end_key = pos->next->value_leaf.key;
+ return ret_type(const_iterator(this, pos), true);
+ }
+ else if (pos->prev && pos->prev->value_leaf.key < key)
+ {
+ value = pos->prev->value_leaf.value;
+ if (start_key)
+ *start_key = pos->prev->value_leaf.key;
+ if (end_key)
+ *end_key = pos->value_leaf.key;
+ return ret_type(const_iterator(this, pos->prev.get()), true);
+ }
+
+ return ret_type(const_iterator(this, true), false);
+}
+
+template<typename Key, typename Value>
+::std::pair<typename flat_segment_tree<Key, Value>::const_iterator, bool> flat_segment_tree<Key, Value>::search(
+ key_type key, value_type& value, key_type* start_key, key_type* end_key) const
+{
+ typedef ::std::pair<const_iterator, bool> ret_type;
+
+ if (key < m_left_leaf->value_leaf.key || m_right_leaf->value_leaf.key <= key)
+ // key value is out-of-bound.
+ return ret_type(const_iterator(this, true), false);
+
+ const node* pos = get_insertion_pos_leaf(key, m_left_leaf.get());
+ return search_impl(pos, key, value, start_key, end_key);
+}
+
+template<typename Key, typename Value>
+::std::pair<typename flat_segment_tree<Key, Value>::const_iterator, bool> flat_segment_tree<Key, Value>::search(
+ const const_iterator& pos, key_type key, value_type& value, key_type* start_key, key_type* end_key) const
+{
+ typedef ::std::pair<const_iterator, bool> ret_type;
+
+ if (key < m_left_leaf->value_leaf.key || m_right_leaf->value_leaf.key <= key)
+ // key value is out-of-bound.
+ return ret_type(const_iterator(this, true), false);
+
+ const node* p = pos.get_pos();
+ if (!p || this != pos.get_parent())
+ {
+ // Switch to normal search.
+ return search(key, value, start_key, end_key);
+ }
+
+ assert(p->is_leaf);
+
+ if (key < p->value_leaf.key)
+ {
+ // Specified position is already past the start key position. Fall
+ // back to normal search.
+ return search(key, value, start_key, end_key);
+ }
+
+ p = get_insertion_pos_leaf(key, p);
+ return search_impl(p, key, value, start_key, end_key);
+}
+
+template<typename Key, typename Value>
+typename flat_segment_tree<Key, Value>::const_iterator flat_segment_tree<Key, Value>::search(key_type key) const
+{
+ return search_by_key_impl(m_left_leaf.get(), key);
+}
+
+template<typename Key, typename Value>
+typename flat_segment_tree<Key, Value>::const_iterator flat_segment_tree<Key, Value>::search(
+ const const_iterator& pos, key_type key) const
+{
+ return search_by_key_impl(pos.get_pos(), key);
+}
+
+template<typename Key, typename Value>
+typename flat_segment_tree<Key, Value>::const_iterator flat_segment_tree<Key, Value>::search_by_key_impl(
+ const node* start_pos, key_type key) const
+{
+ if (key < m_left_leaf->value_leaf.key || m_right_leaf->value_leaf.key <= key)
+ // key value is out-of-bound.
+ return const_iterator(this, true);
+
+ const node* pos = get_insertion_pos_leaf(key, start_pos);
+ if (!pos)
+ return const_iterator(this, true);
+
+ if (pos->value_leaf.key == key)
+ return const_iterator(this, pos);
+ else if (pos->prev && pos->prev->value_leaf.key < key)
+ return const_iterator(this, pos->prev.get());
+
+ return const_iterator(this, true);
+}
+
+template<typename Key, typename Value>
+std::pair<typename flat_segment_tree<Key, Value>::const_iterator, bool> flat_segment_tree<Key, Value>::search_tree(
+ key_type key, value_type& value, key_type* start_key, key_type* end_key) const
+{
+ using ret_type = std::pair<const_iterator, bool>;
+
+ const node* dest_node = search_tree_for_leaf_node(key);
+ if (!dest_node)
+ return ret_type(const_iterator(this, true), false);
+
+ value = dest_node->value_leaf.value;
+ if (start_key)
+ *start_key = dest_node->value_leaf.key;
+
+ if (end_key)
+ {
+ assert(dest_node->next);
+ if (dest_node->next)
+ *end_key = dest_node->next->value_leaf.key;
+ else
+ // This should never happen, but just in case....
+ *end_key = m_right_leaf->value_leaf.key;
+ }
+
+ return ret_type(const_iterator(this, dest_node), true);
+}
+
+template<typename Key, typename Value>
+typename flat_segment_tree<Key, Value>::const_iterator flat_segment_tree<Key, Value>::search_tree(key_type key) const
+{
+ const node* dest_node = search_tree_for_leaf_node(key);
+ if (!dest_node)
+ return const_iterator(this, true);
+
+ return const_iterator(this, dest_node);
+}
+
+template<typename Key, typename Value>
+const typename flat_segment_tree<Key, Value>::node* flat_segment_tree<Key, Value>::search_tree_for_leaf_node(
+ key_type key) const
+{
+ if (!m_root_node || !m_valid_tree)
+ {
+ // either tree has not been built, or is in an invalid state.
+ return nullptr;
+ }
+
+ if (key < m_left_leaf->value_leaf.key || m_right_leaf->value_leaf.key <= key)
+ {
+ // key value is out-of-bound.
+ return nullptr;
+ }
+
+ // Descend down the tree through the last non-leaf layer.
+
+ const nonleaf_node* cur_node = m_root_node;
+ while (true)
+ {
+ if (cur_node->left)
+ {
+ if (cur_node->left->is_leaf)
+ break;
+
+ const nonleaf_node* left_nonleaf = static_cast<const nonleaf_node*>(cur_node->left);
+ const nonleaf_value_type& v = left_nonleaf->value_nonleaf;
+ if (v.low <= key && key < v.high)
+ {
+ // Descend one level through the left child node.
+ cur_node = left_nonleaf;
+ continue;
+ }
+ }
+ else
+ {
+ // left child node can't be missing !
+ return nullptr;
+ }
+
+ if (cur_node->right)
+ {
+ assert(!cur_node->right->is_leaf);
+ const nonleaf_node* right_nonleaf = static_cast<const nonleaf_node*>(cur_node->right);
+ const nonleaf_value_type& v = right_nonleaf->value_nonleaf;
+ if (v.low <= key && key < v.high)
+ {
+ // Descend one level through the right child node.
+ cur_node = right_nonleaf;
+ continue;
+ }
+ }
+ return nullptr;
+ }
+
+ // Current node must be a non-leaf whose child nodes are leaf nodes.
+ assert(cur_node->left->is_leaf && cur_node->right->is_leaf);
+
+ const node* dest_node = nullptr;
+ const node* leaf_left = static_cast<const node*>(cur_node->left);
+ const node* leaf_right = static_cast<const node*>(cur_node->right);
+ key_type key1 = leaf_left->value_leaf.key;
+ key_type key2 = leaf_right->value_leaf.key;
+
+ if (key1 <= key && key < key2)
+ {
+ dest_node = leaf_left;
+ }
+ else if (key2 <= key && key < cur_node->value_nonleaf.high)
+ {
+ dest_node = leaf_right;
+ }
+
+ if (!dest_node)
+ return nullptr;
+
+ return dest_node;
+}
+
+template<typename Key, typename Value>
+void flat_segment_tree<Key, Value>::build_tree()
+{
+ if (!m_left_leaf)
+ return;
+
+ m_nonleaf_node_pool.clear();
+
+ // Count the number of leaf nodes.
+ size_t leaf_count = leaf_size();
+
+ // Determine the total number of non-leaf nodes needed to build the whole tree.
+ size_t nonleaf_count = __st::count_needed_nonleaf_nodes(leaf_count);
+
+ m_nonleaf_node_pool.resize(nonleaf_count);
+ mdds::__st::tree_builder<flat_segment_tree> builder(m_nonleaf_node_pool);
+ m_root_node = builder.build(m_left_leaf);
+ m_valid_tree = true;
+}
+
+template<typename Key, typename Value>
+typename flat_segment_tree<Key, Value>::size_type flat_segment_tree<Key, Value>::leaf_size() const
+{
+ return __st::count_leaf_nodes(m_left_leaf.get(), m_right_leaf.get());
+}
+
+template<typename Key, typename Value>
+bool flat_segment_tree<Key, Value>::operator==(const flat_segment_tree& other) const
+{
+ const node* n1 = m_left_leaf.get();
+ const node* n2 = other.m_left_leaf.get();
+
+ if ((!n1 && n2) || (n1 && !n2))
+ // Either one of them is nullptr;
+ return false;
+
+ while (n1)
+ {
+ if (!n2)
+ return false;
+
+ if (!n1->equals(*n2))
+ return false;
+
+ n1 = n1->next.get();
+ n2 = n2->next.get();
+ }
+
+ if (n2)
+ // n1 is nullptr, but n2 is not.
+ return false;
+
+ // All leaf nodes are equal.
+ return true;
+}
+
+template<typename Key, typename Value>
+const typename flat_segment_tree<Key, Value>::node* flat_segment_tree<Key, Value>::get_insertion_pos_leaf_reverse(
+ const key_type& key, const node* start_pos) const
+{
+ const node* cur_node = start_pos;
+ while (cur_node)
+ {
+ if (cur_node->value_leaf.key < key)
+ {
+ // Found the insertion position.
+ return cur_node;
+ }
+ cur_node = cur_node->prev.get();
+ }
+ return nullptr;
+}
+
+template<typename Key, typename Value>
+const typename flat_segment_tree<Key, Value>::node* flat_segment_tree<Key, Value>::get_insertion_pos_leaf(
+ const key_type& key, const node* start_pos) const
+{
+ assert(m_left_leaf->value_leaf.key <= key);
+
+ const node* cur_node = start_pos;
+ while (cur_node)
+ {
+ if (key <= cur_node->value_leaf.key)
+ {
+ // Found the insertion position.
+ return cur_node;
+ }
+ cur_node = cur_node->next.get();
+ }
+ return nullptr;
+}
+
+template<typename Key, typename Value>
+void flat_segment_tree<Key, Value>::destroy()
+{
+ disconnect_leaf_nodes(m_left_leaf.get(), m_right_leaf.get());
+ m_nonleaf_node_pool.clear();
+ m_root_node = nullptr;
+}
+
+template<typename Key, typename Value>
+bool flat_segment_tree<Key, Value>::adjust_segment_range(key_type& start_key, key_type& end_key) const
+{
+ if (end_key <= start_key)
+ // Invalid order of segment range.
+ return false;
+
+ if (end_key < m_left_leaf->value_leaf.key || m_right_leaf->value_leaf.key <= start_key)
+ // The new segment does not overlap the current interval.
+ return false;
+
+ if (start_key < m_left_leaf->value_leaf.key)
+ // The start value should not be smaller than the current minimum.
+ start_key = m_left_leaf->value_leaf.key;
+
+ if (m_right_leaf->value_leaf.key < end_key)
+ // The end value should not be larger than the current maximum.
+ end_key = m_right_leaf->value_leaf.key;
+
+ return true;
+}
+
+} // namespace mdds
diff --git a/include/mdds/flat_segment_tree_itr.hpp b/include/mdds/flat_segment_tree_itr.hpp
new file mode 100644
index 0000000..77b077d
--- /dev/null
+++ b/include/mdds/flat_segment_tree_itr.hpp
@@ -0,0 +1,348 @@
+/*************************************************************************
+ *
+ * Copyright (c) 2010-2023 Kohei Yoshida
+ *
+ * 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, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ ************************************************************************/
+
+#ifndef INCLUDED_MDDS_FLAT_SEGMENT_TREE_ITR_HPP
+#define INCLUDED_MDDS_FLAT_SEGMENT_TREE_ITR_HPP
+
+namespace mdds { namespace fst { namespace detail {
+
+/**
+ * Handler for forward iterator
+ */
+template<typename FstType>
+struct forward_itr_handler
+{
+ using fst_type = FstType;
+
+ static const typename fst_type::node* init_pos(const fst_type* _db, bool _end)
+ {
+ return _end ? _db->m_right_leaf.get() : _db->m_left_leaf.get();
+ }
+
+ static void inc(const fst_type* _db, const typename fst_type::node*& p, bool& end)
+ {
+ if (p == _db->m_right_leaf.get())
+ end = true;
+ else
+ p = p->next.get();
+ }
+
+ static void dec(const typename fst_type::node*& p, bool& end)
+ {
+ if (end)
+ end = false;
+ else
+ p = p->prev.get();
+ }
+};
+
+/**
+ * Handler for reverse iterator
+ */
+template<typename FstType>
+struct reverse_itr_handler
+{
+ using fst_type = FstType;
+
+ static const typename fst_type::node* init_pos(const fst_type* _db, bool _end)
+ {
+ return _end ? _db->m_left_leaf.get() : _db->m_right_leaf.get();
+ }
+
+ static void inc(const fst_type* _db, const typename fst_type::node*& p, bool& end)
+ {
+ if (p == _db->m_left_leaf.get())
+ end = true;
+ else
+ p = p->prev.get();
+ }
+
+ static void dec(const typename fst_type::node*& p, bool& end)
+ {
+ if (end)
+ end = false;
+ else
+ p = p->next.get();
+ }
+};
+
+template<typename FstType, typename Hdl>
+class const_iterator_base
+{
+ typedef Hdl handler_type;
+
+public:
+ typedef FstType fst_type;
+
+ // iterator traits
+ typedef ::std::pair<typename fst_type::key_type, typename fst_type::value_type> value_type;
+ typedef value_type* pointer;
+ typedef value_type& reference;
+ typedef ptrdiff_t difference_type;
+ typedef ::std::bidirectional_iterator_tag iterator_category;
+
+ explicit const_iterator_base(const fst_type* _db, bool _end) : m_db(_db), m_pos(nullptr), m_end_pos(_end)
+ {
+ if (!_db)
+ return;
+
+ m_pos = handler_type::init_pos(_db, _end);
+ }
+
+ explicit const_iterator_base(const fst_type* _db, const typename fst_type::node* pos)
+ : m_db(_db), m_pos(pos), m_end_pos(false)
+ {}
+
+ const_iterator_base(const const_iterator_base& r) : m_db(r.m_db), m_pos(r.m_pos), m_end_pos(r.m_end_pos)
+ {}
+
+ const_iterator_base& operator=(const const_iterator_base& r)
+ {
+ m_db = r.m_db;
+ m_pos = r.m_pos;
+ m_end_pos = r.m_end_pos;
+ return *this;
+ }
+
+ const_iterator_base& operator++()
+ {
+ assert(m_pos);
+ handler_type::inc(m_db, m_pos, m_end_pos);
+ return *this;
+ }
+
+ const_iterator_base& operator--()
+ {
+ assert(m_pos);
+ handler_type::dec(m_pos, m_end_pos);
+ return *this;
+ }
+
+ bool operator==(const const_iterator_base& r) const
+ {
+ if (m_db != r.m_db)
+ return false;
+
+ return (m_pos == r.m_pos) && (m_end_pos == r.m_end_pos);
+ }
+
+ bool operator!=(const const_iterator_base& r) const
+ {
+ return !operator==(r);
+ }
+
+ const value_type& operator*()
+ {
+ return get_current_node_pair();
+ }
+
+ const value_type* operator->()
+ {
+ return &get_current_node_pair();
+ }
+
+protected:
+ const typename fst_type::node* get_pos() const
+ {
+ return m_pos;
+ }
+
+ const fst_type* get_parent() const
+ {
+ return m_db;
+ }
+
+ bool is_end_pos() const
+ {
+ return m_end_pos;
+ }
+
+private:
+ const value_type& get_current_node_pair()
+ {
+ m_current_pair = value_type(m_pos->value_leaf.key, m_pos->value_leaf.value);
+ return m_current_pair;
+ }
+
+ const fst_type* m_db;
+ const typename fst_type::node* m_pos;
+ value_type m_current_pair;
+ bool m_end_pos;
+};
+
+template<typename FstType>
+class const_segment_iterator
+{
+ typedef FstType fst_type;
+ friend fst_type;
+
+ const_segment_iterator(const typename fst_type::node* start, const typename fst_type::node* end)
+ : m_start(start), m_end(end)
+ {
+ update_node();
+ }
+
+public:
+ struct value_type
+ {
+ typename fst_type::key_type start;
+ typename fst_type::key_type end;
+ typename fst_type::value_type value;
+
+ value_type() : start(), end(), value()
+ {}
+
+ value_type(
+ typename fst_type::key_type _start, typename fst_type::key_type _end, typename fst_type::value_type _value)
+ : start(std::move(_start)), end(std::move(_end)), value(std::move(_value))
+ {}
+
+ bool operator==(const value_type& other) const
+ {
+ return start == other.start && end == other.end && value == other.value;
+ }
+
+ bool operator!=(const value_type& other) const
+ {
+ return !operator==(other);
+ }
+ };
+
+ const_segment_iterator() : m_start(nullptr), m_end(nullptr)
+ {}
+ const_segment_iterator(const const_segment_iterator& other) : m_start(other.m_start), m_end(other.m_end)
+ {
+ if (m_start)
+ update_node();
+ }
+ const_segment_iterator(const_segment_iterator&& other)
+ : m_start(std::move(other.m_start)), m_end(std::move(other.m_end))
+ {
+ if (m_start)
+ update_node();
+ }
+
+ ~const_segment_iterator()
+ {}
+
+ bool operator==(const const_segment_iterator& other) const
+ {
+ return m_start == other.m_start && m_end == other.m_end;
+ }
+
+ bool operator!=(const const_segment_iterator& other) const
+ {
+ return !operator==(other);
+ }
+
+ const_segment_iterator& operator=(const const_segment_iterator& other)
+ {
+ m_start = other.m_start;
+ m_end = other.m_end;
+ if (m_start)
+ update_node();
+ return *this;
+ }
+
+ const_segment_iterator& operator=(const_segment_iterator&& other)
+ {
+ m_start = std::move(other.m_start);
+ m_end = std::move(other.m_end);
+ if (m_start)
+ update_node();
+ return *this;
+ }
+
+ const value_type& operator*()
+ {
+ return m_node;
+ }
+
+ const value_type* operator->()
+ {
+ return &m_node;
+ }
+
+ const_segment_iterator& operator++()
+ {
+ assert(m_start);
+ m_start = m_start->next.get();
+ m_end = m_start->next.get();
+ update_node();
+ return *this;
+ }
+
+ const_segment_iterator operator++(int)
+ {
+ assert(m_start);
+ const_segment_iterator ret = *this;
+ m_start = m_start->next.get();
+ m_end = m_start->next.get();
+ update_node();
+ return ret;
+ }
+
+ const_segment_iterator& operator--()
+ {
+ assert(m_start);
+ m_start = m_start->prev.get();
+ m_end = m_start->next.get();
+ update_node();
+ return *this;
+ }
+
+ const_segment_iterator operator--(int)
+ {
+ assert(m_start);
+ const_segment_iterator ret = *this;
+ m_start = m_start->prev.get();
+ m_end = m_start->next.get();
+ update_node();
+ return ret;
+ }
+
+private:
+ void update_node()
+ {
+ if (!m_end)
+ // The iterator is at its end position. Nothing to do.
+ return;
+
+ m_node.start = m_start->value_leaf.key;
+ m_node.end = m_end->value_leaf.key;
+ m_node.value = m_start->value_leaf.value;
+ }
+
+private:
+ const typename fst_type::node* m_start;
+ const typename fst_type::node* m_end;
+ value_type m_node;
+};
+
+}}} // namespace mdds::fst::detail
+
+#endif
diff --git a/include/mdds/global.hpp b/include/mdds/global.hpp
new file mode 100644
index 0000000..ba5f770
--- /dev/null
+++ b/include/mdds/global.hpp
@@ -0,0 +1,216 @@
+/*************************************************************************
+ *
+ * Copyright (c) 2008-2020 Kohei Yoshida
+ *
+ * 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, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ ************************************************************************/
+
+#ifndef INCLUDED_MDDS_GLOBAL_HPP
+#define INCLUDED_MDDS_GLOBAL_HPP
+
+#include <exception>
+#include <string>
+#include <memory>
+#include <utility>
+#include <type_traits>
+
+/**
+ * \def MDDS_ASCII(literal)
+ *
+ * Expands a \a literal string into two arguments: the first one is the
+ * literal string itself, and the second one is the length of that string.
+ *
+ * Note that this macro only works with literal strings defined inline; it
+ * does not work with pointer values that point to strings defined
+ * elsewhere.
+ */
+#define MDDS_ASCII(literal) literal, sizeof(literal) - 1
+
+/**
+ * \def MDDS_N_ELEMENTS(name)
+ *
+ * Calculates the length of \a name array provided that the array definition
+ * is given in the same compilation unit.
+ *
+ * @deprecated Please use \c std::size instead.
+ */
+#define MDDS_N_ELEMENTS(name) sizeof(name) / sizeof(name[0])
+
+#ifdef __GNUC__
+#define MDDS_DEPRECATED __attribute__((deprecated))
+#elif defined(_MSC_VER)
+#define MDDS_DEPRECATED __declspec(deprecated)
+#else
+#define MDDS_DEPRECATED
+#endif
+
+#ifndef MDDS_LOOP_UNROLLING
+#define MDDS_LOOP_UNROLLING 1
+#endif
+
+#ifndef MDDS_USE_OPENMP
+#define MDDS_USE_OPENMP 0
+#endif
+
+#if defined(__AVX__) || defined(__AVX2__)
+#ifndef __SSE2__
+#define __SSE2__ 1
+#endif
+#endif
+
+namespace mdds {
+
+class general_error : public ::std::exception
+{
+public:
+ general_error(const ::std::string& msg) : m_msg(msg)
+ {}
+ virtual ~general_error() noexcept
+ {}
+
+ virtual const char* what() const noexcept
+ {
+ return m_msg.c_str();
+ }
+
+private:
+ ::std::string m_msg;
+};
+
+class invalid_arg_error : public general_error
+{
+public:
+ invalid_arg_error(const ::std::string& msg) : general_error(msg)
+ {}
+};
+
+class size_error : public general_error
+{
+public:
+ size_error(const std::string& msg) : general_error(msg)
+ {}
+};
+
+class type_error : public general_error
+{
+public:
+ type_error(const std::string& msg) : general_error(msg)
+ {}
+};
+
+class integrity_error : public general_error
+{
+public:
+ integrity_error(const std::string& msg) : general_error(msg)
+ {}
+};
+
+namespace detail {
+
+template<typename T>
+class has_value_type
+{
+ using y_type = char;
+ using n_type = long;
+
+ template<typename U>
+ static y_type test(typename U::value_type);
+ template<typename U>
+ static n_type test(...);
+
+public:
+ static constexpr bool value = sizeof(test<T>(0)) == sizeof(y_type);
+};
+
+template<typename T, typename IsConst>
+struct const_or_not;
+
+template<typename T>
+struct const_or_not<T, std::true_type>
+{
+ using type = typename std::add_const<T>::type;
+};
+
+template<typename T>
+struct const_or_not<T, std::false_type>
+{
+ using type = T;
+};
+
+template<typename T, bool Const>
+using const_t = typename const_or_not<T, std::bool_constant<Const>>::type;
+
+template<typename T, typename Mutable>
+struct mutable_or_not;
+
+template<typename T>
+struct mutable_or_not<T, std::true_type>
+{
+ using type = T;
+};
+
+template<typename T>
+struct mutable_or_not<T, std::false_type>
+{
+ using type = typename std::add_const<T>::type;
+};
+
+template<typename T, bool Mutable>
+using mutable_t = typename mutable_or_not<T, std::bool_constant<Mutable>>::type;
+
+template<typename T, typename IsConst>
+struct get_iterator_type;
+
+template<typename T>
+struct get_iterator_type<T, std::true_type>
+{
+ using type = typename T::const_iterator;
+};
+
+template<typename T>
+struct get_iterator_type<T, std::false_type>
+{
+ using type = typename T::iterator;
+};
+
+template<int T>
+constexpr bool invalid_static_int()
+{
+ return false;
+}
+
+template<typename T, typename = void>
+struct is_complete : std::false_type
+{
+};
+
+template<typename T>
+struct is_complete<T, std::void_t<decltype(sizeof(T) != 0)>> : std::true_type
+{
+};
+
+} // namespace detail
+
+} // namespace mdds
+
+#endif
diff --git a/include/mdds/multi_type_matrix.hpp b/include/mdds/multi_type_matrix.hpp
new file mode 100644
index 0000000..0cac868
--- /dev/null
+++ b/include/mdds/multi_type_matrix.hpp
@@ -0,0 +1,844 @@
+/*************************************************************************
+ *
+ * Copyright (c) 2012-2021 Kohei Yoshida
+ *
+ * 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, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ ************************************************************************/
+
+#ifndef __MDDS_MULTI_TYPE_MATRIX_HPP__
+#define __MDDS_MULTI_TYPE_MATRIX_HPP__
+
+#ifdef MDDS_MULTI_TYPE_MATRIX_DEBUG
+#ifndef MDDS_MULTI_TYPE_VECTOR_DEBUG
+#define MDDS_MULTI_TYPE_VECTOR_DEBUG 1
+#endif
+#endif
+
+#include "multi_type_vector.hpp"
+
+namespace mdds {
+
+namespace mtm {
+
+/**
+ * Element type for multi_type_matrix.
+ */
+enum element_t
+{
+ element_empty = mdds::mtv::element_type_empty,
+ element_boolean = mdds::mtv::element_type_boolean,
+ element_string = mdds::mtv::element_type_string,
+ element_numeric = mdds::mtv::element_type_double,
+ element_integer = mdds::mtv::element_type_int32
+};
+
+/**
+ * Default matrix trait that uses std::string as its string type.
+ */
+struct std_string_traits
+{
+ typedef mdds::mtv::int32_element_block integer_element_block;
+ typedef mdds::mtv::string_element_block string_element_block;
+};
+
+} // namespace mtm
+
+/**
+ * Matrix that can store numeric, integer, boolean, empty and string types.
+ * The string and integer types can be specified in the matrix trait
+ * template parameter. To use std::string as the string type and int as the
+ * integer type, use mdds::mtm::std_string_traits.
+ *
+ * Internally it uses mdds::multi_type_vector as its value store. The
+ * element values are linearly stored in column-major order.
+ */
+template<typename Traits>
+class multi_type_matrix
+{
+ typedef Traits traits_type;
+
+public:
+ typedef typename traits_type::string_element_block string_block_type;
+ typedef typename traits_type::integer_element_block integer_block_type;
+
+ typedef typename string_block_type::value_type string_type;
+ typedef typename integer_block_type::value_type integer_type;
+ typedef size_t size_type;
+
+private:
+ struct mtv_trait : public mdds::mtv::default_traits
+ {
+ using block_funcs = mdds::mtv::element_block_funcs<
+ mdds::mtv::boolean_element_block, mdds::mtv::int8_element_block, mdds::mtv::double_element_block,
+ typename traits_type::string_element_block, typename traits_type::integer_element_block>;
+ };
+
+ using store_type = mdds::multi_type_vector<mtv_trait>;
+
+public:
+ typedef typename store_type::position_type position_type;
+ typedef typename store_type::const_position_type const_position_type;
+
+ typedef typename store_type::element_block_type element_block_type;
+
+ typedef typename mtv::boolean_element_block boolean_block_type;
+ typedef typename mtv::double_element_block numeric_block_type;
+
+ struct size_pair_type
+ {
+ size_type row;
+ size_type column;
+ size_pair_type() : row(0), column(0)
+ {}
+ size_pair_type(size_type _row, size_type _column) : row(_row), column(_column)
+ {}
+ size_pair_type(std::initializer_list<size_type> vs)
+ {
+ if (vs.size() != 2)
+ throw invalid_arg_error("size_pair_type requires exactly 2 elements.");
+
+ size_type* ptrs[2] = {&row, &column};
+ size_type** p = ptrs;
+
+ for (size_type v : vs)
+ **p++ = v;
+ }
+
+ bool operator==(const size_pair_type& r) const
+ {
+ return row == r.row && column == r.column;
+ }
+ bool operator!=(const size_pair_type& r) const
+ {
+ return !operator==(r);
+ }
+ };
+
+ struct element_block_node_type
+ {
+ friend class multi_type_matrix;
+
+ mtm::element_t type;
+ size_type offset;
+ size_type size;
+ const element_block_type* data;
+
+ element_block_node_type();
+ element_block_node_type(const element_block_node_type& other);
+
+ template<typename _Blk>
+ typename _Blk::const_iterator begin() const;
+
+ template<typename _Blk>
+ typename _Blk::const_iterator end() const;
+
+ private:
+ void assign(const const_position_type& pos, size_type section_size);
+ };
+
+ static mtm::element_t to_mtm_type(mdds::mtv::element_t mtv_type)
+ {
+ switch (mtv_type)
+ {
+ case string_block_type::block_type:
+ return mdds::mtm::element_string;
+ case integer_block_type::block_type:
+ return mdds::mtm::element_integer;
+ case mdds::mtv::element_type_double:
+ case mdds::mtv::element_type_boolean:
+ case mdds::mtv::element_type_empty:
+ // These types share the same numeric values.
+ return static_cast<mtm::element_t>(mtv_type);
+ default:
+ throw type_error("multi_type_matrix: unknown element type.");
+ }
+ }
+
+private:
+ template<typename FuncT>
+ struct walk_func
+ {
+ FuncT& m_func;
+ walk_func(FuncT& func) : m_func(func)
+ {}
+
+ void operator()(const typename store_type::const_iterator::value_type& mtv_node)
+ {
+ element_block_node_type mtm_node;
+ mtm_node.type = to_mtm_type(mtv_node.type);
+ mtm_node.size = mtv_node.size;
+ mtm_node.data = mtv_node.data;
+ m_func(mtm_node);
+ }
+ };
+
+public:
+ /**
+ * Move to the next logical position. The movement is in the top-to-bottom
+ * then left-to-right direction.
+ *
+ * @param pos position object.
+ *
+ * @return position object that references the element at the next logical
+ * position.
+ */
+ static position_type next_position(const position_type& pos);
+
+ /**
+ * Move to the next logical position. The movement is in the top-to-bottom
+ * then left-to-right direction.
+ *
+ * @param pos position object.
+ *
+ * @return non-mutable position object that references the element at the
+ * next logical position.
+ */
+ static const_position_type next_position(const const_position_type& pos);
+
+ /**
+ * Default constructor.
+ */
+ multi_type_matrix();
+
+ /**
+ * Construct a matrix of specified size.
+ *
+ * @param rows size of rows.
+ * @param cols size of columns.
+ */
+ multi_type_matrix(size_type rows, size_type cols);
+
+ /**
+ * Construct a matrix of specified size and initialize all its elements
+ * with specified value.
+ *
+ * @param rows size of rows.
+ * @param cols size of columns.
+ * @param value value to initialize all its elements with.
+ */
+ template<typename _T>
+ multi_type_matrix(size_type rows, size_type cols, const _T& value);
+
+ /**
+ * Construct a matrix of specified size and initialize its elements with
+ * specified values. The values are assigned to 2-dimensional matrix
+ * layout in column-major order. The size of the value array must equal
+ * <code>rows</code> x <code>cols</code>.
+ *
+ * @param rows size of rows.
+ * @param cols size of columns.
+ * @param it_begin iterator that points to the value of the first element.
+ * @param it_end iterator that points to the position after the last
+ * element value.
+ */
+ template<typename _T>
+ multi_type_matrix(size_type rows, size_type cols, const _T& it_begin, const _T& it_end);
+
+ /**
+ * Copy constructor.
+ */
+ multi_type_matrix(const multi_type_matrix& r);
+
+ /**
+ * Destructor.
+ */
+ ~multi_type_matrix();
+
+ bool operator==(const multi_type_matrix& other) const;
+ bool operator!=(const multi_type_matrix& other) const;
+
+ multi_type_matrix& operator=(const multi_type_matrix& r);
+
+ /**
+ * Get a mutable reference of an element (position object) at specified
+ * position. The position object can then be passed to an additional
+ * method to get the type or value of the element it references, or set a
+ * new value to it.
+ *
+ * @param row row position of the referenced element.
+ * @param col column position of the referenced element.
+ *
+ * @return reference object of element at specified position.
+ */
+ position_type position(size_type row, size_type col);
+
+ /**
+ * Get a mutable reference of an element (position object) at specified
+ * position. The position object can then be passed to an additional
+ * method to get the type or value of the element it references, or set a
+ * new value to it.
+ *
+ * @param pos_hint position object to be used as a position hint for
+ * faster lookup.
+ * @param row row position of the referenced element.
+ * @param col column position of the referenced element.
+ *
+ * @return reference object of element at specified position.
+ */
+ position_type position(const position_type& pos_hint, size_type row, size_type col);
+
+ /**
+ * Get an immutable reference of an element (position object) at specified
+ * position. The position object can then be passed to an additional
+ * method to get the type or value of the element it references.
+ *
+ * @param row row position of the referenced element.
+ * @param col column position of the referenced element.
+ *
+ * @return reference object of element at specified position.
+ */
+ const_position_type position(size_type row, size_type col) const;
+
+ /**
+ * Get an immutable reference of an element (position object) at specified
+ * position. The position object can then be passed to an additional
+ * method to get the type or value of the element it references.
+ *
+ * @param pos_hint position object to be used as a position hint for
+ * faster lookup.
+ * @param row row position of the referenced element.
+ * @param col column position of the referenced element.
+ *
+ * @return reference object of element at specified position.
+ */
+ const_position_type position(const const_position_type& pos_hint, size_type row, size_type col) const;
+
+ /**
+ * Get the row and column positions of the current element from a position
+ * object.
+ *
+ * @param pos position object.
+ *
+ * @return 0-based row and column positions.
+ */
+ size_pair_type matrix_position(const const_position_type& pos) const;
+
+ /**
+ * Return a position type that represents an end position. This can be
+ * used to compare with another position object to see if it is past the
+ * last element position.
+ *
+ * @return end position object.
+ */
+ position_type end_position();
+
+ /**
+ * Return a position type that represents an end position. This can be
+ * used to compare with another position object to see if it is past the
+ * last element position.
+ *
+ * @return end position object.
+ */
+ const_position_type end_position() const;
+
+ /**
+ * Get the type of element from a position object. The type can be one
+ * of empty, string, numeric, or boolean.
+ *
+ * @param pos position object of an element
+ *
+ * @return element type.
+ */
+ mtm::element_t get_type(const const_position_type& pos) const;
+
+ /**
+ * Get the type of element specified by its position. The type can be one
+ * of empty, string, numeric, or boolean.
+ *
+ * @return element type.
+ */
+ mtm::element_t get_type(size_type row, size_type col) const;
+
+ /**
+ * Get a numeric representation of the element. If the element is of
+ * numeric type, its value is returned. If it's of boolean type, either 1
+ * or 0 is returned depending on whether it's true or false. If it's of
+ * empty or string type, 0 is returned.
+ *
+ * @param row row position of the element.
+ * @param col column position of the element.
+ *
+ * @return numeric representation of the element.
+ */
+ double get_numeric(size_type row, size_type col) const;
+
+ /**
+ * Get a numeric representation of the element from a position object.
+ * If the element is of numeric type, its value is returned. If it's of
+ * boolean type, either 1 or 0 is returned depending on whether it's true
+ * or false. If it's of empty or string type, 0 is returned.
+ *
+ * @param pos position object of an element
+ *
+ * @return numeric representation of the element.
+ */
+ double get_numeric(const const_position_type& pos) const;
+
+ /**
+ * Get an integer representation of the element. If the element is of
+ * integer type, its value is returned. If it's of boolean type, either 1
+ * or 0 is returned depending on whether it's true or false. If it's of
+ * empty or string type, 0 is returned.
+ *
+ * @param row row position of the element.
+ * @param col column position of the element.
+ *
+ * @return integer representation of the element.
+ */
+ integer_type get_integer(size_type row, size_type col) const;
+
+ /**
+ * Get an integer representation of the element. If the element is of
+ * integer type, its value is returned. If it's of boolean type, either 1
+ * or 0 is returned depending on whether it's true or false. If it's of
+ * empty or string type, 0 is returned.
+ *
+ * @param pos position object of an element
+ *
+ * @return integer representation of the element.
+ */
+ integer_type get_integer(const const_position_type& pos) const;
+
+ /**
+ * Get a boolean representation of the element. If the element is of
+ * numeric type, true is returned if it's non-zero, otherwise false is
+ * returned. If it's of boolean type, its value is returned. If it's of
+ * empty or string type, false is returned.
+ *
+ * @param row row position of the element.
+ * @param col column position of the element.
+ *
+ * @return boolean representation of the element.
+ */
+ bool get_boolean(size_type row, size_type col) const;
+
+ /**
+ * Get a boolean representation of the element from a position object.
+ * If the element is of numeric type, true is returned if it's non-zero,
+ * otherwise false is returned. If it's of boolean type, its value is
+ * returned. If it's of empty or string type, false is returned.
+ *
+ * @param pos position object of an element
+ *
+ * @return boolean representation of the element.
+ */
+ bool get_boolean(const const_position_type& pos) const;
+
+ /**
+ * Get the value of a string element. If the element is not of string
+ * type, it throws an exception.
+ *
+ * @param row row position of the element.
+ * @param col column position of the element.
+ *
+ * @return value of the element.
+ */
+ const string_type& get_string(size_type row, size_type col) const;
+
+ /**
+ * Get the value of a string element from a position object. If the
+ * element is not of string type, it throws an exception.
+ *
+ * @param pos position object of an element
+ *
+ * @return value of the element.
+ */
+ const string_type& get_string(const const_position_type& pos) const;
+
+ /**
+ * Get the value of element at specified position. The caller must
+ * explicitly specify the return type. If the element is not of the
+ * specified type, it throws an exception.
+ *
+ * @param row row position of the element.
+ * @param col column position of the element.
+ *
+ * @return value of the element.
+ */
+ template<typename _T>
+ _T get(size_type row, size_type col) const;
+
+ /**
+ * Set specified element position empty.
+ *
+ * @param row row position of the element.
+ * @param col column position of the element.
+ */
+ void set_empty(size_type row, size_type col);
+
+ /**
+ * Set a range of elements empty. The range starts from the position
+ * specified by the <code>row</code> and <code>col</code>, and extends
+ * downward first then to the right.
+ *
+ * @param row row position of the first element.
+ * @param col column position of the first element.
+ * @param length length of the range to set empty. When the length is
+ * greater than 1, the range extends downward first then to
+ * the right.
+ */
+ void set_empty(size_type row, size_type col, size_type length);
+
+ /**
+ * Set element referenced by the position object empty.
+ *
+ * @param pos position object that references element.
+ *
+ * @return position of the element that has just been made empty.
+ */
+ position_type set_empty(const position_type& pos);
+
+ /**
+ * Set the entire column empty.
+ *
+ * @param col index of the column to empty.
+ */
+ void set_column_empty(size_type col);
+
+ /**
+ * Set the entire row empty.
+ *
+ * @param row index of the row to empty.
+ */
+ void set_row_empty(size_type row);
+
+ /**
+ * Set a numeric value to an element at specified position.
+ *
+ * @param row row index of the element.
+ * @param col column index of the element.
+ * @param val new value to set.
+ */
+ void set(size_type row, size_type col, double val);
+
+ /**
+ * Set a numeric value to an element at specified position.
+ *
+ * @param pos position of the element to update.
+ * @param val new value to set.
+ *
+ * @return position of the element block where the new value has been set.
+ */
+ position_type set(const position_type& pos, double val);
+
+ /**
+ * Set a boolean value to an element at specified position.
+ *
+ * @param row row index of the element.
+ * @param col column index of the element.
+ * @param val new value to set.
+ */
+ void set(size_type row, size_type col, bool val);
+
+ /**
+ * Set a boolean value to an element at specified position.
+ *
+ * @param pos position of the element to update.
+ * @param val new value to set.
+ *
+ * @return position of the element where the new value has been set.
+ */
+ position_type set(const position_type& pos, bool val);
+
+ /**
+ * Set a string value to an element at specified position.
+ *
+ * @param row row index of the element.
+ * @param col column index of the element.
+ * @param str new value to set.
+ */
+ void set(size_type row, size_type col, const string_type& str);
+
+ /**
+ * Set a string value to an element at specified position.
+ *
+ * @param pos position of the element to update.
+ * @param str new value to set.
+ *
+ * @return position of the element block where the new value has been set.
+ */
+ position_type set(const position_type& pos, const string_type& str);
+
+ /**
+ * Set an integer value to an element at specified position.
+ *
+ * @param row row index of the element.
+ * @param col column index of the element.
+ * @param val new value to set.
+ */
+ void set(size_type row, size_type col, integer_type val);
+
+ /**
+ * Set an integer value to an element at specified position.
+ *
+ * @param pos position of the element to update.
+ * @param val new value to set.
+ *
+ * @return position of the element block where the new value has been set.
+ */
+ position_type set(const position_type& pos, integer_type val);
+
+ /**
+ * Set values of multiple elements at once, starting at specified element
+ * position following the direction of columns. When the new value series
+ * does not fit in the first column, it gets wrapped into the next
+ * column(s).
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception
+ * if the specified position is outside the current container range.</p>
+ *
+ * @param row row position of the start element.
+ * @param col column position of the start element.
+ * @param it_begin iterator that points to the begin position of the
+ * values being set.
+ * @param it_end iterator that points to the end position of the values
+ * being set.
+ */
+ template<typename _T>
+ void set(size_type row, size_type col, const _T& it_begin, const _T& it_end);
+
+ /**
+ * Set values of multiple elements at once, starting at specified element
+ * position following the direction of columns. When the new value series
+ * does not fit in the first column, it gets wrapped into the next
+ * column(s).
+ *
+ * @param pos position of the first element.
+ * @param it_begin iterator that points to the begin position of the
+ * values being set.
+ * @param it_end iterator that points to the end position of the values
+ * being set.
+ *
+ * @return position of the first element that has been modified.
+ */
+ template<typename _T>
+ position_type set(const position_type& pos, const _T& it_begin, const _T& it_end);
+
+ /**
+ * Set values of multiple elements at once in a single column. When the
+ * length of passed elements exceeds that of the column, it gets truncated
+ * to the column size.
+ *
+ * @param col column position
+ * @param it_begin iterator that points to the begin position of the
+ * values being set.
+ * @param it_end iterator that points to the end position of the values
+ * being set.
+ */
+ template<typename _T>
+ void set_column(size_type col, const _T& it_begin, const _T& it_end);
+
+ /**
+ * Return the size of matrix as a pair. The first value is the row size,
+ * while the second value is the column size.
+ *
+ * @return matrix size as a value pair.
+ */
+ size_pair_type size() const;
+
+ /**
+ * Transpose the stored matrix data.
+ *
+ * @return reference to this matrix instance.
+ */
+ multi_type_matrix& transpose();
+
+ /**
+ * Copy values from the passed matrix instance. If the size of the passed
+ * matrix is smaller, then the element values are copied by their
+ * positions, while the rest of the elements that fall outside the size of
+ * the passed matrix instance will remain unmodified. If the size of the
+ * passed matrix instance is larger, then only the elements within the
+ * size of this matrix instance will get copied.
+ *
+ * @param src passed matrix object to copy element values from.
+ */
+ void copy(const multi_type_matrix& src);
+
+ /**
+ * Copy values from an array or array-like container, to a specified
+ * sub-matrix range. The length of the array must match the number of
+ * elements in the destination range, else it will throw a
+ * mdds::size_error.
+ *
+ * @param rows row size of the destination range.
+ * @param cols column size of the destination range.
+ * @param it_begin iterator pointing to the beginning of the input array.
+ * @param it_end iterator pointing to the end position of the input array.
+ */
+ template<typename _T>
+ void copy(size_type rows, size_type cols, const _T& it_begin, const _T& it_end);
+
+ /**
+ * Resize the matrix to specified size. This method supports resizing to
+ * zero-sized matrix; however, either specifying the row or column size to
+ * zero will resize the matrix to 0 x 0. When resizing the matrix larger,
+ * empty elements will be inserted in the region where no elements
+ * existed prior to the call.
+ *
+ * @param rows new row size
+ * @param cols new column size
+ */
+ void resize(size_type rows, size_type cols);
+
+ /**
+ * Resize the matrix to specified size and initial value. The initial
+ * value will be applied to new elements created when the specified size
+ * is larger than the current one.
+ *
+ * @param rows new row size
+ * @param cols new column size
+ * @param value initial value for new elements
+ */
+ template<typename _T>
+ void resize(size_type rows, size_type cols, const _T& value);
+
+ /**
+ * Empty the matrix.
+ */
+ void clear();
+
+ /**
+ * Check whether or not this matrix is numeric. A numeric matrix contains
+ * only numeric or boolean elements. An empty matrix is not numeric.
+ *
+ * @return true if the matrix contains only numeric or boolean elements,
+ * or false otherwise.
+ */
+ bool numeric() const;
+
+ /**
+ * Check whether or not this matrix is empty.
+ *
+ * @return true if this matrix is empty, or false otherwise.
+ */
+ bool empty() const;
+
+ /**
+ * Swap the content of the matrix with another instance.
+ */
+ void swap(multi_type_matrix& r);
+
+ /**
+ * Walk all element blocks that consist of the matrix.
+ *
+ * @param func function object whose operator() gets called on each
+ * element block.
+ *
+ * @return function object passed to this method.
+ */
+ template<typename FuncT>
+ FuncT walk(FuncT func) const;
+
+ /**
+ * Walk through the element blocks in a sub-matrix range defined by start
+ * and end positions passed to this method.
+ *
+ * @param func function object whose operator() gets called on the
+ * element block.
+ *
+ * @param start the column/row position of the upper-left corner of the
+ * sub-matrix.
+ *
+ * @param end the column/row position of the lower-right corner of the
+ * sub-matrix. Both column and row must be greater or equal to
+ * those of the start position.
+ *
+ * @return function object passed to this method.
+ */
+ template<typename FuncT>
+ FuncT walk(FuncT func, const size_pair_type& start, const size_pair_type& end) const;
+
+ /**
+ * Walk through all element blocks in parallel with another matrix
+ * instance. It stops at the block boundaries of both matrix instances
+ * during the walk.
+ *
+ * @param func function object whose operator() gets called on each
+ * element block.
+ *
+ * @param right another matrix instance to parallel-walk with.
+ */
+ template<typename FuncT>
+ FuncT walk(FuncT func, const multi_type_matrix& right) const;
+
+ /**
+ * Walk through the element blocks in a sub-matrix range in parallel with
+ * another matrix instance. It stops at the block boundaries of both
+ * matrix instances during the walk. The sub-matrix range is defined by
+ * start and end positions passed to this method.
+ *
+ * @param func function object whose operator() gets called on each
+ * element block.
+ *
+ * @param right another matrix instance to parallel-walk with.
+ *
+ * @param start the column/row position of the upper-left corner of the
+ * sub-matrix.
+ *
+ * @param end the column/row position of the lower-right corner of the
+ * sub-matrix. Both column and row must be greater or equal to
+ * those of the start position.
+ */
+ template<typename FuncT>
+ FuncT walk(
+ FuncT func, const multi_type_matrix& right, const size_pair_type& start, const size_pair_type& end) const;
+
+#ifdef MDDS_MULTI_TYPE_MATRIX_DEBUG
+ void dump() const
+ {
+ m_store.dump_blocks(std::cout);
+ }
+#endif
+
+private:
+ /**
+ * Get an array position of the data referenced by the row and column
+ * indices. The array consists of multiple columns, the content of column
+ * 0 followed by the content of column 1, and so on. <b>Note that no
+ * boundary check is performed in this method.</b>
+ *
+ * @param row 0-based row index.
+ * @param col 0-based column index.
+ * @return position in the data array.
+ */
+ inline size_type get_pos(size_type row, size_type col) const
+ {
+ return m_size.row * col + row;
+ }
+
+ inline size_type get_pos(const const_position_type& pos) const
+ {
+ return pos.first->position + pos.second;
+ }
+
+private:
+ store_type m_store;
+ size_pair_type m_size;
+};
+
+} // namespace mdds
+
+#include "multi_type_matrix_def.inl"
+
+#endif
diff --git a/include/mdds/multi_type_matrix_def.inl b/include/mdds/multi_type_matrix_def.inl
new file mode 100644
index 0000000..5409071
--- /dev/null
+++ b/include/mdds/multi_type_matrix_def.inl
@@ -0,0 +1,814 @@
+/*************************************************************************
+ *
+ * Copyright (c) 2012-2018 Kohei Yoshida
+ *
+ * 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, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ ************************************************************************/
+
+namespace mdds {
+
+template<typename Traits>
+multi_type_matrix<Traits>::element_block_node_type::element_block_node_type()
+ : type(mtm::element_empty), offset(0), size(0), data(nullptr)
+{}
+
+template<typename Traits>
+multi_type_matrix<Traits>::element_block_node_type::element_block_node_type(const element_block_node_type& other)
+ : type(other.type), offset(other.offset), size(other.size), data(other.data)
+{}
+
+template<typename Traits>
+void multi_type_matrix<Traits>::element_block_node_type::assign(const const_position_type& pos, size_type section_size)
+{
+ assert(section_size <= pos.first->size - pos.second);
+
+ type = to_mtm_type(pos.first->type);
+ offset = pos.second;
+ size = section_size;
+ data = pos.first->data;
+}
+
+template<typename Traits>
+template<typename _Blk>
+typename _Blk::const_iterator multi_type_matrix<Traits>::element_block_node_type::begin() const
+{
+ typename _Blk::const_iterator it = _Blk::begin(*data);
+ std::advance(it, offset);
+ return it;
+}
+
+template<typename Traits>
+template<typename _Blk>
+typename _Blk::const_iterator multi_type_matrix<Traits>::element_block_node_type::end() const
+{
+ typename _Blk::const_iterator it = _Blk::begin(*data);
+ std::advance(it, offset + size);
+ return it;
+}
+
+template<typename Traits>
+typename multi_type_matrix<Traits>::position_type multi_type_matrix<Traits>::next_position(const position_type& pos)
+{
+ return store_type::next_position(pos);
+}
+
+template<typename Traits>
+typename multi_type_matrix<Traits>::const_position_type multi_type_matrix<Traits>::next_position(
+ const const_position_type& pos)
+{
+ return store_type::next_position(pos);
+}
+
+template<typename Traits>
+multi_type_matrix<Traits>::multi_type_matrix() : m_size(0, 0)
+{}
+
+template<typename Traits>
+multi_type_matrix<Traits>::multi_type_matrix(size_type rows, size_type cols) : m_store(rows * cols), m_size(rows, cols)
+{}
+
+template<typename Traits>
+template<typename _T>
+multi_type_matrix<Traits>::multi_type_matrix(size_type rows, size_type cols, const _T& value)
+ : m_store(rows * cols, value), m_size(rows, cols)
+{}
+
+template<typename Traits>
+template<typename _T>
+multi_type_matrix<Traits>::multi_type_matrix(size_type rows, size_type cols, const _T& it_begin, const _T& it_end)
+ : m_store(rows * cols, it_begin, it_end), m_size(rows, cols)
+{
+ if (m_store.empty())
+ return;
+
+ // Throw an exception when trying to construct with data that the matrix doesn't support.
+ typename store_type::iterator it = m_store.begin();
+ to_mtm_type(it->type);
+}
+
+template<typename Traits>
+multi_type_matrix<Traits>::multi_type_matrix(const multi_type_matrix& r) : m_store(r.m_store), m_size(r.m_size)
+{}
+
+template<typename Traits>
+multi_type_matrix<Traits>::~multi_type_matrix()
+{}
+
+template<typename Traits>
+bool multi_type_matrix<Traits>::operator==(const multi_type_matrix& r) const
+{
+ return m_size == r.m_size && m_store == r.m_store;
+}
+
+template<typename Traits>
+bool multi_type_matrix<Traits>::operator!=(const multi_type_matrix& r) const
+{
+ return !operator==(r);
+}
+
+template<typename Traits>
+multi_type_matrix<Traits>& multi_type_matrix<Traits>::operator=(const multi_type_matrix& r)
+{
+ if (this == &r)
+ return *this;
+
+ store_type tmp(r.m_store);
+ m_store.swap(tmp);
+ m_size = r.m_size;
+ return *this;
+}
+
+template<typename Traits>
+typename multi_type_matrix<Traits>::position_type multi_type_matrix<Traits>::position(size_type row, size_type col)
+{
+ return m_store.position(get_pos(row, col));
+}
+
+template<typename Traits>
+typename multi_type_matrix<Traits>::position_type multi_type_matrix<Traits>::position(
+ const position_type& pos_hint, size_type row, size_type col)
+{
+ return m_store.position(pos_hint.first, get_pos(row, col));
+}
+
+template<typename Traits>
+typename multi_type_matrix<Traits>::const_position_type multi_type_matrix<Traits>::position(
+ size_type row, size_type col) const
+{
+ return m_store.position(get_pos(row, col));
+}
+
+template<typename Traits>
+typename multi_type_matrix<Traits>::const_position_type multi_type_matrix<Traits>::position(
+ const const_position_type& pos_hint, size_type row, size_type col) const
+{
+ return m_store.position(pos_hint.first, get_pos(row, col));
+}
+
+template<typename Traits>
+typename multi_type_matrix<Traits>::size_pair_type multi_type_matrix<Traits>::matrix_position(
+ const const_position_type& pos) const
+{
+ size_type mtv_pos = store_type::logical_position(pos);
+ size_type col = mtv_pos / m_size.row;
+ size_type row = mtv_pos - m_size.row * col;
+ return size_pair_type(row, col);
+}
+
+template<typename Traits>
+typename multi_type_matrix<Traits>::position_type multi_type_matrix<Traits>::end_position()
+{
+ return position_type(m_store.end(), 0);
+}
+
+template<typename Traits>
+typename multi_type_matrix<Traits>::const_position_type multi_type_matrix<Traits>::end_position() const
+{
+ return const_position_type(m_store.end(), 0);
+}
+
+template<typename Traits>
+mtm::element_t multi_type_matrix<Traits>::get_type(const const_position_type& pos) const
+{
+ return to_mtm_type(pos.first->type);
+}
+
+template<typename Traits>
+mtm::element_t multi_type_matrix<Traits>::get_type(size_type row, size_type col) const
+{
+ return to_mtm_type(m_store.get_type(get_pos(row, col)));
+}
+
+template<typename Traits>
+double multi_type_matrix<Traits>::get_numeric(size_type row, size_type col) const
+{
+ return get_numeric(m_store.position(get_pos(row, col)));
+}
+
+template<typename Traits>
+double multi_type_matrix<Traits>::get_numeric(const const_position_type& pos) const
+{
+ switch (pos.first->type)
+ {
+ case mtv::element_type_double:
+ return mtv::double_element_block::at(*pos.first->data, pos.second);
+ case integer_block_type::block_type:
+ return integer_block_type::at(*pos.first->data, pos.second);
+ case mtv::element_type_boolean:
+ {
+ // vector<bool> cannot return reference i.e. we can't use at() here.
+ typename mtv::boolean_element_block::const_iterator it =
+ mtv::boolean_element_block::begin(*pos.first->data);
+ std::advance(it, pos.second);
+ return *it;
+ }
+ case string_block_type::block_type:
+ case mtv::element_type_empty:
+ return 0.0;
+ default:
+ throw general_error("multi_type_matrix: unknown element type.");
+ }
+}
+
+template<typename Traits>
+typename multi_type_matrix<Traits>::integer_type multi_type_matrix<Traits>::get_integer(
+ size_type row, size_type col) const
+{
+ return get_integer(m_store.position(get_pos(row, col)));
+}
+
+template<typename Traits>
+typename multi_type_matrix<Traits>::integer_type multi_type_matrix<Traits>::get_integer(
+ const const_position_type& pos) const
+{
+ return static_cast<integer_type>(get_numeric(pos));
+}
+
+template<typename Traits>
+bool multi_type_matrix<Traits>::get_boolean(size_type row, size_type col) const
+{
+ return static_cast<bool>(get_numeric(row, col));
+}
+
+template<typename Traits>
+bool multi_type_matrix<Traits>::get_boolean(const const_position_type& pos) const
+{
+ return static_cast<bool>(get_numeric(pos));
+}
+
+template<typename Traits>
+const typename multi_type_matrix<Traits>::string_type& multi_type_matrix<Traits>::get_string(
+ size_type row, size_type col) const
+{
+ return get_string(m_store.position(get_pos(row, col)));
+}
+
+template<typename Traits>
+const typename multi_type_matrix<Traits>::string_type& multi_type_matrix<Traits>::get_string(
+ const const_position_type& pos) const
+{
+ if (pos.first->type != string_block_type::block_type)
+ throw general_error("multi_type_matrix: unknown element type.");
+
+ return string_block_type::at(*pos.first->data, pos.second);
+}
+
+template<typename Traits>
+template<typename _T>
+_T multi_type_matrix<Traits>::get(size_type row, size_type col) const
+{
+ _T val;
+ m_store.get(get_pos(row, col), val);
+ return val;
+}
+
+template<typename Traits>
+void multi_type_matrix<Traits>::set_empty(size_type row, size_type col)
+{
+ m_store.set_empty(get_pos(row, col), get_pos(row, col));
+}
+
+template<typename Traits>
+void multi_type_matrix<Traits>::set_empty(size_type row, size_type col, size_type length)
+{
+ if (length == 0)
+ throw general_error("multi_type_matrix::set_empty: length of zero is not permitted.");
+
+ size_type pos1 = get_pos(row, col);
+ m_store.set_empty(pos1, pos1 + length - 1);
+}
+
+template<typename Traits>
+typename multi_type_matrix<Traits>::position_type multi_type_matrix<Traits>::set_empty(const position_type& pos)
+{
+ size_type store_pos = get_pos(pos);
+ typename store_type::iterator it = m_store.set_empty(pos.first, store_pos, store_pos);
+ return position_type(it, store_pos - it->position);
+}
+
+template<typename Traits>
+void multi_type_matrix<Traits>::set_column_empty(size_type col)
+{
+ m_store.set_empty(get_pos(0, col), get_pos(m_size.row - 1, col));
+}
+
+template<typename Traits>
+void multi_type_matrix<Traits>::set_row_empty(size_type row)
+{
+ for (size_type col = 0; col < m_size.column; ++col)
+ {
+ size_type pos = get_pos(row, col);
+ m_store.set_empty(pos, pos);
+ }
+}
+
+template<typename Traits>
+void multi_type_matrix<Traits>::set(size_type row, size_type col, double val)
+{
+ m_store.set(get_pos(row, col), val);
+}
+
+template<typename Traits>
+typename multi_type_matrix<Traits>::position_type multi_type_matrix<Traits>::set(const position_type& pos, double val)
+{
+ size_type store_pos = get_pos(pos);
+ typename store_type::iterator it = m_store.set(pos.first, store_pos, val);
+ return position_type(it, store_pos - it->position);
+}
+
+template<typename Traits>
+void multi_type_matrix<Traits>::set(size_type row, size_type col, bool val)
+{
+ m_store.set(get_pos(row, col), val);
+}
+
+template<typename Traits>
+typename multi_type_matrix<Traits>::position_type multi_type_matrix<Traits>::set(const position_type& pos, bool val)
+{
+ size_type store_pos = get_pos(pos);
+ typename store_type::iterator it = m_store.set(pos.first, store_pos, val);
+ return position_type(it, store_pos - it->position);
+}
+
+template<typename Traits>
+void multi_type_matrix<Traits>::set(size_type row, size_type col, const string_type& str)
+{
+ m_store.set(get_pos(row, col), str);
+}
+
+template<typename Traits>
+typename multi_type_matrix<Traits>::position_type multi_type_matrix<Traits>::set(
+ const position_type& pos, const string_type& str)
+{
+ size_type store_pos = get_pos(pos);
+ typename store_type::iterator it = m_store.set(pos.first, store_pos, str);
+ return position_type(it, store_pos - it->position);
+}
+
+template<typename Traits>
+void multi_type_matrix<Traits>::set(size_type row, size_type col, integer_type val)
+{
+ m_store.set(get_pos(row, col), val);
+}
+
+template<typename Traits>
+typename multi_type_matrix<Traits>::position_type multi_type_matrix<Traits>::set(
+ const position_type& pos, integer_type val)
+{
+ size_type store_pos = get_pos(pos);
+ typename store_type::iterator it = m_store.set(pos.first, store_pos, val);
+ return position_type(it, store_pos - it->position);
+}
+
+template<typename Traits>
+template<typename _T>
+void multi_type_matrix<Traits>::set(size_type row, size_type col, const _T& it_begin, const _T& it_end)
+{
+ m_store.set(get_pos(row, col), it_begin, it_end);
+}
+
+template<typename Traits>
+template<typename _T>
+typename multi_type_matrix<Traits>::position_type multi_type_matrix<Traits>::set(
+ const position_type& pos, const _T& it_begin, const _T& it_end)
+{
+ size_type store_pos = get_pos(pos);
+ typename store_type::iterator it = m_store.set(pos.first, store_pos, it_begin, it_end);
+ return position_type(it, store_pos - it->position);
+}
+
+template<typename Traits>
+template<typename _T>
+void multi_type_matrix<Traits>::set_column(size_type col, const _T& it_begin, const _T& it_end)
+{
+ size_type pos = get_pos(0, col);
+ size_type len = std::distance(it_begin, it_end);
+
+ if (len <= m_size.row)
+ {
+ m_store.set(pos, it_begin, it_end);
+ return;
+ }
+
+ _T it_end2 = it_begin;
+ std::advance(it_end2, m_size.row);
+ m_store.set(pos, it_begin, it_end2);
+}
+
+template<typename Traits>
+typename multi_type_matrix<Traits>::size_pair_type multi_type_matrix<Traits>::size() const
+{
+ return m_size;
+}
+
+template<typename Traits>
+multi_type_matrix<Traits>& multi_type_matrix<Traits>::transpose()
+{
+ multi_type_matrix tmp(m_size.column, m_size.row);
+ for (size_type old_row_new_col = 0; old_row_new_col < m_size.row; ++old_row_new_col)
+ {
+ for (size_type old_col_new_row = 0; old_col_new_row < m_size.column; ++old_col_new_row)
+ {
+ switch (get_type(old_row_new_col, old_col_new_row))
+ {
+ case mtm::element_numeric:
+ {
+ double val;
+ m_store.get(get_pos(old_row_new_col, old_col_new_row), val);
+ tmp.set(old_col_new_row, old_row_new_col, val);
+ }
+ break;
+ case mtm::element_boolean:
+ {
+ bool val;
+ m_store.get(get_pos(old_row_new_col, old_col_new_row), val);
+ tmp.set(old_col_new_row, old_row_new_col, val);
+ }
+ break;
+ case mtm::element_string:
+ {
+ string_type val;
+ m_store.get(get_pos(old_row_new_col, old_col_new_row), val);
+ tmp.set(old_col_new_row, old_row_new_col, val);
+ }
+ break;
+ case mtm::element_empty:
+ break;
+ default:
+ throw general_error("multi_type_matrix: unknown element type.");
+ }
+ }
+ }
+
+ swap(tmp);
+ return *this;
+}
+
+template<typename Traits>
+void multi_type_matrix<Traits>::copy(const multi_type_matrix& src)
+{
+ if (&src == this)
+ // Self assignment.
+ return;
+
+ if (empty() || src.empty())
+ return;
+
+ size_type rows = std::min(m_size.row, src.m_size.row);
+ size_type cols = std::min(m_size.column, src.m_size.column);
+
+ position_type pos_dest = position(0, 0);
+ const_position_type pos_src = src.position(0, 0);
+
+ element_block_node_type src_node;
+
+ for (size_t col = 0; col < cols; ++col)
+ {
+ pos_dest = position(pos_dest, 0, col);
+ pos_src = src.position(pos_src, 0, col);
+
+ size_t remaining_rows = rows;
+
+ do
+ {
+ size_type src_blk_left = pos_src.first->size - pos_src.second;
+ size_type section_size = std::min(src_blk_left, remaining_rows);
+ src_node.assign(pos_src, section_size);
+
+ size_type logical_pos_dest = store_type::logical_position(pos_dest);
+
+ typename store_type::iterator blk_pos;
+
+ switch (to_mtm_type(pos_src.first->type))
+ {
+ case mtm::element_numeric:
+ {
+ auto it = src_node.template begin<numeric_block_type>();
+ auto ite = src_node.template end<numeric_block_type>();
+
+ blk_pos = m_store.set(pos_dest.first, logical_pos_dest, it, ite);
+ }
+ break;
+ case mtm::element_boolean:
+ {
+ auto it = src_node.template begin<boolean_block_type>();
+ auto ite = src_node.template end<boolean_block_type>();
+
+ blk_pos = m_store.set(pos_dest.first, logical_pos_dest, it, ite);
+ }
+ break;
+ case mtm::element_string:
+ {
+ auto it = src_node.template begin<string_block_type>();
+ auto ite = src_node.template end<string_block_type>();
+
+ blk_pos = m_store.set(pos_dest.first, logical_pos_dest, it, ite);
+ }
+ break;
+ case mtm::element_empty:
+ {
+ size_type end = logical_pos_dest + section_size - 1;
+ blk_pos = m_store.set_empty(pos_dest.first, logical_pos_dest, end);
+ }
+ break;
+ default:
+ throw general_error("multi_type_matrix: unknown element type.");
+ }
+
+ remaining_rows -= section_size;
+
+ size_type logical_pos_next = logical_pos_dest + section_size;
+ if (logical_pos_next >= m_store.size())
+ // No more room left in the destination store. Bail out.
+ return;
+
+ pos_dest = m_store.position(blk_pos, logical_pos_next);
+
+ // Move source to the head of the next block in the column.
+ pos_src = const_position_type(++pos_src.first, 0);
+ } while (remaining_rows);
+ }
+}
+
+template<typename Traits>
+template<typename _T>
+void multi_type_matrix<Traits>::copy(size_type rows, size_type cols, const _T& it_begin, const _T& it_end)
+{
+ size_t n = std::distance(it_begin, it_end);
+ if (!n || empty())
+ return;
+
+ if (n != rows * cols)
+ throw size_error("multi_type_matrix: size of the array does not match the destination size.");
+
+ if (rows > m_size.row || cols > m_size.column)
+ throw size_error("multi_type_matrix: specified destination size is larger than the current matrix.");
+
+ // Ensure that the passed array is supported by this matrix.
+ to_mtm_type(store_type::get_element_type(*it_begin));
+
+ auto it = it_begin;
+ position_type pos_dest = position(0, 0);
+
+ for (size_t col = 0; col < cols; ++col)
+ {
+ pos_dest = position(pos_dest, 0, col);
+
+ auto it_this_end = it;
+ std::advance(it_this_end, rows);
+
+ pos_dest.first = m_store.set(pos_dest.first, get_pos(0, col), it, it_this_end);
+ it = it_this_end;
+ }
+}
+
+template<typename Traits>
+void multi_type_matrix<Traits>::resize(size_type rows, size_type cols)
+{
+ if (!rows || !cols)
+ {
+ m_size.row = 0;
+ m_size.column = 0;
+ m_store.clear();
+ return;
+ }
+
+ multi_type_matrix temp(rows, cols);
+ temp.copy(*this);
+ temp.swap(*this);
+}
+
+template<typename Traits>
+template<typename _T>
+void multi_type_matrix<Traits>::resize(size_type rows, size_type cols, const _T& value)
+{
+ if (!rows || !cols)
+ {
+ m_size.row = 0;
+ m_size.column = 0;
+ m_store.clear();
+ return;
+ }
+
+ multi_type_matrix temp(rows, cols, value);
+ temp.copy(*this);
+ temp.swap(*this);
+}
+
+template<typename Traits>
+void multi_type_matrix<Traits>::clear()
+{
+ m_store.clear();
+ m_size.row = 0;
+ m_size.column = 0;
+}
+
+template<typename Traits>
+bool multi_type_matrix<Traits>::numeric() const
+{
+ if (m_store.empty())
+ return false;
+
+ typename store_type::const_iterator i = m_store.begin(), iend = m_store.end();
+ for (; i != iend; ++i)
+ {
+ mtv::element_t mtv_type = i->type;
+ switch (mtv_type)
+ {
+ case mtv::element_type_double:
+ case mtv::element_type_boolean:
+ case integer_block_type::block_type:
+ // These are numeric types.
+ continue;
+ case string_block_type::block_type:
+ case mtv::element_type_empty:
+ // These are not.
+ return false;
+ default:
+ throw general_error("multi_type_matrix: unknown element type.");
+ }
+ }
+
+ return true;
+}
+
+template<typename Traits>
+bool multi_type_matrix<Traits>::empty() const
+{
+ return m_store.empty();
+}
+
+template<typename Traits>
+void multi_type_matrix<Traits>::swap(multi_type_matrix& r)
+{
+ m_store.swap(r.m_store);
+ std::swap(m_size.row, r.m_size.row);
+ std::swap(m_size.column, r.m_size.column);
+}
+
+template<typename Traits>
+template<typename FuncT>
+FuncT multi_type_matrix<Traits>::walk(FuncT func) const
+{
+ walk_func<FuncT> wf(func);
+ std::for_each(m_store.begin(), m_store.end(), wf);
+ return func;
+}
+
+template<typename Traits>
+template<typename FuncT>
+FuncT multi_type_matrix<Traits>::walk(FuncT func, const size_pair_type& start, const size_pair_type& end) const
+{
+ if (end.row < start.row || end.column < start.column)
+ {
+ std::ostringstream os;
+ os << "multi_type_matrix: invalid start/end position pair: (row=" << start.row << "; column=" << start.column
+ << ") - (row=" << end.row << "; column=" << end.column << ")";
+ throw size_error(os.str());
+ }
+
+ if (end.row > m_size.row || end.column > m_size.column)
+ throw size_error("multi_type_matrix: end position is out-of-bound.");
+
+ size_t rows = end.row - start.row + 1;
+ element_block_node_type mtm_node;
+ const_position_type pos = position(0, 0);
+
+ // we need to handle columns manually, as the columns are continuously in memory.
+ // To go from one column to the next we need to jump in the memory.
+ for (size_t col = start.column; col <= end.column; ++col)
+ {
+ pos = position(pos, start.row, col);
+ size_t remaining_rows = rows;
+
+ do
+ {
+ size_type remaining_blk = pos.first->size - pos.second;
+
+ // handle the two possible cases:
+ // 1.) the current block is completely contained in our selection
+ // 2.) the current block contains the end of the selection
+
+ size_type section_size = std::min(remaining_blk, remaining_rows);
+ mtm_node.assign(pos, section_size);
+
+ remaining_rows -= section_size;
+ func(mtm_node);
+
+ // Move to the head of the next block in the column.
+ pos = const_position_type(++pos.first, 0);
+ } while (remaining_rows != 0);
+ }
+
+ return func;
+}
+
+template<typename Traits>
+template<typename FuncT>
+FuncT multi_type_matrix<Traits>::walk(FuncT func, const multi_type_matrix& right) const
+{
+ if (size() != right.size())
+ throw size_error("multi_type_matrix: left and right matrices must have the same geometry.");
+
+ if (m_store.empty())
+ return func;
+
+ size_t remaining_size = m_store.size();
+
+ typename store_type::const_iterator it1 = m_store.begin();
+ typename store_type::const_iterator it2 = right.m_store.begin();
+ const_position_type pos1(it1, 0), pos2(it2, 0);
+ element_block_node_type node1, node2;
+
+ while (remaining_size)
+ {
+ size_t section_size = std::min(pos1.first->size - pos1.second, pos2.first->size - pos2.second);
+
+ node1.assign(pos1, section_size);
+ node2.assign(pos2, section_size);
+
+ func(node1, node2);
+
+ pos1 = store_type::advance_position(pos1, section_size);
+ pos2 = store_type::advance_position(pos2, section_size);
+
+ remaining_size -= section_size;
+ }
+
+ return func;
+}
+
+template<typename Traits>
+template<typename FuncT>
+FuncT multi_type_matrix<Traits>::walk(
+ FuncT func, const multi_type_matrix& right, const size_pair_type& start, const size_pair_type& end) const
+{
+ if (end.row < start.row || end.column < start.column)
+ {
+ std::ostringstream os;
+ os << "multi_type_matrix: invalid start/end position pair: (row=" << start.row << "; column=" << start.column
+ << ") - (row=" << end.row << "; column=" << end.column << ")";
+ throw size_error(os.str());
+ }
+
+ if (end.row > m_size.row || end.column > m_size.column || end.row > right.size().row ||
+ end.column > right.size().column)
+ throw size_error("multi_type_matrix: end position is out-of-bound.");
+
+ size_t rows = end.row - start.row + 1;
+
+ element_block_node_type node1, node2;
+ const_position_type pos1 = position(0, 0), pos2 = right.position(0, 0);
+
+ for (size_t col = start.column; col <= end.column; ++col)
+ {
+ pos1 = position(pos1, start.row, col);
+ pos2 = right.position(pos2, start.row, col);
+
+ size_t remaining_rows = rows;
+
+ do
+ {
+ size_type blk1_left = pos1.first->size - pos1.second;
+ size_type blk2_left = pos2.first->size - pos2.second;
+
+ // Section size should be the smallest of blk1_left, blk2_left and remaining_rows.
+ size_type section_size = std::min(blk1_left, blk2_left);
+ section_size = std::min(section_size, remaining_rows);
+
+ node1.assign(pos1, section_size);
+ node2.assign(pos2, section_size);
+
+ func(node1, node2);
+
+ pos1 = store_type::advance_position(pos1, section_size);
+ pos2 = store_type::advance_position(pos2, section_size);
+
+ remaining_rows -= section_size;
+ } while (remaining_rows);
+ }
+
+ return func;
+}
+
+} // namespace mdds
diff --git a/include/mdds/multi_type_vector.hpp b/include/mdds/multi_type_vector.hpp
new file mode 100644
index 0000000..0ee7b54
--- /dev/null
+++ b/include/mdds/multi_type_vector.hpp
@@ -0,0 +1,44 @@
+/*************************************************************************
+ *
+ * Copyright (c) 2011-2021 Kohei Yoshida
+ *
+ * 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, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ ************************************************************************/
+
+#ifndef INCLUDED_MDDS_MULTI_TYPE_VECTOR_HPP
+#define INCLUDED_MDDS_MULTI_TYPE_VECTOR_HPP
+
+#include "./multi_type_vector/soa/main.hpp"
+
+namespace mdds {
+
+/**
+ * Type alias for the concrete implementation to maintain backward API
+ * compatibility.
+ */
+template<typename Traits = mtv::default_traits>
+using multi_type_vector = mtv::soa::multi_type_vector<Traits>;
+
+} // namespace mdds
+
+#endif
diff --git a/include/mdds/multi_type_vector/Makefile.am b/include/mdds/multi_type_vector/Makefile.am
new file mode 100644
index 0000000..29e26be
--- /dev/null
+++ b/include/mdds/multi_type_vector/Makefile.am
@@ -0,0 +1,16 @@
+SUBDIRS = aos soa
+
+headersdir = $(includedir)/mdds-@API_VERSION@/mdds/multi_type_vector
+
+headers_HEADERS = \
+ block_funcs.hpp \
+ collection_def.inl \
+ collection.hpp \
+ env.hpp \
+ iterator_node.hpp \
+ macro.hpp \
+ standard_element_blocks.hpp \
+ types.hpp \
+ types_util.hpp \
+ util.hpp
+
diff --git a/include/mdds/multi_type_vector/Makefile.in b/include/mdds/multi_type_vector/Makefile.in
new file mode 100644
index 0000000..e53e42d
--- /dev/null
+++ b/include/mdds/multi_type_vector/Makefile.in
@@ -0,0 +1,707 @@
+# Makefile.in generated by automake 1.16.5 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2021 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+subdir = include/mdds/multi_type_vector
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4/ax_cxx_compile_stdcxx_17.m4 \
+ $(top_srcdir)/m4/m4_ax_valgrind_check.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(headers_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+SOURCES =
+DIST_SOURCES =
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+ ctags-recursive dvi-recursive html-recursive info-recursive \
+ install-data-recursive install-dvi-recursive \
+ install-exec-recursive install-html-recursive \
+ install-info-recursive install-pdf-recursive \
+ install-ps-recursive install-recursive installcheck-recursive \
+ installdirs-recursive pdf-recursive ps-recursive \
+ tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(headersdir)"
+HEADERS = $(headers_HEADERS)
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+ $(RECURSIVE_TARGETS) \
+ $(RECURSIVE_CLEAN_TARGETS) \
+ $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+ distdir distdir-am
+am__extra_recursive_targets = check-valgrind-recursive \
+ check-valgrind-memcheck-recursive \
+ check-valgrind-helgrind-recursive check-valgrind-drd-recursive \
+ check-valgrind-sgcheck-recursive
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+DIST_SUBDIRS = $(SUBDIRS)
+am__DIST_COMMON = $(srcdir)/Makefile.in
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+ dir0=`pwd`; \
+ sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+ sed_rest='s,^[^/]*/*,,'; \
+ sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+ sed_butlast='s,/*[^/]*$$,,'; \
+ while test -n "$$dir1"; do \
+ first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+ if test "$$first" != "."; then \
+ if test "$$first" = ".."; then \
+ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+ else \
+ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+ if test "$$first2" = "$$first"; then \
+ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+ else \
+ dir2="../$$dir2"; \
+ fi; \
+ dir0="$$dir0"/"$$first"; \
+ fi; \
+ fi; \
+ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+ done; \
+ reldir="$$dir2"
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+API_VERSION = @API_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_NODEBUG = @CPPFLAGS_NODEBUG@
+CSCOPE = @CSCOPE@
+CTAGS = @CTAGS@
+CXX = @CXX@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CXXFLAGS_UNITTESTS = @CXXFLAGS_UNITTESTS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DOXYGEN = @DOXYGEN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+ENABLE_VALGRIND_drd = @ENABLE_VALGRIND_drd@
+ENABLE_VALGRIND_helgrind = @ENABLE_VALGRIND_helgrind@
+ENABLE_VALGRIND_memcheck = @ENABLE_VALGRIND_memcheck@
+ENABLE_VALGRIND_sgcheck = @ENABLE_VALGRIND_sgcheck@
+ETAGS = @ETAGS@
+EXEEXT = @EXEEXT@
+EXPECT = @EXPECT@
+GDB = @GDB@
+HAVE_CXX17 = @HAVE_CXX17@
+INCDIR = @INCDIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MISCDIR = @MISCDIR@
+MKDIR_P = @MKDIR_P@
+OBJDIR = @OBJDIR@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+QUICKCHECKDIR = @QUICKCHECKDIR@
+RUNTEST_BIN = @RUNTEST_BIN@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPHINX = @SPHINX@
+STRIP = @STRIP@
+VALGRIND = @VALGRIND@
+VALGRIND_ENABLED = @VALGRIND_ENABLED@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build_alias = @build_alias@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host_alias = @host_alias@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+valgrind_enabled_tools = @valgrind_enabled_tools@
+valgrind_tools = @valgrind_tools@
+SUBDIRS = aos soa
+headersdir = $(includedir)/mdds-@API_VERSION@/mdds/multi_type_vector
+headers_HEADERS = \
+ block_funcs.hpp \
+ collection_def.inl \
+ collection.hpp \
+ env.hpp \
+ iterator_node.hpp \
+ macro.hpp \
+ standard_element_blocks.hpp \
+ types.hpp \
+ types_util.hpp \
+ util.hpp
+
+all: all-recursive
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign include/mdds/multi_type_vector/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign include/mdds/multi_type_vector/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-headersHEADERS: $(headers_HEADERS)
+ @$(NORMAL_INSTALL)
+ @list='$(headers_HEADERS)'; test -n "$(headersdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(headersdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(headersdir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(headersdir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(headersdir)" || exit $$?; \
+ done
+
+uninstall-headersHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(headers_HEADERS)'; test -n "$(headersdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(headersdir)'; $(am__uninstall_files_from_dir)
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+# (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+ @fail=; \
+ if $(am__make_keepgoing); then \
+ failcom='fail=yes'; \
+ else \
+ failcom='exit 1'; \
+ fi; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+check-valgrind-local:
+check-valgrind-memcheck-local:
+check-valgrind-helgrind-local:
+check-valgrind-drd-local:
+check-valgrind-sgcheck-local:
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ $(am__make_dryrun) \
+ || test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+ $(am__relativize); \
+ new_distdir=$$reldir; \
+ dir1=$$subdir; dir2="$(top_distdir)"; \
+ $(am__relativize); \
+ new_top_distdir=$$reldir; \
+ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+ ($(am__cd) $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$new_top_distdir" \
+ distdir="$$new_distdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ am__skip_mode_fix=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-recursive
+all-am: Makefile $(HEADERS)
+installdirs: installdirs-recursive
+installdirs-am:
+ for dir in "$(DESTDIR)$(headersdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+check-valgrind: check-valgrind-recursive
+
+check-valgrind-am: check-valgrind-local
+
+check-valgrind-drd: check-valgrind-drd-recursive
+
+check-valgrind-drd-am: check-valgrind-drd-local
+
+check-valgrind-helgrind: check-valgrind-helgrind-recursive
+
+check-valgrind-helgrind-am: check-valgrind-helgrind-local
+
+check-valgrind-memcheck: check-valgrind-memcheck-recursive
+
+check-valgrind-memcheck-am: check-valgrind-memcheck-local
+
+check-valgrind-sgcheck: check-valgrind-sgcheck-recursive
+
+check-valgrind-sgcheck-am: check-valgrind-sgcheck-local
+
+clean: clean-recursive
+
+clean-am: clean-generic mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am: install-headersHEADERS
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-generic
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am: uninstall-headersHEADERS
+
+.MAKE: $(am__recursive_targets) install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \
+ check-am check-valgrind-am check-valgrind-drd-am \
+ check-valgrind-drd-local check-valgrind-helgrind-am \
+ check-valgrind-helgrind-local check-valgrind-local \
+ check-valgrind-memcheck-am check-valgrind-memcheck-local \
+ check-valgrind-sgcheck-am check-valgrind-sgcheck-local clean \
+ clean-generic cscopelist-am ctags ctags-am distclean \
+ distclean-generic distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-headersHEADERS install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ installdirs-am maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-generic pdf pdf-am ps ps-am tags \
+ tags-am uninstall uninstall-am uninstall-headersHEADERS
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/include/mdds/multi_type_vector/aos/Makefile.am b/include/mdds/multi_type_vector/aos/Makefile.am
new file mode 100644
index 0000000..0315e43
--- /dev/null
+++ b/include/mdds/multi_type_vector/aos/Makefile.am
@@ -0,0 +1,8 @@
+
+headersdir = $(includedir)/mdds-@API_VERSION@/mdds/multi_type_vector/aos
+
+headers_HEADERS = \
+ block_util.hpp \
+ iterator.hpp \
+ main_def.inl \
+ main.hpp
diff --git a/include/mdds/multi_type_vector/aos/Makefile.in b/include/mdds/multi_type_vector/aos/Makefile.in
new file mode 100644
index 0000000..60644e6
--- /dev/null
+++ b/include/mdds/multi_type_vector/aos/Makefile.in
@@ -0,0 +1,585 @@
+# Makefile.in generated by automake 1.16.5 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2021 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+subdir = include/mdds/multi_type_vector/aos
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4/ax_cxx_compile_stdcxx_17.m4 \
+ $(top_srcdir)/m4/m4_ax_valgrind_check.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(headers_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+SOURCES =
+DIST_SOURCES =
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(headersdir)"
+HEADERS = $(headers_HEADERS)
+am__extra_recursive_targets = check-valgrind-recursive \
+ check-valgrind-memcheck-recursive \
+ check-valgrind-helgrind-recursive check-valgrind-drd-recursive \
+ check-valgrind-sgcheck-recursive
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+am__DIST_COMMON = $(srcdir)/Makefile.in
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+API_VERSION = @API_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_NODEBUG = @CPPFLAGS_NODEBUG@
+CSCOPE = @CSCOPE@
+CTAGS = @CTAGS@
+CXX = @CXX@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CXXFLAGS_UNITTESTS = @CXXFLAGS_UNITTESTS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DOXYGEN = @DOXYGEN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+ENABLE_VALGRIND_drd = @ENABLE_VALGRIND_drd@
+ENABLE_VALGRIND_helgrind = @ENABLE_VALGRIND_helgrind@
+ENABLE_VALGRIND_memcheck = @ENABLE_VALGRIND_memcheck@
+ENABLE_VALGRIND_sgcheck = @ENABLE_VALGRIND_sgcheck@
+ETAGS = @ETAGS@
+EXEEXT = @EXEEXT@
+EXPECT = @EXPECT@
+GDB = @GDB@
+HAVE_CXX17 = @HAVE_CXX17@
+INCDIR = @INCDIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MISCDIR = @MISCDIR@
+MKDIR_P = @MKDIR_P@
+OBJDIR = @OBJDIR@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+QUICKCHECKDIR = @QUICKCHECKDIR@
+RUNTEST_BIN = @RUNTEST_BIN@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPHINX = @SPHINX@
+STRIP = @STRIP@
+VALGRIND = @VALGRIND@
+VALGRIND_ENABLED = @VALGRIND_ENABLED@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build_alias = @build_alias@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host_alias = @host_alias@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+valgrind_enabled_tools = @valgrind_enabled_tools@
+valgrind_tools = @valgrind_tools@
+headersdir = $(includedir)/mdds-@API_VERSION@/mdds/multi_type_vector/aos
+headers_HEADERS = \
+ block_util.hpp \
+ iterator.hpp \
+ main_def.inl \
+ main.hpp
+
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign include/mdds/multi_type_vector/aos/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign include/mdds/multi_type_vector/aos/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-headersHEADERS: $(headers_HEADERS)
+ @$(NORMAL_INSTALL)
+ @list='$(headers_HEADERS)'; test -n "$(headersdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(headersdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(headersdir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(headersdir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(headersdir)" || exit $$?; \
+ done
+
+uninstall-headersHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(headers_HEADERS)'; test -n "$(headersdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(headersdir)'; $(am__uninstall_files_from_dir)
+check-valgrind-local:
+check-valgrind-memcheck-local:
+check-valgrind-helgrind-local:
+check-valgrind-drd-local:
+check-valgrind-sgcheck-local:
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(HEADERS)
+installdirs:
+ for dir in "$(DESTDIR)$(headersdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+check-valgrind: check-valgrind-am
+
+check-valgrind-am: check-valgrind-local
+
+check-valgrind-drd: check-valgrind-drd-am
+
+check-valgrind-drd-am: check-valgrind-drd-local
+
+check-valgrind-helgrind: check-valgrind-helgrind-am
+
+check-valgrind-helgrind-am: check-valgrind-helgrind-local
+
+check-valgrind-memcheck: check-valgrind-memcheck-am
+
+check-valgrind-memcheck-am: check-valgrind-memcheck-local
+
+check-valgrind-sgcheck: check-valgrind-sgcheck-am
+
+check-valgrind-sgcheck-am: check-valgrind-sgcheck-local
+
+clean: clean-am
+
+clean-am: clean-generic mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-headersHEADERS
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-headersHEADERS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am check check-am check-valgrind-am \
+ check-valgrind-drd-am check-valgrind-drd-local \
+ check-valgrind-helgrind-am check-valgrind-helgrind-local \
+ check-valgrind-local check-valgrind-memcheck-am \
+ check-valgrind-memcheck-local check-valgrind-sgcheck-am \
+ check-valgrind-sgcheck-local clean clean-generic cscopelist-am \
+ ctags ctags-am distclean distclean-generic distclean-tags \
+ distdir dvi dvi-am html html-am info info-am install \
+ install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am \
+ install-headersHEADERS install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-generic pdf \
+ pdf-am ps ps-am tags tags-am uninstall uninstall-am \
+ uninstall-headersHEADERS
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/include/mdds/multi_type_vector/aos/block_util.hpp b/include/mdds/multi_type_vector/aos/block_util.hpp
new file mode 100644
index 0000000..4f13eed
--- /dev/null
+++ b/include/mdds/multi_type_vector/aos/block_util.hpp
@@ -0,0 +1,241 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * Copyright (c) 2021 Kohei Yoshida
+ *
+ * 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, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ ************************************************************************/
+
+#ifndef INCLUDED_MDDS_MULTI_TYPE_VECTOR_DIR_AOS_BLOCK_UTIL_HPP
+#define INCLUDED_MDDS_MULTI_TYPE_VECTOR_DIR_AOS_BLOCK_UTIL_HPP
+
+#include "mdds/global.hpp"
+#include "../types.hpp"
+
+namespace mdds { namespace mtv { namespace aos { namespace detail {
+
+template<typename Blks, lu_factor_t F>
+struct adjust_block_positions
+{
+ void operator()(Blks& blocks, int64_t start_block_index, int64_t delta) const
+ {
+ static_assert(
+ mdds::detail::invalid_static_int<F>, "The loop-unrolling factor must be one of 0, 4, 8, 16, or 32.");
+ }
+};
+
+template<typename Blks>
+struct adjust_block_positions<Blks, lu_factor_t::none>
+{
+ void operator()(Blks& blocks, int64_t start_block_index, int64_t delta) const
+ {
+ int64_t n = blocks.size();
+
+ if (start_block_index >= n)
+ return;
+
+#if MDDS_USE_OPENMP
+#pragma omp parallel for
+#endif
+ for (int64_t i = start_block_index; i < n; ++i)
+ blocks[i].position += delta;
+ }
+};
+
+template<typename Blks>
+struct adjust_block_positions<Blks, lu_factor_t::lu4>
+{
+ void operator()(Blks& blocks, int64_t start_block_index, int64_t delta) const
+ {
+ int64_t n = blocks.size();
+
+ if (start_block_index >= n)
+ return;
+
+ // Ensure that the section length is divisible by 4.
+ int64_t len = n - start_block_index;
+ int64_t rem = len & 3; // % 4
+ len -= rem;
+ len += start_block_index;
+#if MDDS_USE_OPENMP
+#pragma omp parallel for
+#endif
+ for (int64_t i = start_block_index; i < len; i += 4)
+ {
+ blocks[i].position += delta;
+ blocks[i + 1].position += delta;
+ blocks[i + 2].position += delta;
+ blocks[i + 3].position += delta;
+ }
+
+ rem += len;
+ for (int64_t i = len; i < rem; ++i)
+ blocks[i].position += delta;
+ }
+};
+
+template<typename Blks>
+struct adjust_block_positions<Blks, lu_factor_t::lu8>
+{
+ void operator()(Blks& blocks, int64_t start_block_index, int64_t delta) const
+ {
+ int64_t n = blocks.size();
+
+ if (start_block_index >= n)
+ return;
+
+ // Ensure that the section length is divisible by 8.
+ int64_t len = n - start_block_index;
+ int64_t rem = len & 7; // % 8
+ len -= rem;
+ len += start_block_index;
+#if MDDS_USE_OPENMP
+#pragma omp parallel for
+#endif
+ for (int64_t i = start_block_index; i < len; i += 8)
+ {
+ blocks[i].position += delta;
+ blocks[i + 1].position += delta;
+ blocks[i + 2].position += delta;
+ blocks[i + 3].position += delta;
+ blocks[i + 4].position += delta;
+ blocks[i + 5].position += delta;
+ blocks[i + 6].position += delta;
+ blocks[i + 7].position += delta;
+ }
+
+ rem += len;
+ for (int64_t i = len; i < rem; ++i)
+ blocks[i].position += delta;
+ }
+};
+
+template<typename Blks>
+struct adjust_block_positions<Blks, lu_factor_t::lu16>
+{
+ void operator()(Blks& blocks, int64_t start_block_index, int64_t delta) const
+ {
+ int64_t n = blocks.size();
+
+ if (start_block_index >= n)
+ return;
+
+ // Ensure that the section length is divisible by 16.
+ int64_t len = n - start_block_index;
+ int64_t rem = len & 15; // % 16
+ len -= rem;
+ len += start_block_index;
+#if MDDS_USE_OPENMP
+#pragma omp parallel for
+#endif
+ for (int64_t i = start_block_index; i < len; i += 16)
+ {
+ blocks[i].position += delta;
+ blocks[i + 1].position += delta;
+ blocks[i + 2].position += delta;
+ blocks[i + 3].position += delta;
+ blocks[i + 4].position += delta;
+ blocks[i + 5].position += delta;
+ blocks[i + 6].position += delta;
+ blocks[i + 7].position += delta;
+ blocks[i + 8].position += delta;
+ blocks[i + 9].position += delta;
+ blocks[i + 10].position += delta;
+ blocks[i + 11].position += delta;
+ blocks[i + 12].position += delta;
+ blocks[i + 13].position += delta;
+ blocks[i + 14].position += delta;
+ blocks[i + 15].position += delta;
+ }
+
+ rem += len;
+ for (int64_t i = len; i < rem; ++i)
+ blocks[i].position += delta;
+ }
+};
+
+template<typename Blks>
+struct adjust_block_positions<Blks, lu_factor_t::lu32>
+{
+ void operator()(Blks& blocks, int64_t start_block_index, int64_t delta) const
+ {
+ int64_t n = blocks.size();
+
+ if (start_block_index >= n)
+ return;
+
+ // Ensure that the section length is divisible by 32.
+ int64_t len = n - start_block_index;
+ int64_t rem = len & 31; // % 32
+ len -= rem;
+ len += start_block_index;
+#if MDDS_USE_OPENMP
+#pragma omp parallel for
+#endif
+ for (int64_t i = start_block_index; i < len; i += 32)
+ {
+ blocks[i].position += delta;
+ blocks[i + 1].position += delta;
+ blocks[i + 2].position += delta;
+ blocks[i + 3].position += delta;
+ blocks[i + 4].position += delta;
+ blocks[i + 5].position += delta;
+ blocks[i + 6].position += delta;
+ blocks[i + 7].position += delta;
+ blocks[i + 8].position += delta;
+ blocks[i + 9].position += delta;
+ blocks[i + 10].position += delta;
+ blocks[i + 11].position += delta;
+ blocks[i + 12].position += delta;
+ blocks[i + 13].position += delta;
+ blocks[i + 14].position += delta;
+ blocks[i + 15].position += delta;
+ blocks[i + 16].position += delta;
+ blocks[i + 17].position += delta;
+ blocks[i + 18].position += delta;
+ blocks[i + 19].position += delta;
+ blocks[i + 20].position += delta;
+ blocks[i + 21].position += delta;
+ blocks[i + 22].position += delta;
+ blocks[i + 23].position += delta;
+ blocks[i + 24].position += delta;
+ blocks[i + 25].position += delta;
+ blocks[i + 26].position += delta;
+ blocks[i + 27].position += delta;
+ blocks[i + 28].position += delta;
+ blocks[i + 29].position += delta;
+ blocks[i + 30].position += delta;
+ blocks[i + 31].position += delta;
+ }
+
+ rem += len;
+ for (int64_t i = len; i < rem; ++i)
+ blocks[i].position += delta;
+ }
+};
+
+}}}} // namespace mdds::mtv::aos::detail
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/mdds/multi_type_vector/aos/iterator.hpp b/include/mdds/multi_type_vector/aos/iterator.hpp
new file mode 100644
index 0000000..619d630
--- /dev/null
+++ b/include/mdds/multi_type_vector/aos/iterator.hpp
@@ -0,0 +1,303 @@
+/*************************************************************************
+ *
+ * Copyright (c) 2012-2021 Kohei Yoshida
+ *
+ * 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, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ ************************************************************************/
+
+#ifndef INCLUDED_MDDS_MULTI_TYPE_VECTOR_DIR_AOS_ITERATOR_HPP
+#define INCLUDED_MDDS_MULTI_TYPE_VECTOR_DIR_AOS_ITERATOR_HPP
+
+#include "../iterator_node.hpp"
+
+#include <cstddef>
+
+namespace mdds { namespace mtv { namespace aos { namespace detail {
+
+/**
+ * Common base for both const and non-const iterators. Its protected inc()
+ * and dec() methods have non-const return type, and the derived classes
+ * wrap them and return values with their respective const modifiers.
+ */
+template<typename Traits>
+class iterator_common_base
+{
+protected:
+ typedef typename Traits::parent parent_type;
+ typedef typename Traits::blocks blocks_type;
+ typedef typename Traits::base_iterator base_iterator_type;
+
+ typedef typename parent_type::size_type size_type;
+ typedef mdds::detail::mtv::iterator_value_node<parent_type, size_type> node;
+
+ iterator_common_base() : m_cur_node(nullptr, 0)
+ {}
+
+ iterator_common_base(
+ const base_iterator_type& pos, const base_iterator_type& end, const parent_type* parent, size_type block_index)
+ : m_cur_node(parent, block_index), m_pos(pos), m_end(end)
+ {
+ if (m_pos != m_end)
+ update_node();
+ }
+
+ iterator_common_base(const iterator_common_base& other)
+ : m_cur_node(other.m_cur_node), m_pos(other.m_pos), m_end(other.m_end)
+ {}
+
+ void update_node()
+ {
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ if (m_pos == m_end)
+ throw general_error("Current node position should never equal the end position during node update.");
+#endif
+ // blocks_type::value_type is a pointer to multi_type_vector::block.
+ const typename blocks_type::value_type& blk = *m_pos;
+ if (blk.data)
+ m_cur_node.type = mdds::mtv::get_block_type(*blk.data);
+ else
+ m_cur_node.type = mdds::mtv::element_type_empty;
+
+ m_cur_node.position = blk.position;
+ m_cur_node.size = blk.size;
+ m_cur_node.data = blk.data;
+ }
+
+ node* inc()
+ {
+ ++m_pos;
+ if (m_pos == m_end)
+ return nullptr;
+
+ update_node();
+ return &m_cur_node;
+ }
+
+ node* dec()
+ {
+ --m_pos;
+ update_node();
+ return &m_cur_node;
+ }
+
+ node m_cur_node;
+ base_iterator_type m_pos;
+ base_iterator_type m_end;
+
+public:
+ bool operator==(const iterator_common_base& other) const
+ {
+ if (m_pos != m_end && other.m_pos != other.m_end)
+ {
+ // TODO: Set hard-coded values to the current node for the end
+ // position nodes to remove this if block.
+ if (m_cur_node != other.m_cur_node)
+ return false;
+ }
+ return m_pos == other.m_pos && m_end == other.m_end;
+ }
+
+ bool operator!=(const iterator_common_base& other) const
+ {
+ return !operator==(other);
+ }
+
+ iterator_common_base& operator=(const iterator_common_base& other)
+ {
+ m_cur_node = other.m_cur_node;
+ m_pos = other.m_pos;
+ m_end = other.m_end;
+ return *this;
+ }
+
+ void swap(iterator_common_base& other)
+ {
+ m_cur_node.swap(other.m_cur_node);
+ std::swap(m_pos, other.m_pos);
+ std::swap(m_end, other.m_end);
+ }
+
+ const node& get_node() const
+ {
+ return m_cur_node;
+ }
+ const base_iterator_type& get_pos() const
+ {
+ return m_pos;
+ }
+ const base_iterator_type& get_end() const
+ {
+ return m_end;
+ }
+};
+
+template<typename Traits, typename NodeUpdateFunc>
+class iterator_base : public iterator_common_base<Traits>
+{
+ using parent_type = typename Traits::parent;
+ typedef NodeUpdateFunc node_update_func;
+ typedef iterator_common_base<Traits> common_base;
+
+ typedef typename Traits::base_iterator base_iterator_type;
+ typedef typename common_base::size_type size_type;
+
+ using common_base::dec;
+ using common_base::inc;
+ using common_base::m_cur_node;
+
+public:
+ using common_base::get_end;
+ using common_base::get_pos;
+
+ // iterator traits
+ typedef typename common_base::node value_type;
+ typedef value_type* pointer;
+ typedef value_type& reference;
+ typedef ptrdiff_t difference_type;
+ typedef std::bidirectional_iterator_tag iterator_category;
+
+public:
+ iterator_base()
+ {}
+ iterator_base(
+ const base_iterator_type& pos, const base_iterator_type& end, const parent_type* parent, size_type block_index)
+ : common_base(pos, end, parent, block_index)
+ {}
+
+ value_type& operator*()
+ {
+ return m_cur_node;
+ }
+
+ const value_type& operator*() const
+ {
+ return m_cur_node;
+ }
+
+ value_type* operator->()
+ {
+ return &m_cur_node;
+ }
+
+ const value_type* operator->() const
+ {
+ return &m_cur_node;
+ }
+
+ iterator_base& operator++()
+ {
+ node_update_func::inc(m_cur_node);
+ inc();
+ return *this;
+ }
+
+ iterator_base& operator--()
+ {
+ dec();
+ node_update_func::dec(m_cur_node);
+ return *this;
+ }
+};
+
+template<typename Traits, typename NodeUpdateFunc, typename NonConstItrBase>
+class const_iterator_base : public iterator_common_base<Traits>
+{
+ using parent_type = typename Traits::parent;
+ typedef NodeUpdateFunc node_update_func;
+ typedef iterator_common_base<Traits> common_base;
+
+ typedef typename Traits::base_iterator base_iterator_type;
+ typedef typename common_base::size_type size_type;
+
+ using common_base::dec;
+ using common_base::inc;
+ using common_base::m_cur_node;
+
+public:
+ using common_base::get_end;
+ using common_base::get_pos;
+
+ typedef NonConstItrBase iterator_base;
+
+ // iterator traits
+ typedef typename common_base::node value_type;
+ typedef value_type* pointer;
+ typedef value_type& reference;
+ typedef ptrdiff_t difference_type;
+ typedef std::bidirectional_iterator_tag iterator_category;
+
+public:
+ const_iterator_base() : common_base()
+ {}
+ const_iterator_base(
+ const base_iterator_type& pos, const base_iterator_type& end, const parent_type* parent, size_type block_index)
+ : common_base(pos, end, parent, block_index)
+ {}
+
+ /**
+ * Take the non-const iterator counterpart to create a const iterator.
+ */
+ const_iterator_base(const iterator_base& other)
+ : common_base(
+ other.get_pos(), other.get_end(), other.get_node().__private_data.parent,
+ other.get_node().__private_data.block_index)
+ {}
+
+ const value_type& operator*() const
+ {
+ return m_cur_node;
+ }
+
+ const value_type* operator->() const
+ {
+ return &m_cur_node;
+ }
+
+ const_iterator_base& operator++()
+ {
+ node_update_func::inc(m_cur_node);
+ inc();
+ return *this;
+ }
+
+ const_iterator_base& operator--()
+ {
+ dec();
+ node_update_func::dec(m_cur_node);
+ return *this;
+ }
+
+ bool operator==(const const_iterator_base& other) const
+ {
+ return iterator_common_base<Traits>::operator==(other);
+ }
+
+ bool operator!=(const const_iterator_base& other) const
+ {
+ return iterator_common_base<Traits>::operator!=(other);
+ }
+};
+
+}}}} // namespace mdds::mtv::aos::detail
+
+#endif
diff --git a/include/mdds/multi_type_vector/aos/main.hpp b/include/mdds/multi_type_vector/aos/main.hpp
new file mode 100644
index 0000000..667a1b0
--- /dev/null
+++ b/include/mdds/multi_type_vector/aos/main.hpp
@@ -0,0 +1,1335 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * Copyright (c) 2021 Kohei Yoshida
+ *
+ * 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, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ ************************************************************************/
+
+#ifndef INCLUDED_MDDS_MULTI_TYPE_VECTOR_DIR_AOS_MAIN_HPP
+#define INCLUDED_MDDS_MULTI_TYPE_VECTOR_DIR_AOS_MAIN_HPP
+
+#include "../../global.hpp"
+#include "../types.hpp"
+#include "../util.hpp"
+#include "./iterator.hpp"
+#include "./block_util.hpp"
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+#include <iostream>
+#endif
+
+namespace mdds { namespace mtv { namespace aos {
+
+/**
+ * Multi-type vector consists of a series of one or more blocks, and each
+ * block may either be empty, or stores a series of non-empty elements of
+ * identical type. These blocks collectively represent a single logical
+ * one-dimensional array that may store elements of different types. It is
+ * guaranteed that the block types of neighboring blocks are always
+ * different.
+ *
+ * Structurally, the primary array stores block instances whose types
+ * are of <code>value_type</code>, which in turn consists of the following
+ * data members:
+ *
+ * <ul>
+ * <li><code>type</code> which indicates the block type,</li>
+ * <li><code>position</code> which stores the logical position of the
+ * first element of the block,</li>
+ * <li><code>size</code> which stores the logical size of the block,
+ * and</li>
+ * <li><code>data</code> which stores the pointer to a secondary array
+ * (a.k.a. element block) which stores the actual element values, or
+ * <code>nullptr</code> in case the block represents an empty segment.</li>
+ * </ul>
+ *
+ * This variant implements an array-of-structures (AoS) storage.
+ *
+ * @see mdds::mtv::aos::multi_type_vector::value_type
+ */
+template<typename Traits = mdds::mtv::default_traits>
+class multi_type_vector
+{
+public:
+ typedef size_t size_type;
+
+ typedef mdds::mtv::base_element_block element_block_type;
+ typedef mdds::mtv::element_t element_category_type;
+ using block_funcs = typename Traits::block_funcs;
+
+ /**
+ * Optional event handler function structure, whose functions get called
+ * at specific events. The following events are currently supported:
+ *
+ * <ul>
+ * <li><code>element_block_acquired</code> - this gets called whenever
+ * the container acquires a new element block either as a result of a new
+ * element block creation or a tranfer of an existing
+ * element block from another container.</li>
+ * <li><code>element_block_released</code> - this gets called whenever
+ * the container releases an existing element block either because
+ * the block gets deleted or gets transferred to another container.</li>
+ * </ul>
+ *
+ * @see mdds::mtv::empty_event_func for the precise function signatures of
+ * the event handler functions.
+ */
+ using event_func = typename Traits::event_func;
+
+private:
+ struct block
+ {
+ size_type position;
+ size_type size;
+ element_block_type* data;
+
+ block();
+ block(size_type _position, size_type _size);
+ block(size_type _position, size_type _size, element_block_type* _data);
+ block(const block& other) = default;
+ block(block&& other) = default;
+ ~block() = default;
+ void swap(block& other);
+ void clone_to(block& other) const;
+
+ block& operator=(const block&) = default;
+ };
+
+ struct element_block_deleter
+ {
+ void operator()(const element_block_type* p)
+ {
+ block_funcs::delete_block(p);
+ }
+ };
+
+ typedef std::vector<block> blocks_type;
+
+ struct blocks_to_transfer
+ {
+ blocks_type blocks;
+ size_type insert_index;
+
+ blocks_to_transfer();
+ };
+
+ struct iterator_trait
+ {
+ typedef multi_type_vector parent;
+ typedef blocks_type blocks;
+ typedef typename blocks_type::iterator base_iterator;
+ };
+
+ struct reverse_iterator_trait
+ {
+ typedef multi_type_vector parent;
+ typedef blocks_type blocks;
+ typedef typename blocks_type::reverse_iterator base_iterator;
+ };
+
+ struct const_iterator_trait
+ {
+ typedef multi_type_vector parent;
+ typedef blocks_type blocks;
+ typedef typename blocks_type::const_iterator base_iterator;
+ };
+
+ struct const_reverse_iterator_trait
+ {
+ typedef multi_type_vector parent;
+ typedef blocks_type blocks;
+ typedef typename blocks_type::const_reverse_iterator base_iterator;
+ };
+
+ typedef mdds::detail::mtv::iterator_value_node<multi_type_vector, size_type> itr_node;
+ typedef mdds::detail::mtv::private_data_forward_update<multi_type_vector, size_type> itr_forward_update;
+ typedef mdds::detail::mtv::private_data_no_update<multi_type_vector, size_type> itr_no_update;
+
+public:
+ typedef detail::iterator_base<iterator_trait, itr_forward_update> iterator;
+ typedef detail::iterator_base<reverse_iterator_trait, itr_no_update> reverse_iterator;
+
+ typedef detail::const_iterator_base<const_iterator_trait, itr_forward_update, iterator> const_iterator;
+ typedef detail::const_iterator_base<const_reverse_iterator_trait, itr_no_update, reverse_iterator>
+ const_reverse_iterator;
+
+ /**
+ * value_type is the type of a block stored in the primary array. It
+ * consists of the following data members:
+ *
+ * <ul>
+ * <li><code>type</code> which indicates the block type,</li>
+ * <li><code>position</code> which stores the logical position of the
+ * first element of the block,</li>
+ * <li><code>size</code> which stores the logical size of the block,
+ * and</li>
+ * <li><code>data</code> which stores the pointer to a secondary array
+ * (a.k.a. element block) which stores the actual element values, or
+ * <code>nullptr</code> in case the block represents an empty segment.</li>
+ * </ul>
+ */
+ typedef itr_node value_type;
+
+ typedef std::pair<iterator, size_type> position_type;
+ typedef std::pair<const_iterator, size_type> const_position_type;
+
+ /**
+ * Move the position object to the next logical position. Caller must
+ * ensure the the position object is valid.
+ *
+ * @param pos position object.
+ *
+ * @return position object that points to the next logical position.
+ */
+ static position_type next_position(const position_type& pos);
+
+ /**
+ * Increment or decrement the position object by specified steps. Caller
+ * must ensure the the position object is valid.
+ *
+ * @param pos position object.
+ * @param steps steps to advance the position object.
+ *
+ * @return position object that points to the new logical position.
+ */
+ static position_type advance_position(const position_type& pos, int steps);
+
+ /**
+ * Move the position object to the next logical position. Caller must
+ * ensure the the position object is valid.
+ *
+ * @param pos position object.
+ *
+ * @return position object that points to the next logical position.
+ */
+ static const_position_type next_position(const const_position_type& pos);
+
+ /**
+ * Increment or decrement the position object by specified steps. Caller
+ * must ensure the the position object is valid.
+ *
+ * @param pos position object.
+ * @param steps steps to advance the position object.
+ *
+ * @return position object that points to the new logical position.
+ */
+ static const_position_type advance_position(const const_position_type& pos, int steps);
+
+ /**
+ * Extract the logical position from a position object.
+ *
+ * @param pos position object.
+ *
+ * @return logical position of the element that the position object
+ * references.
+ */
+ static size_type logical_position(const const_position_type& pos);
+
+ /**
+ * Get element value from a position object. The caller must specify the
+ * type of block in which the element is expected to be stored.
+ *
+ * @param pos position object.
+ *
+ * @return element value.
+ */
+ template<typename Blk>
+ static typename Blk::value_type get(const const_position_type& pos);
+
+ iterator begin();
+ iterator end();
+
+ const_iterator begin() const;
+ const_iterator end() const;
+
+ const_iterator cbegin() const;
+ const_iterator cend() const;
+
+ reverse_iterator rbegin();
+ reverse_iterator rend();
+
+ const_reverse_iterator rbegin() const;
+ const_reverse_iterator rend() const;
+
+ const_reverse_iterator crbegin() const;
+ const_reverse_iterator crend() const;
+
+ event_func& event_handler();
+ const event_func& event_handler() const;
+
+ /**
+ * Default constructor. It initializes the container with empty size.
+ */
+ multi_type_vector();
+
+ /**
+ * Constructor that takes an lvalue reference to an event handler object.
+ * The event handler instance will be copy-constructed.
+ *
+ * @param hdl lvalue reference to an event handler object.
+ */
+ multi_type_vector(const event_func& hdl);
+
+ /**
+ * Constructor that takes an rvalue reference to an event handler object.
+ * The event handler instance will be move-constructed.
+ *
+ * @param hdl rvalue reference to an event handler object.
+ */
+ multi_type_vector(event_func&& hdl);
+
+ /**
+ * Constructor that takes initial size of the container. When the size
+ * specified is greater than 0, it initializes the container with empty
+ * elements.
+ *
+ * @param init_size initial container size.
+ */
+ multi_type_vector(size_type init_size);
+
+ /**
+ * Constructor that takes initial size of the container and an element
+ * value to initialize the elements to. When the size specified is greater
+ * than 0, it initializes the container with elements that are copies of
+ * the value specified.
+ *
+ * @param init_size initial container size.
+ * @param value initial element value.
+ */
+ template<typename T>
+ multi_type_vector(size_type init_size, const T& value);
+
+ /**
+ * Constructor that takes initial size of the container and begin and end
+ * iterator positions that specify a series of elements to initialize the
+ * container to. The container will contain copies of the elements
+ * specified after this call returns.
+ *
+ * @param init_size initial container size.
+ * @param it_begin iterator that points to the begin position of the
+ * values the container is being initialized to.
+ * @param it_end iterator that points to the end position of the values
+ * the container is being initialized to. The end position
+ * is <i>not</i> inclusive.
+ */
+ template<typename T>
+ multi_type_vector(size_type init_size, const T& it_begin, const T& it_end);
+
+ /**
+ * Copy constructor.
+ *
+ * @param other the other instance to copy values from.
+ */
+ multi_type_vector(const multi_type_vector& other);
+
+ /**
+ * Move constructor.
+ *
+ * @param other the other instance to move values from.
+ */
+ multi_type_vector(multi_type_vector&& other);
+
+ /**
+ * Destructor. It deletes all allocated data blocks.
+ */
+ ~multi_type_vector();
+
+ /**
+ * Set a value of an arbitrary type to a specified position. The type of
+ * the value is inferred from the value passed to this method. The new
+ * value will overwrite an existing value at the specified position
+ * position if any.
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception
+ * if the specified position is outside the current container range.</p>
+ *
+ * <p>Calling this method will not change the size of the container.</p>
+ *
+ * @param pos position to insert the value to.
+ * @param value value to insert.
+ * @return iterator position pointing to the block where the value is
+ * inserted.
+ */
+ template<typename T>
+ iterator set(size_type pos, const T& value);
+
+ /**
+ * Set a value of an arbitrary type to a specified position. The type of
+ * the value is inferred from the value passed to this method. The new
+ * value will overwrite an existing value at the specified position
+ * position if any.
+ *
+ * <p>This variant takes an iterator as an additional parameter, which is
+ * used as a block position hint to speed up the lookup of the
+ * right block to insert the value into. The other variant that doesn't
+ * take an iterator always starts the block lookup from the first block,
+ * which does not scale well as the block size grows.</p>
+ *
+ * <p>This position hint iterator must <b>precede</b> the insertion
+ * position to yield any performance benefit.</p>
+ *
+ * <p>The caller is responsible for ensuring that the passed iterator is
+ * valid. The behavior of this method when passing an invalid iterator is
+ * undefined.</p>
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception
+ * if the specified position is outside the current container range.</p>
+ *
+ * <p>Calling this method will not change the size of the container.</p>
+ *
+ * @param pos_hint iterator used as a block position hint, to specify
+ * which block to start when searching for the right block
+ * to insert the value into.
+ * @param pos position to insert the value to.
+ * @param value value to insert.
+ * @return iterator position pointing to the block where the value is
+ * inserted.
+ */
+ template<typename T>
+ iterator set(const iterator& pos_hint, size_type pos, const T& value);
+
+ /**
+ * Set multiple values of identical type to a range of elements starting
+ * at specified position. Any existing values will be overwritten by the
+ * new values.
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception if
+ * the range of new values would fall outside the current container
+ * range.</p>
+ *
+ * <p>Calling this method will not change the size of the container.</p>
+ *
+ * @param pos position of the first value of the series of new values
+ * being inserted.
+ * @param it_begin iterator that points to the begin position of the
+ * values being set.
+ * @param it_end iterator that points to the end position of the values
+ * being set.
+ * @return iterator position pointing to the block where the value is
+ * inserted. When no value insertion occurs because the value set
+ * is empty, the end iterator position is returned.
+ */
+ template<typename T>
+ iterator set(size_type pos, const T& it_begin, const T& it_end);
+
+ /**
+ * Set multiple values of identical type to a range of elements starting
+ * at specified position. Any existing values will be overwritten by the
+ * new values.
+ *
+ * <p>This variant takes an iterator as an additional parameter, which is
+ * used as a block position hint to speed up the lookup of the first
+ * insertion block. The other variant that doesn't take an iterator
+ * always starts the block lookup from the first block, which does not
+ * scale well as the block size grows.</p>
+ *
+ * <p>This position hint iterator must <b>precede</b> the insertion
+ * position to yield any performance benefit.</p>
+ *
+ * <p>The caller is responsible for ensuring that the passed iterator is
+ * valid. The behavior of this method when passing an invalid iterator is
+ * undefined.</p>
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception if
+ * the range of new values would fall outside the current container
+ * range.</p>
+ *
+ * <p>Calling this method will not change the size of the container.</p>
+ *
+ * @param pos_hint iterator used as a block position hint, to specify
+ * which block to start when searching for the right block
+ * to insert the value into.
+ * @param pos position of the first value of the series of new values
+ * being inserted.
+ * @param it_begin iterator that points to the begin position of the
+ * values being set.
+ * @param it_end iterator that points to the end position of the values
+ * being set.
+ * @return iterator position pointing to the block where the value is
+ * inserted. When no value insertion occurs because the value set
+ * is empty, the end iterator position is returned.
+ */
+ template<typename T>
+ iterator set(const iterator& pos_hint, size_type pos, const T& it_begin, const T& it_end);
+
+ /**
+ * Append a new value to the end of the container.
+ *
+ * @param value new value to be appended to the end of the container.
+ *
+ * @return iterator position pointing to the block where the value is
+ * appended, which in this case is always the last block of the
+ * container.
+ */
+ template<typename T>
+ iterator push_back(const T& value);
+
+ /**
+ * Append a new empty element to the end of the container.
+ *
+ * @return iterator position pointing to the block where the new empty
+ * element is appended, which in this case is always the last
+ * block of the container.
+ */
+ iterator push_back_empty();
+
+ /**
+ * Insert multiple values of identical type to a specified position.
+ * Existing values that occur at or below the specified position will get
+ * shifted after the insertion. No existing values will be overwritten by
+ * the inserted values.
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception
+ * if the specified position is outside the current container range.</p>
+ *
+ * <p>Calling this method will increase the size of the container by
+ * the length of the new values inserted.</p>
+ *
+ * @param pos position at which the new values are to be inserted.
+ * @param it_begin iterator that points to the begin position of the
+ * values being inserted.
+ * @param it_end iterator that points to the end position of the values
+ * being inserted.
+ * @return iterator position pointing to the block where the value is
+ * inserted. When no value insertion occurs because the value set
+ * is empty, the end iterator position is returned.
+ */
+ template<typename T>
+ iterator insert(size_type pos, const T& it_begin, const T& it_end);
+
+ /**
+ * Insert multiple values of identical type to a specified position.
+ * Existing values that occur at or below the specified position will get
+ * shifted after the insertion. No existing values will be overwritten by
+ * the inserted values.
+ *
+ * <p>This variant takes an iterator as an additional parameter, which is
+ * used as a block position hint to speed up the lookup of the first
+ * insertion block. The other variant that doesn't take an iterator
+ * always starts the block lookup from the first block, which does not
+ * scale well as the block size grows.</p>
+ *
+ * <p>This position hint iterator must <b>precede</b> the insertion
+ * position to yield any performance benefit.</p>
+ *
+ * <p>The caller is responsible for ensuring that the passed iterator is
+ * valid. The behavior of this method when passing an invalid iterator is
+ * undefined.</p>
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception
+ * if the specified position is outside the current container range.</p>
+ *
+ * <p>Calling this method will increase the size of the container by
+ * the length of the new values inserted.</p>
+ *
+ * @param pos_hint iterator used as a block position hint, to specify
+ * which block to start when searching for the right block
+ * to insert the value into.
+ * @param pos position at which the new values are to be inserted.
+ * @param it_begin iterator that points to the begin position of the
+ * values being inserted.
+ * @param it_end iterator that points to the end position of the values
+ * being inserted.
+ * @return iterator position pointing to the block where the value is
+ * inserted. When no value insertion occurs because the value set
+ * is empty, the end iterator position is returned.
+ */
+ template<typename T>
+ iterator insert(const iterator& pos_hint, size_type pos, const T& it_begin, const T& it_end);
+
+ /**
+ * Get the value of an element at specified position. The caller must
+ * pass a variable of the correct type to store the value.
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception if
+ * the specified position is outside the current container range.</p>
+ *
+ * @param pos position of the element value to retrieve.
+ * @param value (out) variable to store the retrieved value.
+ */
+ template<typename T>
+ void get(size_type pos, T& value) const;
+
+ /**
+ * Get the value of an element at specified position. The caller must
+ * specify the type of the element as the template parameter e.g.
+ * get<double>(1).
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception if
+ * the specified position is outside the current container range.</p>
+ *
+ * @param pos position of the element value to retrieve.
+ * @return element value.
+ */
+ template<typename T>
+ T get(size_type pos) const;
+
+ /**
+ * Return the value of an element at specified position and set that
+ * position empty. If the element resides in a managed element block,
+ * this call will release that element from that block. If the element is
+ * on a non-managed element block, this call is equivalent to
+ * set_empty(pos, pos).
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception if
+ * the specified position is outside the current container range.</p>
+ *
+ * @param pos position of the element to release.
+ *
+ * @return element value.
+ */
+ template<typename T>
+ T release(size_type pos);
+
+ /**
+ * Retrieve the value of an element at specified position and set that
+ * position empty. If the element resides in a managed element block,
+ * this call will release that element from that block. If the element is
+ * on a non-managed element block, this call is equivalent to
+ * set_empty(pos, pos).
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception if
+ * the specified position is outside the current container range.</p>
+ *
+ * @param pos position of the element to release.
+ * @param value element value.
+ *
+ * @return iterator referencing the block where the position of the
+ * released element is.
+ */
+ template<typename T>
+ iterator release(size_type pos, T& value);
+
+ /**
+ * Retrieve the value of an element at specified position and set that
+ * position empty. If the element resides in a managed element block,
+ * this call will release that element from that block. If the element is
+ * on a non-managed element block, this call is equivalent to
+ * set_empty(pos, pos).
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception if
+ * the specified position is outside the current container range.</p>
+ *
+ * @param pos_hint iterator used as a block position hint, to specify
+ * which block to start when searching for the block where
+ * the element resides.
+ * @param pos position of the element to release.
+ * @param value element value.
+ *
+ * @return iterator referencing the block where the position of the
+ * released element is.
+ */
+ template<typename T>
+ iterator release(const iterator& pos_hint, size_type pos, T& value);
+
+ /**
+ * Release all its elements, and empties its content. Calling this method
+ * relinquishes the ownership of all elements stored in managed element
+ * blocks if any.
+ *
+ * <p>This call is equivalent of clear() if the container consists of no
+ * managed element blocks.</p>
+ */
+ void release();
+
+ /**
+ * Make all elements within specified range empty, and relinquish the
+ * ownership of the elements in that range. All elements in the managed
+ * element blocks within the range will be released and the container will
+ * no longer manage their life cycles after the call.
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception if
+ * either the starting or the ending position is outside the current
+ * container size.</p>
+ *
+ * @param start_pos starting position
+ * @param end_pos ending position, inclusive.
+ * @return iterator position pointing to the block where the elements are
+ * released.
+ */
+ iterator release_range(size_type start_pos, size_type end_pos);
+
+ /**
+ * Make all elements within specified range empty, and relinquish the
+ * ownership of the elements in that range. All elements in the managed
+ * element blocks within the range will be released and the container will
+ * no longer manage their life cycles after the call.
+ *
+ * <p>This variant takes an iterator as an additional parameter, which is
+ * used as a block position hint to speed up the lookup of the first block
+ * to empty. The other variant that doesn't take an iterator always
+ * starts the block lookup from the first block, which does not
+ * scale well as the block size grows.</p>
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception if
+ * either the starting or the ending position is outside the current
+ * container size.</p>
+ *
+ * @param pos_hint iterator used as a block position hint, to specify
+ * which block to start when searching for the right
+ * blocks in which elements are to be released.
+ * @param start_pos starting position
+ * @param end_pos ending position, inclusive.
+ * @return iterator position pointing to the block where the elements are
+ * released.
+ */
+ iterator release_range(const iterator& pos_hint, size_type start_pos, size_type end_pos);
+
+ /**
+ * Given the logical position of an element, get the iterator of the block
+ * where the element is located, and its offset from the first element of
+ * that block.
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception if
+ * the specified position is outside the current container range, except
+ * when the specified position is the position immediately after the last
+ * valid position, it will return a valid position object representing
+ * the end position.</p>
+ *
+ * @param pos logical position of the element.
+ * @return position object that stores an iterator referencing the element
+ * block where the element resides, and its offset within that
+ * block.
+ */
+ position_type position(size_type pos);
+
+ /**
+ * Given the logical position of an element, get the iterator of the block
+ * where the element is located, and its offset from the first element of
+ * that block.
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception if
+ * the specified position is outside the current container range, except
+ * when the specified position is the position immediately after the last
+ * valid position, it will return a valid position object representing
+ * the end position.</p>
+ *
+ * @param pos_hint iterator used as a block position hint, to specify
+ * which block to start when searching for the element
+ * position.
+ * @param pos logical position of the element.
+ * @return position object that stores an iterator referencing the element
+ * block where the element resides, and its offset within that
+ * block.
+ */
+ position_type position(const iterator& pos_hint, size_type pos);
+
+ /**
+ * Given the logical position of an element, get an iterator referencing
+ * the element block where the element is located, and its offset from the
+ * first element of that block.
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception if
+ * the specified position is outside the current container range.</p>
+ *
+ * @param pos position of the element.
+ * @return position object that stores an iterator referencing the element
+ * block where the element resides, and its offset within that
+ * block.
+ */
+ const_position_type position(size_type pos) const;
+
+ /**
+ * Given the logical position of an element, get an iterator referencing
+ * the element block where the element is located, and its offset from the
+ * first element of that block.
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception if
+ * the specified position is outside the current container range.</p>
+ *
+ * @param pos_hint iterator used as a block position hint, to specify
+ * which block to start when searching for the element
+ * position.
+ * @param pos logical position of the element.
+ * @return position object that stores an iterator referencing the element
+ * block where the element resides, and its offset within the
+ * block.
+ */
+ const_position_type position(const const_iterator& pos_hint, size_type pos) const;
+
+ /**
+ * Move elements from one container to another. After the move, the
+ * segment where the elements were in the source container becomes empty.
+ * When transferring managed elements, this call transfers ownership of
+ * the moved elements to the destination container. The moved elements
+ * will overwrite any existing elements in the destination range of the
+ * receiving container. Transfer of elements within the same container is
+ * not allowed.
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception if
+ * either the starting or the ending position is greater than or equal to
+ * the source container size, or the destination container is not
+ * large enough to accommodate the transferred elements.</p>
+ *
+ * @param start_pos starting position
+ * @param end_pos ending position, inclusive.
+ * @param dest destination container to which the elements are to be
+ * moved.
+ * @param dest_pos position in the destination container to which the
+ * elements are to be moved.
+ *
+ * @return iterator referencing the block where the moved elements were
+ * prior to the transfer.
+ */
+ iterator transfer(size_type start_pos, size_type end_pos, multi_type_vector& dest, size_type dest_pos);
+
+ /**
+ * Move elements from one container to another. After the move, the
+ * segment where the elements were in the source container becomes empty.
+ * When transferring managed elements, this call transfers ownership of
+ * the moved elements to the new container. The moved elements will
+ * overwrite any existing elements in the destination range of the
+ * receiving container. Transfer of elements within the same container is
+ * not allowed.
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception if
+ * either the starting or the ending position is greater than or equal to
+ * the source container size, or the destination container is not large
+ * enough to accommodate the transferred elements.</p>
+ *
+ * @param pos_hint iterator used as a block position hint, to specify
+ * which block to start when searching for the blocks
+ * where the elements to be transferred reside.
+ * @param start_pos starting position
+ * @param end_pos ending position, inclusive.
+ * @param dest destination container to which the elements are to be
+ * moved.
+ * @param dest_pos position in the destination container to which the
+ * elements are to be moved.
+ *
+ * @return iterator referencing the block where the moved elements were
+ * prior to the transfer.
+ */
+ iterator transfer(
+ const iterator& pos_hint, size_type start_pos, size_type end_pos, multi_type_vector& dest, size_type dest_pos);
+
+ /**
+ * Get the type of an element at specified position.
+ *
+ * @param pos position of the element.
+ *
+ * @return element type.
+ */
+ mtv::element_t get_type(size_type pos) const;
+
+ /**
+ * Check if element at specified position is empty of not.
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception if
+ * the specified position is outside the current container
+ * range.</p>
+ *
+ * @param pos position of the element to check.
+ *
+ * @return true if the element is empty, false otherwise.
+ */
+ bool is_empty(size_type pos) const;
+
+ /**
+ * Set specified range of elements to be empty. Any existing values will
+ * be overwritten.
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception if
+ * either the starting or the ending position is outside the current
+ * container size.</p>
+ *
+ * @param start_pos starting position
+ * @param end_pos ending position, inclusive.
+ * @return iterator position pointing to the block where the elements are
+ * emptied.
+ */
+ iterator set_empty(size_type start_pos, size_type end_pos);
+
+ /**
+ * Set specified range of elements to be empty. Any existing values will
+ * be overwritten.
+ *
+ * <p>This variant takes an iterator as an additional parameter, which is
+ * used as a block position hint to speed up the lookup of the first block
+ * to empty. The other variant that doesn't take an iterator always
+ * starts the block lookup from the first block, which does not
+ * scale well as the block size grows.</p>
+ *
+ * <p>This position hint iterator must <b>precede</b> the start
+ * position to yield any performance benefit.</p>
+ *
+ * <p>The caller is responsible for ensuring that the passed iterator is
+ * valid. The behavior of this method when passing an invalid iterator is
+ * undefined.</p>
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception if
+ * either the starting or the ending position is outside the current
+ * container size.</p>
+ *
+ * @param pos_hint iterator used as a block position hint, to specify
+ * which block to start when searching for the right
+ * blocks to empty.
+ * @param start_pos starting position
+ * @param end_pos ending position, inclusive.
+ * @return iterator position pointing to the block where the elements are
+ * emptied.
+ */
+ iterator set_empty(const iterator& pos_hint, size_type start_pos, size_type end_pos);
+
+ /**
+ * Erase elements located between specified start and end positions. The
+ * end positions are both inclusive. Those elements originally located
+ * after the specified end position will get shifted up after the erasure.
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception if
+ * either the starting or the ending position is outside the current
+ * container range.</p>
+ *
+ * <p>Calling this method will decrease the size of the container by
+ * the length of the erased range.</p>
+ *
+ * @param start_pos starting position
+ * @param end_pos ending position, inclusive.
+ */
+ void erase(size_type start_pos, size_type end_pos);
+
+ /**
+ * Insert a range of empty elements at specified position. Those elements
+ * originally located after the insertion position will get shifted down
+ * after the insertion.
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception if
+ * either the specified position is outside the current container
+ * range.</p>
+ *
+ * <p>Calling this method will increase the size of the container by
+ * the length of the inserted empty elements.</p>
+ *
+ * @param pos position at which to insert a range of empty elements.
+ * @param length number of empty elements to insert.
+ * @return iterator position pointing to the block where the empty range
+ * is inserted. When no insertion occurs because the length is
+ * zero, the end iterator position is returned.
+ */
+ iterator insert_empty(size_type pos, size_type length);
+
+ /**
+ * Insert a range of empty elements at specified position. Those elements
+ * originally located after the insertion position will get shifted down
+ * after the insertion.
+ *
+ * <p>This variant takes an iterator as an additional parameter, which is
+ * used as a block position hint to speed up the lookup of the block in
+ * which to insert the new empty segment. The other variant that doesn't
+ * take an iterator always starts the block lookup from the first block,
+ * which does not scale well as the block size grows.</p>
+ *
+ * <p>This position hint iterator must <b>precede</b> the start
+ * position to yield any performance benefit.</p>
+ *
+ * <p>The caller is responsible for ensuring that the passed iterator is
+ * valid. The behavior of this method when passing an invalid iterator is
+ * undefined.</p>
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception if
+ * either the specified position is outside the current container
+ * range.</p>
+ *
+ * <p>Calling this method will increase the size of the container by
+ * the length of the inserted empty elements.</p>
+ *
+ * @param pos_hint iterator used as a block position hint, to specify
+ * which block to start when searching for the right block
+ * in which to insert the empty segment.
+ * @param pos position at which to insert a range of empty elements.
+ * @param length number of empty elements to insert.
+ * @return iterator position pointing to the block where the empty range
+ * is inserted. When no insertion occurs because the length is
+ * zero, the end iterator position is returned.
+ */
+ iterator insert_empty(const iterator& pos_hint, size_type pos, size_type length);
+
+ /**
+ * Clear the content of the container. The size of the container will
+ * become zero after calling this method.
+ */
+ void clear();
+
+ /**
+ * Return the current container size.
+ *
+ * @return current container size.
+ */
+ size_type size() const;
+
+ /**
+ * Return the current number of blocks in the primary array. Each
+ * non-empty block stores a secondary block that stores elements in a
+ * contiguous region in memory (element block) and the number of elements
+ * it stores. An empty block only stores its logical size and does not
+ * store an actual element block.
+ *
+ * <p>For instance, if the container stores values of double-precision
+ * type at rows 0 to 2, values of std::string type at 3 to 7, and empty
+ * values at 8 to 10, it would consist of three blocks: one that stores
+ * double values, one that stores std::string values, and one that
+ * represents the empty value range in this exact order. In this specific
+ * scenario, <code>block_size()</code> returns 3, and <code>size()</code>
+ * returns 11.</p>
+ *
+ * @return current number of blocks in the primary array.
+ */
+ size_type block_size() const;
+
+ /**
+ * Return whether or not the container is empty.
+ *
+ * @return true if the container is empty, false otherwise.
+ */
+ bool empty() const;
+
+ /**
+ * Extend or shrink the container. When extending the container, it
+ * appends a series of empty elements to the end. When shrinking, the
+ * elements at the end of the container get stripped off.
+ *
+ * @param new_size size of the container after the resize.
+ */
+ void resize(size_type new_size);
+
+ /**
+ * Swap the content with another container.
+ *
+ * @param other another container to swap content with.
+ */
+ void swap(multi_type_vector& other);
+
+ /**
+ * Swap a part of the content with another instance.
+ *
+ * @param start_pos starting position
+ * @param end_pos ending position, inclusive.
+ * @param other another instance to swap the content with.
+ * @param other_pos insertion position in the other instance.
+ */
+ void swap(size_type start_pos, size_type end_pos, multi_type_vector& other, size_type other_pos);
+
+ /**
+ * Trim excess capacity from all non-empty blocks.
+ */
+ void shrink_to_fit();
+
+ bool operator==(const multi_type_vector& other) const;
+ bool operator!=(const multi_type_vector& other) const;
+
+ multi_type_vector& operator=(const multi_type_vector& other);
+ multi_type_vector& operator=(multi_type_vector&& other);
+
+ /**
+ * Return the numerical identifier that represents passed element.
+ *
+ * @param elem element value.
+ *
+ * @return numerical identifier representing the element.
+ */
+ template<typename T>
+ static mtv::element_t get_element_type(const T& elem);
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ void dump_blocks(std::ostream& os) const;
+
+ void check_block_integrity() const;
+#endif
+
+private:
+ /**
+ * Delete only the element block owned by an outer block.
+ *
+ * @param blk reference to the instance of an outer block that may own an
+ * element block instance.
+ */
+ void delete_element_block(block& blk);
+
+ /**
+ * Delete the element block(s) owned by one or more outer blocks in the
+ * specified iterator ranges.
+ *
+ * @param it start position.
+ * @param it_end end position (not inclusive).
+ */
+ void delete_element_blocks(typename blocks_type::iterator it, typename blocks_type::iterator it_end);
+
+ template<typename T>
+ iterator set_impl(size_type pos, size_type block_index, const T& value);
+
+ template<typename T>
+ iterator release_impl(size_type pos, size_type block_index, T& value);
+
+ template<typename T>
+ iterator push_back_impl(const T& value);
+
+ /**
+ * Find the correct block position for a given logical row ID.
+ *
+ * @param row logical position of an element.
+ * @param start_block_index index of the first block to start the search
+ * from.
+ *
+ * @return index of the block that contains the specified logical row ID.
+ */
+ size_type get_block_position(size_type row, size_type start_block_index = 0) const;
+
+ /**
+ * Same as above, but try to infer block position from the private position
+ * data stored in the iterator first before trying full search.
+ */
+ size_type get_block_position(const typename value_type::private_data& pos_data, size_type row) const;
+
+ void resize_impl(size_type new_size);
+
+ template<typename T>
+ void create_new_block_with_new_cell(element_block_type*& data, const T& cell);
+
+ template<typename T>
+ iterator set_cell_to_middle_of_block(size_type block_index, size_type pos_in_block, const T& cell);
+
+ template<typename T>
+ void append_cell_to_block(size_type block_index, const T& cell);
+
+ template<typename T>
+ iterator set_cell_to_empty_block(size_type block_index, size_type pos_in_block, const T& cell);
+
+ template<typename T>
+ iterator set_cell_to_block_of_size_one(size_type block_index, const T& cell);
+
+ template<typename T>
+ void set_cell_to_top_of_data_block(size_type block_index, const T& cell);
+
+ template<typename T>
+ void set_cell_to_bottom_of_data_block(size_type block_index, const T& cell);
+
+ iterator transfer_impl(
+ size_type start_pos, size_type end_pos, size_type block_index1, multi_type_vector& dest, size_type dest_pos);
+
+ /**
+ * All elements to transfer to the other container is in the same block.
+ */
+ iterator transfer_single_block(
+ size_type start_pos, size_type end_pos, size_type block_index1, multi_type_vector& dest, size_type dest_pos);
+
+ /**
+ * Elements to transfer to the other container span across multiple
+ * blocks.
+ */
+ iterator transfer_multi_blocks(
+ size_type start_pos, size_type end_pos, size_type block_index1, size_type block_index2, multi_type_vector& dest,
+ size_type dest_pos);
+
+ /**
+ * @param start_pos logical start position.
+ * @param end_pos logical end position.
+ * @param block_index1 index of the first block
+ * @param overwrite when true, and when the stored values are pointers to
+ * heap objects, objects pointed to by the overwritten
+ * pointers should be freed from memory.
+ */
+ iterator set_empty_impl(size_type start_pos, size_type end_pos, size_type block_index1, bool overwrite);
+
+ void swap_impl(
+ multi_type_vector& other, size_type start_pos, size_type end_pos, size_type other_pos, size_type block_index1,
+ size_type block_index2, size_type dblock_index1, size_type dblock_index2);
+
+ void swap_single_block(
+ multi_type_vector& other, size_type start_pos, size_type end_pos, size_type other_pos, size_type block_index,
+ size_type other_block_index);
+
+ void swap_single_to_multi_blocks(
+ multi_type_vector& other, size_type start_pos, size_type end_pos, size_type other_pos, size_type block_index,
+ size_type dst_block_index1, size_type dst_block_index2);
+
+ void swap_multi_to_multi_blocks(
+ multi_type_vector& other, size_type start_pos, size_type end_pos, size_type other_pos, size_type block_index1,
+ size_type block_index2, size_type dblock_index1, size_type dblock_index2);
+
+ void insert_blocks_at(size_type position, size_type insert_pos, blocks_type& new_blocks);
+
+ void prepare_blocks_to_transfer(
+ blocks_to_transfer& bucket, size_type block_index1, size_type offset1, size_type block_index2,
+ size_type offset2);
+
+ iterator set_whole_block_empty(size_type block_index, bool overwrite);
+
+ iterator set_empty_in_single_block(size_type start_row, size_type end_row, size_type block_index, bool overwrite);
+
+ /**
+ * @param start_row logical start position.
+ * @param end_row logical end position.
+ * @param block_index1 index of the first block.
+ * @param block_index2 index of the last block.
+ * @param overwrite when true, and when the stored values are pointers to
+ * heap objects, objects pointed to by the overwritten
+ * pointers should be freed from memory.
+ */
+ iterator set_empty_in_multi_blocks(
+ size_type start_row, size_type end_row, size_type block_index1, size_type block_index2, bool overwrite);
+
+ void erase_impl(size_type start_pos, size_type end_pos);
+ void erase_in_single_block(size_type start_pos, size_type end_pos, size_type block_pos);
+
+ /**
+ * @param pos logical position at which to insert an empty segment.
+ * @param block_index index of the block.
+ * @param length length of the emtpy segment to insert.
+ */
+ iterator insert_empty_impl(size_type pos, size_type block_index, size_type length);
+
+ template<typename T>
+ iterator set_cells_impl(
+ size_type row, size_type end_row, size_type block_index1, const T& it_begin, const T& it_end);
+
+ template<typename T>
+ iterator insert_cells_impl(size_type row, size_type block_index, const T& it_begin, const T& it_end);
+
+ template<typename T>
+ iterator set_cells_to_single_block(
+ size_type start_row, size_type end_row, size_type block_index, const T& it_begin, const T& it_end);
+
+ template<typename T>
+ iterator set_cells_to_multi_blocks(
+ size_type start_row, size_type end_row, size_type block_index1, size_type block_index2, const T& it_begin,
+ const T& it_end);
+
+ template<typename T>
+ iterator set_cells_to_multi_blocks_block1_non_equal(
+ size_type start_row, size_type end_row, size_type block_index1, size_type block_index2, const T& it_begin,
+ const T& it_end);
+
+ template<typename T>
+ iterator set_cells_to_multi_blocks_block1_non_empty(
+ size_type start_row, size_type end_row, size_type block_index1, size_type block_index2, const T& it_begin,
+ const T& it_end);
+
+ /**
+ * Merge with previous or next block as needed.
+ *
+ * @param block_index index of the block that may need merging.
+ *
+ * @return size of previous block if the block is merged with the previous
+ * block, or 0 if it didn't merge with the previous block.
+ */
+ size_type merge_with_adjacent_blocks(size_type block_index);
+
+ /**
+ * Merge only with the next block if the two are of the same type.
+ *
+ * @param block_index index of the block that may need merging.
+ *
+ * @return true if merge occurs, false otherwise.
+ */
+ bool merge_with_next_block(size_type block_index);
+
+ template<typename T>
+ bool append_to_prev_block(
+ size_type block_index, element_category_type cat, size_type length, const T& it_begin, const T& it_end);
+
+ template<typename T>
+ void insert_cells_to_middle(size_type row, size_type block_index, const T& it_begin, const T& it_end);
+
+ /**
+ * Set a new block in the middle of an existing block. This call inserts
+ * two new blocks below the specificed block position. The first one will
+ * be empty, and the second one will contain the lower elements of the
+ * existing block.
+ *
+ * @param block_index index of block into which to set a new block.
+ * @param offset position in the existing block to set the new block to.
+ * @param new_block_size size of the new block
+ * @param overwrite whether or not to overwrite the elements replaced by
+ * the new block.
+ * @return reference to the middle block
+ */
+ block& set_new_block_to_middle(size_type block_index, size_type offset, size_type new_block_size, bool overwrite);
+
+ block* get_previous_block_of_type(size_type block_index, element_category_type cat);
+
+ /**
+ * @param block_index index of the current block.
+ * @param cat desired block type.
+ *
+ * @return pointer to the next block if the next block exists and it's of
+ * specified type, otherwise nullptr will be returned.
+ */
+ block* get_next_block_of_type(size_type block_index, element_category_type cat);
+
+ /**
+ * Send elements from a source block to place them in a destination block.
+ * In return, the method returns the elements in the destination block
+ * that have been replaced by the elements sent by the caller. The caller
+ * needs to manage the life cycle of the returned block.
+ *
+ * @param src_data source data block from which the elements are sent.
+ * @param src_offset position of the first element in the source block.
+ * @param dst_index destination block index.
+ * @param dst_offset position in the destination block where the sent
+ * elements are to be placed.
+ * @param len length of elements.
+ *
+ * @return heap allocated block that contains the overwritten elements
+ * originally in the destination block. The caller needs to manage
+ * its life cycle.
+ */
+ element_block_type* exchange_elements(
+ const element_block_type& src_data, size_type src_offset, size_type dst_index, size_type dst_offset,
+ size_type len);
+
+ void exchange_elements(
+ const element_block_type& src_data, size_type src_offset, size_type dst_index1, size_type dst_offset1,
+ size_type dst_index2, size_type dst_offset2, size_type len, blocks_type& new_blocks);
+
+ bool append_empty(size_type len);
+
+ inline iterator get_iterator(size_type block_index)
+ {
+ typename blocks_type::iterator block_pos = m_blocks.begin();
+ std::advance(block_pos, block_index);
+ return iterator(block_pos, m_blocks.end(), this, block_index);
+ }
+
+ inline const_iterator get_const_iterator(size_type block_index) const
+ {
+ typename blocks_type::const_iterator block_pos = m_blocks.begin();
+ std::advance(block_pos, block_index);
+ return const_iterator(block_pos, m_blocks.end(), this, block_index);
+ }
+
+private:
+ using adjust_block_positions_func = detail::adjust_block_positions<blocks_type, Traits::loop_unrolling>;
+
+ event_func m_hdl_event;
+ blocks_type m_blocks;
+ size_type m_cur_size;
+};
+
+}}} // namespace mdds::mtv::aos
+
+#include "main_def.inl"
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/mdds/multi_type_vector/aos/main_def.inl b/include/mdds/multi_type_vector/aos/main_def.inl
new file mode 100644
index 0000000..9e1140e
--- /dev/null
+++ b/include/mdds/multi_type_vector/aos/main_def.inl
@@ -0,0 +1,4837 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * Copyright (c) 2021 Kohei Yoshida
+ *
+ * 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, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ ************************************************************************/
+
+#include "../env.hpp"
+#include "../util.hpp"
+#if MDDS_MTV_USE_STANDARD_ELEMENT_BLOCKS
+#include "../standard_element_blocks.hpp"
+#endif
+
+#include <stdexcept>
+#include <memory>
+
+namespace mdds { namespace mtv { namespace aos {
+
+namespace detail {
+
+template<typename Blk, typename SizeT>
+inline SizeT calc_next_block_position(const std::vector<Blk>& blocks, SizeT block_index)
+{
+ assert(block_index < blocks.size());
+ const Blk& blk = blocks[block_index];
+ return blk.position + blk.size;
+}
+
+template<typename Blk>
+inline auto calc_next_block_position(const Blk& blk) -> decltype(blk.position)
+{
+ return blk.position + blk.size;
+}
+
+template<typename Blk>
+bool compare_blocks(const Blk& left, const Blk& right)
+{
+ return left.position < right.position;
+}
+
+} // namespace detail
+
+template<typename Traits>
+multi_type_vector<Traits>::block::block() : position(0), size(0), data(nullptr)
+{}
+
+template<typename Traits>
+multi_type_vector<Traits>::block::block(size_type _position, size_type _size)
+ : position(_position), size(_size), data(nullptr)
+{}
+
+template<typename Traits>
+multi_type_vector<Traits>::block::block(size_type _position, size_type _size, element_block_type* _data)
+ : position(_position), size(_size), data(_data)
+{}
+
+template<typename Traits>
+void multi_type_vector<Traits>::block::swap(block& other)
+{
+ std::swap(position, other.position);
+ std::swap(size, other.size);
+ std::swap(data, other.data);
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::block::clone_to(block& other) const
+{
+ other.position = position;
+ other.size = size;
+ if (data)
+ other.data = block_funcs::clone_block(*data);
+ else
+ other.data = nullptr;
+}
+
+template<typename Traits>
+multi_type_vector<Traits>::blocks_to_transfer::blocks_to_transfer() : insert_index(0)
+{}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::position_type multi_type_vector<Traits>::next_position(const position_type& pos)
+{
+ position_type ret = pos;
+ if (pos.second + 1 < pos.first->size)
+ {
+ // Next element is still in the same block.
+ ++ret.second;
+ }
+ else
+ {
+ ++ret.first;
+ ret.second = 0;
+ }
+
+ return ret;
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::position_type multi_type_vector<Traits>::advance_position(
+ const position_type& pos, int steps)
+{
+ return mdds::mtv::detail::advance_position<position_type>(pos, steps);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::const_position_type multi_type_vector<Traits>::next_position(
+ const const_position_type& pos)
+{
+ const_position_type ret = pos;
+ if (pos.second + 1 < pos.first->size)
+ {
+ // Next element is still in the same block.
+ ++ret.second;
+ }
+ else
+ {
+ ++ret.first;
+ ret.second = 0;
+ }
+
+ return ret;
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::const_position_type multi_type_vector<Traits>::advance_position(
+ const const_position_type& pos, int steps)
+{
+ return mdds::mtv::detail::advance_position<const_position_type>(pos, steps);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::size_type multi_type_vector<Traits>::logical_position(
+ const const_position_type& pos)
+{
+ return pos.first->position + pos.second;
+}
+
+template<typename Traits>
+template<typename Blk>
+typename Blk::value_type multi_type_vector<Traits>::get(const const_position_type& pos)
+{
+ return mdds::mtv::detail::get_block_element_at<Blk>(*pos.first->data, pos.second);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::begin()
+{
+ return iterator(m_blocks.begin(), m_blocks.end(), this, 0);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::end()
+{
+ return iterator(m_blocks.end(), m_blocks.end(), this, m_blocks.size());
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::const_iterator multi_type_vector<Traits>::begin() const
+{
+ return cbegin();
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::const_iterator multi_type_vector<Traits>::end() const
+{
+ return cend();
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::const_iterator multi_type_vector<Traits>::cbegin() const
+{
+ return const_iterator(m_blocks.cbegin(), m_blocks.cend(), this, 0);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::const_iterator multi_type_vector<Traits>::cend() const
+{
+ return const_iterator(m_blocks.end(), m_blocks.end(), this, m_blocks.size());
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::reverse_iterator multi_type_vector<Traits>::rbegin()
+{
+ return reverse_iterator(m_blocks.rbegin(), m_blocks.rend(), this, 0);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::reverse_iterator multi_type_vector<Traits>::rend()
+{
+ return reverse_iterator(m_blocks.rend(), m_blocks.rend(), this, 0);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::const_reverse_iterator multi_type_vector<Traits>::rbegin() const
+{
+ return const_reverse_iterator(m_blocks.rbegin(), m_blocks.rend(), this, 0);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::const_reverse_iterator multi_type_vector<Traits>::rend() const
+{
+ return const_reverse_iterator(m_blocks.rend(), m_blocks.rend(), this, 0);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::const_reverse_iterator multi_type_vector<Traits>::crbegin() const
+{
+ return const_reverse_iterator(m_blocks.crbegin(), m_blocks.crend(), this, 0);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::const_reverse_iterator multi_type_vector<Traits>::crend() const
+{
+ return const_reverse_iterator(m_blocks.crend(), m_blocks.crend(), this, 0);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::event_func& multi_type_vector<Traits>::event_handler()
+{
+ return m_hdl_event;
+}
+
+template<typename Traits>
+const typename multi_type_vector<Traits>::event_func& multi_type_vector<Traits>::event_handler() const
+{
+ return m_hdl_event;
+}
+
+template<typename Traits>
+multi_type_vector<Traits>::multi_type_vector() : m_cur_size(0)
+{}
+
+template<typename Traits>
+multi_type_vector<Traits>::multi_type_vector(const event_func& hdl) : m_hdl_event(hdl), m_cur_size(0)
+{}
+
+template<typename Traits>
+multi_type_vector<Traits>::multi_type_vector(event_func&& hdl) : m_hdl_event(std::move(hdl)), m_cur_size(0)
+{}
+
+template<typename Traits>
+multi_type_vector<Traits>::multi_type_vector(size_type init_size) : m_cur_size(init_size)
+{
+ if (!init_size)
+ return;
+
+ // Initialize with an empty block that spans from 0 to max.
+ m_blocks.emplace_back(0, init_size);
+}
+
+template<typename Traits>
+template<typename T>
+multi_type_vector<Traits>::multi_type_vector(size_type init_size, const T& value) : m_cur_size(init_size)
+{
+ if (!init_size)
+ return;
+
+ element_block_type* data = mdds_mtv_create_new_block(init_size, value);
+ m_hdl_event.element_block_acquired(data);
+ m_blocks.emplace_back(0, init_size, data);
+}
+
+template<typename Traits>
+template<typename T>
+multi_type_vector<Traits>::multi_type_vector(size_type init_size, const T& it_begin, const T& it_end)
+ : m_cur_size(init_size)
+{
+ if (!m_cur_size)
+ return;
+
+ size_type data_len = std::distance(it_begin, it_end);
+ if (m_cur_size != data_len)
+ throw invalid_arg_error("Specified size does not match the size of the initial data array.");
+
+ element_block_type* data = mdds_mtv_create_new_block(*it_begin, it_begin, it_end);
+ m_hdl_event.element_block_acquired(data);
+ m_blocks.emplace_back(0, m_cur_size, data);
+}
+
+template<typename Traits>
+multi_type_vector<Traits>::multi_type_vector(const multi_type_vector& other)
+ : m_hdl_event(other.m_hdl_event), m_cur_size(other.m_cur_size)
+{
+ // Clone all the blocks.
+ m_blocks.reserve(other.m_blocks.size());
+ typename blocks_type::const_iterator it = other.m_blocks.begin(), it_end = other.m_blocks.end();
+ block tmp;
+ for (; it != it_end; ++it)
+ {
+ it->clone_to(tmp);
+ m_blocks.emplace_back(tmp.position, tmp.size, tmp.data);
+ if (tmp.data)
+ m_hdl_event.element_block_acquired(tmp.data);
+ }
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ try
+ {
+ check_block_integrity();
+ }
+ catch (const mdds::integrity_error& e)
+ {
+ std::ostringstream os;
+ os << e.what() << std::endl;
+ os << "block integrity check failed in copy construction" << std::endl;
+ std::cerr << os.str() << std::endl;
+ abort();
+ }
+#endif
+}
+
+template<typename Traits>
+multi_type_vector<Traits>::multi_type_vector(multi_type_vector&& other)
+ : m_hdl_event(std::move(other.m_hdl_event)), m_blocks(std::move(other.m_blocks)), m_cur_size(other.m_cur_size)
+{
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ try
+ {
+ check_block_integrity();
+ }
+ catch (const mdds::integrity_error& e)
+ {
+ std::ostringstream os;
+ os << e.what() << std::endl;
+ os << "block integrity check failed in move construction" << std::endl;
+ std::cerr << os.str() << std::endl;
+ abort();
+ }
+#endif
+}
+
+template<typename Traits>
+multi_type_vector<Traits>::~multi_type_vector()
+{
+ delete_element_blocks(m_blocks.begin(), m_blocks.end());
+}
+
+template<typename Traits>
+template<typename T>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::set(size_type pos, const T& value)
+{
+ size_type block_index = get_block_position(pos);
+ if (block_index == m_blocks.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::set", __LINE__, pos, block_size(), size());
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ std::ostringstream os_prev_block;
+ dump_blocks(os_prev_block);
+#endif
+
+ iterator ret = set_impl(pos, block_index, value);
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ try
+ {
+ check_block_integrity();
+ }
+ catch (const mdds::integrity_error& e)
+ {
+ std::ostringstream os;
+ os << e.what() << std::endl;
+ os << "block integrity check failed in set (pos=" << pos << ")" << std::endl;
+ os << "previous block state:" << std::endl;
+ os << os_prev_block.str();
+ std::cerr << os.str() << std::endl;
+ abort();
+ }
+#endif
+
+ return ret;
+}
+
+template<typename Traits>
+template<typename T>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::set(
+ const iterator& pos_hint, size_type pos, const T& value)
+{
+ size_type block_index = get_block_position(pos_hint->__private_data, pos);
+ if (block_index == m_blocks.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::set", __LINE__, pos, block_size(), size());
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ std::ostringstream os_prev_block;
+ dump_blocks(os_prev_block);
+#endif
+
+ iterator ret = set_impl(pos, block_index, value);
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ try
+ {
+ check_block_integrity();
+ }
+ catch (const mdds::integrity_error& e)
+ {
+ std::ostringstream os;
+ os << e.what() << std::endl;
+ os << "block integrity check failed in set (pos=" << pos << ")" << std::endl;
+ os << "previous block state:" << std::endl;
+ os << os_prev_block.str();
+ std::cerr << os.str() << std::endl;
+ abort();
+ }
+#endif
+
+ return ret;
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::delete_element_block(block& blk)
+{
+ if (!blk.data)
+ // This block is empty.
+ return;
+
+ m_hdl_event.element_block_released(blk.data);
+ block_funcs::delete_block(blk.data);
+ blk.data = nullptr;
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::delete_element_blocks(
+ typename blocks_type::iterator it, typename blocks_type::iterator it_end)
+{
+ std::for_each(it, it_end, [&](block& r) { delete_element_block(r); });
+}
+
+template<typename Traits>
+template<typename T>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::set_impl(
+ size_type pos, size_type block_index, const T& value)
+{
+ size_type start_row = m_blocks[block_index].position;
+ element_category_type cat = mdds_mtv_get_element_type(value);
+
+ typename blocks_type::iterator block_pos = m_blocks.begin();
+ std::advance(block_pos, block_index);
+ block* blk = &*block_pos;
+ assert(blk->size > 0); // block size should never be zero at any time.
+
+ assert(pos >= start_row);
+ size_type pos_in_block = pos - start_row;
+ assert(pos_in_block < blk->size);
+
+ if (!blk->data)
+ {
+ // This is an empty block.
+ return set_cell_to_empty_block(block_index, pos_in_block, value);
+ }
+
+ assert(blk->data);
+ element_category_type blk_cat = mdds::mtv::get_block_type(*blk->data);
+
+ if (blk_cat == cat)
+ {
+ // This block is of the same type as the cell being inserted.
+ size_type i = pos - start_row;
+ block_funcs::overwrite_values(*blk->data, i, 1);
+ mdds_mtv_set_value(*blk->data, i, value);
+ return iterator(block_pos, m_blocks.end(), this, block_index);
+ }
+
+ assert(blk_cat != cat);
+
+ if (pos == start_row)
+ {
+ // Insertion point is at the start of the block.
+ if (blk->size == 1)
+ {
+ return set_cell_to_block_of_size_one(block_index, value);
+ }
+
+ assert(blk->size > 1);
+ block* blk_prev = get_previous_block_of_type(block_index, cat);
+ if (blk_prev)
+ {
+ // Append to the previous block.
+ blk->size -= 1;
+ blk->position += 1;
+ block_funcs::overwrite_values(*blk->data, 0, 1);
+ block_funcs::erase(*blk->data, 0);
+ blk_prev->size += 1;
+ mdds_mtv_append_value(*blk_prev->data, value);
+ return get_iterator(block_index - 1);
+ }
+
+ set_cell_to_top_of_data_block(block_index, value);
+ return get_iterator(block_index);
+ }
+
+ if (pos < (start_row + blk->size - 1))
+ {
+ // Insertion point is somewhere in the middle of the block.
+ return set_cell_to_middle_of_block(block_index, pos_in_block, value);
+ }
+
+ // Insertion point is at the end of the block.
+ assert(pos == (start_row + blk->size - 1));
+ assert(pos > start_row);
+ assert(blk->size > 1);
+
+ if (block_index == 0)
+ {
+ if (m_blocks.size() == 1)
+ {
+ // This is the only block. Pop the last value from the
+ // previous block, and insert a new block for the cell being
+ // inserted.
+ set_cell_to_bottom_of_data_block(0, value);
+ iterator itr = end();
+ --itr;
+ return itr;
+ }
+
+ block* blk_next = get_next_block_of_type(block_index, cat);
+ if (!blk_next)
+ {
+ // Pop the last cell of the current block, and insert a new block
+ // with the new cell.
+ set_cell_to_bottom_of_data_block(0, value);
+ iterator itr = begin();
+ ++itr;
+ return itr;
+ }
+
+ // Pop the last cell off the current block, and prepend the
+ // new value to the next block.
+ block_funcs::overwrite_values(*blk->data, blk->size - 1, 1);
+ block_funcs::erase(*blk->data, blk->size - 1);
+ blk->size -= 1;
+ mdds_mtv_prepend_value(*blk_next->data, value);
+ blk_next->size += 1;
+ blk_next->position -= 1;
+
+ return get_iterator(block_index + 1);
+ }
+
+ assert(block_index > 0);
+
+ if (block_index == m_blocks.size() - 1)
+ {
+ // This is the last block.
+ set_cell_to_bottom_of_data_block(block_index, value);
+ iterator itr = end();
+ --itr;
+ return itr;
+ }
+
+ block* blk_next = get_next_block_of_type(block_index, cat);
+ if (!blk_next)
+ {
+ // Next block is either empty or of different type than that of the cell being inserted.
+ set_cell_to_bottom_of_data_block(block_index, value); // This invalidates m_blocks.
+ return get_iterator(block_index + 1);
+ }
+
+ // Pop the last element from the current block, and prepend the cell
+ // into the next block.
+ block_funcs::overwrite_values(*blk->data, blk->size - 1, 1);
+ block_funcs::erase(*blk->data, blk->size - 1);
+ blk->size -= 1;
+ mdds_mtv_prepend_value(*blk_next->data, value);
+ blk_next->size += 1;
+ blk_next->position -= 1;
+
+ return get_iterator(block_index + 1);
+}
+
+template<typename Traits>
+template<typename T>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::release_impl(
+ size_type pos, size_type block_index, T& value)
+{
+ const block& blk = m_blocks[block_index];
+ size_type start_pos = blk.position;
+
+ if (!blk.data)
+ {
+ // Empty cell block. There is no element to release.
+ mdds_mtv_get_empty_value(value);
+ return get_iterator(block_index);
+ }
+
+ assert(pos >= start_pos);
+ assert(blk.data); // data for non-empty blocks should never be nullptr.
+ size_type idx = pos - start_pos;
+ mdds_mtv_get_value(*blk.data, idx, value);
+
+ // Set the element slot empty without overwriting it.
+ return set_empty_in_single_block(pos, pos, block_index, false);
+}
+
+template<typename Traits>
+template<typename T>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::set(
+ size_type pos, const T& it_begin, const T& it_end)
+{
+ auto res = mdds::mtv::detail::calc_input_end_position(it_begin, it_end, pos, m_cur_size);
+
+ if (!res.second)
+ return end();
+
+ size_type end_pos = res.first;
+
+ size_type block_index1 = get_block_position(pos);
+ if (block_index1 == m_blocks.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::set", __LINE__, pos, block_size(), size());
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ std::ostringstream os_prev_block;
+ dump_blocks(os_prev_block);
+#endif
+
+ auto ret = set_cells_impl(pos, end_pos, block_index1, it_begin, it_end);
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ try
+ {
+ check_block_integrity();
+ }
+ catch (const mdds::integrity_error& e)
+ {
+ std::ostringstream os;
+ os << e.what() << std::endl;
+ os << "block integrity check failed in set (pos=" << pos << ")" << std::endl;
+ os << "previous block state:" << std::endl;
+ os << os_prev_block.str();
+ std::cerr << os.str() << std::endl;
+ abort();
+ }
+#endif
+
+ return ret;
+}
+
+template<typename Traits>
+template<typename T>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::set(
+ const iterator& pos_hint, size_type pos, const T& it_begin, const T& it_end)
+{
+ auto res = mdds::mtv::detail::calc_input_end_position(it_begin, it_end, pos, m_cur_size);
+ if (!res.second)
+ return end();
+
+ size_type end_pos = res.first;
+
+ size_type block_index1 = get_block_position(pos_hint->__private_data, pos);
+ return set_cells_impl(pos, end_pos, block_index1, it_begin, it_end);
+}
+
+template<typename Traits>
+template<typename T>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::push_back(const T& value)
+{
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ std::ostringstream os_prev_block;
+ dump_blocks(os_prev_block);
+#endif
+
+ auto ret = push_back_impl(value);
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ try
+ {
+ check_block_integrity();
+ }
+ catch (const mdds::integrity_error& e)
+ {
+ std::ostringstream os;
+ os << e.what() << std::endl;
+ os << "block integrity check failed in push_back" << std::endl;
+ os << "previous block state:" << std::endl;
+ os << os_prev_block.str();
+ std::cerr << os.str() << std::endl;
+ abort();
+ }
+#endif
+
+ return ret;
+}
+
+template<typename Traits>
+template<typename T>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::push_back_impl(const T& value)
+{
+ element_category_type cat = mdds_mtv_get_element_type(value);
+
+ block* blk_last = m_blocks.empty() ? nullptr : &m_blocks.back();
+ if (!blk_last || !blk_last->data || cat != get_block_type(*blk_last->data))
+ {
+ // Either there is no block, or the last block is empty or of
+ // different type. Append a new block.
+ size_type block_index = m_blocks.size();
+ size_type start_pos = m_cur_size;
+
+ m_blocks.emplace_back(start_pos, 1);
+ create_new_block_with_new_cell(m_blocks.back().data, value);
+ ++m_cur_size;
+
+ return get_iterator(block_index);
+ }
+
+ assert(blk_last);
+ assert(blk_last->data);
+ assert(cat == get_block_type(*blk_last->data));
+
+ // Append the new value to the last block.
+ size_type block_index = m_blocks.size() - 1;
+
+ mdds_mtv_append_value(*blk_last->data, value);
+ ++blk_last->size;
+ ++m_cur_size;
+
+ return get_iterator(block_index);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::push_back_empty()
+{
+ size_type block_index = m_blocks.size();
+
+ if (!append_empty(1))
+ {
+ // Last empty block has been extended.
+ --block_index;
+ }
+
+ // Get the iterator of the last block.
+ typename blocks_type::iterator block_pos = m_blocks.end();
+ --block_pos;
+
+ return iterator(block_pos, m_blocks.end(), this, block_index);
+}
+
+template<typename Traits>
+template<typename T>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::insert(
+ size_type pos, const T& it_begin, const T& it_end)
+{
+ size_type block_index = get_block_position(pos);
+ if (block_index == m_blocks.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::insert", __LINE__, pos, block_size(), size());
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ std::ostringstream os_prev_block;
+ dump_blocks(os_prev_block);
+#endif
+
+ iterator ret = insert_cells_impl(pos, block_index, it_begin, it_end);
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ try
+ {
+ check_block_integrity();
+ }
+ catch (const mdds::integrity_error& e)
+ {
+ std::ostringstream os;
+ os << e.what() << std::endl;
+ element_category_type cat = mdds_mtv_get_element_type(*it_begin);
+ os << "block integrity check failed in insert (pos=" << pos
+ << "; value-size=" << std::distance(it_begin, it_end) << "; value-type=" << cat << ")" << std::endl;
+ os << "previous block state:" << std::endl;
+ os << os_prev_block.str();
+ std::cerr << os.str() << std::endl;
+ abort();
+ }
+#endif
+
+ return ret;
+}
+
+template<typename Traits>
+template<typename T>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::insert(
+ const iterator& pos_hint, size_type pos, const T& it_begin, const T& it_end)
+{
+ size_type block_index = get_block_position(pos_hint->__private_data, pos);
+ if (block_index == m_blocks.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::insert", __LINE__, pos, block_size(), size());
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ std::ostringstream os_prev_block;
+ dump_blocks(os_prev_block);
+#endif
+
+ iterator ret = insert_cells_impl(pos, block_index, it_begin, it_end);
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ try
+ {
+ check_block_integrity();
+ }
+ catch (const mdds::integrity_error& e)
+ {
+ std::ostringstream os;
+ os << e.what() << std::endl;
+ element_category_type cat = mdds_mtv_get_element_type(*it_begin);
+ os << "block integrity check failed in insert (pos=" << pos
+ << "; value-size=" << std::distance(it_begin, it_end) << "; value-type=" << cat << ")" << std::endl;
+ os << "previous block state:" << std::endl;
+ os << os_prev_block.str();
+ std::cerr << os.str() << std::endl;
+ abort();
+ }
+#endif
+
+ return ret;
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::size_type multi_type_vector<Traits>::get_block_position(
+ size_type row, size_type start_block_index) const
+{
+ if (row >= m_cur_size || start_block_index >= m_blocks.size())
+ return m_blocks.size();
+
+ auto it0 = m_blocks.begin();
+ std::advance(it0, start_block_index);
+
+ block b(row, 0);
+ auto it = std::lower_bound(it0, m_blocks.end(), b, detail::compare_blocks<block>);
+
+ if (it == m_blocks.end() || it->position != row)
+ {
+ // Binary search has overshot by one block. Move back one.
+ assert(it != it0);
+ --it;
+ }
+
+ assert(it->position <= row);
+ assert(row < it->position + it->size);
+ return std::distance(it0, it) + start_block_index;
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::size_type multi_type_vector<Traits>::get_block_position(
+ const typename value_type::private_data& pos_data, size_type row) const
+{
+ size_type block_index = 0;
+ if (pos_data.parent == this && pos_data.block_index < m_blocks.size())
+ block_index = pos_data.block_index;
+
+ size_type start_row = m_blocks[block_index].position;
+ if (row < start_row)
+ {
+ // Position hint is past the insertion position.
+ // Walk back if that seems efficient.
+ if (row > start_row / 2)
+ {
+ for (size_type i = block_index; i > 0;)
+ {
+ --i;
+ start_row = m_blocks[i].position;
+ if (row >= start_row)
+ {
+ // Row is in this block.
+ return i;
+ }
+ // Specified row is not in this block.
+ }
+ assert(start_row == 0);
+ }
+ // Otherwise reset.
+ block_index = 0;
+ }
+
+ return get_block_position(row, block_index);
+}
+
+template<typename Traits>
+template<typename T>
+void multi_type_vector<Traits>::create_new_block_with_new_cell(element_block_type*& data, const T& cell)
+{
+ if (data)
+ {
+ m_hdl_event.element_block_released(data);
+ block_funcs::delete_block(data);
+ }
+
+ // New cell block with size 1.
+ data = mdds_mtv_create_new_block(1, cell);
+ if (!data)
+ throw general_error("Failed to create new block.");
+
+ m_hdl_event.element_block_acquired(data);
+}
+
+template<typename Traits>
+template<typename T>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::set_cell_to_middle_of_block(
+ size_type block_index, size_type pos_in_block, const T& cell)
+{
+ block& blk_new = set_new_block_to_middle(block_index, pos_in_block, 1, true);
+ create_new_block_with_new_cell(blk_new.data, cell);
+
+ // Return the iterator referencing the inserted block.
+ return get_iterator(block_index + 1);
+}
+
+template<typename Traits>
+template<typename T>
+void multi_type_vector<Traits>::append_cell_to_block(size_type block_index, const T& cell)
+{
+ m_blocks[block_index].size += 1;
+ mdds_mtv_append_value(*m_blocks[block_index].data, cell);
+}
+
+template<typename Traits>
+template<typename T>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::set_cell_to_empty_block(
+ size_type block_index, size_type pos_in_block, const T& cell)
+{
+ block* blk = &m_blocks[block_index];
+ assert(!blk->data); // In this call, the current block is an empty block.
+
+ if (block_index == 0)
+ {
+ // Topmost block.
+ if (m_blocks.size() == 1)
+ {
+ // this is the only block.
+ assert(blk->size == m_cur_size);
+ if (m_cur_size == 1)
+ {
+ // This column is allowed to have only one row!
+ assert(pos_in_block == 0);
+ create_new_block_with_new_cell(blk->data, cell);
+ return begin();
+ }
+
+ // block has multiple rows.
+ if (pos_in_block == 0)
+ {
+ // Insert into the first cell in this block.
+ blk->size -= 1;
+ assert(blk->size > 0);
+
+ m_blocks.emplace(m_blocks.begin(), 0, 1);
+ blk = &m_blocks[0]; // old pointer is invalid.
+ create_new_block_with_new_cell(blk->data, cell);
+
+ m_blocks[1].position = 1;
+ return begin();
+ }
+
+ if (pos_in_block == blk->size - 1)
+ {
+ // Insert into the last cell in block.
+ blk->size -= 1;
+ assert(blk->size > 0);
+
+ m_blocks.emplace_back(blk->size, 1);
+ blk = &m_blocks.back(); // old pointer is invalid.
+
+ create_new_block_with_new_cell(blk->data, cell);
+ iterator ret = end();
+ --ret;
+ return ret;
+ }
+
+ // Insert into the middle of the block.
+ return set_cell_to_middle_of_block(block_index, pos_in_block, cell);
+ }
+
+ // This topmost empty block is followed by a non-empty block.
+
+ if (pos_in_block == 0)
+ {
+ assert(block_index < m_blocks.size() - 1);
+ if (blk->size == 1)
+ {
+ // Top empty block with only one cell size.
+ element_category_type cat = mdds_mtv_get_element_type(cell);
+ block* blk_next = get_next_block_of_type(block_index, cat);
+ if (blk_next)
+ {
+ // Remove this one-cell empty block from the top, and
+ // prepend the cell to the next block.
+ delete_element_block(m_blocks.front());
+ m_blocks.erase(m_blocks.begin());
+ blk = &m_blocks.front(); // old pointer is invalid.
+ blk->size += 1;
+ blk->position -= 1;
+ mdds_mtv_prepend_value(*blk->data, cell);
+ }
+ else
+ {
+ create_new_block_with_new_cell(blk->data, cell);
+ }
+ }
+ else
+ {
+ // Shrink this topmost block by one and set the new value above it.
+ assert(blk->size > 1);
+ blk->size -= 1;
+ blk->position = 1;
+ m_blocks.emplace(m_blocks.begin(), 0, 1);
+ create_new_block_with_new_cell(m_blocks.front().data, cell);
+ }
+
+ return begin();
+ }
+
+ if (pos_in_block == blk->size - 1)
+ {
+ // Immediately above a non-empty block.
+ element_category_type cat = mdds_mtv_get_element_type(cell);
+ block* blk_next = get_next_block_of_type(block_index, cat);
+ if (blk_next)
+ {
+ assert(blk->size > 1);
+ // Shrink this empty block by one, and prepend the cell to the next block.
+ blk->size -= 1;
+ blk_next->size += 1;
+ blk_next->position -= 1;
+ mdds_mtv_prepend_value(*blk_next->data, cell);
+ }
+ else
+ {
+ // Shrink the current empty block by one, and create a new block of size 1 to store the new value.
+ blk->size -= 1;
+ typename blocks_type::iterator it = m_blocks.begin();
+ std::advance(it, block_index + 1);
+ size_type new_position = detail::calc_next_block_position(*blk);
+ m_blocks.emplace(it, new_position, 1);
+ blk = &m_blocks[block_index]; // old pointer is invalid.
+ block* blk2 = &m_blocks[block_index + 1];
+ create_new_block_with_new_cell(blk2->data, cell);
+ }
+
+ return get_iterator(block_index + 1);
+ }
+
+ // Inserting into the middle of an empty block.
+ return set_cell_to_middle_of_block(block_index, pos_in_block, cell);
+ }
+
+ // This empty block is right below a non-empty block.
+ assert(block_index > 0 && m_blocks[block_index - 1].data != nullptr);
+
+ if (pos_in_block == 0)
+ {
+ // New cell is right below the non-empty block.
+ element_category_type blk_cat_prev = mdds::mtv::get_block_type(*m_blocks[block_index - 1].data);
+ element_category_type cat = mdds_mtv_get_element_type(cell);
+ if (blk_cat_prev == cat)
+ {
+ // Extend the previous block by one to insert this cell.
+
+ if (blk->size == 1)
+ {
+ // Check if we need to merge with the following block.
+ if (block_index == m_blocks.size() - 1)
+ {
+ // Last block. Delete this block and extend the previous
+ // block by one.
+ delete_element_block(m_blocks[block_index]);
+ m_blocks.pop_back();
+ append_cell_to_block(block_index - 1, cell);
+ }
+ else
+ {
+ // Block exists below.
+ block* blk_next = get_next_block_of_type(block_index, blk_cat_prev);
+ if (blk_next)
+ {
+ assert(blk_next->data); // Empty block must not be followed by another empty block.
+
+ // We need to merge the previous and next blocks, then
+ // delete the current and next blocks. Be sure to
+ // resize the next block to zero to prevent the
+ // transferred cells to be deleted.
+ block& blk_prev = m_blocks[block_index - 1];
+
+ // Check if the next block is bigger.
+ if (blk_prev.size < blk_next->size)
+ {
+ // Prepend the new item to the next block, then
+ // prepend the content of the previous block and
+ // release both previous and current blocks.
+
+ size_type position = blk_prev.position;
+
+ // Increase the size of block and prepend the new cell
+ blk_next->size += 1;
+ mdds_mtv_prepend_value(*blk_next->data, cell);
+
+ // Preprend the content of previous block to the next block.
+ block_funcs::prepend_values_from_block(*blk_next->data, *blk_prev.data, 0, blk_prev.size);
+ blk_next->size += blk_prev.size;
+ blk_next->position = position;
+
+ // Resize the previous block to zero
+ block_funcs::resize_block(*blk_prev.data, 0);
+ m_hdl_event.element_block_released(blk_prev.data);
+
+ // Release both blocks which are no longer used
+ block_funcs::delete_block(blk->data);
+ block_funcs::delete_block(blk_prev.data);
+
+ // Get an iterator to previous block
+ typename blocks_type::iterator it = m_blocks.begin() + block_index - 1;
+
+ // So we can use it to remove the two blocks
+ m_blocks.erase(it, it + 2);
+ }
+ else
+ {
+ // Be sure to resize the next block to zero to prevent the
+ // transferred cells to be deleted.
+ blk_prev.size += 1 + blk_next->size;
+ mdds_mtv_append_value(*blk_prev.data, cell);
+ block_funcs::append_block(*blk_prev.data, *blk_next->data);
+ block_funcs::resize_block(*blk_next->data, 0);
+ m_hdl_event.element_block_released(blk_next->data);
+ block_funcs::delete_block(blk->data);
+ block_funcs::delete_block(blk_next->data);
+ typename blocks_type::iterator it = m_blocks.begin() + block_index;
+ m_blocks.erase(it, it + 2);
+ }
+ }
+ else
+ {
+ // Ignore the next block. Just extend the previous block.
+ delete_element_block(m_blocks[block_index]);
+ m_blocks.erase(m_blocks.begin() + block_index);
+ append_cell_to_block(block_index - 1, cell);
+ }
+ }
+ }
+ else
+ {
+ // Extend the previous block to append the cell.
+ assert(blk->size > 1);
+ blk->size -= 1;
+ blk->position += 1;
+ append_cell_to_block(block_index - 1, cell);
+ }
+
+ return get_iterator(block_index - 1);
+ }
+ else
+ {
+ // Cell type is different from the type of the previous block.
+ if (blk->size == 1)
+ {
+ if (block_index == m_blocks.size() - 1)
+ {
+ // There is no more block below. Simply turn this empty block into a non-empty one.
+ create_new_block_with_new_cell(blk->data, cell);
+ }
+ else
+ {
+ // Check the type of the following non-empty block.
+ assert(block_index < m_blocks.size() - 1);
+ block* blk_next = get_next_block_of_type(block_index, cat);
+ if (blk_next)
+ {
+ // Remove this empty block, and prepend the cell to the next block.
+ blk_next->size += 1;
+ blk_next->position -= 1;
+ mdds_mtv_prepend_value(*blk_next->data, cell);
+ delete_element_block(m_blocks[block_index]);
+ m_blocks.erase(m_blocks.begin() + block_index);
+ }
+ else
+ {
+ // Simply turn this empty block into a non-empty one.
+ create_new_block_with_new_cell(blk->data, cell);
+ }
+ }
+ }
+ else
+ {
+ // Replace the current empty block of size > 1 with a
+ // non-empty block of size 1, and insert a new empty block
+ // below whose size is one shorter than the current empty
+ // block.
+ size_type new_block_size = blk->size - 1;
+ size_type new_block_position = blk->position + 1;
+ blk->size = 1;
+ create_new_block_with_new_cell(blk->data, cell);
+ m_blocks.emplace(m_blocks.begin() + block_index + 1, new_block_position, new_block_size);
+ }
+
+ return get_iterator(block_index);
+ }
+ }
+ else if (pos_in_block == blk->size - 1)
+ {
+ // New cell is at the end of the current block.
+ assert(blk->size > 1);
+ if (block_index == m_blocks.size() - 1)
+ {
+ // The current block is the last block.
+ blk->size -= 1;
+ size_type new_position = detail::calc_next_block_position(*blk);
+ m_blocks.emplace_back(new_position, 1);
+ blk = &m_blocks.back(); // old pointer is invalid.
+ create_new_block_with_new_cell(blk->data, cell);
+ iterator it = end();
+ --it;
+ return it;
+ }
+ else
+ {
+ // A non-empty block exists below.
+ element_category_type cat = mdds_mtv_get_element_type(cell);
+ block* blk_next = get_next_block_of_type(block_index, cat);
+ if (blk_next)
+ {
+ // Shrink this empty block and extend the next block.
+ blk->size -= 1;
+ blk_next->size += 1;
+ blk_next->position -= 1;
+ mdds_mtv_prepend_value(*blk_next->data, cell);
+ }
+ else
+ {
+ // Shrink the current empty block by one, and insert a new
+ // block of size 1 below it to store the new value.
+ blk->size -= 1;
+ size_type new_position = detail::calc_next_block_position(*blk);
+ m_blocks.emplace(m_blocks.begin() + block_index + 1, new_position, 1);
+ blk = &m_blocks[block_index]; // old pointer is invalid.
+ block& blk2 = m_blocks[block_index + 1];
+ create_new_block_with_new_cell(blk2.data, cell);
+ }
+
+ return get_iterator(block_index + 1);
+ }
+ }
+
+ // New cell is somewhere in the middle of an empty block.
+ return set_cell_to_middle_of_block(block_index, pos_in_block, cell);
+}
+
+template<typename Traits>
+template<typename T>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::set_cell_to_block_of_size_one(
+ size_type block_index, const T& cell)
+{
+ block* blk = &m_blocks[block_index];
+ assert(blk->size == 1);
+ assert(blk->data);
+ element_category_type cat = mdds_mtv_get_element_type(cell);
+ assert(mdds::mtv::get_block_type(*blk->data) != cat);
+
+ if (block_index == 0)
+ {
+ // This is the topmost block of size 1.
+ if (block_index == m_blocks.size() - 1)
+ {
+ // This is the only block.
+ create_new_block_with_new_cell(blk->data, cell);
+ return begin();
+ }
+
+ // There is an existing block below.
+ block* blk_next = get_next_block_of_type(block_index, cat);
+ if (!blk_next)
+ {
+ // Next block is empty or of different type.
+ create_new_block_with_new_cell(blk->data, cell);
+ return begin();
+ }
+
+ // Delete the current block, and prepend the cell to the next block.
+ blk_next->size += 1;
+ blk_next->position -= 1;
+ mdds_mtv_prepend_value(*blk_next->data, cell);
+ delete_element_block(*blk);
+ m_blocks.erase(m_blocks.begin() + block_index);
+ return begin();
+ }
+
+ assert(block_index > 0);
+
+ if (block_index == m_blocks.size() - 1)
+ {
+ // This is the last block, and a block exists above.
+ block* blk_prev = &m_blocks[block_index - 1];
+ if (!blk_prev->data || mdds::mtv::get_block_type(*blk_prev->data) != cat)
+ {
+ // Previous block is empty. Replace the current block with a new one.
+ create_new_block_with_new_cell(blk->data, cell);
+ }
+ else
+ {
+ // Append the cell to the previos block, and remove the
+ // current block.
+ mdds_mtv_append_value(*blk_prev->data, cell);
+ blk_prev->size += 1;
+ delete_element_block(*blk);
+ m_blocks.erase(m_blocks.begin() + block_index);
+ }
+
+ iterator itr = end();
+ --itr;
+ return itr;
+ }
+
+ // Remove the current block, and check if the cell can be append
+ // to the previous block, or prepended to the following block.
+ // Also check if the blocks above and below need to be combined.
+
+ block* blk_prev = &m_blocks[block_index - 1];
+ block* blk_next = &m_blocks[block_index + 1];
+ if (!blk_prev->data)
+ {
+ // Previous block is empty.
+ if (!blk_next->data)
+ {
+ // Next block is empty too.
+ create_new_block_with_new_cell(blk->data, cell);
+ return get_iterator(block_index);
+ }
+
+ // Previous block is empty, but the next block is not.
+ element_category_type blk_cat_next = mdds::mtv::get_block_type(*blk_next->data);
+ if (blk_cat_next == cat)
+ {
+ // Delete the current block, and prepend the new cell to the next block.
+ delete_element_block(*blk);
+ m_blocks.erase(m_blocks.begin() + block_index);
+ blk = &m_blocks[block_index];
+ blk->size += 1;
+ blk->position -= 1;
+ mdds_mtv_prepend_value(*blk->data, cell);
+ return get_iterator(block_index);
+ }
+
+ assert(blk_cat_next != cat);
+ create_new_block_with_new_cell(blk->data, cell);
+ return get_iterator(block_index);
+ }
+
+ if (!blk_next->data)
+ {
+ // Next block is empty, and the previous block is not.
+ assert(blk_prev->data);
+ element_category_type blk_cat_prev = mdds::mtv::get_block_type(*blk_prev->data);
+ if (blk_cat_prev == cat)
+ {
+ // Append to the previous block.
+ blk_prev->size += 1;
+ mdds_mtv_append_value(*blk_prev->data, cell);
+ delete_element_block(*blk);
+ m_blocks.erase(m_blocks.begin() + block_index);
+ return get_iterator(block_index - 1);
+ }
+
+ // Just overwrite the current block.
+ create_new_block_with_new_cell(blk->data, cell);
+ return get_iterator(block_index);
+ }
+
+ assert(blk_prev && blk_prev->data);
+ assert(blk_next && blk_next->data);
+ element_category_type blk_cat_prev = mdds::mtv::get_block_type(*blk_prev->data);
+ element_category_type blk_cat_next = mdds::mtv::get_block_type(*blk_next->data);
+
+ if (blk_cat_prev == blk_cat_next)
+ {
+ if (blk_cat_prev == cat)
+ {
+ // Merge the previous block with the cell being inserted and
+ // the next block. Resize the next block to zero to prevent
+ // deletion of mananged cells on block deletion.
+ blk_prev->size += 1 + blk_next->size;
+ mdds_mtv_append_value(*blk_prev->data, cell);
+ block_funcs::append_block(*blk_prev->data, *blk_next->data);
+ block_funcs::resize_block(*blk_next->data, 0);
+
+ // Delete the current and next blocks.
+ delete_element_block(*blk);
+ delete_element_block(*blk_next);
+ typename blocks_type::iterator it = m_blocks.begin() + block_index;
+ typename blocks_type::iterator it_last = it + 2;
+ m_blocks.erase(it, it_last);
+ return get_iterator(block_index - 1);
+ }
+
+ // Just overwrite the current block.
+ create_new_block_with_new_cell(blk->data, cell);
+ return get_iterator(block_index);
+ }
+
+ assert(blk_cat_prev != blk_cat_next);
+
+ if (blk_cat_prev == cat)
+ {
+ // Append to the previous block.
+ blk_prev->size += 1;
+ mdds_mtv_append_value(*blk_prev->data, cell);
+ delete_element_block(*blk);
+ m_blocks.erase(m_blocks.begin() + block_index);
+ return get_iterator(block_index - 1);
+ }
+
+ if (blk_cat_next == cat)
+ {
+ // Prepend to the next block.
+ blk_next->size += 1;
+ blk_next->position -= 1;
+ mdds_mtv_prepend_value(*blk_next->data, cell);
+ delete_element_block(*blk);
+ m_blocks.erase(m_blocks.begin() + block_index);
+ return get_iterator(block_index);
+ }
+
+ // Just overwrite the current block.
+ create_new_block_with_new_cell(blk->data, cell);
+ return get_iterator(block_index);
+}
+
+template<typename Traits>
+template<typename T>
+void multi_type_vector<Traits>::set_cell_to_top_of_data_block(size_type block_index, const T& cell)
+{
+ block& blk = m_blocks[block_index];
+ blk.size -= 1;
+ size_type position = blk.position;
+ blk.position += 1;
+
+ if (blk.data)
+ {
+ block_funcs::overwrite_values(*blk.data, 0, 1);
+ block_funcs::erase(*blk.data, 0);
+ }
+
+ m_blocks.emplace(m_blocks.begin() + block_index, position, 1);
+ create_new_block_with_new_cell(m_blocks[block_index].data, cell);
+}
+
+template<typename Traits>
+template<typename T>
+void multi_type_vector<Traits>::set_cell_to_bottom_of_data_block(size_type block_index, const T& cell)
+{
+ assert(block_index < m_blocks.size());
+ block& blk = m_blocks[block_index];
+ if (blk.data)
+ {
+ block_funcs::overwrite_values(*blk.data, blk.size - 1, 1);
+ block_funcs::erase(*blk.data, blk.size - 1);
+ }
+ blk.size -= 1;
+ size_type next_position = detail::calc_next_block_position(blk);
+ m_blocks.emplace(m_blocks.begin() + block_index + 1, next_position, 1);
+ create_new_block_with_new_cell(m_blocks[block_index + 1].data, cell);
+}
+
+template<typename Traits>
+template<typename T>
+void multi_type_vector<Traits>::get(size_type pos, T& value) const
+{
+ size_type block_index = get_block_position(pos);
+ if (block_index == m_blocks.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::get", __LINE__, pos, block_size(), size());
+
+ const block* blk = &m_blocks[block_index];
+ assert(blk);
+
+ if (!blk->data)
+ {
+ // empty cell block.
+ mdds_mtv_get_empty_value(value);
+ return;
+ }
+
+ size_type start_row = blk->position;
+ assert(pos >= start_row);
+ assert(blk->data); // data for non-empty blocks should never be nullptr.
+ size_type idx = pos - start_row;
+ mdds_mtv_get_value(*blk->data, idx, value);
+}
+
+template<typename Traits>
+template<typename T>
+T multi_type_vector<Traits>::get(size_type pos) const
+{
+ T cell;
+ get(pos, cell);
+ return cell;
+}
+
+template<typename Traits>
+template<typename T>
+T multi_type_vector<Traits>::release(size_type pos)
+{
+ size_type block_index = get_block_position(pos);
+ if (block_index == m_blocks.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::release", __LINE__, pos, block_size(), size());
+
+ T value;
+ release_impl(pos, block_index, value);
+ return value;
+}
+
+template<typename Traits>
+template<typename T>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::release(size_type pos, T& value)
+{
+ size_type block_index = get_block_position(pos);
+ if (block_index == m_blocks.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::release", __LINE__, pos, block_size(), size());
+
+ return release_impl(pos, block_index, value);
+}
+
+template<typename Traits>
+template<typename T>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::release(
+ const iterator& pos_hint, size_type pos, T& value)
+{
+ size_type block_index = get_block_position(pos_hint->__private_data, pos);
+ if (block_index == m_blocks.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::release", __LINE__, pos, block_size(), size());
+
+ return release_impl(pos, block_index, value);
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::release()
+{
+ typename blocks_type::iterator it = m_blocks.begin(), it_end = m_blocks.end();
+ for (; it != it_end; ++it)
+ {
+ block* blk = &(*it);
+ if (blk->data)
+ {
+ block_funcs::resize_block(*blk->data, 0);
+ m_hdl_event.element_block_released(blk->data);
+ block_funcs::delete_block(blk->data);
+ }
+ }
+
+ m_blocks.clear();
+ m_cur_size = 0;
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::release_range(
+ size_type start_pos, size_type end_pos)
+{
+ size_type block_index1 = get_block_position(start_pos);
+ if (block_index1 == m_blocks.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::release_range", __LINE__, start_pos, block_size(), size());
+
+ return set_empty_impl(start_pos, end_pos, block_index1, false);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::release_range(
+ const iterator& pos_hint, size_type start_pos, size_type end_pos)
+{
+ size_type block_index1 = get_block_position(pos_hint->__private_data, start_pos);
+ if (block_index1 == m_blocks.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::release_range", __LINE__, start_pos, block_size(), size());
+
+ return set_empty_impl(start_pos, end_pos, block_index1, false);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::position_type multi_type_vector<Traits>::position(size_type pos)
+{
+ if (pos == m_cur_size)
+ {
+ // This is a valid end position. Create a valid position object that
+ // represents a valid end position.
+ return position_type(end(), 0);
+ }
+
+ size_type block_index = get_block_position(pos);
+ if (block_index == m_blocks.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::position", __LINE__, pos, block_size(), size());
+
+ size_type start_pos = m_blocks[block_index].position;
+
+ iterator it = get_iterator(block_index);
+ return position_type(it, pos - start_pos);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::position_type multi_type_vector<Traits>::position(
+ const iterator& pos_hint, size_type pos)
+{
+ if (pos == m_cur_size)
+ {
+ // This is a valid end position. Create a valid position object that
+ // represents a valid end position.
+ return position_type(end(), 0);
+ }
+
+ size_type block_index = get_block_position(pos_hint->__private_data, pos);
+ if (block_index == m_blocks.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::position", __LINE__, pos, block_size(), size());
+
+ iterator it = get_iterator(block_index);
+ size_type start_pos = m_blocks[block_index].position;
+ return position_type(it, pos - start_pos);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::const_position_type multi_type_vector<Traits>::position(size_type pos) const
+{
+ if (pos == m_cur_size)
+ {
+ // This is a valid end position. Create a valid position object that
+ // represents a valid end position.
+ return const_position_type(cend(), 0);
+ }
+
+ size_type block_index = get_block_position(pos);
+ if (block_index == m_blocks.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::position", __LINE__, pos, block_size(), size());
+
+ size_type start_row = m_blocks[block_index].position;
+
+ typename blocks_type::const_iterator block_pos = m_blocks.begin();
+ std::advance(block_pos, block_index);
+ const_iterator it = const_iterator(block_pos, m_blocks.end(), this, block_index);
+ return const_position_type(it, pos - start_row);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::const_position_type multi_type_vector<Traits>::position(
+ const const_iterator& pos_hint, size_type pos) const
+{
+ if (pos == m_cur_size)
+ {
+ // This is a valid end position. Create a valid position object that
+ // represents a valid end position.
+ return const_position_type(cend(), 0);
+ }
+
+ size_type block_index = get_block_position(pos_hint->__private_data, pos);
+ if (block_index == m_blocks.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::position", __LINE__, pos, block_size(), size());
+
+ const_iterator it = get_const_iterator(block_index);
+ size_type start_pos = m_blocks[block_index].position;
+ return const_position_type(it, pos - start_pos);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::transfer(
+ size_type start_pos, size_type end_pos, multi_type_vector& dest, size_type dest_pos)
+{
+ if (&dest == this)
+ throw invalid_arg_error("You cannot transfer between the same container.");
+
+ size_type block_index1 = get_block_position(start_pos);
+ if (block_index1 == m_blocks.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::transfer", __LINE__, start_pos, block_size(), size());
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ std::ostringstream os_prev_block, os_prev_block_dest;
+ dump_blocks(os_prev_block);
+ dest.dump_blocks(os_prev_block_dest);
+#endif
+
+ iterator ret = transfer_impl(start_pos, end_pos, block_index1, dest, dest_pos);
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ std::ostringstream os_block, os_block_dest;
+ dump_blocks(os_block);
+ dest.dump_blocks(os_block_dest);
+
+ try
+ {
+ check_block_integrity();
+ dest.check_block_integrity();
+ }
+ catch (const mdds::integrity_error& e)
+ {
+ std::ostringstream os;
+ os << e.what() << std::endl;
+ os << std::endl
+ << "block integrity check failed in transfer (start_pos=" << start_pos << "; end_pos=" << end_pos
+ << "; dest_pos=" << dest_pos << ")" << std::endl;
+ os << std::endl << "previous block state (source):" << std::endl;
+ os << os_prev_block.str();
+ os << std::endl << "previous block state (destination):" << std::endl;
+ os << os_prev_block_dest.str();
+ os << std::endl << "altered block state (source):" << std::endl;
+ os << os_block.str();
+ os << std::endl << "altered block state (destination):" << std::endl;
+ os << os_block_dest.str();
+ std::cerr << os.str() << std::endl;
+ abort();
+ }
+#endif
+
+ return ret;
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::transfer(
+ const iterator& pos_hint, size_type start_pos, size_type end_pos, multi_type_vector& dest, size_type dest_pos)
+{
+ size_type block_index1 = get_block_position(pos_hint->__private_data, start_pos);
+ if (block_index1 == m_blocks.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::transfer", __LINE__, start_pos, block_size(), size());
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ std::ostringstream os_prev_block, os_prev_block_dest;
+ dump_blocks(os_prev_block);
+ dest.dump_blocks(os_prev_block_dest);
+#endif
+
+ iterator ret = transfer_impl(start_pos, end_pos, block_index1, dest, dest_pos);
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ std::ostringstream os_block, os_block_dest;
+ dump_blocks(os_block);
+ dest.dump_blocks(os_block_dest);
+
+ try
+ {
+ check_block_integrity();
+ dest.check_block_integrity();
+ }
+ catch (const mdds::integrity_error& e)
+ {
+ std::ostringstream os;
+ os << e.what() << std::endl;
+ os << std::endl
+ << "block integrity check failed in transfer (start_pos=" << start_pos << "; end_pos=" << end_pos
+ << "; dest_pos=" << dest_pos << ")" << std::endl;
+ os << std::endl << "previous block state (source):" << std::endl;
+ os << os_prev_block.str();
+ os << std::endl << "previous block state (destination):" << std::endl;
+ os << os_prev_block_dest.str();
+ os << std::endl << "altered block state (source):" << std::endl;
+ os << os_block.str();
+ os << std::endl << "altered block state (destination):" << std::endl;
+ os << os_block_dest.str();
+ std::cerr << os.str() << std::endl;
+ abort();
+ }
+#endif
+
+ return ret;
+}
+
+template<typename Traits>
+mtv::element_t multi_type_vector<Traits>::get_type(size_type pos) const
+{
+ size_type block_index = get_block_position(pos);
+ if (block_index == m_blocks.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::get_type", __LINE__, pos, block_size(), size());
+
+ const block* blk = &m_blocks[block_index];
+ if (!blk->data)
+ return mtv::element_type_empty;
+
+ return mtv::get_block_type(*blk->data);
+}
+
+template<typename Traits>
+bool multi_type_vector<Traits>::is_empty(size_type pos) const
+{
+ size_type block_index = get_block_position(pos);
+ if (block_index == m_blocks.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::is_empty", __LINE__, pos, block_size(), size());
+
+ return m_blocks[block_index].data == nullptr;
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::set_empty(
+ size_type start_pos, size_type end_pos)
+{
+ size_type block_index1 = get_block_position(start_pos);
+ if (block_index1 == m_blocks.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::set_empty", __LINE__, start_pos, block_size(), size());
+
+ return set_empty_impl(start_pos, end_pos, block_index1, true);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::set_empty(
+ const iterator& pos_hint, size_type start_pos, size_type end_pos)
+{
+ size_type block_index1 = get_block_position(pos_hint->__private_data, start_pos);
+ if (block_index1 == m_blocks.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::set_empty", __LINE__, start_pos, block_size(), size());
+
+ return set_empty_impl(start_pos, end_pos, block_index1, true);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::transfer_impl(
+ size_type start_pos, size_type end_pos, size_type block_index1, multi_type_vector& dest, size_type dest_pos)
+{
+ if (start_pos > end_pos)
+ {
+ std::ostringstream os;
+ os << "multi_type_vector::transfer_impl: start position is larger than the end position. (start=";
+ os << start_pos << ", end=" << end_pos << ")";
+ throw std::out_of_range(os.str());
+ }
+
+ size_type block_index2 = get_block_position(end_pos, block_index1);
+ if (block_index2 == m_blocks.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::transfer_impl", __LINE__, end_pos, block_size(), size());
+
+ size_type len = end_pos - start_pos + 1;
+ size_type last_dest_pos = dest_pos + len - 1;
+
+ // Make sure the destination container is large enough.
+ if (last_dest_pos >= dest.size())
+ throw std::out_of_range("Destination vector is too small for the elements being transferred.");
+
+ if (block_index1 == block_index2)
+ {
+ // All elements are in the same block.
+ return transfer_single_block(start_pos, end_pos, block_index1, dest, dest_pos);
+ }
+
+ return transfer_multi_blocks(start_pos, end_pos, block_index1, block_index2, dest, dest_pos);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::transfer_single_block(
+ size_type start_pos, size_type end_pos, size_type block_index1, multi_type_vector& dest, size_type dest_pos)
+{
+ size_type len = end_pos - start_pos + 1;
+ size_type last_dest_pos = dest_pos + len - 1;
+
+ // All elements are in the same block.
+ block* blk = &m_blocks[block_index1];
+ size_type start_pos_in_block1 = blk->position;
+
+ // Empty the region in the destination container where the elements
+ // are to be transferred to. This ensures that the destination region
+ // consists of a single block.
+ iterator it_dest_blk = dest.set_empty(dest_pos, last_dest_pos);
+
+ if (!blk->data)
+ return get_iterator(block_index1);
+
+ element_category_type cat = get_block_type(*blk->data);
+
+ size_type dest_block_index = it_dest_blk->__private_data.block_index;
+ block* blk_dest = &dest.m_blocks[dest_block_index];
+
+ size_type dest_pos_in_block = dest_pos - it_dest_blk->position;
+ if (dest_pos_in_block == 0)
+ {
+ // Copy to the top part of the destination block.
+
+ assert(!blk_dest->data); // should be already emptied.
+
+ if (len < blk_dest->size)
+ {
+ // Shrink the existing block and insert a new block before it.
+ assert(len < blk_dest->size);
+ size_type position = blk_dest->position;
+ blk_dest->position += len;
+ blk_dest->size -= len;
+ dest.m_blocks.emplace(dest.m_blocks.begin() + dest_block_index, position, len);
+ blk_dest = &dest.m_blocks[dest_block_index]; // The old pointer is invalid.
+ }
+ }
+ else if (dest_pos_in_block + len - 1 == it_dest_blk->size - 1)
+ {
+ // Copy to the bottom part of destination block.
+
+ // Insert a new block below current, and shrink the current block.
+ dest.m_blocks[dest_block_index].size -= len;
+ size_type position = detail::calc_next_block_position(dest.m_blocks, dest_block_index);
+ dest.m_blocks.emplace(dest.m_blocks.begin() + dest_block_index + 1, position, len);
+ blk_dest = &dest.m_blocks[dest_block_index + 1];
+ ++dest_block_index; // Must point to the new copied block.
+ }
+ else
+ {
+ // Copy to the middle of destination block.
+
+ // Insert two new blocks below current.
+ size_type blk2_size = blk_dest->size - dest_pos_in_block - len;
+ dest.m_blocks.insert(dest.m_blocks.begin() + dest_block_index + 1, 2u, block());
+ dest.m_blocks[dest_block_index].size = dest_pos_in_block;
+ dest.m_blocks[dest_block_index + 1].size = len;
+ dest.m_blocks[dest_block_index + 2].size = blk2_size;
+
+ dest.m_blocks[dest_block_index + 1].position =
+ detail::calc_next_block_position(dest.m_blocks, dest_block_index);
+ dest.m_blocks[dest_block_index + 2].position =
+ detail::calc_next_block_position(dest.m_blocks, dest_block_index + 1);
+
+ blk_dest = &dest.m_blocks[dest_block_index + 1];
+
+ ++dest_block_index; // Must point to the new copied block.
+ }
+
+ assert(blk_dest->size == len);
+ size_type offset = start_pos - start_pos_in_block1;
+ if (offset == 0 && len == blk->size)
+ {
+ // Just move the whole data array.
+ blk_dest->data = blk->data;
+ dest.m_hdl_event.element_block_acquired(blk_dest->data);
+
+ m_hdl_event.element_block_released(blk->data);
+ blk->data = nullptr;
+
+ dest.merge_with_adjacent_blocks(dest_block_index);
+ size_type start_pos_offset = merge_with_adjacent_blocks(block_index1);
+ if (start_pos_offset)
+ {
+ // Merged with the previous block. Adjust the return block position.
+ --block_index1;
+ start_pos_in_block1 -= start_pos_offset;
+ }
+
+ return get_iterator(block_index1);
+ }
+
+ blk_dest->data = block_funcs::create_new_block(cat, 0);
+ assert(blk_dest->data);
+ dest.m_hdl_event.element_block_acquired(blk_dest->data);
+
+ // Shallow-copy the elements to the destination block.
+ block_funcs::assign_values_from_block(*blk_dest->data, *blk->data, offset, len);
+ dest.merge_with_adjacent_blocks(dest_block_index);
+
+ // Set the source range empty without overwriting the elements.
+ return set_empty_in_single_block(start_pos, end_pos, block_index1, false);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::transfer_multi_blocks(
+ size_type start_pos, size_type end_pos, size_type block_index1, size_type block_index2, multi_type_vector& dest,
+ size_type dest_pos)
+{
+ assert(block_index1 < block_index2);
+ size_type start_pos_in_block1 = m_blocks[block_index1].position;
+ size_type start_pos_in_block2 = m_blocks[block_index2].position;
+
+ size_type len = end_pos - start_pos + 1;
+ size_type last_dest_pos = dest_pos + len - 1;
+
+ // Empty the region in the destination container where the elements
+ // are to be transferred to. This ensures that the destination region
+ // consists of a single block.
+ iterator it_dest_blk = dest.set_empty(dest_pos, last_dest_pos);
+
+ size_type dest_block_index = it_dest_blk->__private_data.block_index;
+ size_type dest_pos_in_block = dest_pos - it_dest_blk->position;
+ block* blk_dest = &dest.m_blocks[dest_block_index];
+ assert(!blk_dest->data); // should be already emptied.
+
+ size_type block_len = block_index2 - block_index1 + 1;
+
+ // Create slots for new blocks in the destination.
+
+ size_type dest_block_index1 = dest_block_index;
+
+ if (dest_pos_in_block == 0)
+ {
+ // Copy to the top part of destination block.
+ if (len < blk_dest->size)
+ {
+ // Shrink the existing block and insert slots for the new blocks before it.
+ blk_dest->size -= len;
+ blk_dest->position += len;
+ dest.m_blocks.insert(dest.m_blocks.begin() + dest_block_index, block_len, block());
+ }
+ else
+ {
+ // Destination block is exactly of the length of the elements being transferred.
+ dest.delete_element_block(*blk_dest);
+ blk_dest->size = 0;
+ if (block_len > 1)
+ dest.m_blocks.insert(dest.m_blocks.begin() + dest_block_index, block_len - 1, block());
+ }
+ }
+ else if (dest_pos_in_block + len - 1 == it_dest_blk->size - 1)
+ {
+ // Copy to the bottom part of destination block. Insert slots for new
+ // blocks below current, and shrink the current block.
+ dest.m_blocks.insert(dest.m_blocks.begin() + dest_block_index + 1, block_len, block());
+ blk_dest = &dest.m_blocks[dest_block_index];
+ blk_dest->size -= len;
+
+ ++dest_block_index1;
+ }
+ else
+ {
+ // Copy to the middle of the destination block. Insert slots for the
+ // new blocks (plus one extra for the bottom empty block) below the
+ // current block.
+ size_type blk2_size = blk_dest->size - dest_pos_in_block - len;
+ dest.m_blocks.insert(dest.m_blocks.begin() + dest_block_index + 1, block_len + 1, block());
+ blk_dest = &dest.m_blocks[dest_block_index];
+ assert(dest.m_blocks.size() > dest_block_index + block_len + 1);
+ blk_dest->size = dest_pos_in_block;
+
+ // Re-calculate the size and position of the lower part of the destination block.
+ block& blk_dest_lower = dest.m_blocks[dest_block_index + block_len + 1];
+ blk_dest_lower.position = detail::calc_next_block_position(*blk_dest) + len;
+ blk_dest_lower.size = blk2_size;
+
+ ++dest_block_index1;
+ }
+
+ size_type del_index1 = block_index1, del_index2 = block_index2;
+
+ // Now that the new slots have been created, start transferring the blocks.
+
+ // Transfer the first block.
+ size_type offset = start_pos - start_pos_in_block1;
+ if (offset)
+ {
+ // Transfer the lower part of the first block.
+ block* blk = &m_blocks[block_index1];
+
+ assert(dest.m_blocks[dest_block_index1].size == 0);
+ dest.m_blocks[dest_block_index1].size = blk->size - offset;
+ if (dest_block_index1 > 0)
+ dest.m_blocks[dest_block_index1].position =
+ detail::calc_next_block_position(dest.m_blocks, dest_block_index1 - 1);
+
+ if (blk->data)
+ {
+ element_category_type cat = mtv::get_block_type(*blk->data);
+ blk_dest = &dest.m_blocks[dest_block_index1];
+ blk_dest->data = block_funcs::create_new_block(cat, 0);
+ assert(blk_dest->data);
+ dest.m_hdl_event.element_block_acquired(blk_dest->data);
+
+ // Shallow-copy the elements to the destination block, and shrink
+ // the source block to remove the transferred elements.
+ block_funcs::assign_values_from_block(*blk_dest->data, *blk->data, offset, blk->size - offset);
+ block_funcs::resize_block(*blk->data, offset);
+ }
+
+ blk->size = offset;
+ ++del_index1; // Retain this block.
+ }
+ else
+ {
+ // Just move the whole block over.
+ block& blk = m_blocks[block_index1];
+ dest.m_blocks[dest_block_index1] = blk; // copied.
+ dest.m_blocks[dest_block_index1].position =
+ dest_block_index1 > 0 ? detail::calc_next_block_position(dest.m_blocks, dest_block_index1 - 1) : 0;
+
+ if (blk.data)
+ {
+ dest.m_hdl_event.element_block_acquired(blk.data);
+ m_hdl_event.element_block_released(blk.data);
+ blk.data = nullptr;
+ }
+
+ blk.size = 0;
+ }
+
+ if (block_len > 2)
+ {
+ // Transfer all blocks in between.
+ size_type position = detail::calc_next_block_position(dest.m_blocks, dest_block_index1);
+ for (size_type i = 0; i < block_len - 2; ++i)
+ {
+ size_type src_block_pos = block_index1 + 1 + i;
+ size_type dest_block_pos = dest_block_index1 + 1 + i;
+ assert(dest.m_blocks[dest_block_pos].size == 0);
+ block& blk = m_blocks[src_block_pos];
+ dest.m_blocks[dest_block_pos] = blk; // copied.
+ dest.m_blocks[dest_block_pos].position = position;
+ position += blk.size;
+ blk.size = 0;
+
+ if (blk.data)
+ {
+ dest.m_hdl_event.element_block_acquired(blk.data);
+ m_hdl_event.element_block_released(blk.data);
+ blk.data = nullptr;
+ }
+ }
+ }
+
+ // Transfer the last block.
+ if (block_len > 1)
+ {
+ size_type size_to_trans = end_pos - start_pos_in_block2 + 1;
+ size_type dest_block_pos = dest_block_index1 + block_len - 1;
+ assert(dest.m_blocks[dest_block_pos].size == 0);
+
+ block& blk = m_blocks[block_index2];
+ if (size_to_trans < blk.size)
+ {
+ // Transfer the upper part of this block.
+ assert(dest_block_pos > 0);
+ dest.m_blocks[dest_block_pos].position =
+ detail::calc_next_block_position(dest.m_blocks, dest_block_pos - 1);
+ dest.m_blocks[dest_block_pos].size = size_to_trans;
+ blk_dest = &dest.m_blocks[dest_block_pos];
+ if (blk.data)
+ {
+ element_category_type cat = mtv::get_block_type(*blk.data);
+ blk_dest->data = block_funcs::create_new_block(cat, 0);
+ dest.m_hdl_event.element_block_acquired(blk_dest->data);
+
+ block_funcs::assign_values_from_block(*blk_dest->data, *blk.data, 0, size_to_trans);
+ block_funcs::erase(*blk.data, 0, size_to_trans);
+ }
+
+ blk.position += size_to_trans;
+ blk.size -= size_to_trans;
+ --del_index2; // Retain this block.
+ }
+ else
+ {
+ // Just move the whole block over.
+ dest.m_blocks[dest_block_pos] = blk;
+ dest.m_blocks[dest_block_pos].position =
+ dest_block_pos > 0 ? detail::calc_next_block_position(dest.m_blocks, dest_block_pos - 1) : 0;
+
+ if (blk.data)
+ {
+ dest.m_hdl_event.element_block_acquired(blk.data);
+ m_hdl_event.element_block_released(blk.data);
+ blk.data = nullptr;
+ }
+ m_blocks[block_index2].size = 0;
+ }
+ }
+
+ // Now that all the elements have been transferred, check the bordering
+ // blocks in the destination and merge them as needed.
+ if (block_len > 1)
+ dest.merge_with_adjacent_blocks(dest_block_index1 + block_len - 1);
+ dest.merge_with_adjacent_blocks(dest_block_index1);
+
+ // Delete all transferred blocks, and replace it with one empty block.
+ if (del_index2 < del_index1)
+ {
+ // No blocks will be deleted. See if we can just extend one of the
+ // neighboring empty blocks.
+
+ block& blk1 = m_blocks[block_index1];
+ block& blk2 = m_blocks[block_index2];
+
+ if (!blk1.data)
+ {
+ assert(blk2.data);
+
+ // Block 1 is empty. Extend this block downward.
+ blk1.size += len;
+ return get_iterator(block_index1);
+ }
+
+ if (!blk2.data)
+ {
+ assert(blk1.data);
+
+ // Block 2 is empty. Extend this block upward.
+ blk2.size += len;
+ blk2.position -= len;
+ return get_iterator(block_index2);
+ }
+
+ // Neither block1 nor block2 are empty. Just insert a new empty block
+ // between them. After the insertion, the old block2 position becomes
+ // the position of the inserted block.
+ size_type position = detail::calc_next_block_position(blk1);
+ m_blocks.emplace(m_blocks.begin() + block_index2, position, len);
+ // No need to adjust local index vars
+ return get_iterator(block_index2);
+ }
+
+ if (del_index1 > 0 && !m_blocks[del_index1 - 1].data)
+ {
+ // The block before the first block to be deleted is empty. Simply
+ // extend that block to cover the deleted block segment.
+ block& blk_prev = m_blocks[del_index1 - 1];
+
+ // Extend the previous block.
+ blk_prev.size += len;
+ }
+ else
+ {
+ // Block before is not empty (or doesn't exist). Keep the first slot,
+ // and erase the rest.
+ m_blocks[del_index1].size = len; // Insert an empty
+ ++del_index1;
+ }
+
+ size_type ret_block_index = del_index1 - 1;
+
+ if (del_index2 >= del_index1)
+ {
+ typename blocks_type::iterator it_blk = m_blocks.begin();
+ typename blocks_type::iterator it_blk_end = m_blocks.begin();
+ std::advance(it_blk, del_index1);
+ std::advance(it_blk_end, del_index2 + 1);
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ typename blocks_type::iterator it_test = it_blk;
+ for (; it_test != it_blk_end; ++it_test)
+ {
+ // All slots to be erased should have zero size
+ assert(it_test->size == 0);
+ }
+#endif
+ m_blocks.erase(it_blk, it_blk_end);
+ }
+
+ // The block pointed to by ret_block_index is guaranteed to be empty by
+ // this point.
+ assert(!m_blocks[ret_block_index].data);
+ // Merging with the previous block never happens.
+ size_type start_pos_offset = merge_with_adjacent_blocks(ret_block_index);
+ (void)start_pos_offset; // avoid unused variable compiler warning.
+ assert(!start_pos_offset);
+
+ m_blocks[ret_block_index].position =
+ ret_block_index > 0 ? detail::calc_next_block_position(m_blocks, ret_block_index - 1) : 0;
+
+ return get_iterator(ret_block_index);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::set_empty_impl(
+ size_type start_pos, size_type end_pos, size_type block_index1, bool overwrite)
+{
+ if (start_pos > end_pos)
+ throw std::out_of_range("Start row is larger than the end row.");
+
+ size_type block_index2 = get_block_position(end_pos, block_index1);
+ if (block_index2 == m_blocks.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::set_empty_impl", __LINE__, end_pos, block_size(), size());
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ std::ostringstream os_prev_block;
+ dump_blocks(os_prev_block);
+#endif
+
+ iterator ret_it;
+ if (block_index1 == block_index2)
+ ret_it = set_empty_in_single_block(start_pos, end_pos, block_index1, overwrite);
+ else
+ ret_it = set_empty_in_multi_blocks(start_pos, end_pos, block_index1, block_index2, overwrite);
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ try
+ {
+ check_block_integrity();
+ }
+ catch (const mdds::integrity_error& e)
+ {
+ std::ostringstream os;
+ os << e.what() << std::endl;
+ os << "block integrity check failed in set_empty(start_pos=" << start_pos << "; end_pos=" << end_pos << ")"
+ << std::endl;
+ os << "previous block state:" << std::endl;
+ os << os_prev_block.str();
+ std::cerr << os.str() << std::endl;
+ abort();
+ }
+#endif
+ return ret_it;
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::swap_impl(
+ multi_type_vector& other, size_type start_pos, size_type end_pos, size_type other_pos, size_type block_index1,
+ size_type block_index2, size_type dblock_index1, size_type dblock_index2)
+{
+ if (block_index1 == block_index2)
+ {
+ // Source range is in a single block.
+ if (dblock_index1 == dblock_index2)
+ {
+ // Destination range is also in a single block.
+ swap_single_block(other, start_pos, end_pos, other_pos, block_index1, dblock_index1);
+ }
+ else
+ {
+ // Source is single-, and destination is multi-blocks.
+ swap_single_to_multi_blocks(
+ other, start_pos, end_pos, other_pos, block_index1, dblock_index1, dblock_index2);
+ }
+ }
+ else if (dblock_index1 == dblock_index2)
+ {
+ // Destination range is over a single block. Switch source and destination.
+ size_type len = end_pos - start_pos + 1;
+ other.swap_single_to_multi_blocks(
+ *this, other_pos, other_pos + len - 1, start_pos, dblock_index1, block_index1, block_index2);
+ }
+ else
+ {
+ // Both source and destinations are multi-block.
+ swap_multi_to_multi_blocks(
+ other, start_pos, end_pos, other_pos, block_index1, block_index2, dblock_index1, dblock_index2);
+ }
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::swap_single_block(
+ multi_type_vector& other, size_type start_pos, size_type end_pos, size_type other_pos, size_type block_index,
+ size_type other_block_index)
+{
+ block* blk_src = &m_blocks[block_index];
+ block* blk_dst = &other.m_blocks[other_block_index];
+ size_type start_pos_in_block = blk_src->position;
+ size_type start_pos_in_other_block = blk_dst->position;
+ element_category_type cat_src = mtv::element_type_empty;
+ element_category_type cat_dst = mtv::element_type_empty;
+
+ if (blk_src->data)
+ cat_src = mtv::get_block_type(*blk_src->data);
+ if (blk_dst->data)
+ cat_dst = mtv::get_block_type(*blk_dst->data);
+
+ size_t other_end_pos = other_pos + end_pos - start_pos;
+ size_t len = end_pos - start_pos + 1; // length of elements to swap.
+ size_type src_offset = start_pos - start_pos_in_block;
+ size_type dst_offset = other_pos - start_pos_in_other_block;
+
+ // length of the tail that will not be swapped.
+ size_type src_tail_len = blk_src->size - src_offset - len;
+
+ if (cat_src == cat_dst)
+ {
+ // Source and destination blocks are of the same type.
+ if (cat_src == mtv::element_type_empty)
+ // Both are empty blocks. Nothing to swap.
+ return;
+
+ block_funcs::swap_values(*blk_src->data, *blk_dst->data, src_offset, dst_offset, len);
+ return;
+ }
+
+ // Source and destination blocks are of different types.
+
+ if (cat_src == mtv::element_type_empty)
+ {
+ // Source is empty but destination is not. This is equivalent of transfer.
+ other.transfer_single_block(other_pos, other_end_pos, other_block_index, *this, start_pos);
+ // No update of local index vars needed.
+ return;
+ }
+
+ if (cat_dst == mtv::element_type_empty)
+ {
+ // Source is not empty but destination is. Use transfer.
+ transfer_single_block(start_pos, end_pos, block_index, other, other_pos);
+ // No update of local index vars needed.
+ return;
+ }
+
+ // Neither the source nor destination blocks are empty, and they are of different types.
+ if (src_offset == 0)
+ {
+ // Source range is at the top of a block.
+ if (src_tail_len == 0)
+ {
+ // the whole block needs to be replaced.
+ std::unique_ptr<element_block_type, element_block_deleter> src_data(blk_src->data);
+ m_hdl_event.element_block_released(blk_src->data);
+ blk_src->data = other.exchange_elements(*src_data, src_offset, other_block_index, dst_offset, len);
+ m_hdl_event.element_block_acquired(blk_src->data);
+
+ // Release elements in the source block to prevent double-deletion.
+ block_funcs::resize_block(*src_data, 0);
+ merge_with_adjacent_blocks(block_index);
+ return;
+ }
+
+ // Get the new elements from the other container.
+ std::unique_ptr<element_block_type, element_block_deleter> dst_data(
+ other.exchange_elements(*blk_src->data, src_offset, other_block_index, dst_offset, len));
+
+ // Shrink the current block by erasing the top part.
+ block_funcs::erase(*blk_src->data, 0, len);
+ blk_src->position += len;
+ blk_src->size -= len;
+
+ block* blk_prev = get_previous_block_of_type(block_index, cat_dst);
+ if (blk_prev)
+ {
+ // Append the new elements to the previous block.
+ block_funcs::append_block(*blk_prev->data, *dst_data);
+ block_funcs::resize_block(*dst_data, 0); // prevent double-delete.
+ blk_prev->size += len;
+ }
+ else
+ {
+ // Insert a new block to store the new elements.
+ size_type position = blk_src->position - len;
+ m_blocks.emplace(m_blocks.begin() + block_index, position, len);
+ block& blk = m_blocks[block_index];
+ blk.data = dst_data.release();
+ m_hdl_event.element_block_acquired(blk.data);
+ }
+ return;
+ }
+
+ // Get the new elements from the other container.
+ std::unique_ptr<element_block_type, element_block_deleter> dst_data(
+ other.exchange_elements(*blk_src->data, src_offset, other_block_index, dst_offset, len));
+
+ if (src_tail_len == 0)
+ {
+ // Source range is at the bottom of a block.
+
+ // Shrink the current block.
+ block_funcs::resize_block(*blk_src->data, src_offset);
+ blk_src->size = src_offset;
+
+ block* blk_next = get_next_block_of_type(block_index, cat_dst);
+ if (blk_next)
+ {
+ // Merge with the next block.
+ block_funcs::prepend_values_from_block(*blk_next->data, *dst_data, 0, len);
+ block_funcs::resize_block(*dst_data, 0); // prevent double-delete.
+ blk_next->size += len;
+ blk_next->position -= len;
+ }
+ else
+ {
+ size_type position = detail::calc_next_block_position(*blk_src);
+ m_blocks.emplace(m_blocks.begin() + block_index + 1, position, len);
+ block& blk = m_blocks[block_index + 1];
+ blk.data = dst_data.release();
+ m_hdl_event.element_block_acquired(blk.data);
+ }
+ return;
+ }
+
+ // Source range is in the middle of a block.
+ assert(src_offset && src_tail_len);
+ block& blk = set_new_block_to_middle(block_index, src_offset, len, false);
+ blk.data = dst_data.release();
+ m_hdl_event.element_block_acquired(blk.data);
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::swap_single_to_multi_blocks(
+ multi_type_vector& other, size_type start_pos, size_type end_pos, size_type other_pos, size_type block_index,
+ size_type dst_block_index1, size_type dst_block_index2)
+{
+ block* blk_src = &m_blocks[block_index];
+ size_type start_pos_in_block = blk_src->position;
+ size_type dst_start_pos_in_block1 = other.m_blocks[dst_block_index1].position;
+ size_type dst_start_pos_in_block2 = other.m_blocks[dst_block_index2].position;
+
+ element_category_type cat_src = mtv::element_type_empty;
+ if (blk_src->data)
+ cat_src = mtv::get_block_type(*blk_src->data);
+
+ size_type len = end_pos - start_pos + 1;
+
+ if (cat_src == mtv::element_type_empty)
+ {
+ // The source block is empty. Use transfer.
+ other.transfer_multi_blocks(
+ other_pos, other_pos + len - 1, dst_block_index1, dst_block_index2, *this, start_pos);
+ return;
+ }
+
+ size_type src_offset = start_pos - start_pos_in_block;
+ size_type dst_offset1 = other_pos - dst_start_pos_in_block1;
+ size_type dst_offset2 = other_pos + len - 1 - dst_start_pos_in_block2;
+
+ // length of the tail that will not be swapped.
+ size_type src_tail_len = blk_src->size - src_offset - len;
+
+ // Get the new elements from the other container.
+ blocks_type new_blocks;
+ other.exchange_elements(
+ *blk_src->data, src_offset, dst_block_index1, dst_offset1, dst_block_index2, dst_offset2, len, new_blocks);
+
+ if (new_blocks.empty())
+ throw general_error("multi_type_vector::swap_single_to_multi_blocks: failed to exchange elements.");
+
+ if (src_offset == 0)
+ {
+ // Source range is at the top of a block.
+
+ size_type src_position = blk_src->position;
+
+ if (src_tail_len == 0)
+ {
+ // the whole block needs to be replaced. Delete the block, but
+ // don't delete the managed elements the block contains since they
+ // have been transferred over to the destination block.
+ block_funcs::resize_block(*blk_src->data, 0);
+ delete_element_block(*blk_src);
+ m_blocks.erase(m_blocks.begin() + block_index);
+ }
+ else
+ {
+ // Shrink the current block by erasing the top part.
+ block_funcs::erase(*blk_src->data, 0, len);
+ blk_src->size -= len;
+ blk_src->position += len;
+ }
+
+ insert_blocks_at(src_position, block_index, new_blocks);
+ merge_with_next_block(block_index + new_blocks.size() - 1); // last block inserted.
+ if (block_index > 0)
+ merge_with_next_block(block_index - 1); // block before the first block inserted.
+
+ return;
+ }
+
+ size_type position = 0;
+
+ if (src_tail_len == 0)
+ {
+ // Source range is at the bottom of a block.
+
+ // Shrink the current block.
+ block_funcs::resize_block(*blk_src->data, src_offset);
+ blk_src->size = src_offset;
+ position = detail::calc_next_block_position(*blk_src);
+ }
+ else
+ {
+ // Source range is in the middle of a block.
+ assert(src_offset && src_tail_len);
+
+ // This will create two new slots at block_index+1, the first of which
+ // we will immediately remove. The new blocks from the other
+ // container will be inserted at the removed slot.
+ set_new_block_to_middle(block_index, src_offset, len, false);
+ delete_element_block(m_blocks[block_index + 1]);
+ m_blocks.erase(m_blocks.begin() + block_index + 1);
+ position = detail::calc_next_block_position(m_blocks, block_index);
+ }
+
+ insert_blocks_at(position, block_index + 1, new_blocks);
+ merge_with_next_block(block_index + new_blocks.size()); // last block inserted.
+ merge_with_next_block(block_index); // block before the first block inserted.
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::swap_multi_to_multi_blocks(
+ multi_type_vector& other, size_type start_pos, size_type end_pos, size_type other_pos, size_type block_index1,
+ size_type block_index2, size_type dblock_index1, size_type dblock_index2)
+{
+ assert(block_index1 < block_index2);
+ assert(dblock_index1 < dblock_index2);
+
+ size_type start_pos_in_block1 = m_blocks[block_index1].position;
+ size_type start_pos_in_block2 = m_blocks[block_index2].position;
+ size_type start_pos_in_dblock1 = other.m_blocks[dblock_index1].position;
+ size_type start_pos_in_dblock2 = other.m_blocks[dblock_index2].position;
+
+ size_type len = end_pos - start_pos + 1;
+ size_type src_offset1 = start_pos - start_pos_in_block1;
+ size_type src_offset2 = end_pos - start_pos_in_block2;
+ size_type dst_offset1 = other_pos - start_pos_in_dblock1;
+ size_type dst_offset2 = other_pos + len - 1 - start_pos_in_dblock2;
+
+ blocks_to_transfer src_bucket, dst_bucket;
+ prepare_blocks_to_transfer(src_bucket, block_index1, src_offset1, block_index2, src_offset2);
+ other.prepare_blocks_to_transfer(dst_bucket, dblock_index1, dst_offset1, dblock_index2, dst_offset2);
+
+ size_type position =
+ src_bucket.insert_index > 0 ? detail::calc_next_block_position(m_blocks, src_bucket.insert_index - 1) : 0;
+ insert_blocks_at(position, src_bucket.insert_index, dst_bucket.blocks);
+
+ // Merge the boundary blocks in the source.
+ merge_with_next_block(src_bucket.insert_index + dst_bucket.blocks.size() - 1);
+ if (src_bucket.insert_index > 0)
+ merge_with_next_block(src_bucket.insert_index - 1);
+
+ position =
+ dst_bucket.insert_index > 0 ? detail::calc_next_block_position(other.m_blocks, dst_bucket.insert_index - 1) : 0;
+ other.insert_blocks_at(position, dst_bucket.insert_index, src_bucket.blocks);
+
+ // Merge the boundary blocks in the destination.
+ other.merge_with_next_block(dst_bucket.insert_index + src_bucket.blocks.size() - 1);
+ if (dst_bucket.insert_index > 0)
+ other.merge_with_next_block(dst_bucket.insert_index - 1);
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::insert_blocks_at(size_type position, size_type insert_pos, blocks_type& new_blocks)
+{
+ std::for_each(new_blocks.begin(), new_blocks.end(), [&](block& r) {
+ r.position = position;
+ position += r.size;
+
+ if (r.data)
+ m_hdl_event.element_block_acquired(r.data);
+ });
+
+ m_blocks.insert(m_blocks.begin() + insert_pos, new_blocks.begin(), new_blocks.end());
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::prepare_blocks_to_transfer(
+ blocks_to_transfer& bucket, size_type block_index1, size_type offset1, size_type block_index2, size_type offset2)
+{
+ assert(block_index1 < block_index2);
+ assert(offset1 < m_blocks[block_index1].size);
+ assert(offset2 < m_blocks[block_index2].size);
+
+ block block_first;
+ block block_last;
+ typename blocks_type::iterator it_begin = m_blocks.begin();
+ typename blocks_type::iterator it_end = m_blocks.begin();
+
+ std::advance(it_begin, block_index1 + 1);
+ std::advance(it_end, block_index2);
+ bucket.insert_index = block_index1 + 1;
+
+ if (offset1 == 0)
+ {
+ // The whole first block needs to be swapped.
+ --it_begin;
+ --bucket.insert_index;
+ }
+ else
+ {
+ // Copy the lower part of the block for transfer.
+ block* blk = &m_blocks[block_index1];
+ size_type blk_size = blk->size - offset1;
+ block_first.size = blk_size;
+ if (blk->data)
+ {
+ block_first.data = block_funcs::create_new_block(mtv::get_block_type(*blk->data), 0);
+ block_funcs::assign_values_from_block(*block_first.data, *blk->data, offset1, blk_size);
+
+ // Shrink the existing block.
+ block_funcs::resize_block(*blk->data, offset1);
+ }
+
+ blk->size = offset1;
+ }
+
+ block* blk = &m_blocks[block_index2];
+ if (offset2 == blk->size - 1)
+ {
+ // The whole last block needs to be swapped.
+ ++it_end;
+ }
+ else
+ {
+ // Copy the upper part of the block for transfer.
+ size_type blk_size = offset2 + 1;
+ block_last.size = blk_size;
+ if (blk->data)
+ {
+ block_last.data = block_funcs::create_new_block(mtv::get_block_type(*blk->data), 0);
+ block_funcs::assign_values_from_block(*block_last.data, *blk->data, 0, blk_size);
+
+ // Shrink the existing block.
+ block_funcs::erase(*blk->data, 0, blk_size);
+ }
+
+ blk->position += blk_size;
+ blk->size -= blk_size;
+ }
+
+ // Copy all blocks into the bucket.
+ if (block_first.size)
+ bucket.blocks.push_back(std::move(block_first));
+
+ std::for_each(it_begin, it_end, [&](block& r) {
+ if (r.data)
+ m_hdl_event.element_block_released(r.data);
+ bucket.blocks.push_back(r);
+ });
+
+ if (block_last.size)
+ bucket.blocks.push_back(std::move(block_last));
+
+ // Remove the slots for these blocks (but don't delete the blocks).
+ m_blocks.erase(it_begin, it_end);
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::erase(size_type start_pos, size_type end_pos)
+{
+ if (start_pos > end_pos)
+ throw std::out_of_range("Start row is larger than the end row.");
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ std::ostringstream os_prev_block;
+ dump_blocks(os_prev_block);
+#endif
+
+ erase_impl(start_pos, end_pos);
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ try
+ {
+ check_block_integrity();
+ }
+ catch (const mdds::integrity_error& e)
+ {
+ std::ostringstream os;
+ os << e.what() << std::endl;
+ os << "block integrity check failed in erase (" << start_pos << "-" << end_pos << ")" << std::endl;
+ os << "block integrity check failed in push_back" << std::endl;
+ os << "previous block state:" << std::endl;
+ os << os_prev_block.str();
+ std::cerr << os.str() << std::endl;
+ abort();
+ }
+#endif
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::erase_impl(size_type start_row, size_type end_row)
+{
+ assert(start_row <= end_row);
+
+ // Keep the logic similar to set_empty().
+
+ size_type block_pos1 = get_block_position(start_row);
+ if (block_pos1 == m_blocks.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::erase_impl", __LINE__, start_row, block_size(), size());
+
+ size_type block_pos2 = get_block_position(end_row, block_pos1);
+ if (block_pos2 == m_blocks.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::erase_impl", __LINE__, start_row, block_size(), size());
+
+ size_type start_row_in_block1 = m_blocks[block_pos1].position;
+ size_type start_row_in_block2 = m_blocks[block_pos2].position;
+
+ if (block_pos1 == block_pos2)
+ {
+ erase_in_single_block(start_row, end_row, block_pos1);
+ return;
+ }
+
+ assert(block_pos1 < block_pos2);
+
+ // Initially, we set to erase all blocks between the first and the last.
+ typename blocks_type::iterator it_erase_begin = m_blocks.begin() + block_pos1 + 1;
+ typename blocks_type::iterator it_erase_end = m_blocks.begin() + block_pos2;
+
+ // First, inspect the first block.
+ if (start_row_in_block1 == start_row)
+ {
+ // Erase the whole block.
+ --it_erase_begin;
+ }
+ else
+ {
+ // Erase the lower part of the first block.
+ block* blk = &m_blocks[block_pos1];
+ size_type new_size = start_row - start_row_in_block1;
+ if (blk->data)
+ {
+ // Shrink the data array.
+ block_funcs::overwrite_values(*blk->data, new_size, blk->size - new_size);
+ block_funcs::resize_block(*blk->data, new_size);
+ }
+ blk->size = new_size;
+ }
+
+ size_type adjust_block_offset = 0;
+
+ // Then inspect the last block.
+ block* blk = &m_blocks[block_pos2];
+ size_type last_row_in_block = start_row_in_block2 + blk->size - 1;
+ if (last_row_in_block == end_row)
+ {
+ // Delete the whole block.
+ ++it_erase_end;
+ }
+ else
+ {
+ size_type size_to_erase = end_row - start_row_in_block2 + 1;
+ blk->size -= size_to_erase;
+ blk->position = start_row;
+ if (blk->data)
+ {
+ // Erase the upper part.
+ block_funcs::overwrite_values(*blk->data, 0, size_to_erase);
+ block_funcs::erase(*blk->data, 0, size_to_erase);
+ }
+
+ adjust_block_offset = 1; // Exclude this block from later block position adjustment.
+ }
+
+ // Get the index of the block that sits before the blocks being erased.
+ block_pos1 = std::distance(m_blocks.begin(), it_erase_begin);
+ if (block_pos1 > 0)
+ --block_pos1;
+
+ // Now, erase all blocks in between.
+ delete_element_blocks(it_erase_begin, it_erase_end);
+ auto it_adjust_block = m_blocks.erase(it_erase_begin, it_erase_end);
+ int64_t delta = end_row - start_row + 1;
+ m_cur_size -= delta;
+
+ if (m_blocks.empty())
+ return;
+
+ size_t adjust_pos = std::distance(m_blocks.begin(), it_adjust_block);
+ adjust_pos += adjust_block_offset;
+ adjust_block_positions_func{}(m_blocks, adjust_pos, -delta);
+ merge_with_next_block(block_pos1);
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::erase_in_single_block(size_type start_pos, size_type end_pos, size_type block_pos)
+{
+ // Range falls within the same block.
+ block* blk = &m_blocks[block_pos];
+ int64_t size_to_erase = end_pos - start_pos + 1;
+ if (blk->data)
+ {
+ // Erase data in the data block.
+ size_type offset = start_pos - blk->position;
+ block_funcs::overwrite_values(*blk->data, offset, size_to_erase);
+ block_funcs::erase(*blk->data, offset, size_to_erase);
+ }
+
+ blk->size -= size_to_erase;
+ m_cur_size -= size_to_erase;
+
+ if (blk->size)
+ {
+ // Block still contains data. Bail out.
+ adjust_block_positions_func{}(m_blocks, block_pos + 1, -size_to_erase);
+ return;
+ }
+
+ // Delete the current block since it's become empty.
+ delete_element_block(*blk);
+ m_blocks.erase(m_blocks.begin() + block_pos);
+ // No need to update blk_index which is not used again.
+
+ if (block_pos == 0)
+ {
+ // Deleted block was the first block.
+ adjust_block_positions_func{}(m_blocks, block_pos, -size_to_erase);
+ return;
+ }
+
+ if (block_pos >= m_blocks.size())
+ // Deleted block was the last block.
+ return;
+
+ // Check the previous and next blocks to see if they should be merged.
+ block* blk_prev = &m_blocks[block_pos - 1];
+ block* blk_next = &m_blocks[block_pos];
+ if (blk_prev->data)
+ {
+ // Previous block has data.
+ if (!blk_next->data)
+ {
+ // Next block is empty. Nothing to do.
+ adjust_block_positions_func{}(m_blocks, block_pos, -size_to_erase);
+ return;
+ }
+
+ element_category_type cat1 = mdds::mtv::get_block_type(*blk_prev->data);
+ element_category_type cat2 = mdds::mtv::get_block_type(*blk_next->data);
+ if (cat1 == cat2)
+ {
+ // Merge the two blocks.
+ block_funcs::append_block(*blk_prev->data, *blk_next->data);
+ blk_prev->size += blk_next->size;
+ // Resize to 0 to prevent deletion of cells in case of managed cells.
+ block_funcs::resize_block(*blk_next->data, 0);
+ delete_element_block(*blk_next);
+ m_blocks.erase(m_blocks.begin() + block_pos);
+ }
+
+ adjust_block_positions_func{}(m_blocks, block_pos, -size_to_erase);
+ }
+ else
+ {
+ // Previous block is empty.
+ if (blk_next->data)
+ {
+ // Next block is not empty. Nothing to do.
+ adjust_block_positions_func{}(m_blocks, block_pos, -size_to_erase);
+ return;
+ }
+
+ // Both blocks are empty. Simply increase the size of the
+ // previous block.
+ blk_prev->size += blk_next->size;
+ delete_element_block(*blk_next);
+ m_blocks.erase(m_blocks.begin() + block_pos);
+ adjust_block_positions_func{}(m_blocks, block_pos, -size_to_erase);
+ }
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::insert_empty(size_type pos, size_type length)
+{
+ if (!length)
+ // Nothing to insert.
+ return end();
+
+ size_type block_index = get_block_position(pos);
+ if (block_index == m_blocks.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::insert_empty", __LINE__, pos, block_size(), size());
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ std::ostringstream os_prev_block;
+ dump_blocks(os_prev_block);
+#endif
+
+ iterator ret = insert_empty_impl(pos, block_index, length);
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ try
+ {
+ check_block_integrity();
+ }
+ catch (const mdds::integrity_error& e)
+ {
+ std::ostringstream os;
+ os << e.what() << std::endl;
+ os << "block integrity check failed in insert_empty (pos=" << pos << "; length=" << length << ")" << std::endl;
+ os << "previous block state:" << std::endl;
+ os << os_prev_block.str();
+ std::cerr << os.str() << std::endl;
+ abort();
+ }
+#endif
+
+ return ret;
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::insert_empty(
+ const iterator& pos_hint, size_type pos, size_type length)
+{
+ if (!length)
+ // Nothing to insert.
+ return end();
+
+ size_type block_index = get_block_position(pos_hint->__private_data, pos);
+ if (block_index == m_blocks.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::insert_empty", __LINE__, pos, block_size(), size());
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ std::ostringstream os_prev_block;
+ dump_blocks(os_prev_block);
+#endif
+
+ iterator ret = insert_empty_impl(pos, block_index, length);
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ try
+ {
+ check_block_integrity();
+ }
+ catch (const mdds::integrity_error& e)
+ {
+ std::ostringstream os;
+ os << e.what() << std::endl;
+ os << "block integrity check failed in insert_empty (pos=" << pos << "; length=" << length << ")" << std::endl;
+ os << "previous block state:" << std::endl;
+ os << os_prev_block.str();
+ std::cerr << os.str() << std::endl;
+ abort();
+ }
+#endif
+
+ return ret;
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::insert_empty_impl(
+ size_type pos, size_type block_index, size_type length)
+{
+ assert(pos < m_cur_size);
+
+ block* blk = &m_blocks[block_index];
+ if (!blk->data)
+ {
+ // Insertion point is already empty. Just expand its size and be done
+ // with it.
+ blk->size += length;
+ m_cur_size += length;
+ adjust_block_positions_func{}(m_blocks, block_index + 1, length);
+ return get_iterator(block_index);
+ }
+
+ size_type start_pos = blk->position;
+
+ if (start_pos == pos)
+ {
+ // Insertion point is at the top of an existing non-empty block.
+ block* blk_prev = get_previous_block_of_type(block_index, mtv::element_type_empty);
+ if (blk_prev)
+ {
+ assert(!blk_prev->data);
+ // Previous block is empty. Expand the size of the previous
+ // block and bail out.
+ blk_prev->size += length;
+ m_cur_size += length;
+ adjust_block_positions_func{}(m_blocks, block_index, length);
+ return get_iterator(block_index - 1);
+ }
+
+ // Insert a new empty block.
+ m_blocks.emplace(m_blocks.begin() + block_index, blk->position, length);
+ m_cur_size += length;
+ adjust_block_positions_func{}(m_blocks, block_index + 1, length);
+ return get_iterator(block_index);
+ }
+
+ assert(blk->data);
+ assert(pos > start_pos);
+
+ size_type size_blk_prev = pos - start_pos;
+ size_type size_blk_next = blk->size - size_blk_prev;
+
+ // Insert two new blocks below the current; one for the empty block being
+ // inserted, and the other for the lower part of the current non-empty
+ // block.
+ m_blocks.insert(m_blocks.begin() + block_index + 1, 2u, block());
+
+ blk = &m_blocks[block_index]; // The old pointer is invalid.
+ m_blocks[block_index + 1].size = length;
+ m_blocks[block_index + 2].size = size_blk_next;
+
+ block* blk_next = &m_blocks[block_index + 2];
+ blk_next->data = block_funcs::create_new_block(mdds::mtv::get_block_type(*blk->data), 0);
+ m_hdl_event.element_block_acquired(blk_next->data);
+
+ // Check if the previous block is the bigger one
+ if (size_blk_prev > size_blk_next)
+ {
+ // Upper (previous) block is larger than the lower (next) block. Copy
+ // the lower values to the next block.
+ block_funcs::assign_values_from_block(*blk_next->data, *blk->data, size_blk_prev, size_blk_next);
+ block_funcs::resize_block(*blk->data, size_blk_prev);
+ blk->size = size_blk_prev;
+ }
+ else
+ {
+ // Lower (next) block is larger than the upper (previous) block. Copy
+ // the upper values to the "next" block.
+ block_funcs::assign_values_from_block(*blk_next->data, *blk->data, 0, size_blk_prev);
+ blk_next->size = size_blk_prev;
+
+ // Remove the copied values and push the rest to the top.
+ block_funcs::erase(*blk->data, 0, size_blk_prev);
+
+ // Set the size of the current block to its new size ( what is after the new block )
+ blk->size = size_blk_next;
+
+ // And now let's swap the blocks, but save the block position.
+ size_type position = blk->position;
+ blk->swap(*blk_next);
+ blk->position = position;
+ }
+
+ m_cur_size += length;
+ m_blocks[block_index + 1].position = detail::calc_next_block_position(m_blocks, block_index);
+ m_blocks[block_index + 2].position = detail::calc_next_block_position(m_blocks, block_index + 1);
+ adjust_block_positions_func{}(m_blocks, block_index + 3, length);
+
+ return get_iterator(block_index + 1);
+}
+
+template<typename Traits>
+template<typename T>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::set_cells_impl(
+ size_type row, size_type end_row, size_type block_index1, const T& it_begin, const T& it_end)
+{
+ size_type block_index2 = get_block_position(end_row, block_index1);
+ if (block_index2 == m_blocks.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::set_cells_impl", __LINE__, end_row, block_size(), size());
+
+ if (block_index1 == block_index2)
+ {
+ // The whole data array will fit in a single block.
+ return set_cells_to_single_block(row, end_row, block_index1, it_begin, it_end);
+ }
+
+ return set_cells_to_multi_blocks(row, end_row, block_index1, block_index2, it_begin, it_end);
+}
+
+template<typename Traits>
+template<typename T>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::insert_cells_impl(
+ size_type row, size_type block_index, const T& it_begin, const T& it_end)
+{
+ size_type start_row = m_blocks[block_index].position;
+ size_type length = std::distance(it_begin, it_end);
+ if (!length)
+ // empty data array. nothing to do.
+ return end();
+
+ element_category_type cat = mdds_mtv_get_element_type(*it_begin);
+ block* blk = &m_blocks[block_index];
+ if (!blk->data)
+ {
+ if (row == start_row)
+ {
+ // Insert into an empty block. Check the previos block (if
+ // exists) to see if the data can be appended to it if inserting
+ // at the top of the block.
+ block* blk0 = get_previous_block_of_type(block_index, cat);
+ if (blk0)
+ {
+ // Append to the previous block.
+ mdds_mtv_append_values(*blk0->data, *it_begin, it_begin, it_end);
+ blk0->size += length;
+ m_cur_size += length;
+ adjust_block_positions_func{}(m_blocks, block_index, length);
+
+ return get_iterator(block_index - 1);
+ }
+
+ // Just insert a new block before the current block.
+ size_type position = m_blocks[block_index].position;
+ m_blocks.emplace(m_blocks.begin() + block_index, position, length);
+ blk = &m_blocks[block_index];
+ blk->data = block_funcs::create_new_block(cat, 0);
+ m_hdl_event.element_block_acquired(blk->data);
+ mdds_mtv_assign_values(*blk->data, *it_begin, it_begin, it_end);
+ m_cur_size += length;
+ adjust_block_positions_func{}(m_blocks, block_index + 1, length);
+
+ return get_iterator(block_index);
+ }
+
+ insert_cells_to_middle(row, block_index, it_begin, it_end);
+ m_cur_size += length;
+
+ return get_iterator(block_index + 1);
+ }
+
+ assert(blk->data);
+ element_category_type blk_cat = mdds::mtv::get_block_type(*blk->data);
+ if (cat == blk_cat)
+ {
+ // Simply insert the new data series into existing block.
+ assert(it_begin != it_end);
+ mdds_mtv_insert_values(*blk->data, row - start_row, *it_begin, it_begin, it_end);
+ blk->size += length;
+ m_cur_size += length;
+ adjust_block_positions_func{}(m_blocks, block_index + 1, length);
+
+ return get_iterator(block_index);
+ }
+
+ assert(cat != blk_cat);
+ if (row == start_row)
+ {
+ // Check the previous block to see if we can append the data there.
+ block* blk0 = get_previous_block_of_type(block_index, cat);
+ if (blk0)
+ {
+ // Append to the previous block.
+ mdds_mtv_append_values(*blk0->data, *it_begin, it_begin, it_end);
+ blk0->size += length;
+ m_cur_size += length;
+ adjust_block_positions_func{}(m_blocks, block_index, length);
+
+ return get_iterator(block_index - 1);
+ }
+
+ // Just insert a new block before the current block.
+ m_blocks.emplace(m_blocks.begin() + block_index, blk->position, length);
+ blk = &m_blocks[block_index];
+ blk->data = block_funcs::create_new_block(cat, 0);
+ m_hdl_event.element_block_acquired(blk->data);
+ mdds_mtv_assign_values(*blk->data, *it_begin, it_begin, it_end);
+ blk->size = length;
+ m_cur_size += length;
+ adjust_block_positions_func{}(m_blocks, block_index + 1, length);
+
+ return get_iterator(block_index);
+ }
+
+ insert_cells_to_middle(row, block_index, it_begin, it_end);
+ m_cur_size += length;
+
+ return get_iterator(block_index + 1);
+}
+
+template<typename Traits>
+template<typename T>
+void multi_type_vector<Traits>::insert_cells_to_middle(
+ size_type row, size_type block_index, const T& it_begin, const T& it_end)
+{
+ size_type start_row = m_blocks[block_index].position;
+ size_type length = std::distance(it_begin, it_end);
+ element_category_type cat = mdds_mtv_get_element_type(*it_begin);
+
+ // Insert two new blocks after the specified block position.
+ size_type n1 = row - start_row;
+ size_type n2 = m_blocks[block_index].size - n1;
+ m_blocks.insert(m_blocks.begin() + block_index + 1, 2u, block());
+ block* blk = &m_blocks[block_index];
+ blk->size = n1;
+
+ m_blocks[block_index + 1].size = length;
+ m_blocks[block_index + 1].position = detail::calc_next_block_position(*blk);
+ m_blocks[block_index + 2].size = n2;
+ m_blocks[block_index + 2].position = detail::calc_next_block_position(m_blocks[block_index + 1]);
+
+ // block for data series.
+ block* blk2 = &m_blocks[block_index + 1];
+ blk2->data = block_funcs::create_new_block(cat, 0);
+ m_hdl_event.element_block_acquired(blk2->data);
+ mdds_mtv_assign_values(*blk2->data, *it_begin, it_begin, it_end);
+
+ if (blk->data)
+ {
+ element_category_type blk_cat = mdds::mtv::get_block_type(*blk->data);
+
+ // block to hold data from the lower part of the existing block.
+ block* blk3 = &m_blocks[block_index + 2];
+ blk3->data = block_funcs::create_new_block(blk_cat, 0);
+ m_hdl_event.element_block_acquired(blk3->data);
+
+ // Transfer the lower part of the current block to the new block.
+ size_type offset = row - start_row;
+ block_funcs::assign_values_from_block(*blk3->data, *blk->data, offset, n2);
+ block_funcs::resize_block(*blk->data, blk->size);
+ }
+
+ adjust_block_positions_func{}(m_blocks, block_index + 3, length);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::block& multi_type_vector<Traits>::set_new_block_to_middle(
+ size_type block_index, size_type offset, size_type new_block_size, bool overwrite)
+{
+ assert(block_index < m_blocks.size());
+
+ // First, insert two new blocks at position past the current block.
+ size_type lower_block_size = m_blocks[block_index].size - offset - new_block_size;
+ m_blocks.insert(m_blocks.begin() + block_index + 1, 2u, block());
+ m_blocks[block_index + 1].size = new_block_size; // empty block.
+ m_blocks[block_index + 2].size = lower_block_size;
+
+ block& blk = m_blocks[block_index];
+
+ if (blk.data)
+ {
+ size_type lower_data_start = offset + new_block_size;
+ block& blk_lower = m_blocks[block_index + 2];
+ assert(blk_lower.size == lower_block_size);
+ element_category_type cat = mtv::get_block_type(*blk.data);
+ blk_lower.data = block_funcs::create_new_block(cat, 0);
+ m_hdl_event.element_block_acquired(blk_lower.data);
+
+ // Try to copy the fewer amount of data to the new non-empty block.
+ if (offset > lower_block_size)
+ {
+ // Keep the upper values in the current block and copy the lower
+ // values to the new non-empty block.
+ block_funcs::assign_values_from_block(*blk_lower.data, *blk.data, lower_data_start, lower_block_size);
+
+ if (overwrite)
+ {
+ // Overwrite cells that will become empty.
+ block_funcs::overwrite_values(*blk.data, offset, new_block_size);
+ }
+
+ // Shrink the current data block.
+ block_funcs::resize_block(*blk.data, offset);
+ blk.size = offset;
+ blk_lower.size = lower_block_size;
+ }
+ else
+ {
+ // Keep the lower values in the current block and copy the upper
+ // values to the new non-empty block (blk_lower), and swap the two
+ // later.
+ block_funcs::assign_values_from_block(*blk_lower.data, *blk.data, 0, offset);
+ blk_lower.size = offset;
+
+ if (overwrite)
+ {
+ // Overwrite cells that will become empty.
+ block_funcs::overwrite_values(*blk.data, offset, new_block_size);
+ }
+
+ // Remove the upper and middle values and push the rest to the top.
+ block_funcs::erase(*blk.data, 0, lower_data_start);
+
+ // Set the size of the current block to its new size ( what is after the new block )
+ blk.size = lower_block_size;
+ blk_lower.size = offset;
+
+ // And now let's swap the blocks, while preserving the position of the original block.
+ size_type position = blk.position;
+ blk.swap(blk_lower);
+ blk.position = position;
+ }
+ }
+ else
+ {
+ // There is no data, we just need to update the size of the block
+ blk.size = offset;
+ }
+
+ // Re-calculate the block positions.
+ m_blocks[block_index + 1].position = detail::calc_next_block_position(m_blocks, block_index);
+ m_blocks[block_index + 2].position = detail::calc_next_block_position(m_blocks, block_index + 1);
+
+ return m_blocks[block_index + 1];
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::block* multi_type_vector<Traits>::get_previous_block_of_type(
+ size_type block_index, element_category_type cat)
+{
+ if (block_index == 0)
+ // No previous block.
+ return nullptr;
+
+ block& blk = m_blocks[block_index - 1];
+ if (blk.data)
+ return (cat == mtv::get_block_type(*blk.data)) ? &blk : nullptr;
+
+ return (cat == mtv::element_type_empty) ? &blk : nullptr;
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::block* multi_type_vector<Traits>::get_next_block_of_type(
+ size_type block_index, element_category_type cat)
+{
+ if (block_index == m_blocks.size() - 1)
+ // No next block.
+ return nullptr;
+
+ block* blk = &m_blocks[block_index + 1];
+ if (blk->data)
+ return (cat == mtv::get_block_type(*blk->data)) ? blk : nullptr;
+
+ return (cat == mtv::element_type_empty) ? blk : nullptr;
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::element_block_type* multi_type_vector<Traits>::exchange_elements(
+ const element_block_type& src_data, size_type src_offset, size_type dst_index, size_type dst_offset, size_type len)
+{
+ assert(dst_index < m_blocks.size());
+ block* blk = &m_blocks[dst_index];
+ element_category_type cat_src = mtv::get_block_type(src_data);
+ block* blk_next = get_next_block_of_type(dst_index, cat_src);
+
+ if (dst_offset == 0)
+ {
+ // Set elements to the top of the destination block.
+ block* blk_prev = get_previous_block_of_type(dst_index, cat_src);
+
+ if (blk->size == len)
+ {
+ // The whole block will get replaced.
+ std::unique_ptr<element_block_type, element_block_deleter> data(blk->data);
+ m_hdl_event.element_block_released(blk->data);
+ blk->data = nullptr; // Prevent its deletion when the parent block gets deleted.
+
+ if (blk_prev)
+ {
+ // Append to the previous block. Remove the current block.
+ block_funcs::append_values_from_block(*blk_prev->data, src_data, src_offset, len);
+ blk_prev->size += len;
+ typename blocks_type::iterator it = m_blocks.begin();
+ std::advance(it, dst_index);
+ typename blocks_type::iterator it_end = it;
+ ++it_end;
+
+ assert(!blk->data);
+ // no need to call delete_block since mp_data is null.
+
+ if (blk_next)
+ {
+ // Apend elements from the next block too.
+ block_funcs::append_block(*blk_prev->data, *blk_next->data);
+ blk_prev->size += blk_next->size;
+ ++it_end;
+ delete_element_block(*blk_next);
+ }
+
+ m_blocks.erase(it, it_end);
+ return data.release();
+ }
+
+ // Check the next block to see if we need to merge.
+ if (blk_next)
+ {
+ // We need to merge with the next block. Remove the current
+ // block and use the next block to store the new elements as
+ // well as the existing ones.
+ block_funcs::prepend_values_from_block(*blk_next->data, src_data, src_offset, len);
+ blk_next->position -= len;
+ blk_next->size += len;
+ m_blocks.erase(m_blocks.begin() + dst_index);
+ }
+ else
+ {
+ blk->data = block_funcs::create_new_block(cat_src, 0);
+ m_hdl_event.element_block_acquired(blk->data);
+ assert(blk->data && blk->data != data.get());
+ block_funcs::assign_values_from_block(*blk->data, src_data, src_offset, len);
+ }
+
+ // Return this data block as-is.
+ return data.release();
+ }
+
+ // New block to send back to the caller.
+ std::unique_ptr<element_block_type, element_block_deleter> data(nullptr);
+
+ if (blk->data)
+ {
+ element_category_type cat_dst = mtv::get_block_type(*blk->data);
+ data.reset(block_funcs::create_new_block(cat_dst, 0));
+
+ // We need to keep the tail elements of the current block.
+ block_funcs::assign_values_from_block(*data, *blk->data, 0, len);
+ block_funcs::erase(*blk->data, 0, len);
+ }
+
+ size_type position = blk->position;
+ blk->position += len;
+ blk->size -= len;
+
+ if (blk_prev)
+ {
+ // Append the new elements to the previous block.
+ block_funcs::append_values_from_block(*blk_prev->data, src_data, src_offset, len);
+ blk_prev->size += len;
+ }
+ else
+ {
+ // Insert a new block to house the new elements.
+ m_blocks.emplace(m_blocks.begin() + dst_index, position, len);
+ blk = &m_blocks[dst_index];
+ blk->data = block_funcs::create_new_block(cat_src, 0);
+ m_hdl_event.element_block_acquired(blk->data);
+ block_funcs::assign_values_from_block(*blk->data, src_data, src_offset, len);
+ }
+
+ return data.release();
+ }
+
+ // New block to send back to the caller.
+ std::unique_ptr<element_block_type, element_block_deleter> data(nullptr);
+
+ if (blk->data)
+ {
+ // Copy the elements of the current block to the block being returned.
+ element_category_type cat_dst = mtv::get_block_type(*blk->data);
+ data.reset(block_funcs::create_new_block(cat_dst, 0));
+ block_funcs::assign_values_from_block(*data, *blk->data, dst_offset, len);
+ }
+
+ assert(dst_offset > 0);
+ size_type dst_end_pos = dst_offset + len;
+
+ if (dst_end_pos == blk->size)
+ {
+ // The new elements will replace the lower part of the block.
+ assert(blk->data && "nullptr block mp_data");
+ block_funcs::resize_block(*blk->data, dst_offset);
+ blk->size = dst_offset;
+
+ if (blk_next)
+ {
+ // Merge with the next block.
+ block_funcs::prepend_values_from_block(*blk_next->data, src_data, src_offset, len);
+ blk_next->position -= len;
+ blk_next->size += len;
+ }
+ else
+ {
+ // Insert a new block to store the new elements.
+ size_type position = blk->position + dst_offset;
+ m_blocks.emplace(m_blocks.begin() + dst_index + 1, position, len);
+ blk = &m_blocks[dst_index + 1];
+ blk->data = block_funcs::create_new_block(cat_src, 0);
+ assert(blk->data);
+ m_hdl_event.element_block_acquired(blk->data);
+ block_funcs::assign_values_from_block(*blk->data, src_data, src_offset, len);
+ }
+ }
+ else
+ {
+ // The new elements will replace the middle of the block.
+ assert(dst_end_pos < blk->size);
+ blk = &set_new_block_to_middle(dst_index, dst_offset, len, false);
+ assert(blk->size == len);
+ blk->data = block_funcs::create_new_block(cat_src, 0);
+ assert(blk->data);
+ m_hdl_event.element_block_acquired(blk->data);
+ block_funcs::assign_values_from_block(*blk->data, src_data, src_offset, len);
+ }
+
+ return data.release();
+}
+
+template<typename Traits>
+bool multi_type_vector<Traits>::append_empty(size_type len)
+{
+ // Append empty cells.
+ if (m_blocks.empty())
+ {
+ // No existing block. Create a new one.
+ assert(m_cur_size == 0);
+ m_blocks.emplace_back(0, len);
+ m_cur_size = len;
+ return true;
+ }
+
+ bool new_block_added = false;
+ block* blk_last = &m_blocks.back();
+
+ if (!blk_last->data)
+ {
+ // Last block is empty. Just increase its size.
+ blk_last->size += len;
+ }
+ else
+ {
+ // Append a new empty block.
+ m_blocks.emplace_back(m_cur_size, len);
+ new_block_added = true;
+ }
+
+ m_cur_size += len;
+
+ return new_block_added;
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::exchange_elements(
+ const element_block_type& src_data, size_type src_offset, size_type dst_index1, size_type dst_offset1,
+ size_type dst_index2, size_type dst_offset2, size_type len, blocks_type& new_blocks)
+{
+ assert(dst_index1 < dst_index2);
+ assert(dst_offset1 < m_blocks[dst_index1].size);
+ assert(dst_offset2 < m_blocks[dst_index2].size);
+
+ blocks_to_transfer bucket;
+ prepare_blocks_to_transfer(bucket, dst_index1, dst_offset1, dst_index2, dst_offset2);
+
+ m_blocks.emplace(m_blocks.begin() + bucket.insert_index, 0, len);
+ block* blk = &m_blocks[bucket.insert_index];
+ if (bucket.insert_index > 0)
+ blk->position = detail::calc_next_block_position(m_blocks, bucket.insert_index - 1);
+
+ blk->data = block_funcs::create_new_block(mtv::get_block_type(src_data), 0);
+ m_hdl_event.element_block_acquired(blk->data);
+ block_funcs::assign_values_from_block(*blk->data, src_data, src_offset, len);
+ merge_with_adjacent_blocks(bucket.insert_index);
+
+ new_blocks.swap(bucket.blocks);
+}
+
+template<typename Traits>
+template<typename T>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::set_cells_to_single_block(
+ size_type start_row, size_type end_row, size_type block_index, const T& it_begin, const T& it_end)
+{
+ assert(it_begin != it_end);
+ assert(!m_blocks.empty());
+
+ element_category_type cat = mdds_mtv_get_element_type(*it_begin);
+ block* blk = &m_blocks[block_index];
+ size_type start_row_in_block = blk->position;
+ size_type data_length = std::distance(it_begin, it_end);
+
+ if (blk->data && mdds::mtv::get_block_type(*blk->data) == cat)
+ {
+ // simple overwrite.
+ size_type offset = start_row - start_row_in_block;
+ block_funcs::overwrite_values(*blk->data, offset, data_length);
+ if (!offset && data_length == blk->size)
+ // Overwrite the whole block. It's faster to use assign_values.
+ mdds_mtv_assign_values(*blk->data, *it_begin, it_begin, it_end);
+ else
+ mdds_mtv_set_values(*blk->data, offset, *it_begin, it_begin, it_end);
+
+ return get_iterator(block_index);
+ }
+
+ size_type end_row_in_block = start_row_in_block + blk->size - 1;
+ if (start_row == start_row_in_block)
+ {
+ if (end_row == end_row_in_block)
+ {
+ // Check if we could append it to the previous block.
+ if (append_to_prev_block(block_index, cat, end_row - start_row + 1, it_begin, it_end))
+ {
+ delete_element_block(*blk);
+ m_blocks.erase(m_blocks.begin() + block_index);
+
+ // Check if we need to merge it with the next block.
+ --block_index;
+ merge_with_next_block(block_index);
+ return get_iterator(block_index);
+ }
+
+ // Replace the whole block.
+ if (blk->data)
+ {
+ m_hdl_event.element_block_released(blk->data);
+ block_funcs::delete_block(blk->data);
+ }
+
+ blk->data = block_funcs::create_new_block(cat, 0);
+ m_hdl_event.element_block_acquired(blk->data);
+ mdds_mtv_assign_values(*blk->data, *it_begin, it_begin, it_end);
+ merge_with_next_block(block_index);
+ return get_iterator(block_index);
+ }
+
+ // Replace the upper part of the block.
+
+ // Shrink the current block first.
+ size_type length = end_row_in_block - end_row;
+ blk->size = length;
+
+ if (blk->data)
+ {
+ // Erase the upper part of the data from the current data array.
+ std::unique_ptr<element_block_type, element_block_deleter> new_data(
+ block_funcs::create_new_block(mdds::mtv::get_block_type(*blk->data), 0));
+
+ if (!new_data)
+ throw std::logic_error("failed to instantiate a new data array.");
+
+ size_type pos = end_row - start_row_in_block + 1;
+ block_funcs::assign_values_from_block(*new_data, *blk->data, pos, length);
+ block_funcs::overwrite_values(*blk->data, 0, pos);
+
+ // Resize the block to zero before deleting, to prevent the
+ // managed cells from being deleted when the block is deleted.
+ block_funcs::resize_block(*blk->data, 0);
+ block_funcs::delete_block(blk->data);
+ blk->data = new_data.release();
+
+ // We don't call element block event listeners here.
+ }
+
+ length = end_row - start_row + 1;
+ if (append_to_prev_block(block_index, cat, length, it_begin, it_end))
+ {
+ // The new values have been successfully appended to the previous block.
+ blk->position += length;
+ return get_iterator(block_index - 1);
+ }
+
+ // Insert a new block before the current block, and populate it with
+ // the new data.
+ size_type position = blk->position;
+ blk->position += length;
+ m_blocks.emplace(m_blocks.begin() + block_index, position, length);
+ blk = &m_blocks[block_index];
+ blk->data = block_funcs::create_new_block(cat, 0);
+ m_hdl_event.element_block_acquired(blk->data);
+ blk->size = length;
+ mdds_mtv_assign_values(*blk->data, *it_begin, it_begin, it_end);
+ return get_iterator(block_index);
+ }
+
+ assert(start_row > start_row_in_block);
+ if (end_row == end_row_in_block)
+ {
+ // Shrink the end of the current block and insert a new block for the new data series after the current block.
+ size_type new_size = start_row - start_row_in_block;
+ blk->size = new_size;
+ if (blk->data)
+ {
+ block_funcs::overwrite_values(*blk->data, new_size, data_length);
+ block_funcs::resize_block(*blk->data, new_size);
+ }
+
+ new_size = end_row - start_row + 1; // size of the data array being inserted.
+
+ if (block_index < m_blocks.size() - 1)
+ {
+ // There is a block (or more) after the current block. Check the next block.
+ block* blk_next = get_next_block_of_type(block_index, cat);
+ if (blk_next)
+ {
+ // Prepend it to the next block.
+ mdds_mtv_prepend_values(*blk_next->data, *it_begin, it_begin, it_end);
+ blk_next->size += new_size;
+ blk_next->position -= new_size;
+ return get_iterator(block_index + 1);
+ }
+
+ // Next block has a different data type. Do the normal insertion.
+ size_type position = detail::calc_next_block_position(*blk);
+ m_blocks.emplace(m_blocks.begin() + block_index + 1, position, new_size);
+ blk = &m_blocks[block_index + 1];
+ blk->data = block_funcs::create_new_block(cat, 0);
+ m_hdl_event.element_block_acquired(blk->data);
+ mdds_mtv_assign_values(*blk->data, *it_begin, it_begin, it_end);
+ return get_iterator(block_index + 1);
+ }
+
+ // Last block.
+ assert(block_index == m_blocks.size() - 1);
+
+ m_blocks.emplace_back(m_cur_size - new_size, new_size);
+ blk = &m_blocks.back();
+ blk->data = block_funcs::create_new_block(cat, 0);
+ m_hdl_event.element_block_acquired(blk->data);
+ mdds_mtv_assign_values(*blk->data, *it_begin, it_begin, it_end);
+ return get_iterator(block_index + 1);
+ }
+
+ // new data array will be in the middle of the current block.
+ assert(start_row_in_block < start_row && end_row < end_row_in_block);
+
+ block& blk_new =
+ set_new_block_to_middle(block_index, start_row - start_row_in_block, end_row - start_row + 1, true);
+
+ blk_new.data = block_funcs::create_new_block(cat, 0);
+ m_hdl_event.element_block_acquired(blk_new.data);
+ mdds_mtv_assign_values(*blk_new.data, *it_begin, it_begin, it_end);
+
+ return get_iterator(block_index + 1);
+}
+
+template<typename Traits>
+template<typename T>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::set_cells_to_multi_blocks(
+ size_type start_row, size_type end_row, size_type block_index1, size_type block_index2, const T& it_begin,
+ const T& it_end)
+{
+ assert(block_index1 < block_index2);
+ assert(it_begin != it_end);
+ assert(!m_blocks.empty());
+
+ block* blk1 = &m_blocks[block_index1];
+ if (blk1->data)
+ {
+ return set_cells_to_multi_blocks_block1_non_empty(
+ start_row, end_row, block_index1, block_index2, it_begin, it_end);
+ }
+
+ // Block 1 is empty.
+ assert(!blk1->data);
+
+ return set_cells_to_multi_blocks_block1_non_equal(start_row, end_row, block_index1, block_index2, it_begin, it_end);
+}
+
+template<typename Traits>
+template<typename T>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::set_cells_to_multi_blocks_block1_non_equal(
+ size_type start_row, size_type end_row, size_type block_index1, size_type block_index2, const T& it_begin,
+ const T& it_end)
+{
+ element_category_type cat = mdds_mtv_get_element_type(*it_begin);
+ block* blk1 = &m_blocks[block_index1];
+ block* blk2 = &m_blocks[block_index2];
+ size_type start_row_in_block1 = blk1->position;
+ size_type start_row_in_block2 = blk2->position;
+ size_type length = std::distance(it_begin, it_end);
+ size_type offset = start_row - start_row_in_block1;
+ size_type end_row_in_block2 = start_row_in_block2 + blk2->size - 1;
+
+ // Initially set to erase blocks between block 1 and block 2 non-inclusive at either end.
+ typename blocks_type::iterator it_erase_begin = m_blocks.begin() + block_index1 + 1;
+ typename blocks_type::iterator it_erase_end = m_blocks.begin() + block_index2;
+
+ // Create the new data block first.
+ block data_blk(start_row, length);
+
+ bool blk0_copied = false;
+ if (offset == 0)
+ {
+ // Remove block 1.
+ --it_erase_begin;
+
+ // Check the type of the previous block (block 0) if exists.
+ if (block_index1 > 0)
+ {
+ block* blk0 = &m_blocks[block_index1 - 1];
+ if (blk0->data && cat == mdds::mtv::get_block_type(*blk0->data))
+ {
+ // Transfer the whole data from block 0 to data block.
+ data_blk.data = blk0->data;
+ blk0->data = nullptr;
+
+ data_blk.size += blk0->size;
+ data_blk.position = blk0->position;
+
+ --it_erase_begin;
+ blk0_copied = true;
+ }
+ }
+ }
+ else
+ {
+ // Shrink block 1 by the end.
+ if (blk1->data)
+ {
+ size_type n = blk1->size - offset;
+ block_funcs::overwrite_values(*blk1->data, offset, n);
+ block_funcs::resize_block(*blk1->data, offset);
+ }
+ blk1->size = offset;
+ }
+
+ if (blk0_copied)
+ mdds_mtv_append_values(*data_blk.data, *it_begin, it_begin, it_end);
+ else
+ {
+ data_blk.data = block_funcs::create_new_block(cat, 0);
+ m_hdl_event.element_block_acquired(data_blk.data);
+ mdds_mtv_assign_values(*data_blk.data, *it_begin, it_begin, it_end);
+ }
+
+ if (end_row == end_row_in_block2)
+ {
+ // Remove block 2.
+ ++it_erase_end;
+
+ if (block_index2 + 1 < m_blocks.size())
+ {
+ // There is at least one block after block 2.
+ block* blk3 = &m_blocks[block_index2 + 1];
+ if (blk3->data && mdds::mtv::get_block_type(*blk3->data) == cat)
+ {
+ // Merge the whole block 3 with the new data. Remove block 3
+ // afterward. Resize block 3 to zero to prevent invalid free.
+ block_funcs::append_block(*data_blk.data, *blk3->data);
+ block_funcs::resize_block(*blk3->data, 0);
+ data_blk.size += blk3->size;
+ ++it_erase_end;
+ }
+ }
+ }
+ else
+ {
+ bool erase_upper = true;
+ if (blk2->data)
+ {
+ element_category_type blk_cat2 = mdds::mtv::get_block_type(*blk2->data);
+ if (blk_cat2 == cat)
+ {
+ // Merge the lower part of block 2 with the new data, and
+ // erase block 2. Resize block 2 to avoid invalid free on the
+ // copied portion of the block.
+ size_type copy_pos = end_row - start_row_in_block2 + 1;
+ size_type size_to_copy = end_row_in_block2 - end_row;
+ block_funcs::append_values_from_block(*data_blk.data, *blk2->data, copy_pos, size_to_copy);
+ block_funcs::resize_block(*blk2->data, copy_pos);
+ data_blk.size += size_to_copy;
+
+ ++it_erase_end;
+ erase_upper = false;
+ }
+ }
+
+ if (erase_upper)
+ {
+ // Erase the upper part of block 2.
+ size_type size_to_erase = end_row - start_row_in_block2 + 1;
+ if (blk2->data)
+ {
+ block_funcs::overwrite_values(*blk2->data, 0, size_to_erase);
+ block_funcs::erase(*blk2->data, 0, size_to_erase);
+ }
+ blk2->size -= size_to_erase;
+ blk2->position += size_to_erase;
+ }
+ }
+
+ size_type insert_pos = std::distance(m_blocks.begin(), it_erase_begin);
+
+ // Remove the in-between blocks first.
+ delete_element_blocks(it_erase_begin, it_erase_end);
+ m_blocks.erase(it_erase_begin, it_erase_end);
+
+ // Insert the new data block.
+ m_blocks.insert(m_blocks.begin() + insert_pos, std::move(data_blk));
+
+ return get_iterator(insert_pos);
+}
+
+template<typename Traits>
+template<typename T>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::set_cells_to_multi_blocks_block1_non_empty(
+ size_type start_row, size_type end_row, size_type block_index1, size_type block_index2, const T& it_begin,
+ const T& it_end)
+{
+ size_type start_row_in_block1 = m_blocks[block_index1].position;
+ size_type start_row_in_block2 = m_blocks[block_index2].position;
+
+ element_category_type cat = mdds_mtv_get_element_type(*it_begin);
+ block* blk1 = &m_blocks[block_index1];
+ assert(blk1->data);
+ element_category_type blk_cat1 = mdds::mtv::get_block_type(*blk1->data);
+
+ if (blk_cat1 == cat)
+ {
+ block* blk2 = &m_blocks[block_index2];
+ size_type length = std::distance(it_begin, it_end);
+ size_type offset = start_row - start_row_in_block1;
+ size_type end_row_in_block2 = start_row_in_block2 + blk2->size - 1;
+
+ // Initially set to erase blocks between block 1 and block 2 non-inclusive at either end.
+ typename blocks_type::iterator it_erase_begin = m_blocks.begin() + block_index1 + 1;
+ typename blocks_type::iterator it_erase_end = m_blocks.begin() + block_index2;
+
+ // Extend the first block to store the new data set.
+
+ // Shrink it first to remove the old values, then append new values.
+ block_funcs::overwrite_values(*blk1->data, offset, blk1->size - offset);
+ block_funcs::resize_block(*blk1->data, offset);
+ mdds_mtv_append_values(*blk1->data, *it_begin, it_begin, it_end);
+ blk1->size = offset + length;
+
+ if (end_row == end_row_in_block2)
+ {
+ // Data overlaps the entire block 2. Erase it.
+ ++it_erase_end;
+ }
+ else if (blk2->data)
+ {
+ element_category_type blk_cat2 = mdds::mtv::get_block_type(*blk2->data);
+ if (blk_cat2 == cat)
+ {
+ // Copy the lower (non-overwritten) part of block 2 to block
+ // 1, and remove the whole block 2. Resize block 2 to zero
+ // first to prevent the transferred / overwritten cells from
+ // being deleted on block deletion.
+ size_type data_length = end_row_in_block2 - end_row;
+ size_type begin_pos = end_row - start_row_in_block2 + 1;
+ block_funcs::append_values_from_block(*blk1->data, *blk2->data, begin_pos, data_length);
+ block_funcs::overwrite_values(*blk2->data, 0, begin_pos);
+ block_funcs::resize_block(*blk2->data, 0);
+ blk1->size += data_length;
+ ++it_erase_end;
+ }
+ else
+ {
+ // Erase the upper part of block 2.
+ size_type size_to_erase = end_row - start_row_in_block2 + 1;
+ block_funcs::erase(*blk2->data, 0, size_to_erase);
+ blk2->size -= size_to_erase;
+ blk2->position += size_to_erase;
+ }
+ }
+ else
+ {
+ // Last block is empty.
+ size_type size_to_erase = end_row - start_row_in_block2 + 1;
+ blk2->size -= size_to_erase;
+ blk2->position += size_to_erase;
+ }
+
+ delete_element_blocks(it_erase_begin, it_erase_end);
+ m_blocks.erase(it_erase_begin, it_erase_end);
+
+ return get_iterator(block_index1);
+ }
+
+ // The first block type is different.
+ assert(blk_cat1 != cat);
+
+ return set_cells_to_multi_blocks_block1_non_equal(start_row, end_row, block_index1, block_index2, it_begin, it_end);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::size_type multi_type_vector<Traits>::merge_with_adjacent_blocks(
+ size_type block_index)
+{
+ assert(!m_blocks.empty());
+ assert(block_index < m_blocks.size());
+ block* blk_prev = block_index > 0 ? &m_blocks[block_index - 1] : nullptr;
+
+ if (!blk_prev)
+ {
+ // No previous block.
+ merge_with_next_block(block_index);
+ return 0;
+ }
+
+ size_type size_prev = blk_prev->size; // size of previous block.
+ block* blk = &m_blocks[block_index];
+ block* blk_next = block_index < (m_blocks.size() - 1) ? &m_blocks[block_index + 1] : nullptr;
+
+ // Check the previous block.
+ if (blk_prev->data)
+ {
+ // Previous block has data.
+ element_category_type cat_prev = mtv::get_block_type(*blk_prev->data);
+ if (!blk->data || cat_prev != mtv::get_block_type(*blk->data))
+ {
+ // Current block is empty or is of different type from the previous one.
+ merge_with_next_block(block_index);
+ return 0;
+ }
+
+ // Previous and current blocks are of the same type.
+ if (blk_next && blk_next->data && cat_prev == get_block_type(*blk_next->data))
+ {
+ // Merge all three blocks.
+ blk_prev->size += blk->size + blk_next->size;
+ block_funcs::append_block(*blk_prev->data, *blk->data);
+ block_funcs::append_block(*blk_prev->data, *blk_next->data);
+ // Avoid overwriting the transferred elements.
+ block_funcs::resize_block(*blk->data, 0);
+ block_funcs::resize_block(*blk_next->data, 0);
+
+ delete_element_block(*blk);
+ delete_element_block(*blk_next);
+
+ typename blocks_type::iterator it = m_blocks.begin();
+ std::advance(it, block_index);
+ typename blocks_type::iterator it_end = it;
+ std::advance(it_end, 2);
+ m_blocks.erase(it, it_end);
+ return size_prev;
+ }
+
+ // Merge only the previous and current blocks.
+ bool merged = merge_with_next_block(block_index - 1);
+ if (!merged)
+ assert(!"Blocks were not merged!");
+
+ return size_prev;
+ }
+
+ assert(!blk_prev->data); // Previous block is empty.
+
+ if (blk->data)
+ {
+ // Current block is not empty. Check with the next block.
+ merge_with_next_block(block_index);
+ return 0;
+ }
+
+ // Previous and current blocks are both empty.
+ assert(!blk->data);
+
+ if (blk_next && !blk_next->data)
+ {
+ // Next block is empty too. Merge all three.
+ blk_prev->size += blk->size + blk_next->size;
+ // No need to call delete_element_block() since we know both blocks are empty.
+
+ typename blocks_type::iterator it = m_blocks.begin();
+ std::advance(it, block_index);
+ typename blocks_type::iterator it_end = it;
+ std::advance(it_end, 2);
+ m_blocks.erase(it, it_end);
+ return size_prev;
+ }
+
+ // Next block is not empty, or does not exist. Merge the current block with the previous one.
+ bool merged = merge_with_next_block(block_index - 1);
+ if (!merged)
+ assert(!"Blocks were not merged!");
+
+ return size_prev;
+}
+
+template<typename Traits>
+bool multi_type_vector<Traits>::merge_with_next_block(size_type block_index)
+{
+ assert(!m_blocks.empty());
+ assert(block_index < m_blocks.size());
+
+ if (block_index >= m_blocks.size() - 1)
+ // No more block below this one.
+ return false;
+
+ // Block exists below.
+ block* blk = &m_blocks[block_index];
+ block* blk_next = &m_blocks[block_index + 1];
+ if (!blk->data)
+ {
+ // Empty block. Merge only if the next block is also empty.
+ if (blk_next->data)
+ // Next block is not empty.
+ return false;
+
+ // Merge the two blocks.
+ blk->size += blk_next->size;
+ m_blocks.erase(m_blocks.begin() + block_index + 1);
+ return true;
+ }
+
+ if (!blk_next->data)
+ return false;
+
+ if (mdds::mtv::get_block_type(*blk->data) != mdds::mtv::get_block_type(*blk_next->data))
+ // Block types differ. Don't merge.
+ return false;
+
+ // Merge it with the next block.
+ block_funcs::append_block(*blk->data, *blk_next->data);
+ block_funcs::resize_block(*blk_next->data, 0);
+ blk->size += blk_next->size;
+ delete_element_block(*blk_next);
+ m_blocks.erase(m_blocks.begin() + block_index + 1);
+ return true;
+}
+
+template<typename Traits>
+template<typename T>
+bool multi_type_vector<Traits>::append_to_prev_block(
+ size_type block_index, element_category_type cat, size_type length, const T& it_begin, const T& it_end)
+{
+ block* blk_prev = get_previous_block_of_type(block_index, cat);
+ if (!blk_prev)
+ return false;
+
+ // Append to the previous block.
+ mdds_mtv_append_values(*blk_prev->data, *it_begin, it_begin, it_end);
+ blk_prev->size += length;
+ return true;
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::clear()
+{
+ delete_element_blocks(m_blocks.begin(), m_blocks.end());
+ m_blocks.clear();
+ m_cur_size = 0;
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::size_type multi_type_vector<Traits>::size() const
+{
+ return m_cur_size;
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::size_type multi_type_vector<Traits>::block_size() const
+{
+ return m_blocks.size();
+}
+
+template<typename Traits>
+bool multi_type_vector<Traits>::empty() const
+{
+ return m_blocks.empty();
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::resize(size_type new_size)
+{
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ std::ostringstream os_prev_block;
+ dump_blocks(os_prev_block);
+#endif
+
+ resize_impl(new_size);
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ try
+ {
+ check_block_integrity();
+ }
+ catch (const mdds::integrity_error& e)
+ {
+ std::ostringstream os;
+ os << e.what() << std::endl;
+ os << "block integrity check failed in resize (new-size=" << new_size << ")" << std::endl;
+ os << "previous block state:" << std::endl;
+ os << os_prev_block.str();
+ std::cerr << os.str() << std::endl;
+ abort();
+ }
+#endif
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::resize_impl(size_type new_size)
+{
+ if (new_size == m_cur_size)
+ return;
+
+ if (!new_size)
+ {
+ clear();
+ return;
+ }
+
+ if (new_size > m_cur_size)
+ {
+ // Append empty cells.
+ append_empty(new_size - m_cur_size);
+ return;
+ }
+
+ assert(new_size < m_cur_size && new_size > 0);
+
+ // Find out in which block the new end row will be.
+ size_type new_end_row = new_size - 1;
+ size_type block_index = get_block_position(new_end_row);
+ if (block_index == m_blocks.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::resize", __LINE__, new_end_row, block_size(), size());
+
+ block* blk = &m_blocks[block_index];
+ size_type start_row_in_block = blk->position;
+ size_type end_row_in_block = start_row_in_block + blk->size - 1;
+
+ if (new_end_row < end_row_in_block)
+ {
+ // Shrink the size of the current block.
+ size_type new_block_size = new_end_row - start_row_in_block + 1;
+ if (blk->data)
+ {
+ block_funcs::overwrite_values(*blk->data, new_end_row + 1, end_row_in_block - new_end_row);
+ block_funcs::resize_block(*blk->data, new_block_size);
+ }
+ blk->size = new_block_size;
+ }
+
+ // Remove all blocks that are below this one.
+ typename blocks_type::iterator it = m_blocks.begin() + block_index + 1;
+ delete_element_blocks(it, m_blocks.end());
+ m_blocks.erase(it, m_blocks.end());
+ m_cur_size = new_size;
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::swap(multi_type_vector& other)
+{
+ std::swap(m_hdl_event, other.m_hdl_event);
+ std::swap(m_cur_size, other.m_cur_size);
+ m_blocks.swap(other.m_blocks);
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::swap(
+ size_type start_pos, size_type end_pos, multi_type_vector& other, size_type other_pos)
+{
+ if (start_pos > end_pos)
+ throw std::out_of_range("multi_type_vector::swap: start position is larger than the end position!");
+
+ size_type other_end_pos = other_pos + end_pos - start_pos;
+
+ if (end_pos >= m_cur_size || other_end_pos >= other.m_cur_size)
+ throw std::out_of_range("multi_type_vector::swap: end position is out of bound!");
+
+ size_type block_index1 = get_block_position(start_pos);
+ if (block_index1 == m_blocks.size())
+ throw std::out_of_range("multi_type_vector::swap: start block position in source not found!");
+
+ size_type block_index2 = get_block_position(end_pos, block_index1);
+ if (block_index2 == m_blocks.size())
+ throw std::out_of_range("multi_type_vector::swap: end block position in source not found!");
+
+ size_type dest_block_index1 = other.get_block_position(other_pos);
+ if (dest_block_index1 == other.m_blocks.size())
+ throw std::out_of_range("multi_type_vector::swap: start block position in destination not found!");
+
+ size_type dest_block_index2 = other.get_block_position(other_end_pos, dest_block_index1);
+ if (dest_block_index2 == other.m_blocks.size())
+ throw std::out_of_range("multi_type_vector::swap: end block position in destination not found!");
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ std::ostringstream os_prev_block, os_prev_block_other;
+ dump_blocks(os_prev_block);
+ other.dump_blocks(os_prev_block_other);
+#endif
+
+ swap_impl(other, start_pos, end_pos, other_pos, block_index1, block_index2, dest_block_index1, dest_block_index2);
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ std::ostringstream os_block, os_block_other;
+ dump_blocks(os_block);
+ other.dump_blocks(os_block_other);
+
+ try
+ {
+ check_block_integrity();
+ other.check_block_integrity();
+ }
+ catch (const mdds::integrity_error& e)
+ {
+ std::ostringstream os;
+ os << e.what() << std::endl;
+ os << std::endl
+ << "block integrity check failed in swap (start_pos=" << start_pos << "; end_pos=" << end_pos
+ << "; other_pos=" << other_pos << ")" << std::endl;
+ os << std::endl << "previous block state (source):" << std::endl;
+ os << os_prev_block.str();
+ os << std::endl << "previous block state (destination):" << std::endl;
+ os << os_prev_block_other.str();
+ os << std::endl << "altered block state (source):" << std::endl;
+ os << os_block.str();
+ os << std::endl << "altered block state (destination):" << std::endl;
+ os << os_block_other.str();
+ std::cerr << os.str() << std::endl;
+ abort();
+ }
+#endif
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::shrink_to_fit()
+{
+ typename blocks_type::iterator it = m_blocks.begin(), it_end = m_blocks.end();
+ for (; it != it_end; ++it)
+ {
+ block* blk = &(*it);
+ assert(blk);
+ if (blk->data)
+ block_funcs::shrink_to_fit(*blk->data);
+ }
+}
+
+template<typename Traits>
+bool multi_type_vector<Traits>::operator==(const multi_type_vector& other) const
+{
+ if (this == &other)
+ // Comparing to self is always equal.
+ return true;
+
+ if (m_blocks.size() != other.m_blocks.size())
+ // Block sizes differ.
+ return false;
+
+ if (m_cur_size != other.m_cur_size)
+ // Row sizes differ.
+ return false;
+
+ typename blocks_type::const_iterator it = m_blocks.begin(), it_end = m_blocks.end();
+ typename blocks_type::const_iterator it2 = other.m_blocks.begin();
+ for (; it != it_end; ++it, ++it2)
+ {
+ const block* blk1 = &(*it);
+ const block* blk2 = &(*it2);
+
+ if (blk1->size != blk2->size)
+ // Block sizes differ.
+ return false;
+
+ if (blk1->data)
+ {
+ if (!blk2->data)
+ // left is non-empty while right is empty.
+ return false;
+ }
+ else
+ {
+ if (blk2->data)
+ // left is empty while right is non-empty.
+ return false;
+ }
+
+ if (!blk1->data)
+ {
+ assert(!blk2->data);
+ continue;
+ }
+
+ assert(blk1->data && blk2->data);
+ if (!block_funcs::equal_block(*blk1->data, *blk2->data))
+ return false;
+ }
+
+ return true;
+}
+
+template<typename Traits>
+bool multi_type_vector<Traits>::operator!=(const multi_type_vector& other) const
+{
+ return !operator==(other);
+}
+
+template<typename Traits>
+multi_type_vector<Traits>& multi_type_vector<Traits>::operator=(const multi_type_vector& other)
+{
+ multi_type_vector assigned(other);
+ swap(assigned);
+ return *this;
+}
+
+template<typename Traits>
+multi_type_vector<Traits>& multi_type_vector<Traits>::operator=(multi_type_vector&& other)
+{
+ multi_type_vector assigned(std::move(other));
+ swap(assigned);
+ return *this;
+}
+
+template<typename Traits>
+template<typename T>
+mtv::element_t multi_type_vector<Traits>::get_element_type(const T& elem)
+{
+ return mdds_mtv_get_element_type(elem);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::set_whole_block_empty(
+ size_type block_index, bool overwrite)
+{
+ block* blk = &m_blocks[block_index];
+ if (!overwrite)
+ // Resize block to 0 before deleting, to prevent its elements from getting deleted.
+ block_funcs::resize_block(*blk->data, 0);
+
+ delete_element_block(*blk);
+
+ block* blk_prev = get_previous_block_of_type(block_index, mtv::element_type_empty);
+ block* blk_next = get_next_block_of_type(block_index, mtv::element_type_empty);
+
+ // Merge with adjacent block(s) if necessary.
+ if (blk_prev)
+ {
+ assert(!blk_prev->data);
+
+ if (blk_next)
+ {
+ // Both preceding and next blocks are empty.
+ assert(!blk_next->data);
+
+ blk_prev->size += blk->size + blk_next->size;
+
+ // no need to call delete_block() on the current and next blocks
+ // since they are empty.
+
+ typename blocks_type::iterator it = m_blocks.begin();
+ std::advance(it, block_index);
+ typename blocks_type::iterator it_end = it;
+ std::advance(it_end, 2);
+ m_blocks.erase(it, it_end);
+
+ return get_iterator(block_index - 1);
+ }
+
+ // Only the preceding block is empty. Merge the current block with the previous.
+ blk_prev->size += blk->size;
+ typename blocks_type::iterator it = m_blocks.begin();
+ std::advance(it, block_index);
+ m_blocks.erase(it);
+
+ return get_iterator(block_index - 1);
+ }
+ else if (blk_next)
+ {
+ assert(!blk_next->data);
+
+ // Only the next block is empty. Merge the next block with the current.
+ blk->size += blk_next->size;
+ typename blocks_type::iterator it = m_blocks.begin();
+ std::advance(it, block_index + 1);
+ m_blocks.erase(it);
+
+ return get_iterator(block_index);
+ }
+
+ return get_iterator(block_index);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::set_empty_in_single_block(
+ size_type start_row, size_type end_row, size_type block_index, bool overwrite)
+{
+ // Range is within a single block.
+ block* blk = &m_blocks[block_index];
+ if (!blk->data)
+ // This block is already empty. Do nothing.
+ return get_iterator(block_index);
+
+ size_type start_row_in_block = blk->position;
+ assert(start_row_in_block + blk->size >= 1);
+ size_type end_row_in_block = start_row_in_block + blk->size - 1;
+ size_type empty_block_size = end_row - start_row + 1;
+
+ if (start_row == start_row_in_block)
+ {
+ // start row coincides with the start of a block.
+
+ if (end_row == end_row_in_block)
+ return set_whole_block_empty(block_index, overwrite);
+
+ // Set the upper part of the block empty.
+ if (overwrite)
+ block_funcs::overwrite_values(*blk->data, 0, empty_block_size);
+ block_funcs::erase(*blk->data, 0, empty_block_size);
+ blk->size -= empty_block_size;
+
+ // Check if the preceding block (if exists) is also empty.
+ block* blk_prev = get_previous_block_of_type(block_index, mtv::element_type_empty);
+ if (blk_prev)
+ {
+ // Extend the previous empty block.
+ blk_prev->size += empty_block_size;
+ blk->position += empty_block_size;
+ return get_iterator(block_index - 1);
+ }
+
+ // Insert a new empty block before the current one.
+ size_type block_position = blk->position;
+ blk->position += empty_block_size;
+ m_blocks.emplace(m_blocks.begin() + block_index, block_position, empty_block_size);
+ return get_iterator(block_index);
+ }
+
+ if (end_row == end_row_in_block)
+ {
+ // end row equals the end of a block.
+ assert(start_row > start_row_in_block);
+
+ // Set the lower part of the block empty.
+ size_type start_pos = start_row - start_row_in_block;
+ if (overwrite)
+ block_funcs::overwrite_values(*blk->data, start_pos, empty_block_size);
+ block_funcs::erase(*blk->data, start_pos, empty_block_size);
+ blk->size -= empty_block_size;
+
+ // Check if the following block (if exists) is also empty.
+ block* blk_next = get_next_block_of_type(block_index, mtv::element_type_empty);
+ if (blk_next)
+ {
+ // Extend the next empty block to cover the new empty segment.
+ blk_next->size += empty_block_size;
+ blk_next->position = start_row;
+ }
+ else
+ {
+ // Insert a new empty block after the current one.
+ m_blocks.emplace(m_blocks.begin() + block_index + 1, start_row, empty_block_size);
+ }
+
+ return get_iterator(block_index + 1);
+ }
+
+ // Empty the middle part of a block.
+ assert(end_row_in_block - end_row > 0);
+ set_new_block_to_middle(block_index, start_row - start_row_in_block, empty_block_size, overwrite);
+
+ return get_iterator(block_index + 1);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::set_empty_in_multi_blocks(
+ size_type start_row, size_type end_row, size_type block_index1, size_type block_index2, bool overwrite)
+{
+ assert(block_index1 < block_index2);
+ size_type start_row_in_block1 = m_blocks[block_index1].position;
+ size_type start_row_in_block2 = m_blocks[block_index2].position;
+
+ {
+ // Empty the lower part of the first block.
+ block* blk = &m_blocks[block_index1];
+ if (blk->data)
+ {
+ if (start_row_in_block1 == start_row)
+ {
+ // Empty the whole block.
+
+ // Check if the previos block (if exists) is also empty.
+ block* blk_prev = nullptr;
+ if (block_index1 > 0)
+ {
+ blk_prev = &m_blocks[block_index1 - 1];
+ if (blk_prev->data)
+ // Not empty. Ignore it.
+ blk_prev = nullptr;
+ }
+
+ if (blk_prev)
+ {
+ // Previous block is empty. Move the start row to the
+ // first row of the previous block, and make the previous
+ // block 'block 1'.
+ start_row -= blk_prev->size;
+ --block_index1;
+ }
+ else
+ {
+ // Make block 1 empty.
+ if (!overwrite)
+ block_funcs::resize_block(*blk->data, 0);
+
+ delete_element_block(*blk);
+ }
+ }
+ else
+ {
+ // Empty the lower part.
+ size_type new_size = start_row - start_row_in_block1;
+ if (overwrite)
+ block_funcs::overwrite_values(*blk->data, new_size, blk->size - new_size);
+
+ block_funcs::resize_block(*blk->data, new_size);
+ blk->size = new_size;
+ }
+ }
+ else
+ {
+ // First block is already empty. Adjust the start row of the new
+ // empty range.
+ start_row = start_row_in_block1;
+ }
+ }
+
+ size_type end_block_to_erase = block_index2; // End block position is non-inclusive.
+
+ {
+ // Empty the upper part of the last block.
+ block* blk = &m_blocks[block_index2];
+ size_type last_row_in_block = start_row_in_block2 + blk->size - 1;
+ if (blk->data)
+ {
+ if (last_row_in_block == end_row)
+ {
+ // Delete the whole block.
+ ++end_block_to_erase;
+
+ // Check if the following block (if exists) is also empty.
+ block* blk_next = nullptr;
+ if (block_index2 + 1 < m_blocks.size())
+ {
+ blk_next = &m_blocks[block_index2 + 1];
+ if (blk_next->data)
+ // Not empty. Ignore it.
+ blk_next = nullptr;
+ }
+
+ if (blk_next)
+ {
+ // The following block is also empty.
+ end_row += blk_next->size;
+ ++end_block_to_erase;
+ }
+ }
+ else
+ {
+ // Empty the upper part.
+ size_type size_to_erase = end_row - start_row_in_block2 + 1;
+ if (overwrite)
+ block_funcs::overwrite_values(*blk->data, 0, size_to_erase);
+
+ block_funcs::erase(*blk->data, 0, size_to_erase);
+ blk->size -= size_to_erase;
+ blk->position = start_row_in_block2 + size_to_erase;
+ }
+ }
+ else
+ {
+ // Last block is empty. Delete this block and adjust the end row
+ // of the new empty range.
+ ++end_block_to_erase;
+ end_row = last_row_in_block;
+ }
+ }
+
+ if (end_block_to_erase - block_index1 > 1)
+ {
+ // Remove all blocks in-between, from block_index1+1 to end_block_to_erase-1.
+
+ for (size_type i = block_index1 + 1; i < end_block_to_erase; ++i)
+ {
+ block& blk = m_blocks[i];
+ if (!overwrite && blk.data)
+ block_funcs::resize_block(*blk.data, 0);
+
+ delete_element_block(blk);
+ }
+
+ typename blocks_type::iterator it = m_blocks.begin() + block_index1 + 1;
+ typename blocks_type::iterator it_end = m_blocks.begin() + end_block_to_erase;
+ m_blocks.erase(it, it_end);
+ }
+
+ block* blk = &m_blocks[block_index1];
+ size_type empty_block_size = end_row - start_row + 1;
+ if (blk->data)
+ {
+ // Insert a new empty block after the first block.
+ m_blocks.emplace(m_blocks.begin() + block_index1 + 1, start_row, empty_block_size);
+ return get_iterator(block_index1 + 1);
+ }
+
+ // Current block is already empty. Just extend its size.
+ blk->size = empty_block_size;
+ blk->position = start_row;
+ return get_iterator(block_index1);
+}
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+template<typename Traits>
+void multi_type_vector<Traits>::dump_blocks(std::ostream& os) const
+{
+ os << "--- blocks" << endl;
+ for (size_type i = 0, n = m_blocks.size(); i < n; ++i)
+ {
+ const block* blk = &m_blocks[i];
+ element_category_type cat = mtv::element_type_empty;
+ if (blk->data)
+ cat = mtv::get_block_type(*blk->data);
+ os << " block " << i << ": position=" << blk->position << " size=" << blk->size << " type=" << cat << endl;
+ }
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::check_block_integrity() const
+{
+ if (m_blocks.empty())
+ // Nothing to check.
+ return;
+
+ if (m_blocks.size() == 1 && m_blocks[0].size == 0)
+ throw mdds::integrity_error("block should never be zero sized!");
+
+ const block* blk_prev = m_blocks.data();
+ if (blk_prev->size == 0)
+ throw mdds::integrity_error("block should never be zero sized!");
+
+ if (blk_prev->position != 0)
+ {
+ std::ostringstream os;
+ os << "position of the first block should be zero!" << std::endl;
+
+ dump_blocks(os);
+ mdds::integrity_error(os.str());
+ }
+
+ element_category_type cat_prev = mtv::element_type_empty;
+ if (blk_prev->data)
+ cat_prev = mtv::get_block_type(*blk_prev->data);
+
+ size_type cur_position = blk_prev->size;
+ size_type total_size = blk_prev->size;
+
+ for (size_type i = 1, n = m_blocks.size(); i < n; ++i)
+ {
+ const block* blk = &m_blocks[i];
+ if (blk->size == 0)
+ throw mdds::integrity_error("block should never be zero sized!");
+
+ if (blk->position != cur_position)
+ {
+ std::ostringstream os;
+ os << "position of the current block is wrong! (expected=" << cur_position << "; actual=" << blk->position
+ << ")" << std::endl;
+
+ dump_blocks(os);
+ throw mdds::integrity_error(os.str());
+ }
+
+ element_category_type cat = mtv::element_type_empty;
+ if (blk->data)
+ {
+ cat = mtv::get_block_type(*blk->data);
+
+ if (block_funcs::size(*blk->data) != blk->size)
+ throw mdds::integrity_error("block size cache and the actual element block size differ!");
+ }
+
+ if (cat_prev == cat)
+ {
+ std::ostringstream os;
+ os << "Two adjacent blocks should never be of the same type." << std::endl;
+ dump_blocks(os);
+ mdds::integrity_error(os.str());
+ }
+
+ blk_prev = blk;
+ cat_prev = cat;
+
+ total_size += blk->size;
+ cur_position += blk->size;
+ }
+
+ if (total_size != m_cur_size)
+ {
+ std::ostringstream os;
+ os << "Current size does not equal the total sizes of all blocks." << std::endl;
+ os << "current size=" << m_cur_size << " total block size=" << total_size << std::endl;
+ dump_blocks(os);
+ mdds::integrity_error(os.str());
+ }
+}
+#endif
+
+}}} // namespace mdds::mtv::aos
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/mdds/multi_type_vector/block_funcs.hpp b/include/mdds/multi_type_vector/block_funcs.hpp
new file mode 100644
index 0000000..7839761
--- /dev/null
+++ b/include/mdds/multi_type_vector/block_funcs.hpp
@@ -0,0 +1,233 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * Copyright (c) 2022 Kohei Yoshida
+ *
+ * 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, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ ************************************************************************/
+
+#pragma once
+
+#include "./types.hpp"
+#include "../global.hpp"
+
+#include <unordered_map>
+#include <functional>
+#include <sstream>
+
+namespace mdds { namespace mtv {
+
+namespace detail {
+
+inline void throw_unknown_block(const char* func, const mdds::mtv::element_t type)
+{
+ std::ostringstream os;
+ os << func << ": failed to map to a element block function (type=" << type << ")";
+ throw general_error(os.str());
+}
+
+template<typename Ret, typename... Args>
+auto& find_func(
+ const std::unordered_map<element_t, std::function<Ret(Args...)>>& func_map, element_t type,
+ const char* src_func_name)
+{
+ auto it = func_map.find(type);
+ if (it == func_map.end())
+ detail::throw_unknown_block(src_func_name, type);
+
+ return it->second;
+}
+
+} // namespace detail
+
+template<typename... Ts>
+struct element_block_funcs
+{
+ static base_element_block* create_new_block(element_t type, std::size_t init_size)
+ {
+ static const std::unordered_map<element_t, std::function<base_element_block*(std::size_t)>> func_map{
+ {Ts::block_type, Ts::create_block}...};
+
+ auto& f = detail::find_func(func_map, type, __func__);
+ return f(init_size);
+ }
+
+ static base_element_block* clone_block(const base_element_block& block)
+ {
+ static const std::unordered_map<element_t, std::function<base_element_block*(const base_element_block&)>>
+ func_map{{Ts::block_type, Ts::clone_block}...};
+
+ auto& f = detail::find_func(func_map, get_block_type(block), __func__);
+ return f(block);
+ }
+
+ static void delete_block(const base_element_block* p)
+ {
+ if (!p)
+ return;
+
+ static const std::unordered_map<element_t, std::function<void(const base_element_block*)>> func_map{
+ {Ts::block_type, Ts::delete_block}...};
+
+ // TODO: We should not throw an exception here as this gets called
+ // from a destructor and destructors should not throw exceptions.
+ auto& f = detail::find_func(func_map, get_block_type(*p), __func__);
+ f(p);
+ }
+
+ static void resize_block(base_element_block& block, std::size_t new_size)
+ {
+ static const std::unordered_map<element_t, std::function<void(base_element_block&, std::size_t)>> func_map{
+ {Ts::block_type, Ts::resize_block}...};
+
+ auto& f = detail::find_func(func_map, get_block_type(block), __func__);
+ f(block, new_size);
+ }
+
+ static void print_block(const base_element_block& block)
+ {
+ static const std::unordered_map<element_t, std::function<void(const base_element_block&)>> func_map{
+ {Ts::block_type, Ts::print_block}...};
+
+ auto& f = detail::find_func(func_map, get_block_type(block), __func__);
+ f(block);
+ }
+
+ static void erase(base_element_block& block, std::size_t pos)
+ {
+ static const std::unordered_map<element_t, std::function<void(base_element_block&, std::size_t)>> func_map{
+ {Ts::block_type, Ts::erase_value}...};
+
+ auto& f = detail::find_func(func_map, get_block_type(block), __func__);
+ f(block, pos);
+ }
+
+ static void erase(base_element_block& block, std::size_t pos, std::size_t size)
+ {
+ static const std::unordered_map<element_t, std::function<void(base_element_block&, std::size_t, std::size_t)>>
+ func_map{{Ts::block_type, Ts::erase_values}...};
+
+ auto& f = detail::find_func(func_map, get_block_type(block), __func__);
+ f(block, pos, size);
+ }
+
+ static void append_block(base_element_block& dest, const base_element_block& src)
+ {
+ static const std::unordered_map<element_t, std::function<void(base_element_block&, const base_element_block&)>>
+ func_map{{Ts::block_type, Ts::append_block}...};
+
+ auto& f = detail::find_func(func_map, get_block_type(dest), __func__);
+ f(dest, src);
+ }
+
+ static void append_values_from_block(
+ base_element_block& dest, const base_element_block& src, std::size_t begin_pos, std::size_t len)
+ {
+ using func_type = std::function<void(base_element_block&, const base_element_block&, std::size_t, std::size_t)>;
+ static const std::unordered_map<element_t, func_type> func_map{
+ {Ts::block_type, Ts::append_values_from_block}...};
+
+ auto& f = detail::find_func(func_map, get_block_type(dest), __func__);
+ f(dest, src, begin_pos, len);
+ }
+
+ static void assign_values_from_block(
+ base_element_block& dest, const base_element_block& src, std::size_t begin_pos, std::size_t len)
+ {
+ using func_type = std::function<void(base_element_block&, const base_element_block&, std::size_t, std::size_t)>;
+ static const std::unordered_map<element_t, func_type> func_map{
+ {Ts::block_type, Ts::assign_values_from_block}...};
+
+ auto& f = detail::find_func(func_map, get_block_type(dest), __func__);
+ f(dest, src, begin_pos, len);
+ }
+
+ static void prepend_values_from_block(
+ base_element_block& dest, const base_element_block& src, std::size_t begin_pos, std::size_t len)
+ {
+ using func_type = std::function<void(base_element_block&, const base_element_block&, std::size_t, std::size_t)>;
+ static const std::unordered_map<element_t, func_type> func_map{
+ {Ts::block_type, Ts::prepend_values_from_block}...};
+
+ auto& f = detail::find_func(func_map, get_block_type(dest), __func__);
+ f(dest, src, begin_pos, len);
+ }
+
+ static void swap_values(
+ base_element_block& blk1, base_element_block& blk2, std::size_t pos1, std::size_t pos2, std::size_t len)
+ {
+ element_t blk1_type = get_block_type(blk1);
+ assert(blk1_type == get_block_type(blk2));
+
+ using func_type =
+ std::function<void(base_element_block&, base_element_block&, std::size_t, std::size_t, std::size_t)>;
+ static const std::unordered_map<element_t, func_type> func_map{{Ts::block_type, Ts::swap_values}...};
+
+ auto& f = detail::find_func(func_map, blk1_type, __func__);
+ f(blk1, blk2, pos1, pos2, len);
+ }
+
+ static bool equal_block(const base_element_block& left, const base_element_block& right)
+ {
+ element_t block_type = get_block_type(left);
+ if (block_type != get_block_type(right))
+ return false;
+
+ using func_type = std::function<bool(const base_element_block&, const base_element_block&)>;
+ static const std::unordered_map<element_t, func_type> func_map{{Ts::block_type, Ts::equal_block}...};
+
+ auto& f = detail::find_func(func_map, block_type, __func__);
+ return f(left, right);
+ }
+
+ static void overwrite_values(base_element_block& block, std::size_t pos, std::size_t len)
+ {
+ using func_type = std::function<void(base_element_block&, std::size_t, std::size_t)>;
+ static const std::unordered_map<element_t, func_type> func_map{{Ts::block_type, Ts::overwrite_values}...};
+
+ auto& f = detail::find_func(func_map, get_block_type(block), __func__);
+ f(block, pos, len);
+ }
+
+ static void shrink_to_fit(base_element_block& block)
+ {
+ using func_type = std::function<void(base_element_block&)>;
+ static const std::unordered_map<element_t, func_type> func_map{{Ts::block_type, Ts::shrink_to_fit}...};
+
+ auto& f = detail::find_func(func_map, get_block_type(block), __func__);
+ f(block);
+ }
+
+ static std::size_t size(const base_element_block& block)
+ {
+ using func_type = std::function<std::size_t(const base_element_block&)>;
+ static const std::unordered_map<element_t, func_type> func_map{{Ts::block_type, Ts::size}...};
+
+ auto& f = detail::find_func(func_map, get_block_type(block), __func__);
+ return f(block);
+ }
+};
+
+}} // namespace mdds::mtv
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/mdds/multi_type_vector/collection.hpp b/include/mdds/multi_type_vector/collection.hpp
new file mode 100644
index 0000000..73467de
--- /dev/null
+++ b/include/mdds/multi_type_vector/collection.hpp
@@ -0,0 +1,280 @@
+/*************************************************************************
+ *
+ * Copyright (c) 2016 Kohei Yoshida
+ *
+ * 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, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ ************************************************************************/
+
+#ifndef INCLUDED_MDDS_COLLECTION_HPP
+#define INCLUDED_MDDS_COLLECTION_HPP
+
+#include "mdds/multi_type_vector/types.hpp"
+
+#include <type_traits>
+#include <vector>
+#include <memory>
+
+namespace mdds { namespace mtv {
+
+template<typename _MtvT>
+class collection;
+
+namespace detail {
+
+template<typename _MtvT>
+class side_iterator
+{
+ typedef _MtvT mtv_type;
+ friend collection<mtv_type>;
+
+ typedef typename mtv_type::size_type size_type;
+ typedef typename mtv_type::const_iterator const_iterator;
+ typedef typename mtv_type::const_position_type const_position_type;
+
+ /** meta-data about each mtv instance. */
+ struct mtv_item
+ {
+ const mtv_type* vector;
+ const_iterator block_pos;
+ const_iterator block_end;
+
+ mtv_item(const mtv_type* v, const const_iterator& bp, const const_iterator& be)
+ : vector(v), block_pos(bp), block_end(be)
+ {}
+ };
+
+ /** single element value. */
+ struct node
+ {
+ friend class side_iterator;
+
+ /** type of current element */
+ mdds::mtv::element_t type;
+
+ /** index of current mtv instance */
+ size_type index;
+
+ /** logical position of the current element within the mtv. */
+ size_type position;
+
+ template<typename _Blk>
+ typename _Blk::value_type get() const
+ {
+ return _Blk::get_value(*__position.first->data, __position.second);
+ }
+
+ private:
+ /** position object of current element within the mtv instance. */
+ const_position_type __position;
+ };
+
+ enum begin_state_type
+ {
+ begin_state
+ };
+ enum end_state_type
+ {
+ end_state
+ };
+
+ std::vector<mtv_item> m_vectors;
+ node m_cur_node;
+ size_type m_elem_pos;
+ size_type m_elem_pos_end;
+ size_type m_index_offset;
+ uintptr_t m_identity;
+
+ side_iterator(
+ std::vector<mtv_item>&& vectors, size_type elem_pos, size_type elem_size, size_type index_offset,
+ uintptr_t identity, begin_state_type);
+
+ side_iterator(
+ std::vector<mtv_item>&& vectors, size_type elem_pos, size_type elem_size, size_type index_offset,
+ uintptr_t identity, end_state_type);
+
+public:
+ typedef node value_type;
+
+ side_iterator();
+
+ template<typename _T>
+ side_iterator(const _T& begin, const _T& end);
+
+ const value_type& operator*() const
+ {
+ return m_cur_node;
+ }
+
+ const value_type* operator->() const
+ {
+ return &m_cur_node;
+ }
+
+ side_iterator& operator++();
+
+ side_iterator operator++(int);
+
+ bool operator==(const side_iterator& other) const;
+ bool operator!=(const side_iterator& other) const;
+};
+
+} // namespace detail
+
+/**
+ * Special-purpose collection of multiple multi_type_vector instances to
+ * allow them to be traversed "sideways" i.e. orthogonal to the direction of
+ * the vector instances. All involved multi_type_vector instances must be of
+ * the same type and length.
+ */
+template<typename _MtvT>
+class collection
+{
+public:
+ typedef _MtvT mtv_type;
+ typedef typename mtv_type::size_type size_type;
+
+private:
+ struct range
+ {
+ size_type start;
+ size_type size;
+
+ range() : start(0), size(0)
+ {}
+ };
+
+ std::vector<const mtv_type*> m_vectors;
+ size_type m_mtv_size;
+ uintptr_t m_identity;
+
+ range m_elem_range; /// element range.
+ range m_col_range; /// collection range.
+
+public:
+ typedef detail::side_iterator<mtv_type> const_iterator;
+
+ collection();
+
+ /**
+ * Constructor that takes the start and end iterators of the
+ * multi_type_vector instances to reference in the collection.
+ *
+ * @param begin iterator that references the first multi_type_vector
+ * instance to place in the collection.
+ * @param end iterator that references the position past the last
+ * multi_type_vector instance to place in the collection.
+ */
+ template<typename _T>
+ collection(const _T& begin, const _T& end);
+
+ /**
+ * Return an iterator that references the first element in the
+ * collection.
+ *
+ * @return iterator that references the first element in the collection.
+ */
+ const_iterator begin() const;
+
+ /**
+ * Return an iterator that references the position past the last element
+ * in the collection.
+ *
+ * @return iterator that references the position past the last element in
+ * the collection.
+ */
+ const_iterator end() const;
+
+ /**
+ * Return the length of the vector instances stored in the collection.
+ * This will be equivalent of the length of each multi_type_vector
+ * instance, since all stored instances have the same length.
+ *
+ * @return length of the stored multi_type_vector instances.
+ */
+ size_type size() const;
+
+ /**
+ * Swap the entire collection with another collection instance.
+ *
+ * @param other another collection instance to swap contents with.
+ */
+ void swap(collection& other);
+
+ /**
+ * Set the sub-range of the collection to iterate through.
+ *
+ * <p>For instance, if the collection consists of 100 multi_type_vector
+ * instances, and you want to iterate through only 50 of them starting
+ * from the second instance, you set the start index to 1 (as it's
+ * 0-based), and the size to 50.</p>
+ *
+ * @param start 0-based index of the first multi_type_vector instance to
+ * iterate through.
+ * @param size length of the collection range i.e. the number of vector
+ * instances to iterate through starting from the specified
+ * first vector instance.
+ */
+ void set_collection_range(size_type start, size_type size);
+
+ /**
+ * Set the sub element range to iterate through. This limits the element
+ * range in each multi_type_vector instance to iterate through. The
+ * direction of the element range is orthogonal to the direction of the
+ * collection range.
+ *
+ * <p>For instance, if the collection consists of multiple
+ * multi_type_vector instances all of which have a length of 50, and you
+ * only wish to iterate from the 3rd element through the 10th element in
+ * each vector instance, then you set the start index to 2 and the
+ * size to 8.</p>
+ *
+ * @param start 0-based index of the starting element position.
+ * @param size length of the element range to iterate through starting
+ * from the specified start element position.
+ */
+ void set_element_range(size_type start, size_type size);
+
+private:
+ void check_collection_range(size_type start, size_type size) const;
+ void check_element_range(size_type start, size_type size) const;
+
+ std::vector<typename const_iterator::mtv_item> build_iterator_state() const;
+
+ void init_insert_vector(const std::unique_ptr<mtv_type>& p);
+
+ void init_insert_vector(const std::shared_ptr<mtv_type>& p);
+
+ template<typename _T>
+ void init_insert_vector(const _T& t, typename std::enable_if<std::is_pointer<_T>::value>::type* = 0);
+
+ template<typename _T>
+ void init_insert_vector(const _T& t, typename std::enable_if<!std::is_pointer<_T>::value>::type* = 0);
+
+ void check_vector_size(const mtv_type& t);
+};
+
+}} // namespace mdds::mtv
+
+#include "collection_def.inl"
+
+#endif
diff --git a/include/mdds/multi_type_vector/collection_def.inl b/include/mdds/multi_type_vector/collection_def.inl
new file mode 100644
index 0000000..6a1a0d2
--- /dev/null
+++ b/include/mdds/multi_type_vector/collection_def.inl
@@ -0,0 +1,305 @@
+/*************************************************************************
+ *
+ * Copyright (c) 2016-2018 Kohei Yoshida
+ *
+ * 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, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ ************************************************************************/
+
+#include <sstream>
+
+namespace mdds { namespace mtv {
+
+namespace detail {
+
+template<typename _MtvT>
+side_iterator<_MtvT>::side_iterator() : m_elem_pos(0), m_elem_pos_end(0), m_index_offset(0), m_identity(0)
+{}
+
+template<typename _MtvT>
+side_iterator<_MtvT>::side_iterator(
+ std::vector<mtv_item>&& vectors, size_type elem_pos, size_type elem_size, size_type index_offset,
+ uintptr_t identity, begin_state_type)
+ : m_vectors(std::move(vectors)), m_elem_pos(elem_pos), m_elem_pos_end(elem_pos + elem_size),
+ m_index_offset(index_offset), m_identity(identity)
+{
+ m_cur_node.index = index_offset;
+
+ if (m_vectors.empty())
+ return;
+
+ mtv_item& col1 = m_vectors.front();
+
+ m_cur_node.__position = col1.vector->position(col1.block_pos, m_elem_pos);
+ col1.block_pos = m_cur_node.__position.first;
+ m_cur_node.type = col1.block_pos->type;
+ m_cur_node.position = m_elem_pos;
+}
+
+template<typename _MtvT>
+side_iterator<_MtvT>::side_iterator(
+ std::vector<mtv_item>&& vectors, size_type elem_pos, size_type elem_size, size_type index_offset,
+ uintptr_t identity, end_state_type)
+ : m_vectors(std::move(vectors)), m_elem_pos(elem_pos), m_elem_pos_end(elem_pos + elem_size),
+ m_index_offset(index_offset), m_identity(identity)
+{
+ m_elem_pos = m_elem_pos_end;
+ m_cur_node.index = index_offset;
+
+ // We can leave the position and type uninitialized since this is an end
+ // position which doesn't reference an actual element.
+}
+
+template<typename _MtvT>
+side_iterator<_MtvT>& side_iterator<_MtvT>::operator++()
+{
+ ++m_cur_node.index;
+ size_type pos = m_cur_node.index - m_index_offset;
+ if (pos >= m_vectors.size())
+ {
+ // Move to the next element position.
+ m_cur_node.index = m_index_offset;
+ ++m_elem_pos;
+ if (m_elem_pos >= m_elem_pos_end)
+ // End position has been reached. Don't update the current node.
+ return *this;
+ }
+
+ pos = m_cur_node.index - m_index_offset;
+ // Get the current vector.
+ assert(pos < m_vectors.size());
+ mtv_item& col = m_vectors[pos];
+
+ // Update the current node.
+ m_cur_node.__position = col.vector->position(col.block_pos, m_elem_pos);
+ m_cur_node.position = m_elem_pos;
+ col.block_pos = m_cur_node.__position.first;
+ m_cur_node.type = col.block_pos->type;
+
+ return *this;
+}
+
+template<typename _MtvT>
+side_iterator<_MtvT> side_iterator<_MtvT>::operator++(int)
+{
+ side_iterator tmp(*this);
+ operator++();
+ return tmp;
+}
+
+template<typename _MtvT>
+bool side_iterator<_MtvT>::operator==(const side_iterator& other) const
+{
+ if (m_identity != other.m_identity)
+ return false;
+
+ if (m_elem_pos_end != other.m_elem_pos_end)
+ return false;
+
+ if (m_elem_pos != other.m_elem_pos || m_elem_pos_end != other.m_elem_pos_end)
+ return false;
+
+ return m_cur_node.index == other.m_cur_node.index;
+}
+
+template<typename _MtvT>
+bool side_iterator<_MtvT>::operator!=(const side_iterator& other) const
+{
+ return !operator==(other);
+}
+
+} // namespace detail
+
+template<typename _MtvT>
+collection<_MtvT>::collection() : m_mtv_size(0), m_identity(0)
+{}
+
+template<typename _MtvT>
+template<typename _T>
+collection<_MtvT>::collection(const _T& begin, const _T& end) : m_mtv_size(0), m_identity(0)
+{
+ size_type n = std::distance(begin, end);
+ m_vectors.reserve(n);
+
+ for (_T it = begin; it != end; ++it)
+ init_insert_vector(*it);
+
+ // Create a single value that identifies the whole collection.
+ auto it = m_vectors.begin();
+ uintptr_t identity = reinterpret_cast<uintptr_t>(*it);
+ ++it;
+ std::for_each(it, m_vectors.end(), [&](const mtv_type* p0) {
+ uintptr_t p = reinterpret_cast<uintptr_t>(p0);
+ identity = identity << 1;
+ identity ^= p;
+ });
+ m_identity = identity;
+
+ assert(m_mtv_size); // This should have been set by this point.
+ m_elem_range.start = 0;
+ m_elem_range.size = m_mtv_size;
+
+ m_col_range.start = 0;
+ m_col_range.size = n;
+}
+
+template<typename _MtvT>
+typename collection<_MtvT>::const_iterator collection<_MtvT>::begin() const
+{
+ return const_iterator(
+ build_iterator_state(), m_elem_range.start, m_elem_range.size, m_col_range.start, m_identity,
+ const_iterator::begin_state);
+}
+
+template<typename _MtvT>
+typename collection<_MtvT>::const_iterator collection<_MtvT>::end() const
+{
+ return const_iterator(
+ build_iterator_state(), m_elem_range.start, m_elem_range.size, m_col_range.start, m_identity,
+ const_iterator::end_state);
+}
+
+template<typename _MtvT>
+typename collection<_MtvT>::size_type collection<_MtvT>::size() const
+{
+ return m_mtv_size;
+}
+
+template<typename _MtvT>
+void collection<_MtvT>::swap(collection& other)
+{
+ m_vectors.swap(other.m_vectors);
+ std::swap(m_mtv_size, other.m_mtv_size);
+ std::swap(m_identity, other.m_identity);
+ std::swap(m_elem_range, other.m_elem_range);
+ std::swap(m_col_range, other.m_col_range);
+}
+
+template<typename _MtvT>
+void collection<_MtvT>::set_collection_range(size_type start, size_type size)
+{
+ check_collection_range(start, size);
+ m_col_range.start = start;
+ m_col_range.size = size;
+}
+
+template<typename _MtvT>
+void collection<_MtvT>::set_element_range(size_type start, size_type size)
+{
+ check_element_range(start, size);
+ m_elem_range.start = start;
+ m_elem_range.size = size;
+}
+
+template<typename _MtvT>
+void collection<_MtvT>::check_collection_range(size_type start, size_type size) const
+{
+ if (start >= m_vectors.size())
+ {
+ std::ostringstream os;
+ os << "range start position must be less than " << m_vectors.size();
+ throw invalid_arg_error(os.str());
+ }
+
+ if (!size)
+ throw invalid_arg_error("size of 0 is not allowed.");
+
+ if ((start + size) > m_vectors.size())
+ throw invalid_arg_error("size is too large.");
+}
+
+template<typename _MtvT>
+void collection<_MtvT>::check_element_range(size_type start, size_type size) const
+{
+ if (start >= m_mtv_size)
+ {
+ std::ostringstream os;
+ os << "range start position must be less than " << m_mtv_size;
+ throw invalid_arg_error(os.str());
+ }
+
+ if (!size)
+ throw invalid_arg_error("size of 0 is not allowed.");
+
+ if ((start + size) > m_mtv_size)
+ throw invalid_arg_error("size is too large.");
+}
+
+template<typename _MtvT>
+std::vector<typename collection<_MtvT>::const_iterator::mtv_item> collection<_MtvT>::build_iterator_state() const
+{
+ std::vector<typename const_iterator::mtv_item> cols;
+ cols.reserve(m_col_range.size);
+
+ auto it = m_vectors.begin();
+ std::advance(it, m_col_range.start);
+ auto it_end = it;
+ std::advance(it_end, m_col_range.size);
+
+ std::for_each(it, it_end, [&](const mtv_type* p) { cols.emplace_back(p, p->cbegin(), p->cend()); });
+
+ return cols;
+}
+
+template<typename _MtvT>
+template<typename _T>
+void collection<_MtvT>::init_insert_vector(const _T& t, typename std::enable_if<std::is_pointer<_T>::value>::type*)
+{
+ check_vector_size(*t);
+ m_vectors.emplace_back(t);
+}
+
+template<typename _MtvT>
+void collection<_MtvT>::init_insert_vector(const std::unique_ptr<mtv_type>& p)
+{
+ check_vector_size(*p);
+ m_vectors.emplace_back(p.get());
+}
+
+template<typename _MtvT>
+void collection<_MtvT>::init_insert_vector(const std::shared_ptr<mtv_type>& p)
+{
+ check_vector_size(*p);
+ m_vectors.emplace_back(p.get());
+}
+
+template<typename _MtvT>
+template<typename _T>
+void collection<_MtvT>::init_insert_vector(const _T& t, typename std::enable_if<!std::is_pointer<_T>::value>::type*)
+{
+ check_vector_size(t);
+ m_vectors.emplace_back(&t);
+}
+
+template<typename _MtvT>
+void collection<_MtvT>::check_vector_size(const mtv_type& t)
+{
+ if (t.empty())
+ throw invalid_arg_error("Empty multi_type_vector instance is not allowed.");
+
+ if (!m_mtv_size)
+ m_mtv_size = t.size();
+ else if (m_mtv_size != t.size())
+ throw invalid_arg_error("All multi_type_vector instances must be of the same length.");
+}
+
+}} // namespace mdds::mtv
diff --git a/include/mdds/multi_type_vector/env.hpp b/include/mdds/multi_type_vector/env.hpp
new file mode 100644
index 0000000..ee81179
--- /dev/null
+++ b/include/mdds/multi_type_vector/env.hpp
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * Copyright (c) 2022 Kohei Yoshida
+ *
+ * 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, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ ************************************************************************/
+
+#pragma once
+
+#ifndef MDDS_MTV_USE_STANDARD_ELEMENT_BLOCKS
+#define MDDS_MTV_USE_STANDARD_ELEMENT_BLOCKS 1
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/mdds/multi_type_vector/iterator_node.hpp b/include/mdds/multi_type_vector/iterator_node.hpp
new file mode 100644
index 0000000..bc8b58e
--- /dev/null
+++ b/include/mdds/multi_type_vector/iterator_node.hpp
@@ -0,0 +1,127 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * Copyright (c) 2021 Kohei Yoshida
+ *
+ * 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, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ ************************************************************************/
+
+#ifndef INCLUDED_MDDS_MULTI_TYPE_VECTOR_DIR_ITERATOR_NODE_HPP
+#define INCLUDED_MDDS_MULTI_TYPE_VECTOR_DIR_ITERATOR_NODE_HPP
+
+namespace mdds { namespace detail { namespace mtv {
+
+/**
+ * Node that represents the content of each iterator. The private data part
+ * is an implementation detail that should never be accessed externally.
+ * What the end position stores in its private data is totally &
+ * intentionally undefined.
+ */
+template<typename ParentT, typename SizeT>
+struct iterator_value_node
+{
+ using parent_type = ParentT;
+ using size_type = SizeT;
+
+ mdds::mtv::element_t type;
+ size_type position;
+ size_type size;
+ mdds::mtv::base_element_block* data;
+
+ iterator_value_node(const parent_type* parent, size_type block_index)
+ : type(mdds::mtv::element_type_empty), position(0), size(0), data(nullptr), __private_data(parent, block_index)
+ {}
+
+ void swap(iterator_value_node& other)
+ {
+ std::swap(type, other.type);
+ std::swap(position, other.position);
+ std::swap(size, other.size);
+ std::swap(data, other.data);
+
+ __private_data.swap(other.__private_data);
+ }
+
+ struct private_data
+ {
+ const parent_type* parent;
+ size_type block_index;
+
+ private_data() : parent(nullptr), block_index(0)
+ {}
+ private_data(const parent_type* _parent, size_type _block_index) : parent(_parent), block_index(_block_index)
+ {}
+
+ void swap(private_data& other)
+ {
+ std::swap(parent, other.parent);
+ std::swap(block_index, other.block_index);
+ }
+ };
+ private_data __private_data;
+
+ bool operator==(const iterator_value_node& other) const
+ {
+ return type == other.type && position == other.position && size == other.size && data == other.data &&
+ __private_data.parent == other.__private_data.parent &&
+ __private_data.block_index == other.__private_data.block_index;
+ }
+
+ bool operator!=(const iterator_value_node& other) const
+ {
+ return !operator==(other);
+ }
+};
+
+template<typename ParentT, typename SizeT>
+struct private_data_no_update
+{
+ using node_type = iterator_value_node<ParentT, SizeT>;
+
+ static void inc(node_type&)
+ {}
+ static void dec(node_type&)
+ {}
+};
+
+template<typename ParentT, typename SizeT>
+struct private_data_forward_update
+{
+ using node_type = iterator_value_node<ParentT, SizeT>;
+
+ static void inc(node_type& nd)
+ {
+ ++nd.__private_data.block_index;
+ }
+
+ static void dec(node_type& nd)
+ {
+ --nd.__private_data.block_index;
+ }
+};
+
+}}} // namespace mdds::detail::mtv
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/mdds/multi_type_vector/macro.hpp b/include/mdds/multi_type_vector/macro.hpp
new file mode 100644
index 0000000..aa9af3b
--- /dev/null
+++ b/include/mdds/multi_type_vector/macro.hpp
@@ -0,0 +1,207 @@
+/*************************************************************************
+ *
+ * Copyright (c) 2012-2021 Kohei Yoshida
+ *
+ * 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, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ ************************************************************************/
+
+#ifndef INCLUDED_MDDS_MULTI_TYPE_VECTOR_DIR_MACRO_HPP
+#define INCLUDED_MDDS_MULTI_TYPE_VECTOR_DIR_MACRO_HPP
+
+/**
+ * @def MDDS_MTV_DEFINE_ELEMENT_CALLBACKS(type, type_id, empty_value,
+ * block_type)
+ *
+ * @param type element value type.
+ * @param type_id constant value used as an ID for the value type. It should
+ * be of type mdds::mtv::element_t.
+ * @param empty_value value that should be used as the default "false" value
+ * for the value type.
+ * @param block_type block type that stores the specified value type.
+ *
+ * Defines required callback functions for multi_type_vector.
+ */
+#define MDDS_MTV_DEFINE_ELEMENT_CALLBACKS(type, type_id, empty_value, block_type) \
+\
+ inline mdds::mtv::element_t mdds_mtv_get_element_type(const type&) \
+ { \
+ return type_id; \
+ } \
+\
+ inline void mdds_mtv_get_empty_value(type& val) \
+ { \
+ val = empty_value; \
+ } \
+\
+ inline void mdds_mtv_set_value(mdds::mtv::base_element_block& block, size_t pos, const type& val) \
+ { \
+ block_type::set_value(block, pos, val); \
+ } \
+\
+ inline void mdds_mtv_get_value(const mdds::mtv::base_element_block& block, size_t pos, type& val) \
+ { \
+ block_type::get_value(block, pos, val); \
+ } \
+\
+ template<typename _Iter> \
+ void mdds_mtv_set_values( \
+ mdds::mtv::base_element_block& block, size_t pos, const type&, const _Iter& it_begin, const _Iter& it_end) \
+ { \
+ block_type::set_values(block, pos, it_begin, it_end); \
+ } \
+\
+ inline void mdds_mtv_append_value(mdds::mtv::base_element_block& block, const type& val) \
+ { \
+ block_type::append_value(block, val); \
+ } \
+\
+ inline void mdds_mtv_prepend_value(mdds::mtv::base_element_block& block, const type& val) \
+ { \
+ block_type::prepend_value(block, val); \
+ } \
+\
+ template<typename _Iter> \
+ void mdds_mtv_prepend_values( \
+ mdds::mtv::base_element_block& block, const type&, const _Iter& it_begin, const _Iter& it_end) \
+ { \
+ block_type::prepend_values(block, it_begin, it_end); \
+ } \
+\
+ template<typename _Iter> \
+ void mdds_mtv_append_values( \
+ mdds::mtv::base_element_block& block, const type&, const _Iter& it_begin, const _Iter& it_end) \
+ { \
+ block_type::append_values(block, it_begin, it_end); \
+ } \
+\
+ template<typename _Iter> \
+ void mdds_mtv_assign_values( \
+ mdds::mtv::base_element_block& dest, const type&, const _Iter& it_begin, const _Iter& it_end) \
+ { \
+ block_type::assign_values(dest, it_begin, it_end); \
+ } \
+\
+ template<typename _Iter> \
+ void mdds_mtv_insert_values( \
+ mdds::mtv::base_element_block& block, size_t pos, const type&, const _Iter& it_begin, const _Iter& it_end) \
+ { \
+ block_type::insert_values(block, pos, it_begin, it_end); \
+ } \
+\
+ inline mdds::mtv::base_element_block* mdds_mtv_create_new_block(size_t init_size, const type& val) \
+ { \
+ return block_type::create_block_with_value(init_size, val); \
+ } \
+\
+ template<typename _Iter> \
+ mdds::mtv::base_element_block* mdds_mtv_create_new_block(const type&, const _Iter& it_begin, const _Iter& it_end) \
+ { \
+ return block_type::create_block_with_values(it_begin, it_end); \
+ }
+
+/**
+ * @def MDDS_MTV_DEFINE_ELEMENT_CALLBACKS_PTR(type, type_id, empty_value,
+ * block_type)
+ *
+ * A variant of MDDS_MTV_DEFINE_ELEMENT_CALLBACKS that should be used for a
+ * pointer type.
+ */
+#define MDDS_MTV_DEFINE_ELEMENT_CALLBACKS_PTR(type, type_id, empty_value, block_type) \
+\
+ inline mdds::mtv::element_t mdds_mtv_get_element_type(const type*) \
+ { \
+ return type_id; \
+ } \
+\
+ inline void mdds_mtv_get_empty_value(type*& val) \
+ { \
+ val = empty_value; \
+ } \
+\
+ inline void mdds_mtv_set_value(mdds::mtv::base_element_block& block, size_t pos, type* val) \
+ { \
+ block_type::set_value(block, pos, val); \
+ } \
+\
+ inline void mdds_mtv_get_value(const mdds::mtv::base_element_block& block, size_t pos, type*& val) \
+ { \
+ block_type::get_value(block, pos, val); \
+ } \
+\
+ template<typename _Iter> \
+ void mdds_mtv_set_values( \
+ mdds::mtv::base_element_block& block, size_t pos, const type*, const _Iter& it_begin, const _Iter& it_end) \
+ { \
+ block_type::set_values(block, pos, it_begin, it_end); \
+ } \
+\
+ inline void mdds_mtv_append_value(mdds::mtv::base_element_block& block, type* val) \
+ { \
+ block_type::append_value(block, val); \
+ } \
+\
+ inline void mdds_mtv_prepend_value(mdds::mtv::base_element_block& block, type* val) \
+ { \
+ block_type::prepend_value(block, val); \
+ } \
+\
+ template<typename _Iter> \
+ void mdds_mtv_prepend_values( \
+ mdds::mtv::base_element_block& block, const type*, const _Iter& it_begin, const _Iter& it_end) \
+ { \
+ block_type::prepend_values(block, it_begin, it_end); \
+ } \
+\
+ template<typename _Iter> \
+ void mdds_mtv_append_values( \
+ mdds::mtv::base_element_block& block, const type*, const _Iter& it_begin, const _Iter& it_end) \
+ { \
+ block_type::append_values(block, it_begin, it_end); \
+ } \
+\
+ template<typename _Iter> \
+ void mdds_mtv_assign_values( \
+ mdds::mtv::base_element_block& dest, const type*, const _Iter& it_begin, const _Iter& it_end) \
+ { \
+ block_type::assign_values(dest, it_begin, it_end); \
+ } \
+\
+ template<typename _Iter> \
+ void mdds_mtv_insert_values( \
+ mdds::mtv::base_element_block& block, size_t pos, const type*, const _Iter& it_begin, const _Iter& it_end) \
+ { \
+ block_type::insert_values(block, pos, it_begin, it_end); \
+ } \
+\
+ inline mdds::mtv::base_element_block* mdds_mtv_create_new_block(size_t init_size, type* val) \
+ { \
+ return block_type::create_block_with_value(init_size, val); \
+ } \
+\
+ template<typename _Iter> \
+ mdds::mtv::base_element_block* mdds_mtv_create_new_block(const type*, const _Iter& it_begin, const _Iter& it_end) \
+ { \
+ return block_type::create_block_with_values(it_begin, it_end); \
+ }
+
+#endif
diff --git a/include/mdds/multi_type_vector/soa/Makefile.am b/include/mdds/multi_type_vector/soa/Makefile.am
new file mode 100644
index 0000000..2730952
--- /dev/null
+++ b/include/mdds/multi_type_vector/soa/Makefile.am
@@ -0,0 +1,8 @@
+
+headersdir = $(includedir)/mdds-@API_VERSION@/mdds/multi_type_vector/soa
+
+headers_HEADERS = \
+ block_util.hpp \
+ iterator.hpp \
+ main_def.inl \
+ main.hpp
diff --git a/include/mdds/multi_type_vector/soa/Makefile.in b/include/mdds/multi_type_vector/soa/Makefile.in
new file mode 100644
index 0000000..06616f6
--- /dev/null
+++ b/include/mdds/multi_type_vector/soa/Makefile.in
@@ -0,0 +1,585 @@
+# Makefile.in generated by automake 1.16.5 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2021 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+subdir = include/mdds/multi_type_vector/soa
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4/ax_cxx_compile_stdcxx_17.m4 \
+ $(top_srcdir)/m4/m4_ax_valgrind_check.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(headers_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+SOURCES =
+DIST_SOURCES =
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(headersdir)"
+HEADERS = $(headers_HEADERS)
+am__extra_recursive_targets = check-valgrind-recursive \
+ check-valgrind-memcheck-recursive \
+ check-valgrind-helgrind-recursive check-valgrind-drd-recursive \
+ check-valgrind-sgcheck-recursive
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+am__DIST_COMMON = $(srcdir)/Makefile.in
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+API_VERSION = @API_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_NODEBUG = @CPPFLAGS_NODEBUG@
+CSCOPE = @CSCOPE@
+CTAGS = @CTAGS@
+CXX = @CXX@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CXXFLAGS_UNITTESTS = @CXXFLAGS_UNITTESTS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DOXYGEN = @DOXYGEN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+ENABLE_VALGRIND_drd = @ENABLE_VALGRIND_drd@
+ENABLE_VALGRIND_helgrind = @ENABLE_VALGRIND_helgrind@
+ENABLE_VALGRIND_memcheck = @ENABLE_VALGRIND_memcheck@
+ENABLE_VALGRIND_sgcheck = @ENABLE_VALGRIND_sgcheck@
+ETAGS = @ETAGS@
+EXEEXT = @EXEEXT@
+EXPECT = @EXPECT@
+GDB = @GDB@
+HAVE_CXX17 = @HAVE_CXX17@
+INCDIR = @INCDIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MISCDIR = @MISCDIR@
+MKDIR_P = @MKDIR_P@
+OBJDIR = @OBJDIR@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+QUICKCHECKDIR = @QUICKCHECKDIR@
+RUNTEST_BIN = @RUNTEST_BIN@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPHINX = @SPHINX@
+STRIP = @STRIP@
+VALGRIND = @VALGRIND@
+VALGRIND_ENABLED = @VALGRIND_ENABLED@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build_alias = @build_alias@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host_alias = @host_alias@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+valgrind_enabled_tools = @valgrind_enabled_tools@
+valgrind_tools = @valgrind_tools@
+headersdir = $(includedir)/mdds-@API_VERSION@/mdds/multi_type_vector/soa
+headers_HEADERS = \
+ block_util.hpp \
+ iterator.hpp \
+ main_def.inl \
+ main.hpp
+
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign include/mdds/multi_type_vector/soa/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign include/mdds/multi_type_vector/soa/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-headersHEADERS: $(headers_HEADERS)
+ @$(NORMAL_INSTALL)
+ @list='$(headers_HEADERS)'; test -n "$(headersdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(headersdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(headersdir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(headersdir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(headersdir)" || exit $$?; \
+ done
+
+uninstall-headersHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(headers_HEADERS)'; test -n "$(headersdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(headersdir)'; $(am__uninstall_files_from_dir)
+check-valgrind-local:
+check-valgrind-memcheck-local:
+check-valgrind-helgrind-local:
+check-valgrind-drd-local:
+check-valgrind-sgcheck-local:
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(HEADERS)
+installdirs:
+ for dir in "$(DESTDIR)$(headersdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+check-valgrind: check-valgrind-am
+
+check-valgrind-am: check-valgrind-local
+
+check-valgrind-drd: check-valgrind-drd-am
+
+check-valgrind-drd-am: check-valgrind-drd-local
+
+check-valgrind-helgrind: check-valgrind-helgrind-am
+
+check-valgrind-helgrind-am: check-valgrind-helgrind-local
+
+check-valgrind-memcheck: check-valgrind-memcheck-am
+
+check-valgrind-memcheck-am: check-valgrind-memcheck-local
+
+check-valgrind-sgcheck: check-valgrind-sgcheck-am
+
+check-valgrind-sgcheck-am: check-valgrind-sgcheck-local
+
+clean: clean-am
+
+clean-am: clean-generic mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-headersHEADERS
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-headersHEADERS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am check check-am check-valgrind-am \
+ check-valgrind-drd-am check-valgrind-drd-local \
+ check-valgrind-helgrind-am check-valgrind-helgrind-local \
+ check-valgrind-local check-valgrind-memcheck-am \
+ check-valgrind-memcheck-local check-valgrind-sgcheck-am \
+ check-valgrind-sgcheck-local clean clean-generic cscopelist-am \
+ ctags ctags-am distclean distclean-generic distclean-tags \
+ distdir dvi dvi-am html html-am info info-am install \
+ install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am \
+ install-headersHEADERS install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-generic pdf \
+ pdf-am ps ps-am tags tags-am uninstall uninstall-am \
+ uninstall-headersHEADERS
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/include/mdds/multi_type_vector/soa/block_util.hpp b/include/mdds/multi_type_vector/soa/block_util.hpp
new file mode 100644
index 0000000..62f1926
--- /dev/null
+++ b/include/mdds/multi_type_vector/soa/block_util.hpp
@@ -0,0 +1,621 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * Copyright (c) 2021 Kohei Yoshida
+ *
+ * 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, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ ************************************************************************/
+
+#ifndef INCLUDED_MDDS_MULTI_TYPE_VECTOR_DIR_SOA_BLOCK_UTIL_HPP
+#define INCLUDED_MDDS_MULTI_TYPE_VECTOR_DIR_SOA_BLOCK_UTIL_HPP
+
+#include "mdds/global.hpp"
+#include "../types.hpp"
+
+#if defined(__SSE2__)
+#include <emmintrin.h>
+#endif
+#if defined(__AVX2__)
+#include <immintrin.h>
+#endif
+
+namespace mdds { namespace mtv { namespace soa { namespace detail {
+
+template<typename Blks, lu_factor_t F>
+struct adjust_block_positions
+{
+ void operator()(Blks& /*block_store*/, int64_t /*start_block_index*/, int64_t /*delta*/) const
+ {
+ static_assert(
+ mdds::detail::invalid_static_int<F>, "The loop-unrolling factor must be one of 0, 4, 8, 16, or 32.");
+ }
+};
+
+template<typename Blks>
+struct adjust_block_positions<Blks, lu_factor_t::none>
+{
+ void operator()(Blks& block_store, int64_t start_block_index, int64_t delta) const
+ {
+ int64_t n = block_store.positions.size();
+
+ if (start_block_index >= n)
+ return;
+
+#if MDDS_USE_OPENMP
+#pragma omp parallel for
+#endif
+ for (int64_t i = start_block_index; i < n; ++i)
+ block_store.positions[i] += delta;
+ }
+};
+
+template<typename Blks>
+struct adjust_block_positions<Blks, lu_factor_t::lu4>
+{
+ void operator()(Blks& block_store, int64_t start_block_index, int64_t delta) const
+ {
+ int64_t n = block_store.positions.size();
+
+ if (start_block_index >= n)
+ return;
+
+ // Ensure that the section length is divisible by 4.
+ int64_t len = n - start_block_index;
+ int64_t rem = len & 3; // % 4
+ len -= rem;
+ len += start_block_index;
+#if MDDS_USE_OPENMP
+#pragma omp parallel for
+#endif
+ for (int64_t i = start_block_index; i < len; i += 4)
+ {
+ block_store.positions[i + 0] += delta;
+ block_store.positions[i + 1] += delta;
+ block_store.positions[i + 2] += delta;
+ block_store.positions[i + 3] += delta;
+ }
+
+ rem += len;
+ for (int64_t i = len; i < rem; ++i)
+ block_store.positions[i] += delta;
+ }
+};
+
+template<typename Blks>
+struct adjust_block_positions<Blks, lu_factor_t::lu8>
+{
+ void operator()(Blks& block_store, int64_t start_block_index, int64_t delta) const
+ {
+ int64_t n = block_store.positions.size();
+
+ if (start_block_index >= n)
+ return;
+
+ // Ensure that the section length is divisible by 8.
+ int64_t len = n - start_block_index;
+ int64_t rem = len & 7; // % 8
+ len -= rem;
+ len += start_block_index;
+#if MDDS_USE_OPENMP
+#pragma omp parallel for
+#endif
+ for (int64_t i = start_block_index; i < len; i += 8)
+ {
+ block_store.positions[i + 0] += delta;
+ block_store.positions[i + 1] += delta;
+ block_store.positions[i + 2] += delta;
+ block_store.positions[i + 3] += delta;
+ block_store.positions[i + 4] += delta;
+ block_store.positions[i + 5] += delta;
+ block_store.positions[i + 6] += delta;
+ block_store.positions[i + 7] += delta;
+ }
+
+ rem += len;
+ for (int64_t i = len; i < rem; ++i)
+ block_store.positions[i] += delta;
+ }
+};
+
+template<typename Blks>
+struct adjust_block_positions<Blks, lu_factor_t::lu16>
+{
+ void operator()(Blks& block_store, int64_t start_block_index, int64_t delta) const
+ {
+ int64_t n = block_store.positions.size();
+
+ if (start_block_index >= n)
+ return;
+
+ // Ensure that the section length is divisible by 16.
+ int64_t len = n - start_block_index;
+ int64_t rem = len & 15; // % 16
+ len -= rem;
+ len += start_block_index;
+#if MDDS_USE_OPENMP
+#pragma omp parallel for
+#endif
+ for (int64_t i = start_block_index; i < len; i += 16)
+ {
+ block_store.positions[i + 0] += delta;
+ block_store.positions[i + 1] += delta;
+ block_store.positions[i + 2] += delta;
+ block_store.positions[i + 3] += delta;
+ block_store.positions[i + 4] += delta;
+ block_store.positions[i + 5] += delta;
+ block_store.positions[i + 6] += delta;
+ block_store.positions[i + 7] += delta;
+ block_store.positions[i + 8] += delta;
+ block_store.positions[i + 9] += delta;
+ block_store.positions[i + 10] += delta;
+ block_store.positions[i + 11] += delta;
+ block_store.positions[i + 12] += delta;
+ block_store.positions[i + 13] += delta;
+ block_store.positions[i + 14] += delta;
+ block_store.positions[i + 15] += delta;
+ }
+
+ rem += len;
+ for (int64_t i = len; i < rem; ++i)
+ block_store.positions[i] += delta;
+ }
+};
+
+template<typename Blks>
+struct adjust_block_positions<Blks, lu_factor_t::lu32>
+{
+ void operator()(Blks& block_store, int64_t start_block_index, int64_t delta) const
+ {
+ int64_t n = block_store.positions.size();
+
+ if (start_block_index >= n)
+ return;
+
+ // Ensure that the section length is divisible by 32.
+ int64_t len = n - start_block_index;
+ int64_t rem = len & 31; // % 32
+ len -= rem;
+ len += start_block_index;
+#if MDDS_USE_OPENMP
+#pragma omp parallel for
+#endif
+ for (int64_t i = start_block_index; i < len; i += 32)
+ {
+ block_store.positions[i + 0] += delta;
+ block_store.positions[i + 1] += delta;
+ block_store.positions[i + 2] += delta;
+ block_store.positions[i + 3] += delta;
+ block_store.positions[i + 4] += delta;
+ block_store.positions[i + 5] += delta;
+ block_store.positions[i + 6] += delta;
+ block_store.positions[i + 7] += delta;
+ block_store.positions[i + 8] += delta;
+ block_store.positions[i + 9] += delta;
+ block_store.positions[i + 10] += delta;
+ block_store.positions[i + 11] += delta;
+ block_store.positions[i + 12] += delta;
+ block_store.positions[i + 13] += delta;
+ block_store.positions[i + 14] += delta;
+ block_store.positions[i + 15] += delta;
+ block_store.positions[i + 16] += delta;
+ block_store.positions[i + 17] += delta;
+ block_store.positions[i + 18] += delta;
+ block_store.positions[i + 19] += delta;
+ block_store.positions[i + 20] += delta;
+ block_store.positions[i + 21] += delta;
+ block_store.positions[i + 22] += delta;
+ block_store.positions[i + 23] += delta;
+ block_store.positions[i + 24] += delta;
+ block_store.positions[i + 25] += delta;
+ block_store.positions[i + 26] += delta;
+ block_store.positions[i + 27] += delta;
+ block_store.positions[i + 28] += delta;
+ block_store.positions[i + 29] += delta;
+ block_store.positions[i + 30] += delta;
+ block_store.positions[i + 31] += delta;
+ }
+
+ rem += len;
+ for (int64_t i = len; i < rem; ++i)
+ block_store.positions[i] += delta;
+ }
+};
+
+#if defined(__SSE2__)
+
+template<typename Blks>
+struct adjust_block_positions<Blks, lu_factor_t::sse2_x64>
+{
+ void operator()(Blks& block_store, int64_t start_block_index, int64_t delta) const
+ {
+ static_assert(
+ sizeof(typename decltype(block_store.positions)::value_type) == 8,
+ "This code works only when the position values are 64-bit wide.");
+
+ int64_t n = block_store.positions.size();
+
+ if (start_block_index >= n)
+ return;
+
+ // Ensure that the section length is divisible by 2.
+ int64_t len = n - start_block_index;
+ bool odd = len & 1;
+ if (odd)
+ len -= 1;
+
+ len += start_block_index;
+
+ __m128i right = _mm_set_epi64x(delta, delta);
+
+#if MDDS_USE_OPENMP
+#pragma omp parallel for
+#endif
+ for (int64_t i = start_block_index; i < len; i += 2)
+ {
+ __m128i* dst = (__m128i*)&block_store.positions[i];
+ _mm_storeu_si128(dst, _mm_add_epi64(_mm_loadu_si128(dst), right));
+ }
+
+ if (odd)
+ block_store.positions[len] += delta;
+ }
+};
+
+template<typename Blks>
+struct adjust_block_positions<Blks, lu_factor_t::sse2_x64_lu4>
+{
+ void operator()(Blks& block_store, int64_t start_block_index, int64_t delta) const
+ {
+ static_assert(
+ sizeof(typename decltype(block_store.positions)::value_type) == 8,
+ "This code works only when the position values are 64-bit wide.");
+
+ int64_t n = block_store.positions.size();
+
+ if (start_block_index >= n)
+ return;
+
+ // Ensure that the section length is divisible by 8.
+ int64_t len = n - start_block_index;
+ int64_t rem = len & 7; // % 8
+ len -= rem;
+ len += start_block_index;
+
+ __m128i right = _mm_set_epi64x(delta, delta);
+
+#if MDDS_USE_OPENMP
+#pragma omp parallel for
+#endif
+ for (int64_t i = start_block_index; i < len; i += 8)
+ {
+ __m128i* dst0 = (__m128i*)&block_store.positions[i];
+ _mm_storeu_si128(dst0, _mm_add_epi64(_mm_loadu_si128(dst0), right));
+
+ __m128i* dst2 = (__m128i*)&block_store.positions[i + 2];
+ _mm_storeu_si128(dst2, _mm_add_epi64(_mm_loadu_si128(dst2), right));
+
+ __m128i* dst4 = (__m128i*)&block_store.positions[i + 4];
+ _mm_storeu_si128(dst4, _mm_add_epi64(_mm_loadu_si128(dst4), right));
+
+ __m128i* dst6 = (__m128i*)&block_store.positions[i + 6];
+ _mm_storeu_si128(dst6, _mm_add_epi64(_mm_loadu_si128(dst6), right));
+ }
+
+ rem += len;
+ for (int64_t i = len; i < rem; ++i)
+ block_store.positions[i] += delta;
+ }
+};
+
+template<typename Blks>
+struct adjust_block_positions<Blks, lu_factor_t::sse2_x64_lu8>
+{
+ void operator()(Blks& block_store, int64_t start_block_index, int64_t delta) const
+ {
+ static_assert(
+ sizeof(typename decltype(block_store.positions)::value_type) == 8,
+ "This code works only when the position values are 64-bit wide.");
+
+ int64_t n = block_store.positions.size();
+
+ if (start_block_index >= n)
+ return;
+
+ // Ensure that the section length is divisible by 16.
+ int64_t len = n - start_block_index;
+ int64_t rem = len & 15; // % 16
+ len -= rem;
+ len += start_block_index;
+
+ __m128i right = _mm_set_epi64x(delta, delta);
+
+#if MDDS_USE_OPENMP
+#pragma omp parallel for
+#endif
+ for (int64_t i = start_block_index; i < len; i += 16)
+ {
+ __m128i* dst0 = (__m128i*)&block_store.positions[i];
+ _mm_storeu_si128(dst0, _mm_add_epi64(_mm_loadu_si128(dst0), right));
+
+ __m128i* dst2 = (__m128i*)&block_store.positions[i + 2];
+ _mm_storeu_si128(dst2, _mm_add_epi64(_mm_loadu_si128(dst2), right));
+
+ __m128i* dst4 = (__m128i*)&block_store.positions[i + 4];
+ _mm_storeu_si128(dst4, _mm_add_epi64(_mm_loadu_si128(dst4), right));
+
+ __m128i* dst6 = (__m128i*)&block_store.positions[i + 6];
+ _mm_storeu_si128(dst6, _mm_add_epi64(_mm_loadu_si128(dst6), right));
+
+ __m128i* dst8 = (__m128i*)&block_store.positions[i + 8];
+ _mm_storeu_si128(dst8, _mm_add_epi64(_mm_loadu_si128(dst8), right));
+
+ __m128i* dst10 = (__m128i*)&block_store.positions[i + 10];
+ _mm_storeu_si128(dst10, _mm_add_epi64(_mm_loadu_si128(dst10), right));
+
+ __m128i* dst12 = (__m128i*)&block_store.positions[i + 12];
+ _mm_storeu_si128(dst12, _mm_add_epi64(_mm_loadu_si128(dst12), right));
+
+ __m128i* dst14 = (__m128i*)&block_store.positions[i + 14];
+ _mm_storeu_si128(dst14, _mm_add_epi64(_mm_loadu_si128(dst14), right));
+ }
+
+ rem += len;
+ for (int64_t i = len; i < rem; ++i)
+ block_store.positions[i] += delta;
+ }
+};
+
+template<typename Blks>
+struct adjust_block_positions<Blks, lu_factor_t::sse2_x64_lu16>
+{
+ void operator()(Blks& block_store, int64_t start_block_index, int64_t delta) const
+ {
+ static_assert(
+ sizeof(typename decltype(block_store.positions)::value_type) == 8,
+ "This code works only when the position values are 64-bit wide.");
+
+ int64_t n = block_store.positions.size();
+
+ if (start_block_index >= n)
+ return;
+
+ // Ensure that the section length is divisible by 32.
+ int64_t len = n - start_block_index;
+ int64_t rem = len & 31; // % 32
+ len -= rem;
+ len += start_block_index;
+
+ __m128i right = _mm_set_epi64x(delta, delta);
+
+#if MDDS_USE_OPENMP
+#pragma omp parallel for
+#endif
+ for (int64_t i = start_block_index; i < len; i += 32)
+ {
+ __m128i* dst0 = (__m128i*)&block_store.positions[i];
+ _mm_storeu_si128(dst0, _mm_add_epi64(_mm_loadu_si128(dst0), right));
+
+ __m128i* dst2 = (__m128i*)&block_store.positions[i + 2];
+ _mm_storeu_si128(dst2, _mm_add_epi64(_mm_loadu_si128(dst2), right));
+
+ __m128i* dst4 = (__m128i*)&block_store.positions[i + 4];
+ _mm_storeu_si128(dst4, _mm_add_epi64(_mm_loadu_si128(dst4), right));
+
+ __m128i* dst6 = (__m128i*)&block_store.positions[i + 6];
+ _mm_storeu_si128(dst6, _mm_add_epi64(_mm_loadu_si128(dst6), right));
+
+ __m128i* dst8 = (__m128i*)&block_store.positions[i + 8];
+ _mm_storeu_si128(dst8, _mm_add_epi64(_mm_loadu_si128(dst8), right));
+
+ __m128i* dst10 = (__m128i*)&block_store.positions[i + 10];
+ _mm_storeu_si128(dst10, _mm_add_epi64(_mm_loadu_si128(dst10), right));
+
+ __m128i* dst12 = (__m128i*)&block_store.positions[i + 12];
+ _mm_storeu_si128(dst12, _mm_add_epi64(_mm_loadu_si128(dst12), right));
+
+ __m128i* dst14 = (__m128i*)&block_store.positions[i + 14];
+ _mm_storeu_si128(dst14, _mm_add_epi64(_mm_loadu_si128(dst14), right));
+
+ __m128i* dst16 = (__m128i*)&block_store.positions[i + 16];
+ _mm_storeu_si128(dst16, _mm_add_epi64(_mm_loadu_si128(dst16), right));
+
+ __m128i* dst18 = (__m128i*)&block_store.positions[i + 18];
+ _mm_storeu_si128(dst18, _mm_add_epi64(_mm_loadu_si128(dst18), right));
+
+ __m128i* dst20 = (__m128i*)&block_store.positions[i + 20];
+ _mm_storeu_si128(dst20, _mm_add_epi64(_mm_loadu_si128(dst20), right));
+
+ __m128i* dst22 = (__m128i*)&block_store.positions[i + 22];
+ _mm_storeu_si128(dst22, _mm_add_epi64(_mm_loadu_si128(dst22), right));
+
+ __m128i* dst24 = (__m128i*)&block_store.positions[i + 24];
+ _mm_storeu_si128(dst24, _mm_add_epi64(_mm_loadu_si128(dst24), right));
+
+ __m128i* dst26 = (__m128i*)&block_store.positions[i + 26];
+ _mm_storeu_si128(dst26, _mm_add_epi64(_mm_loadu_si128(dst26), right));
+
+ __m128i* dst28 = (__m128i*)&block_store.positions[i + 28];
+ _mm_storeu_si128(dst28, _mm_add_epi64(_mm_loadu_si128(dst28), right));
+
+ __m128i* dst30 = (__m128i*)&block_store.positions[i + 30];
+ _mm_storeu_si128(dst30, _mm_add_epi64(_mm_loadu_si128(dst30), right));
+ }
+
+ rem += len;
+ for (int64_t i = len; i < rem; ++i)
+ block_store.positions[i] += delta;
+ }
+};
+
+#endif // __SSE2__
+
+#if defined(__AVX2__)
+
+template<typename Blks>
+struct adjust_block_positions<Blks, lu_factor_t::avx2_x64>
+{
+ void operator()(Blks& block_store, int64_t start_block_index, int64_t delta) const
+ {
+ static_assert(
+ sizeof(typename decltype(block_store.positions)::value_type) == 8,
+ "This code works only when the position values are 64-bit wide.");
+
+ int64_t n = block_store.positions.size();
+
+ if (start_block_index >= n)
+ return;
+
+ // Ensure that the section length is divisible by 4.
+ int64_t len = n - start_block_index;
+ int64_t rem = len & 3; // % 4
+ len -= rem;
+ len += start_block_index;
+
+ __m256i right = _mm256_set1_epi64x(delta);
+
+#if MDDS_USE_OPENMP
+#pragma omp parallel for
+#endif
+ for (int64_t i = start_block_index; i < len; i += 4)
+ {
+ __m256i* dst = (__m256i*)&block_store.positions[i];
+ _mm256_storeu_si256(dst, _mm256_add_epi64(_mm256_loadu_si256(dst), right));
+ }
+
+ rem += len;
+ for (int64_t i = len; i < rem; ++i)
+ block_store.positions[i] += delta;
+ }
+};
+
+template<typename Blks>
+struct adjust_block_positions<Blks, lu_factor_t::avx2_x64_lu4>
+{
+ void operator()(Blks& block_store, int64_t start_block_index, int64_t delta) const
+ {
+ static_assert(
+ sizeof(typename decltype(block_store.positions)::value_type) == 8,
+ "This code works only when the position values are 64-bit wide.");
+
+ int64_t n = block_store.positions.size();
+
+ if (start_block_index >= n)
+ return;
+
+ // Ensure that the section length is divisible by 16.
+ int64_t len = n - start_block_index;
+ int64_t rem = len & 15; // % 16
+ len -= rem;
+ len += start_block_index;
+
+ __m256i right = _mm256_set1_epi64x(delta);
+
+#if MDDS_USE_OPENMP
+#pragma omp parallel for
+#endif
+ for (int64_t i = start_block_index; i < len; i += 16)
+ {
+ __m256i* dst = (__m256i*)&block_store.positions[i];
+ _mm256_storeu_si256(dst, _mm256_add_epi64(_mm256_loadu_si256(dst), right));
+
+ __m256i* dst4 = (__m256i*)&block_store.positions[i + 4];
+ _mm256_storeu_si256(dst4, _mm256_add_epi64(_mm256_loadu_si256(dst4), right));
+
+ __m256i* dst8 = (__m256i*)&block_store.positions[i + 8];
+ _mm256_storeu_si256(dst8, _mm256_add_epi64(_mm256_loadu_si256(dst8), right));
+
+ __m256i* dst12 = (__m256i*)&block_store.positions[i + 12];
+ _mm256_storeu_si256(dst12, _mm256_add_epi64(_mm256_loadu_si256(dst12), right));
+ }
+
+ rem += len;
+ for (int64_t i = len; i < rem; ++i)
+ block_store.positions[i] += delta;
+ }
+};
+
+template<typename Blks>
+struct adjust_block_positions<Blks, lu_factor_t::avx2_x64_lu8>
+{
+ void operator()(Blks& block_store, int64_t start_block_index, int64_t delta) const
+ {
+ static_assert(
+ sizeof(typename decltype(block_store.positions)::value_type) == 8,
+ "This code works only when the position values are 64-bit wide.");
+
+ int64_t n = block_store.positions.size();
+
+ if (start_block_index >= n)
+ return;
+
+ // Ensure that the section length is divisible by 16.
+ int64_t len = n - start_block_index;
+ int64_t rem = len & 31; // % 32
+ len -= rem;
+ len += start_block_index;
+
+ __m256i right = _mm256_set1_epi64x(delta);
+
+#if MDDS_USE_OPENMP
+#pragma omp parallel for
+#endif
+ for (int64_t i = start_block_index; i < len; i += 32)
+ {
+ __m256i* dst = (__m256i*)&block_store.positions[i];
+ _mm256_storeu_si256(dst, _mm256_add_epi64(_mm256_loadu_si256(dst), right));
+
+ __m256i* dst4 = (__m256i*)&block_store.positions[i + 4];
+ _mm256_storeu_si256(dst4, _mm256_add_epi64(_mm256_loadu_si256(dst4), right));
+
+ __m256i* dst8 = (__m256i*)&block_store.positions[i + 8];
+ _mm256_storeu_si256(dst8, _mm256_add_epi64(_mm256_loadu_si256(dst8), right));
+
+ __m256i* dst12 = (__m256i*)&block_store.positions[i + 12];
+ _mm256_storeu_si256(dst12, _mm256_add_epi64(_mm256_loadu_si256(dst12), right));
+
+ __m256i* dst16 = (__m256i*)&block_store.positions[i + 16];
+ _mm256_storeu_si256(dst16, _mm256_add_epi64(_mm256_loadu_si256(dst16), right));
+
+ __m256i* dst20 = (__m256i*)&block_store.positions[i + 20];
+ _mm256_storeu_si256(dst20, _mm256_add_epi64(_mm256_loadu_si256(dst20), right));
+
+ __m256i* dst24 = (__m256i*)&block_store.positions[i + 24];
+ _mm256_storeu_si256(dst24, _mm256_add_epi64(_mm256_loadu_si256(dst24), right));
+
+ __m256i* dst28 = (__m256i*)&block_store.positions[i + 28];
+ _mm256_storeu_si256(dst28, _mm256_add_epi64(_mm256_loadu_si256(dst28), right));
+ }
+
+ rem += len;
+ for (int64_t i = len; i < rem; ++i)
+ block_store.positions[i] += delta;
+ }
+};
+
+#endif // __AVX2__
+
+}}}} // namespace mdds::mtv::soa::detail
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/mdds/multi_type_vector/soa/iterator.hpp b/include/mdds/multi_type_vector/soa/iterator.hpp
new file mode 100644
index 0000000..c8339c9
--- /dev/null
+++ b/include/mdds/multi_type_vector/soa/iterator.hpp
@@ -0,0 +1,414 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * Copyright (c) 2021 Kohei Yoshida
+ *
+ * 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, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ ************************************************************************/
+
+#ifndef INCLUDED_MDDS_MULTI_TYPE_VECTOR_DIR_SOA_ITERATOR_HPP
+#define INCLUDED_MDDS_MULTI_TYPE_VECTOR_DIR_SOA_ITERATOR_HPP
+
+#include "../iterator_node.hpp"
+
+#include <ostream>
+
+namespace mdds { namespace mtv { namespace soa { namespace detail {
+
+/**
+ * Common base for both const and non-const iterators to handle traversal of
+ * the internal source iterators.
+ *
+ * Its protected inc() and dec() methods have non-const return type, and the
+ * derived classes wrap them and return values with their respective const
+ * modifiers.
+ *
+ * The trait struct needs to have the following static member types:
+ * <ul>
+ * <li><code>parent</code></li>
+ * <li><code>positions_type</code></li>
+ * <li><code>sizes_type</code></li>
+ * <li><code>element_blocks_type</code></li>
+ * </ul>
+ */
+template<typename Traits>
+class iterator_updater
+{
+protected:
+ using parent_type = typename Traits::parent;
+ using positions_type = typename Traits::positions_type;
+ using sizes_type = typename Traits::sizes_type;
+ using element_blocks_type = typename Traits::element_blocks_type;
+ using size_type = typename Traits::parent::size_type;
+
+ using node = mdds::detail::mtv::iterator_value_node<parent_type, size_type>;
+
+ using positions_iterator_type = typename Traits::positions_iterator_type;
+ using sizes_iterator_type = typename Traits::sizes_iterator_type;
+ using element_blocks_iterator_type = typename Traits::element_blocks_iterator_type;
+
+ /**
+ * This struct groups together the iterators for the three array types for
+ * easy synchronized traversal of the arrays.
+ */
+ struct grouped_iterator_type
+ {
+ positions_iterator_type position_iterator;
+ sizes_iterator_type size_iterator;
+ element_blocks_iterator_type element_block_iterator;
+
+ void inc()
+ {
+ ++position_iterator;
+ ++size_iterator;
+ ++element_block_iterator;
+ }
+
+ void dec()
+ {
+ --position_iterator;
+ --size_iterator;
+ --element_block_iterator;
+ }
+
+ bool operator==(const grouped_iterator_type& other) const
+ {
+ return position_iterator == other.position_iterator && size_iterator == other.size_iterator &&
+ element_block_iterator == other.element_block_iterator;
+ }
+
+ bool operator!=(const grouped_iterator_type& other) const
+ {
+ return !operator==(other);
+ }
+
+ grouped_iterator_type() = default;
+
+ grouped_iterator_type(
+ const positions_iterator_type& itr_pos, const sizes_iterator_type& itr_size,
+ const element_blocks_iterator_type& itr_elem_blocks)
+ : position_iterator(itr_pos), size_iterator(itr_size), element_block_iterator(itr_elem_blocks)
+ {}
+ };
+
+ node m_cur_node;
+ grouped_iterator_type m_pos;
+ grouped_iterator_type m_end;
+
+ iterator_updater() : m_cur_node(nullptr, 0)
+ {}
+
+ iterator_updater(
+ const grouped_iterator_type& pos, const grouped_iterator_type& end, const parent_type* parent,
+ size_type block_index)
+ : m_cur_node(parent, block_index), m_pos(pos), m_end(end)
+ {
+ if (m_pos != m_end)
+ update_node();
+ }
+
+ iterator_updater(
+ const positions_iterator_type& positions_pos, const sizes_iterator_type& sizes_pos,
+ const element_blocks_iterator_type& eb_pos, const positions_iterator_type& positions_end,
+ const sizes_iterator_type& sizes_end, const element_blocks_iterator_type& eb_end, const parent_type* parent,
+ size_type block_index)
+ : iterator_updater({positions_pos, sizes_pos, eb_pos}, {positions_end, sizes_end, eb_end}, parent, block_index)
+ {}
+
+ iterator_updater(const iterator_updater& other)
+ : m_cur_node(other.m_cur_node), m_pos(other.m_pos), m_end(other.m_end)
+ {}
+
+ void update_node()
+ {
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ if (m_pos == m_end)
+ throw general_error("Current node position should never equal the end position during node update.");
+#endif
+
+ m_cur_node.position = *m_pos.position_iterator;
+ m_cur_node.size = *m_pos.size_iterator;
+ m_cur_node.data = *m_pos.element_block_iterator;
+
+ if (m_cur_node.data)
+ m_cur_node.type = mdds::mtv::get_block_type(*m_cur_node.data);
+ else
+ m_cur_node.type = mdds::mtv::element_type_empty;
+ }
+
+ node* inc()
+ {
+ m_pos.inc();
+ if (m_pos == m_end)
+ return nullptr;
+
+ update_node();
+ return &m_cur_node;
+ }
+
+ node* dec()
+ {
+ m_pos.dec();
+ update_node();
+ return &m_cur_node;
+ }
+
+ void _print_state(std::ostream& os) const
+ {
+ auto prev_flags = os.flags();
+ os << "parent=" << std::hex << m_cur_node.__private_data.parent
+ << "; block-index=" << m_cur_node.__private_data.block_index << "; position=" << m_cur_node.position
+ << "; size=" << m_cur_node.size << "; type=" << m_cur_node.type << "; data=" << m_cur_node.data;
+ os.flags(prev_flags);
+ }
+
+public:
+ bool operator==(const iterator_updater& other) const
+ {
+ if (m_pos != m_end && other.m_pos != other.m_end)
+ {
+ // TODO: Set hard-coded values to the current node for the end
+ // position nodes to remove this if block.
+ if (m_cur_node != other.m_cur_node)
+ return false;
+ }
+ return m_pos == other.m_pos && m_end == other.m_end;
+ }
+
+ bool operator!=(const iterator_updater& other) const
+ {
+ return !operator==(other);
+ }
+
+ iterator_updater& operator=(const iterator_updater& other)
+ {
+ m_cur_node = other.m_cur_node;
+ m_pos = other.m_pos;
+ m_end = other.m_end;
+ return *this;
+ }
+
+ void swap(iterator_updater& other)
+ {
+ m_cur_node.swap(other.m_cur_node);
+ std::swap(m_pos, other.m_pos);
+ std::swap(m_end, other.m_end);
+ }
+
+ const node& get_node() const
+ {
+ return m_cur_node;
+ }
+ const grouped_iterator_type& get_pos() const
+ {
+ return m_pos;
+ }
+ const grouped_iterator_type& get_end() const
+ {
+ return m_end;
+ }
+};
+
+template<typename Traits>
+class iterator_base : public iterator_updater<Traits>
+{
+ using parent_type = typename Traits::parent;
+ using node_update_func = typename Traits::private_data_update;
+ using updater = iterator_updater<Traits>;
+
+ using grouped_iterator_type = typename updater::grouped_iterator_type;
+ using size_type = typename updater::size_type;
+
+ using updater::dec;
+ using updater::inc;
+ using updater::m_cur_node;
+
+public:
+ using updater::get_end;
+ using updater::get_pos;
+
+ // iterator traits
+ using value_type = typename updater::node;
+ using pointer = value_type*;
+ using reference = value_type&;
+ using difference_type = ptrdiff_t;
+ using iterator_category = std::bidirectional_iterator_tag;
+
+public:
+ iterator_base()
+ {}
+ iterator_base(
+ const grouped_iterator_type& pos, const grouped_iterator_type& end, const parent_type* parent,
+ size_type block_index)
+ : updater(pos, end, parent, block_index)
+ {}
+
+ value_type& operator*()
+ {
+ return m_cur_node;
+ }
+
+ const value_type& operator*() const
+ {
+ return m_cur_node;
+ }
+
+ value_type* operator->()
+ {
+ return &m_cur_node;
+ }
+
+ const value_type* operator->() const
+ {
+ return &m_cur_node;
+ }
+
+ iterator_base& operator++()
+ {
+ node_update_func::inc(m_cur_node);
+ inc();
+ return *this;
+ }
+
+ iterator_base& operator--()
+ {
+ dec();
+ node_update_func::dec(m_cur_node);
+ return *this;
+ }
+
+ void _print_state(std::ostream& os) const
+ {
+ os << "{iterator: ";
+ updater::_print_state(os);
+ os << "}";
+ }
+};
+
+template<typename Traits, typename NonConstItrBase>
+class const_iterator_base : public iterator_updater<Traits>
+{
+ using parent_type = typename Traits::parent;
+ using node_update_func = typename Traits::private_data_update;
+ using updater = iterator_updater<Traits>;
+
+ using grouped_iterator_type = typename updater::grouped_iterator_type;
+ using size_type = typename updater::size_type;
+
+ using updater::dec;
+ using updater::inc;
+ using updater::m_cur_node;
+
+public:
+ using updater::get_end;
+ using updater::get_pos;
+
+ using iterator_base = NonConstItrBase;
+
+ // iterator traits
+ using value_type = typename updater::node;
+ using pointer = value_type*;
+ using reference = value_type&;
+ using difference_type = ptrdiff_t;
+ using iterator_category = std::bidirectional_iterator_tag;
+
+public:
+ const_iterator_base() : updater()
+ {}
+ const_iterator_base(
+ const grouped_iterator_type& pos, const grouped_iterator_type& end, const parent_type* parent,
+ size_type block_index)
+ : updater(pos, end, parent, block_index)
+ {}
+
+ /**
+ * Take the non-const iterator counterpart to create a const iterator.
+ */
+ const_iterator_base(const iterator_base& other)
+ : updater(
+ other.get_pos().position_iterator, other.get_pos().size_iterator, other.get_pos().element_block_iterator,
+ other.get_end().position_iterator, other.get_end().size_iterator, other.get_end().element_block_iterator,
+ other.get_node().__private_data.parent, other.get_node().__private_data.block_index)
+ {}
+
+ const value_type& operator*() const
+ {
+ return m_cur_node;
+ }
+
+ const value_type* operator->() const
+ {
+ return &m_cur_node;
+ }
+
+ const_iterator_base& operator++()
+ {
+ node_update_func::inc(m_cur_node);
+ inc();
+ return *this;
+ }
+
+ const_iterator_base& operator--()
+ {
+ dec();
+ node_update_func::dec(m_cur_node);
+ return *this;
+ }
+
+ bool operator==(const const_iterator_base& other) const
+ {
+ return updater::operator==(other);
+ }
+
+ bool operator!=(const const_iterator_base& other) const
+ {
+ return updater::operator!=(other);
+ }
+
+ void _print_state(std::ostream& os) const
+ {
+ os << "(const-iterator: ";
+ updater::_print_state(os);
+ os << ")";
+ }
+};
+
+template<typename Traits>
+std::ostream& operator<<(std::ostream& os, const iterator_base<Traits>& it)
+{
+ it._print_state(os);
+ return os;
+}
+
+template<typename Traits, typename NonConstItrBase>
+std::ostream& operator<<(std::ostream& os, const const_iterator_base<Traits, NonConstItrBase>& it)
+{
+ it._print_state(os);
+ return os;
+}
+
+}}}} // namespace mdds::mtv::soa::detail
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/mdds/multi_type_vector/soa/main.hpp b/include/mdds/multi_type_vector/soa/main.hpp
new file mode 100644
index 0000000..0519b35
--- /dev/null
+++ b/include/mdds/multi_type_vector/soa/main.hpp
@@ -0,0 +1,1449 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * Copyright (c) 2021 Kohei Yoshida
+ *
+ * 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, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ ************************************************************************/
+
+#ifndef INCLUDED_MDDS_MULTI_TYPE_VECTOR_SOA_MAIN_HPP
+#define INCLUDED_MDDS_MULTI_TYPE_VECTOR_SOA_MAIN_HPP
+
+#include "../../global.hpp"
+#include "../types.hpp"
+#include "../util.hpp"
+#include "./block_util.hpp"
+#include "./iterator.hpp"
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+#include <iostream>
+#endif
+
+namespace mdds { namespace mtv { namespace soa {
+
+/**
+ * Multi-type vector consists of a series of one or more blocks, and each
+ * block may either be empty, or stores a series of non-empty elements of
+ * identical type. These blocks collectively represent a single logical
+ * one-dimensional array that may store elements of different types. It is
+ * guaranteed that the block types of neighboring blocks are always
+ * different.
+ *
+ * Structurally, the primary array stores block instances whose types
+ * are of <code>value_type</code>, which in turn consists of the following
+ * data members:
+ *
+ * <ul>
+ * <li><code>type</code> which indicates the block type,</li>
+ * <li><code>position</code> which stores the logical position of the
+ * first element of the block,</li>
+ * <li><code>size</code> which stores the logical size of the block,
+ * and</li>
+ * <li><code>data</code> which stores the pointer to a secondary array
+ * (a.k.a. element block) which stores the actual element values, or
+ * <code>nullptr</code> in case the block represents an empty segment.</li>
+ * </ul>
+ *
+ * This variant implements a structure-of-arrays (SoA) storage.
+ *
+ * @see mdds::mtv::soa::multi_type_vector::value_type
+ */
+template<typename Traits = mdds::mtv::default_traits>
+class multi_type_vector
+{
+public:
+ using size_type = std::size_t;
+
+ using element_block_type = mdds::mtv::base_element_block;
+ using element_category_type = mdds::mtv::element_t;
+ using block_funcs = typename Traits::block_funcs;
+
+ /**
+ * Optional event handler function structure, whose functions get called
+ * at specific events. The following events are currently supported:
+ *
+ * <ul>
+ * <li><code>element_block_acquired</code> - this gets called whenever
+ * the container acquires a new element block either as a result of a new
+ * element block creation or a tranfer of an existing
+ * element block from another container.</li>
+ * <li><code>element_block_released</code> - this gets called whenever
+ * the container releases an existing element block either because
+ * the block gets deleted or gets transferred to another container.</li>
+ * </ul>
+ *
+ * @see mdds::mtv::empty_event_func for the precise function signatures of
+ * the event handler functions.
+ */
+ using event_func = typename Traits::event_func;
+
+private:
+ struct block_slot_type
+ {
+ size_type position = 0;
+ size_type size = 0;
+ element_block_type* element_block = nullptr;
+
+ block_slot_type()
+ {}
+ block_slot_type(size_type _position, size_type _size) : position(_position), size(_size)
+ {}
+ };
+
+ struct blocks_type
+ {
+ std::vector<size_type> positions;
+ std::vector<size_type> sizes;
+ std::vector<element_block_type*> element_blocks;
+
+ blocks_type();
+ blocks_type(const blocks_type& other);
+ blocks_type(blocks_type&& other);
+
+ void pop_back()
+ {
+ positions.pop_back();
+ sizes.pop_back();
+ element_blocks.pop_back();
+ }
+
+ void push_back(size_type pos, size_type size, element_block_type* data)
+ {
+ positions.push_back(pos);
+ sizes.push_back(size);
+ element_blocks.push_back(data);
+ }
+
+ void push_back(const block_slot_type& slot)
+ {
+ positions.push_back(slot.position);
+ sizes.push_back(slot.size);
+ element_blocks.push_back(slot.element_block);
+ }
+
+ void erase(size_type index);
+ void erase(size_type index, size_type size);
+ void insert(size_type index, size_type size);
+ void insert(size_type index, size_type pos, size_type size, element_block_type* data);
+ void insert(size_type index, const blocks_type& new_blocks);
+
+ /**
+ * Calculate the position of specified block based on the position and
+ * size of the previous block.
+ *
+ * @param index index of the block to calculate the position for.
+ */
+ void calc_block_position(size_type index);
+
+ size_type calc_next_block_position(size_type index);
+
+ void swap(size_type index1, size_type index2);
+
+ void swap(blocks_type& other);
+
+ void reserve(size_type n);
+
+ bool equals(const blocks_type& other) const;
+
+ void clear();
+
+ void check_integrity() const;
+ };
+
+ struct blocks_to_transfer
+ {
+ blocks_type blocks;
+ size_type insert_index = 0;
+ };
+
+ struct iterator_trait
+ {
+ using parent = multi_type_vector;
+ using positions_type = std::vector<size_type>;
+ using sizes_type = std::vector<size_type>;
+ using element_blocks_type = std::vector<element_block_type*>;
+
+ using positions_iterator_type = typename positions_type::iterator;
+ using sizes_iterator_type = typename sizes_type::iterator;
+ using element_blocks_iterator_type = typename element_blocks_type::iterator;
+
+ using private_data_update = mdds::detail::mtv::private_data_forward_update<multi_type_vector, size_type>;
+ };
+
+ struct const_iterator_trait
+ {
+ using parent = multi_type_vector;
+ using positions_type = std::vector<size_type>;
+ using sizes_type = std::vector<size_type>;
+ using element_blocks_type = std::vector<element_block_type*>;
+
+ using positions_iterator_type = typename positions_type::const_iterator;
+ using sizes_iterator_type = typename sizes_type::const_iterator;
+ using element_blocks_iterator_type = typename element_blocks_type::const_iterator;
+
+ using private_data_update = mdds::detail::mtv::private_data_forward_update<multi_type_vector, size_type>;
+ };
+
+ struct reverse_iterator_trait
+ {
+ using parent = multi_type_vector;
+ using positions_type = std::vector<size_type>;
+ using sizes_type = std::vector<size_type>;
+ using element_blocks_type = std::vector<element_block_type*>;
+
+ using positions_iterator_type = typename positions_type::reverse_iterator;
+ using sizes_iterator_type = typename sizes_type::reverse_iterator;
+ using element_blocks_iterator_type = typename element_blocks_type::reverse_iterator;
+
+ using private_data_update = mdds::detail::mtv::private_data_no_update<multi_type_vector, size_type>;
+ };
+
+ struct const_reverse_iterator_trait
+ {
+ using parent = multi_type_vector;
+ using positions_type = std::vector<size_type>;
+ using sizes_type = std::vector<size_type>;
+ using element_blocks_type = std::vector<element_block_type*>;
+
+ using positions_iterator_type = typename positions_type::const_reverse_iterator;
+ using sizes_iterator_type = typename sizes_type::const_reverse_iterator;
+ using element_blocks_iterator_type = typename element_blocks_type::const_reverse_iterator;
+
+ using private_data_update = mdds::detail::mtv::private_data_no_update<multi_type_vector, size_type>;
+ };
+
+ struct element_block_deleter
+ {
+ void operator()(const element_block_type* p)
+ {
+ block_funcs::delete_block(p);
+ }
+ };
+
+public:
+ using iterator = detail::iterator_base<iterator_trait>;
+ using const_iterator = detail::const_iterator_base<const_iterator_trait, iterator>;
+
+ using reverse_iterator = detail::iterator_base<reverse_iterator_trait>;
+ using const_reverse_iterator = detail::const_iterator_base<const_reverse_iterator_trait, reverse_iterator>;
+
+ using position_type = std::pair<iterator, size_type>;
+ using const_position_type = std::pair<const_iterator, size_type>;
+
+ /**
+ * value_type is the type of a block stored in the primary array. It
+ * consists of the following data members:
+ *
+ * <ul>
+ * <li><code>type</code> which indicates the block type,</li>
+ * <li><code>position</code> which stores the logical position of the
+ * first element of the block,</li>
+ * <li><code>size</code> which stores the logical size of the block,
+ * and</li>
+ * <li><code>data</code> which stores the pointer to a secondary array
+ * (a.k.a. element block) which stores the actual element values, or
+ * <code>nullptr</code> in case the block represents an empty segment.</li>
+ * </ul>
+ */
+ using value_type = mdds::detail::mtv::iterator_value_node<multi_type_vector, size_type>;
+
+ /**
+ * Move the position object to the next logical position. Caller must
+ * ensure the the position object is valid.
+ *
+ * @param pos position object.
+ *
+ * @return position object that points to the next logical position.
+ */
+ static position_type next_position(const position_type& pos);
+
+ /**
+ * Increment or decrement the position object by specified steps. Caller
+ * must ensure the the position object is valid.
+ *
+ * @param pos position object.
+ * @param steps steps to advance the position object.
+ *
+ * @return position object that points to the new logical position.
+ */
+ static position_type advance_position(const position_type& pos, int steps);
+
+ /**
+ * Move the position object to the next logical position. Caller must
+ * ensure the the position object is valid.
+ *
+ * @param pos position object.
+ *
+ * @return position object that points to the next logical position.
+ */
+ static const_position_type next_position(const const_position_type& pos);
+
+ /**
+ * Increment or decrement the position object by specified steps. Caller
+ * must ensure the the position object is valid.
+ *
+ * @param pos position object.
+ * @param steps steps to advance the position object.
+ *
+ * @return position object that points to the new logical position.
+ */
+ static const_position_type advance_position(const const_position_type& pos, int steps);
+
+ /**
+ * Extract the logical position from a position object.
+ *
+ * @param pos position object.
+ *
+ * @return logical position of the element that the position object
+ * references.
+ */
+ static size_type logical_position(const const_position_type& pos);
+
+ /**
+ * Get element value from a position object. The caller must specify the
+ * type of block in which the element is expected to be stored.
+ *
+ * @param pos position object.
+ *
+ * @return element value.
+ */
+ template<typename _Blk>
+ static typename _Blk::value_type get(const const_position_type& pos);
+
+ event_func& event_handler();
+ const event_func& event_handler() const;
+
+ /**
+ * Default constructor. It initializes the container with empty size.
+ */
+ multi_type_vector();
+
+ /**
+ * Constructor that takes an lvalue reference to an event handler object.
+ * The event handler instance will be copy-constructed.
+ *
+ * @param hdl lvalue reference to an event handler object.
+ */
+ multi_type_vector(const event_func& hdl);
+
+ /**
+ * Constructor that takes an rvalue reference to an event handler object.
+ * The event handler instance will be move-constructed.
+ *
+ * @param hdl rvalue reference to an event handler object.
+ */
+ multi_type_vector(event_func&& hdl);
+
+ /**
+ * Constructor that takes initial size of the container. When the size
+ * specified is greater than 0, it initializes the container with empty
+ * elements.
+ *
+ * @param init_size initial container size.
+ */
+ multi_type_vector(size_type init_size);
+
+ /**
+ * Constructor that takes initial size of the container and an element
+ * value to initialize the elements to. When the size specified is greater
+ * than 0, it initializes the container with elements that are copies of
+ * the value specified.
+ *
+ * @param init_size initial container size.
+ * @param value initial element value.
+ */
+ template<typename T>
+ multi_type_vector(size_type init_size, const T& value);
+
+ /**
+ * Constructor that takes initial size of the container and begin and end
+ * iterator positions that specify a series of elements to initialize the
+ * container to. The container will contain copies of the elements
+ * specified after this call returns.
+ *
+ * @param init_size initial container size.
+ * @param it_begin iterator that points to the begin position of the
+ * values the container is being initialized to.
+ * @param it_end iterator that points to the end position of the values
+ * the container is being initialized to. The end position
+ * is <i>not</i> inclusive.
+ */
+ template<typename T>
+ multi_type_vector(size_type init_size, const T& it_begin, const T& it_end);
+
+ /**
+ * Copy constructor.
+ *
+ * @param other the other instance to copy values from.
+ */
+ multi_type_vector(const multi_type_vector& other);
+
+ /**
+ * Move constructor.
+ *
+ * @param other the other instance to move values from.
+ */
+ multi_type_vector(multi_type_vector&& other);
+
+ /**
+ * Destructor. It deletes all allocated element blocks.
+ */
+ ~multi_type_vector();
+
+ /**
+ * Given the logical position of an element, get the iterator of the block
+ * where the element is located, and its offset from the first element of
+ * that block.
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception if
+ * the specified position is outside the current container range, except
+ * when the specified position is the position immediately after the last
+ * valid position, it will return a valid position object representing
+ * the end position.</p>
+ *
+ * @param pos logical position of the element.
+ * @return position object that stores an iterator referencing the element
+ * block where the element resides, and its offset within that
+ * block.
+ */
+ position_type position(size_type pos);
+
+ /**
+ * Given the logical position of an element, get the iterator of the block
+ * where the element is located, and its offset from the first element of
+ * that block.
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception if
+ * the specified position is outside the current container range, except
+ * when the specified position is the position immediately after the last
+ * valid position, it will return a valid position object representing
+ * the end position.</p>
+ *
+ * @param pos_hint iterator used as a block position hint, to specify
+ * which block to start when searching for the element
+ * position.
+ * @param pos logical position of the element.
+ * @return position object that stores an iterator referencing the element
+ * block where the element resides, and its offset within that
+ * block.
+ */
+ position_type position(const iterator& pos_hint, size_type pos);
+
+ /**
+ * Given the logical position of an element, get an iterator referencing
+ * the element block where the element is located, and its offset from the
+ * first element of that block.
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception if
+ * the specified position is outside the current container range.</p>
+ *
+ * @param pos position of the element.
+ * @return position object that stores an iterator referencing the element
+ * block where the element resides, and its offset within that
+ * block.
+ */
+ const_position_type position(size_type pos) const;
+
+ /**
+ * Given the logical position of an element, get an iterator referencing
+ * the element block where the element is located, and its offset from the
+ * first element of that block.
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception if
+ * the specified position is outside the current container range.</p>
+ *
+ * @param pos_hint iterator used as a block position hint, to specify
+ * which block to start when searching for the element
+ * position.
+ * @param pos logical position of the element.
+ * @return position object that stores an iterator referencing the element
+ * block where the element resides, and its offset within the
+ * block.
+ */
+ const_position_type position(const const_iterator& pos_hint, size_type pos) const;
+
+ /**
+ * Move elements from one container to another. After the move, the
+ * segment where the elements were in the source container becomes empty.
+ * When transferring managed elements, this call transfers ownership of
+ * the moved elements to the destination container. The moved elements
+ * will overwrite any existing elements in the destination range of the
+ * receiving container. Transfer of elements within the same container is
+ * not allowed.
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception if
+ * either the starting or the ending position is greater than or equal to
+ * the source container size, or the destination container is not
+ * large enough to accommodate the transferred elements.</p>
+ *
+ * @param start_pos starting position
+ * @param end_pos ending position, inclusive.
+ * @param dest destination container to which the elements are to be
+ * moved.
+ * @param dest_pos position in the destination container to which the
+ * elements are to be moved.
+ *
+ * @return iterator referencing the block where the moved elements were
+ * prior to the transfer.
+ */
+ iterator transfer(size_type start_pos, size_type end_pos, multi_type_vector& dest, size_type dest_pos);
+
+ /**
+ * Move elements from one container to another. After the move, the
+ * segment where the elements were in the source container becomes empty.
+ * When transferring managed elements, this call transfers ownership of
+ * the moved elements to the new container. The moved elements will
+ * overwrite any existing elements in the destination range of the
+ * receiving container. Transfer of elements within the same container is
+ * not allowed.
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception if
+ * either the starting or the ending position is greater than or equal to
+ * the source container size, or the destination container is not large
+ * enough to accommodate the transferred elements.</p>
+ *
+ * @param pos_hint iterator used as a block position hint, to specify
+ * which block to start when searching for the blocks
+ * where the elements to be transferred reside.
+ * @param start_pos starting position
+ * @param end_pos ending position, inclusive.
+ * @param dest destination container to which the elements are to be
+ * moved.
+ * @param dest_pos position in the destination container to which the
+ * elements are to be moved.
+ *
+ * @return iterator referencing the block where the moved elements were
+ * prior to the transfer.
+ */
+ iterator transfer(
+ const iterator& pos_hint, size_type start_pos, size_type end_pos, multi_type_vector& dest, size_type dest_pos);
+
+ /**
+ * Set a value of an arbitrary type to a specified position. The type of
+ * the value is inferred from the value passed to this method. The new
+ * value will overwrite an existing value at the specified position
+ * position if any.
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception
+ * if the specified position is outside the current container range.</p>
+ *
+ * <p>Calling this method will not change the size of the container.</p>
+ *
+ * @param pos position to insert the value to.
+ * @param value value to insert.
+ * @return iterator position pointing to the block where the value is
+ * inserted.
+ */
+ template<typename T>
+ iterator set(size_type pos, const T& value);
+
+ /**
+ * Set a value of an arbitrary type to a specified position. The type of
+ * the value is inferred from the value passed to this method. The new
+ * value will overwrite an existing value at the specified position
+ * position if any.
+ *
+ * <p>This variant takes an iterator as an additional parameter, which is
+ * used as a block position hint to speed up the lookup of the
+ * right block to insert the value into. The other variant that doesn't
+ * take an iterator always starts the block lookup from the first block,
+ * which does not scale well as the block size grows.</p>
+ *
+ * <p>This position hint iterator must <b>precede</b> the insertion
+ * position to yield any performance benefit.</p>
+ *
+ * <p>The caller is responsible for ensuring that the passed iterator is
+ * valid. The behavior of this method when passing an invalid iterator is
+ * undefined.</p>
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception
+ * if the specified position is outside the current container range.</p>
+ *
+ * <p>Calling this method will not change the size of the container.</p>
+ *
+ * @param pos_hint iterator used as a block position hint, to specify
+ * which block to start when searching for the right block
+ * to insert the value into.
+ * @param pos position to insert the value to.
+ * @param value value to insert.
+ * @return iterator position pointing to the block where the value is
+ * inserted.
+ */
+ template<typename T>
+ iterator set(const iterator& pos_hint, size_type pos, const T& value);
+
+ /**
+ * Set multiple values of identical type to a range of elements starting
+ * at specified position. Any existing values will be overwritten by the
+ * new values.
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception if
+ * the range of new values would fall outside the current container
+ * range.</p>
+ *
+ * <p>Calling this method will not change the size of the container.</p>
+ *
+ * @param pos position of the first value of the series of new values
+ * being inserted.
+ * @param it_begin iterator that points to the begin position of the
+ * values being set.
+ * @param it_end iterator that points to the end position of the values
+ * being set.
+ * @return iterator position pointing to the block where the value is
+ * inserted. When no value insertion occurs because the value set
+ * is empty, the end iterator position is returned.
+ */
+ template<typename T>
+ iterator set(size_type pos, const T& it_begin, const T& it_end);
+
+ /**
+ * Set multiple values of identical type to a range of elements starting
+ * at specified position. Any existing values will be overwritten by the
+ * new values.
+ *
+ * <p>This variant takes an iterator as an additional parameter, which is
+ * used as a block position hint to speed up the lookup of the first
+ * insertion block. The other variant that doesn't take an iterator
+ * always starts the block lookup from the first block, which does not
+ * scale well as the block size grows.</p>
+ *
+ * <p>This position hint iterator must <b>precede</b> the insertion
+ * position to yield any performance benefit.</p>
+ *
+ * <p>The caller is responsible for ensuring that the passed iterator is
+ * valid. The behavior of this method when passing an invalid iterator is
+ * undefined.</p>
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception if
+ * the range of new values would fall outside the current container
+ * range.</p>
+ *
+ * <p>Calling this method will not change the size of the container.</p>
+ *
+ * @param pos_hint iterator used as a block position hint, to specify
+ * which block to start when searching for the right block
+ * to insert the value into.
+ * @param pos position of the first value of the series of new values
+ * being inserted.
+ * @param it_begin iterator that points to the begin position of the
+ * values being set.
+ * @param it_end iterator that points to the end position of the values
+ * being set.
+ * @return iterator position pointing to the block where the value is
+ * inserted. When no value insertion occurs because the value set
+ * is empty, the end iterator position is returned.
+ */
+ template<typename T>
+ iterator set(const iterator& pos_hint, size_type pos, const T& it_begin, const T& it_end);
+
+ /**
+ * Append a new value to the end of the container.
+ *
+ * @param value new value to be appended to the end of the container.
+ *
+ * @return iterator position pointing to the block where the value is
+ * appended, which in this case is always the last block of the
+ * container.
+ */
+ template<typename T>
+ iterator push_back(const T& value);
+
+ /**
+ * Append a new empty element to the end of the container.
+ *
+ * @return iterator position pointing to the block where the new empty
+ * element is appended, which in this case is always the last
+ * block of the container.
+ */
+ iterator push_back_empty();
+
+ /**
+ * Insert multiple values of identical type to a specified position.
+ * Existing values that occur at or below the specified position will get
+ * shifted after the insertion. No existing values will be overwritten by
+ * the inserted values.
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception
+ * if the specified position is outside the current container range.</p>
+ *
+ * <p>Calling this method will increase the size of the container by
+ * the length of the new values inserted.</p>
+ *
+ * @param pos position at which the new values are to be inserted.
+ * @param it_begin iterator that points to the begin position of the
+ * values being inserted.
+ * @param it_end iterator that points to the end position of the values
+ * being inserted.
+ * @return iterator position pointing to the block where the value is
+ * inserted. When no value insertion occurs because the value set
+ * is empty, the end iterator position is returned.
+ */
+ template<typename T>
+ iterator insert(size_type pos, const T& it_begin, const T& it_end);
+
+ /**
+ * Insert multiple values of identical type to a specified position.
+ * Existing values that occur at or below the specified position will get
+ * shifted after the insertion. No existing values will be overwritten by
+ * the inserted values.
+ *
+ * <p>This variant takes an iterator as an additional parameter, which is
+ * used as a block position hint to speed up the lookup of the first
+ * insertion block. The other variant that doesn't take an iterator
+ * always starts the block lookup from the first block, which does not
+ * scale well as the block size grows.</p>
+ *
+ * <p>This position hint iterator must <b>precede</b> the insertion
+ * position to yield any performance benefit.</p>
+ *
+ * <p>The caller is responsible for ensuring that the passed iterator is
+ * valid. The behavior of this method when passing an invalid iterator is
+ * undefined.</p>
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception
+ * if the specified position is outside the current container range.</p>
+ *
+ * <p>Calling this method will increase the size of the container by
+ * the length of the new values inserted.</p>
+ *
+ * @param pos_hint iterator used as a block position hint, to specify
+ * which block to start when searching for the right block
+ * to insert the value into.
+ * @param pos position at which the new values are to be inserted.
+ * @param it_begin iterator that points to the begin position of the
+ * values being inserted.
+ * @param it_end iterator that points to the end position of the values
+ * being inserted.
+ * @return iterator position pointing to the block where the value is
+ * inserted. When no value insertion occurs because the value set
+ * is empty, the end iterator position is returned.
+ */
+ template<typename T>
+ iterator insert(const iterator& pos_hint, size_type pos, const T& it_begin, const T& it_end);
+
+ /**
+ * Get the type of an element at specified position.
+ *
+ * @param pos position of the element.
+ *
+ * @return element type.
+ */
+ mtv::element_t get_type(size_type pos) const;
+
+ /**
+ * Check if element at specified position is empty of not.
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception if
+ * the specified position is outside the current container
+ * range.</p>
+ *
+ * @param pos position of the element to check.
+ *
+ * @return true if the element is empty, false otherwise.
+ */
+ bool is_empty(size_type pos) const;
+
+ /**
+ * Set specified range of elements to be empty. Any existing values will
+ * be overwritten.
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception if
+ * either the starting or the ending position is outside the current
+ * container size.</p>
+ *
+ * @param start_pos starting position
+ * @param end_pos ending position, inclusive.
+ * @return iterator position pointing to the block where the elements are
+ * emptied.
+ */
+ iterator set_empty(size_type start_pos, size_type end_pos);
+
+ /**
+ * Set specified range of elements to be empty. Any existing values will
+ * be overwritten.
+ *
+ * <p>This variant takes an iterator as an additional parameter, which is
+ * used as a block position hint to speed up the lookup of the first block
+ * to empty. The other variant that doesn't take an iterator always
+ * starts the block lookup from the first block, which does not
+ * scale well as the block size grows.</p>
+ *
+ * <p>This position hint iterator must <b>precede</b> the start
+ * position to yield any performance benefit.</p>
+ *
+ * <p>The caller is responsible for ensuring that the passed iterator is
+ * valid. The behavior of this method when passing an invalid iterator is
+ * undefined.</p>
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception if
+ * either the starting or the ending position is outside the current
+ * container size.</p>
+ *
+ * @param pos_hint iterator used as a block position hint, to specify
+ * which block to start when searching for the right
+ * blocks to empty.
+ * @param start_pos starting position
+ * @param end_pos ending position, inclusive.
+ * @return iterator position pointing to the block where the elements are
+ * emptied.
+ */
+ iterator set_empty(const iterator& pos_hint, size_type start_pos, size_type end_pos);
+
+ /**
+ * Erase elements located between specified start and end positions. The
+ * end positions are both inclusive. Those elements originally located
+ * after the specified end position will get shifted up after the erasure.
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception if
+ * either the starting or the ending position is outside the current
+ * container range.</p>
+ *
+ * <p>Calling this method will decrease the size of the container by
+ * the length of the erased range.</p>
+ *
+ * @param start_pos starting position
+ * @param end_pos ending position, inclusive.
+ */
+ void erase(size_type start_pos, size_type end_pos);
+
+ /**
+ * Insert a range of empty elements at specified position. Those elements
+ * originally located after the insertion position will get shifted down
+ * after the insertion.
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception if
+ * either the specified position is outside the current container
+ * range.</p>
+ *
+ * <p>Calling this method will increase the size of the container by
+ * the length of the inserted empty elements.</p>
+ *
+ * @param pos position at which to insert a range of empty elements.
+ * @param length number of empty elements to insert.
+ * @return iterator position pointing to the block where the empty range
+ * is inserted. When no insertion occurs because the length is
+ * zero, the end iterator position is returned.
+ */
+ iterator insert_empty(size_type pos, size_type length);
+
+ /**
+ * Insert a range of empty elements at specified position. Those elements
+ * originally located after the insertion position will get shifted down
+ * after the insertion.
+ *
+ * <p>This variant takes an iterator as an additional parameter, which is
+ * used as a block position hint to speed up the lookup of the block in
+ * which to insert the new empty segment. The other variant that doesn't
+ * take an iterator always starts the block lookup from the first block,
+ * which does not scale well as the block size grows.</p>
+ *
+ * <p>This position hint iterator must <b>precede</b> the start
+ * position to yield any performance benefit.</p>
+ *
+ * <p>The caller is responsible for ensuring that the passed iterator is
+ * valid. The behavior of this method when passing an invalid iterator is
+ * undefined.</p>
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception if
+ * either the specified position is outside the current container
+ * range.</p>
+ *
+ * <p>Calling this method will increase the size of the container by
+ * the length of the inserted empty elements.</p>
+ *
+ * @param pos_hint iterator used as a block position hint, to specify
+ * which block to start when searching for the right block
+ * in which to insert the empty segment.
+ * @param pos position at which to insert a range of empty elements.
+ * @param length number of empty elements to insert.
+ * @return iterator position pointing to the block where the empty range
+ * is inserted. When no insertion occurs because the length is
+ * zero, the end iterator position is returned.
+ */
+ iterator insert_empty(const iterator& pos_hint, size_type pos, size_type length);
+
+ /**
+ * Clear the content of the container. The size of the container will
+ * become zero after calling this method.
+ */
+ void clear();
+
+ /**
+ * Return the current container size.
+ *
+ * @return current container size.
+ */
+ size_type size() const;
+
+ /**
+ * Return the current number of blocks in the primary array. Each
+ * non-empty block stores a secondary block that stores elements in a
+ * contiguous memory region (element block) and the number of elements it
+ * stores. An empty block only stores its logical size and does not store
+ * an actual element block.
+ *
+ * <p>For instance, if the container stores values of double-precision
+ * type at rows 0 to 2, values of std::string type at 3 to 7, and empty
+ * values at 8 to 10, it would consist of three blocks: one that stores
+ * double values, one that stores std::string values, and one that
+ * represents the empty value range in this exact order. In this specific
+ * scenario, <code>block_size()</code> returns 3, and <code>size()</code>
+ * returns 11.</p>
+ *
+ * @return current number of blocks in the primary array.
+ */
+ size_type block_size() const;
+
+ /**
+ * Return whether or not the container is empty.
+ *
+ * @return true if the container is empty, false otherwise.
+ */
+ bool empty() const;
+
+ /**
+ * Get the value of an element at specified position. The caller must
+ * pass a variable of the correct type to store the value.
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception if
+ * the specified position is outside the current container range.</p>
+ *
+ * @param pos position of the element value to retrieve.
+ * @param value (out) variable to store the retrieved value.
+ */
+ template<typename T>
+ void get(size_type pos, T& value) const;
+
+ /**
+ * Get the value of an element at specified position. The caller must
+ * specify the type of the element as the template parameter e.g.
+ * get<double>(1).
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception if
+ * the specified position is outside the current container range.</p>
+ *
+ * @param pos position of the element value to retrieve.
+ * @return element value.
+ */
+ template<typename T>
+ T get(size_type pos) const;
+
+ /**
+ * Return the value of an element at specified position and set that
+ * position empty. If the element resides in a managed element block,
+ * this call will release that element from that block. If the element is
+ * on a non-managed element block, this call is equivalent to
+ * set_empty(pos, pos).
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception if
+ * the specified position is outside the current container range.</p>
+ *
+ * @param pos position of the element to release.
+ *
+ * @return element value.
+ */
+ template<typename T>
+ T release(size_type pos);
+
+ /**
+ * Retrieve the value of an element at specified position and set that
+ * position empty. If the element resides in a managed element block,
+ * this call will release that element from that block. If the element is
+ * on a non-managed element block, this call is equivalent to
+ * set_empty(pos, pos).
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception if
+ * the specified position is outside the current container range.</p>
+ *
+ * @param pos position of the element to release.
+ * @param value element value.
+ *
+ * @return iterator referencing the block where the position of the
+ * released element is.
+ */
+ template<typename T>
+ iterator release(size_type pos, T& value);
+
+ /**
+ * Retrieve the value of an element at specified position and set that
+ * position empty. If the element resides in a managed element block,
+ * this call will release that element from that block. If the element is
+ * on a non-managed element block, this call is equivalent to
+ * set_empty(pos, pos).
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception if
+ * the specified position is outside the current container range.</p>
+ *
+ * @param pos_hint iterator used as a block position hint, to specify
+ * which block to start when searching for the block where
+ * the element resides.
+ * @param pos position of the element to release.
+ * @param value element value.
+ *
+ * @return iterator referencing the block where the position of the
+ * released element is.
+ */
+ template<typename T>
+ iterator release(const iterator& pos_hint, size_type pos, T& value);
+
+ /**
+ * Release all its elements, and empties its content. Calling this method
+ * relinquishes the ownership of all elements stored in managed element
+ * blocks if any.
+ *
+ * <p>This call is equivalent of clear() if the container consists of no
+ * managed element blocks.</p>
+ */
+ void release();
+
+ /**
+ * Make all elements within specified range empty, and relinquish the
+ * ownership of the elements in that range. All elements in the managed
+ * element blocks within the range will be released and the container will
+ * no longer manage their life cycles after the call.
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception if
+ * either the starting or the ending position is outside the current
+ * container size.</p>
+ *
+ * @param start_pos starting position
+ * @param end_pos ending position, inclusive.
+ * @return iterator position pointing to the block where the elements are
+ * released.
+ */
+ iterator release_range(size_type start_pos, size_type end_pos);
+
+ /**
+ * Make all elements within specified range empty, and relinquish the
+ * ownership of the elements in that range. All elements in the managed
+ * element blocks within the range will be released and the container will
+ * no longer manage their life cycles after the call.
+ *
+ * <p>This variant takes an iterator as an additional parameter, which is
+ * used as a block position hint to speed up the lookup of the first block
+ * to empty. The other variant that doesn't take an iterator always
+ * starts the block lookup from the first block, which does not
+ * scale well as the block size grows.</p>
+ *
+ * <p>The method will throw an <code>std::out_of_range</code> exception if
+ * either the starting or the ending position is outside the current
+ * container size.</p>
+ *
+ * @param pos_hint iterator used as a block position hint, to specify
+ * which block to start when searching for the right
+ * blocks in which elements are to be released.
+ * @param start_pos starting position
+ * @param end_pos ending position, inclusive.
+ * @return iterator position pointing to the block where the elements are
+ * released.
+ */
+ iterator release_range(const iterator& pos_hint, size_type start_pos, size_type end_pos);
+
+ iterator begin();
+ iterator end();
+
+ const_iterator begin() const;
+ const_iterator end() const;
+
+ const_iterator cbegin() const;
+ const_iterator cend() const;
+
+ reverse_iterator rbegin();
+ reverse_iterator rend();
+
+ const_reverse_iterator rbegin() const;
+ const_reverse_iterator rend() const;
+
+ const_reverse_iterator crbegin() const;
+ const_reverse_iterator crend() const;
+
+ /**
+ * Extend or shrink the container. When extending the container, it
+ * appends a series of empty elements to the end. When shrinking, the
+ * elements at the end of the container get stripped off.
+ *
+ * @param new_size size of the container after the resize.
+ */
+ void resize(size_type new_size);
+
+ /**
+ * Swap the content with another container.
+ *
+ * @param other another container to swap content with.
+ */
+ void swap(multi_type_vector& other);
+
+ /**
+ * Swap a part of the content with another instance.
+ *
+ * @param start_pos starting position
+ * @param end_pos ending position, inclusive.
+ * @param other another instance to swap the content with.
+ * @param other_pos insertion position in the other instance.
+ */
+ void swap(size_type start_pos, size_type end_pos, multi_type_vector& other, size_type other_pos);
+
+ /**
+ * Trim excess capacity from all non-empty blocks.
+ */
+ void shrink_to_fit();
+
+ bool operator==(const multi_type_vector& other) const;
+ bool operator!=(const multi_type_vector& other) const;
+
+ multi_type_vector& operator=(const multi_type_vector& other);
+ multi_type_vector& operator=(multi_type_vector&& other);
+
+ /**
+ * Return the numerical identifier that represents passed element.
+ *
+ * @param elem element value.
+ *
+ * @return numerical identifier representing the element.
+ */
+ template<typename T>
+ static mtv::element_t get_element_type(const T& elem);
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ void dump_blocks(std::ostream& os) const;
+
+ void check_block_integrity() const;
+#endif
+
+private:
+ void delete_element_block(size_type block_index);
+
+ void delete_element_blocks(size_type start, size_type end);
+
+ template<typename T>
+ void get_impl(size_type pos, T& value) const;
+
+ template<typename T>
+ bool set_cells_precheck(size_type row, const T& it_begin, const T& it_end, size_type& end_pos);
+
+ template<typename T>
+ iterator set_impl(size_type pos, size_type block_index, const T& value);
+
+ template<typename T>
+ iterator release_impl(size_type pos, size_type block_index, T& value);
+
+ void swap_impl(
+ multi_type_vector& other, size_type start_pos, size_type end_pos, size_type other_pos, size_type block_index1,
+ size_type block_index2, size_type dblock_index1, size_type dblock_index2);
+
+ void swap_single_block(
+ multi_type_vector& other, size_type start_pos, size_type end_pos, size_type other_pos, size_type block_index,
+ size_type other_block_index);
+
+ void swap_single_to_multi_blocks(
+ multi_type_vector& other, size_type start_pos, size_type end_pos, size_type other_pos, size_type block_index,
+ size_type dst_block_index1, size_type dst_block_index2);
+
+ void swap_multi_to_multi_blocks(
+ multi_type_vector& other, size_type start_pos, size_type end_pos, size_type other_pos, size_type block_index1,
+ size_type block_index2, size_type dblock_index1, size_type dblock_index2);
+
+ template<typename T>
+ iterator insert_cells_impl(size_type row, size_type block_index, const T& it_begin, const T& it_end);
+
+ void resize_impl(size_type new_size);
+
+ /**
+ * Elements to transfer to the other container span across multiple
+ * blocks.
+ */
+ iterator transfer_multi_blocks(
+ size_type start_pos, size_type end_pos, size_type block_index1, size_type block_index2, multi_type_vector& dest,
+ size_type dest_pos);
+
+ /**
+ * @param start_pos logical start position.
+ * @param end_pos logical end position.
+ * @param block_index1 index of the first block
+ * @param overwrite when true, and when the stored values are pointers to
+ * heap objects, objects pointed to by the overwritten
+ * pointers should be freed from memory.
+ */
+ iterator set_empty_impl(size_type start_pos, size_type end_pos, size_type block_index1, bool overwrite);
+
+ iterator set_empty_in_single_block(size_type start_row, size_type end_row, size_type block_index, bool overwrite);
+
+ /**
+ * @param start_row logical start position.
+ * @param end_row logical end position.
+ * @param block_index1 index of the first block.
+ * @param block_index2 index of the last block.
+ * @param overwrite when true, and when the stored values are pointers to
+ * heap objects, objects pointed to by the overwritten
+ * pointers should be freed from memory.
+ */
+ iterator set_empty_in_multi_blocks(
+ size_type start_row, size_type end_row, size_type block_index1, size_type block_index2, bool overwrite);
+
+ void erase_impl(size_type start_pos, size_type end_pos);
+ void erase_in_single_block(size_type start_pos, size_type end_pos, size_type block_index);
+
+ /**
+ * @param pos logical position at which to insert an empty segment.
+ * @param block_index index of the block.
+ * @param length length of the emtpy segment to insert.
+ */
+ iterator insert_empty_impl(size_type pos, size_type block_index, size_type length);
+
+ void insert_blocks_at(size_type position, size_type insert_pos, blocks_type& new_blocks);
+
+ void prepare_blocks_to_transfer(
+ blocks_to_transfer& bucket, size_type block_index1, size_type offset1, size_type block_index2,
+ size_type offset2);
+
+ iterator set_whole_block_empty(size_type block_index, bool overwrite);
+
+ template<typename T>
+ iterator push_back_impl(const T& value);
+
+ template<typename T>
+ iterator set_cells_impl(
+ size_type row, size_type end_row, size_type block_index1, const T& it_begin, const T& it_end);
+
+ template<typename T>
+ iterator set_cells_to_single_block(
+ size_type start_row, size_type end_row, size_type block_index, const T& it_begin, const T& it_end);
+
+ template<typename T>
+ iterator set_cells_to_multi_blocks(
+ size_type start_row, size_type end_row, size_type block_index1, size_type block_index2, const T& it_begin,
+ const T& it_end);
+
+ template<typename T>
+ iterator set_cells_to_multi_blocks_block1_non_equal(
+ size_type start_row, size_type end_row, size_type block_index1, size_type block_index2, const T& it_begin,
+ const T& it_end);
+
+ template<typename T>
+ iterator set_cells_to_multi_blocks_block1_non_empty(
+ size_type start_row, size_type end_row, size_type block_index1, size_type block_index2, const T& it_begin,
+ const T& it_end);
+
+ template<typename T>
+ iterator set_cell_to_empty_block(size_type block_index, size_type pos_in_block, const T& cell);
+
+ template<typename T>
+ iterator set_cell_to_non_empty_block_of_size_one(size_type block_index, const T& cell);
+
+ /**
+ * Find the correct block position for a given logical row ID.
+ *
+ * @param row logical position of an element.
+ * @param start_block_index index of the first block to start the search
+ * from.
+ *
+ * @return index of the block that contains the specified logical row ID.
+ */
+ size_type get_block_position(size_type row, size_type start_block_index = 0) const;
+
+ /**
+ * Same as above, but try to infer block position from the private position
+ * data stored in the iterator first before trying full search.
+ */
+ size_type get_block_position(const typename value_type::private_data& pos_data, size_type row) const;
+
+ template<typename T>
+ void create_new_block_with_new_cell(size_type block_index, const T& cell);
+
+ template<typename T>
+ void append_cell_to_block(size_type block_index, const T& cell);
+
+ /**
+ * Try to append a sequence of values to the previous block if the previous
+ * block exists and is of the same type as the new values.
+ *
+ * @return true if the values have been appended successfully, otherwise
+ * false.
+ */
+ template<typename T>
+ bool append_to_prev_block(
+ size_type block_index, element_category_type cat, size_type length, const T& it_begin, const T& it_end);
+
+ template<typename T>
+ void insert_cells_to_middle(size_type row, size_type block_index, const T& it_begin, const T& it_end);
+
+ template<typename T>
+ iterator set_cell_to_middle_of_block(size_type block_index, size_type pos_in_block, const T& cell);
+
+ /**
+ * Set a new value to the top of specified non-empty block. The block is
+ * expected to be of size greater than one, and the previous block is not of
+ * the same type as the value being inserted.
+ */
+ template<typename T>
+ void set_cell_to_top_of_data_block(size_type block_index, const T& cell);
+
+ template<typename T>
+ void set_cell_to_bottom_of_data_block(size_type block_index, const T& cell);
+
+ iterator transfer_impl(
+ size_type start_pos, size_type end_pos, size_type block_index1, multi_type_vector& dest, size_type dest_pos);
+
+ /**
+ * All elements to transfer to the other instance is in the same block.
+ */
+ iterator transfer_single_block(
+ size_type start_pos, size_type end_pos, size_type block_index1, multi_type_vector& dest, size_type dest_pos);
+
+ /**
+ * Merge with previous or next block as needed.
+ *
+ * @param block_index index of the block that may need merging.
+ *
+ * @return size of previous block if the block is merged with the previous
+ * block, or 0 if it didn't merge with the previous block.
+ */
+ size_type merge_with_adjacent_blocks(size_type block_index);
+
+ /**
+ * Merge only with the next block if the two are of the same type.
+ *
+ * @param block_index index of the block that may need merging.
+ *
+ * @return true if merge occurs, false otherwise.
+ */
+ bool merge_with_next_block(size_type block_index);
+
+ /**
+ * Set a new block in the middle of an existing block. This call inserts
+ * two new blocks below the specificed block position. The first one will
+ * be empty, and the second one will contain the lower elements of the
+ * existing block.
+ *
+ * @param block_index index of block into which to set a new block.
+ * @param offset position in the existing block to set the new block to.
+ * @param new_block_size size of the new block
+ * @param overwrite whether or not to overwrite the elements replaced by
+ * the new block.
+ * @return index of the inserted middle block.
+ */
+ size_type set_new_block_to_middle(
+ size_type block_index, size_type offset, size_type new_block_size, bool overwrite);
+
+ /**
+ * Check if the previous block is of specified type, if exists.
+ *
+ * @param block_index index of the current block.
+ * @param cat desired block type.
+ *
+ * @return true if the previous block exists and it's of specified type,
+ * otherwise false.
+ */
+ bool is_previous_block_of_type(size_type block_index, element_category_type cat) const;
+
+ /**
+ * Check if the next block is of specified type, if exists.
+ *
+ * @param block_index index of the current block.
+ * @param cat desired block type.
+ *
+ * @return true if the next block exists and it's of specified type,
+ * otherwise false.
+ */
+ bool is_next_block_of_type(size_type block_index, element_category_type cat) const;
+
+ /**
+ * Send elements from a source block to place them in a destination block.
+ * In return, the method returns the elements in the destination block
+ * that have been replaced by the elements sent by the caller. The caller
+ * needs to manage the life cycle of the returned block.
+ *
+ * Note that the destination block is expected to be non-empty. This also
+ * implies that the returned block is never null and always contains
+ * elements.
+ *
+ * @param src_data source data block from which the elements are sent.
+ * @param src_offset position of the first element in the source block.
+ * @param dst_index destination block index.
+ * @param dst_offset position in the destination block where the sent
+ * elements are to be placed.
+ * @param len length of elements.
+ *
+ * @return heap allocated block that contains the overwritten elements
+ * originally in the destination block. The caller needs to manage
+ * its life cycle.
+ */
+ element_block_type* exchange_elements(
+ const element_block_type& src_data, size_type src_offset, size_type dst_index, size_type dst_offset,
+ size_type len);
+
+ void exchange_elements(
+ const element_block_type& src_blk, size_type src_offset, size_type dst_index1, size_type dst_offset1,
+ size_type dst_index2, size_type dst_offset2, size_type len, blocks_type& new_blocks);
+
+ bool append_empty(size_type len);
+
+ inline iterator get_iterator(size_type block_index)
+ {
+ auto iter_pos = m_block_store.positions.begin();
+ std::advance(iter_pos, block_index);
+ auto iter_size = m_block_store.sizes.begin();
+ std::advance(iter_size, block_index);
+ auto iter_elem = m_block_store.element_blocks.begin();
+ std::advance(iter_elem, block_index);
+
+ return iterator(
+ {iter_pos, iter_size, iter_elem},
+ {m_block_store.positions.end(), m_block_store.sizes.end(), m_block_store.element_blocks.end()}, this,
+ block_index);
+ }
+
+ inline const_iterator get_const_iterator(size_type block_index) const
+ {
+ auto iter_pos = m_block_store.positions.cbegin();
+ std::advance(iter_pos, block_index);
+ auto iter_size = m_block_store.sizes.cbegin();
+ std::advance(iter_size, block_index);
+ auto iter_elem = m_block_store.element_blocks.cbegin();
+ std::advance(iter_elem, block_index);
+
+ return const_iterator(
+ {iter_pos, iter_size, iter_elem},
+ {m_block_store.positions.cend(), m_block_store.sizes.cend(), m_block_store.element_blocks.cend()}, this,
+ block_index);
+ }
+
+private:
+ using adjust_block_positions_func = detail::adjust_block_positions<blocks_type, Traits::loop_unrolling>;
+
+ event_func m_hdl_event;
+ blocks_type m_block_store;
+ size_type m_cur_size;
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ mutable int m_trace_call_depth = 0;
+#endif
+};
+
+}}} // namespace mdds::mtv::soa
+
+#include "main_def.inl"
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/mdds/multi_type_vector/soa/main_def.inl b/include/mdds/multi_type_vector/soa/main_def.inl
new file mode 100644
index 0000000..de3f520
--- /dev/null
+++ b/include/mdds/multi_type_vector/soa/main_def.inl
@@ -0,0 +1,5238 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * Copyright (c) 2021 Kohei Yoshida
+ *
+ * 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, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ ************************************************************************/
+
+#include "../env.hpp"
+#include "../util.hpp"
+#if MDDS_MTV_USE_STANDARD_ELEMENT_BLOCKS
+#include "../standard_element_blocks.hpp"
+#endif
+
+namespace mdds { namespace mtv { namespace soa {
+
+namespace detail {
+
+template<typename SizeT, typename VecT>
+void erase(VecT& arr, SizeT index, SizeT size)
+{
+ auto it = arr.begin() + index;
+ arr.erase(it, it + size);
+}
+
+} // namespace detail
+
+template<typename Traits>
+multi_type_vector<Traits>::blocks_type::blocks_type()
+{}
+
+template<typename Traits>
+multi_type_vector<Traits>::blocks_type::blocks_type(const blocks_type& other)
+ : positions(other.positions), sizes(other.sizes), element_blocks(other.element_blocks)
+{
+ for (element_block_type*& data : element_blocks)
+ {
+ if (data)
+ data = block_funcs::clone_block(*data);
+ }
+}
+
+template<typename Traits>
+multi_type_vector<Traits>::blocks_type::blocks_type(blocks_type&& other)
+ : positions(std::move(other.positions)), sizes(std::move(other.sizes)),
+ element_blocks(std::move(other.element_blocks))
+{}
+
+template<typename Traits>
+void multi_type_vector<Traits>::blocks_type::erase(size_type index)
+{
+ positions.erase(positions.begin() + index);
+ sizes.erase(sizes.begin() + index);
+ element_blocks.erase(element_blocks.begin() + index);
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::blocks_type::erase(size_type index, size_type size)
+{
+ detail::erase(positions, index, size);
+ detail::erase(sizes, index, size);
+ detail::erase(element_blocks, index, size);
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::blocks_type::insert(size_type index, size_type size)
+{
+ positions.insert(positions.begin() + index, size, 0);
+ sizes.insert(sizes.begin() + index, size, 0);
+ element_blocks.insert(element_blocks.begin() + index, size, nullptr);
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::blocks_type::insert(
+ size_type index, size_type pos, size_type size, element_block_type* data)
+{
+ positions.insert(positions.begin() + index, pos);
+ sizes.insert(sizes.begin() + index, size);
+ element_blocks.insert(element_blocks.begin() + index, data);
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::blocks_type::insert(size_type index, const blocks_type& new_blocks)
+{
+ positions.insert(positions.begin() + index, new_blocks.positions.begin(), new_blocks.positions.end());
+ sizes.insert(sizes.begin() + index, new_blocks.sizes.begin(), new_blocks.sizes.end());
+ element_blocks.insert(
+ element_blocks.begin() + index, new_blocks.element_blocks.begin(), new_blocks.element_blocks.end());
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::blocks_type::calc_block_position(size_type index)
+{
+ if (index == 0)
+ {
+ positions[index] = 0;
+ return;
+ }
+
+ assert(index < positions.size());
+ positions[index] = positions[index - 1] + sizes[index - 1];
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::size_type multi_type_vector<Traits>::blocks_type::calc_next_block_position(
+ size_type index)
+{
+ return positions[index] + sizes[index];
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::blocks_type::swap(size_type index1, size_type index2)
+{
+ std::swap(positions[index1], positions[index2]);
+ std::swap(sizes[index1], sizes[index2]);
+ std::swap(element_blocks[index1], element_blocks[index2]);
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::blocks_type::swap(blocks_type& other)
+{
+ positions.swap(other.positions);
+ sizes.swap(other.sizes);
+ element_blocks.swap(other.element_blocks);
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::blocks_type::reserve(size_type n)
+{
+ positions.reserve(n);
+ sizes.reserve(n);
+ element_blocks.reserve(n);
+}
+
+template<typename Traits>
+bool multi_type_vector<Traits>::blocks_type::equals(const blocks_type& other) const
+{
+ if (positions != other.positions)
+ return false;
+
+ if (sizes != other.sizes)
+ return false;
+
+ if (element_blocks.size() != other.element_blocks.size())
+ return false;
+
+ auto it2 = other.element_blocks.cbegin();
+
+ for (const element_block_type* data1 : element_blocks)
+ {
+ const element_block_type* data2 = *it2++;
+
+ if (data1)
+ {
+ if (!data2)
+ // left is non-empty while right is empty.
+ return false;
+ }
+ else
+ {
+ if (data2)
+ // left is empty while right is non-empty.
+ return false;
+ }
+
+ if (!data1)
+ {
+ // Both are empty blocks.
+ assert(!data2);
+ continue;
+ }
+
+ assert(data1 && data2);
+ if (!block_funcs::equal_block(*data1, *data2))
+ return false;
+ }
+
+ return true;
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::blocks_type::clear()
+{
+ positions.clear();
+ sizes.clear();
+ element_blocks.clear();
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::blocks_type::check_integrity() const
+{
+ if (positions.size() != sizes.size())
+ throw mdds::integrity_error("position and size arrays are of different sizes!");
+
+ if (positions.size() != element_blocks.size())
+ throw mdds::integrity_error("position and element-block arrays are of different sizes!");
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::position_type multi_type_vector<Traits>::next_position(const position_type& pos)
+{
+ position_type ret = pos;
+ if (pos.second + 1 < pos.first->size)
+ {
+ // Next element is still in the same block.
+ ++ret.second;
+ }
+ else
+ {
+ ++ret.first;
+ ret.second = 0;
+ }
+
+ return ret;
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::position_type multi_type_vector<Traits>::advance_position(
+ const position_type& pos, int steps)
+{
+ return mdds::mtv::detail::advance_position<position_type>(pos, steps);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::const_position_type multi_type_vector<Traits>::next_position(
+ const const_position_type& pos)
+{
+ const_position_type ret = pos;
+ if (pos.second + 1 < pos.first->size)
+ {
+ // Next element is still in the same block.
+ ++ret.second;
+ }
+ else
+ {
+ ++ret.first;
+ ret.second = 0;
+ }
+
+ return ret;
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::const_position_type multi_type_vector<Traits>::advance_position(
+ const const_position_type& pos, int steps)
+{
+ return mdds::mtv::detail::advance_position<const_position_type>(pos, steps);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::size_type multi_type_vector<Traits>::logical_position(
+ const const_position_type& pos)
+{
+ return pos.first->position + pos.second;
+}
+
+template<typename Traits>
+template<typename _Blk>
+typename _Blk::value_type multi_type_vector<Traits>::get(const const_position_type& pos)
+{
+ return mdds::mtv::detail::get_block_element_at<_Blk>(*pos.first->data, pos.second);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::event_func& multi_type_vector<Traits>::event_handler()
+{
+ MDDS_MTV_TRACE(accessor);
+
+ return m_hdl_event;
+}
+
+template<typename Traits>
+const typename multi_type_vector<Traits>::event_func& multi_type_vector<Traits>::event_handler() const
+{
+ MDDS_MTV_TRACE(accessor);
+
+ return m_hdl_event;
+}
+
+template<typename Traits>
+multi_type_vector<Traits>::multi_type_vector() : m_cur_size(0)
+{
+ MDDS_MTV_TRACE(constructor);
+}
+
+template<typename Traits>
+multi_type_vector<Traits>::multi_type_vector(const event_func& hdl) : m_hdl_event(hdl), m_cur_size(0)
+{
+ MDDS_MTV_TRACE_ARGS(constructor, "event_func=?");
+}
+
+template<typename Traits>
+multi_type_vector<Traits>::multi_type_vector(event_func&& hdl) : m_hdl_event(std::move(hdl)), m_cur_size(0)
+{
+ MDDS_MTV_TRACE_ARGS(constructor, "event_func=? (move)");
+}
+
+template<typename Traits>
+multi_type_vector<Traits>::multi_type_vector(size_type init_size) : m_cur_size(init_size)
+{
+ MDDS_MTV_TRACE_ARGS(constructor, "init_size=" << init_size);
+
+ if (!init_size)
+ return;
+
+ // Initialize with an empty block that spans from 0 to max.
+ m_block_store.positions.emplace_back(0);
+ m_block_store.sizes.emplace_back(init_size);
+ m_block_store.element_blocks.emplace_back(nullptr);
+}
+
+template<typename Traits>
+template<typename T>
+multi_type_vector<Traits>::multi_type_vector(size_type init_size, const T& value) : m_cur_size(init_size)
+{
+ MDDS_MTV_TRACE_ARGS(
+ constructor, "init_size=" << init_size << "; value=? (type=" << mdds_mtv_get_element_type(value) << ")");
+
+ if (!init_size)
+ return;
+
+ element_block_type* data = mdds_mtv_create_new_block(init_size, value);
+ m_hdl_event.element_block_acquired(data);
+ m_block_store.positions.emplace_back(0);
+ m_block_store.sizes.emplace_back(init_size);
+ m_block_store.element_blocks.emplace_back(data);
+}
+
+template<typename Traits>
+template<typename T>
+multi_type_vector<Traits>::multi_type_vector(size_type init_size, const T& it_begin, const T& it_end)
+ : m_cur_size(init_size)
+{
+ MDDS_MTV_TRACE_ARGS(
+ constructor,
+ "init_size=" << init_size << "; it_begin=?; it_end=? (length=" << std::distance(it_begin, it_end) << ")");
+
+ if (!m_cur_size)
+ return;
+
+ size_type data_len = std::distance(it_begin, it_end);
+ if (m_cur_size != data_len)
+ throw mdds::invalid_arg_error("Specified size does not match the size of the initial data array.");
+
+ element_block_type* data = mdds_mtv_create_new_block(*it_begin, it_begin, it_end);
+ m_hdl_event.element_block_acquired(data);
+ m_block_store.positions.emplace_back(0);
+ m_block_store.sizes.emplace_back(m_cur_size);
+ m_block_store.element_blocks.emplace_back(data);
+}
+
+template<typename Traits>
+multi_type_vector<Traits>::multi_type_vector(const multi_type_vector& other)
+ : m_hdl_event(other.m_hdl_event), m_block_store(other.m_block_store), m_cur_size(other.m_cur_size)
+{
+ MDDS_MTV_TRACE_ARGS(constructor, "other=? (copy)");
+
+ for (const element_block_type* data : m_block_store.element_blocks)
+ {
+ if (data)
+ m_hdl_event.element_block_acquired(data);
+ }
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ try
+ {
+ check_block_integrity();
+ }
+ catch (const mdds::integrity_error& e)
+ {
+ std::ostringstream os;
+ os << e.what() << std::endl;
+ os << "block integrity check failed in copy construction" << std::endl;
+ std::cerr << os.str() << std::endl;
+ abort();
+ }
+#endif
+}
+
+template<typename Traits>
+multi_type_vector<Traits>::multi_type_vector(multi_type_vector&& other)
+ : m_hdl_event(std::move(other.m_hdl_event)), m_block_store(std::move(other.m_block_store)),
+ m_cur_size(other.m_cur_size)
+{
+ MDDS_MTV_TRACE_ARGS(constructor, "other=? (move)");
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ try
+ {
+ check_block_integrity();
+ }
+ catch (const mdds::integrity_error& e)
+ {
+ std::ostringstream os;
+ os << e.what() << std::endl;
+ os << "block integrity check failed in copy construction" << std::endl;
+ std::cerr << os.str() << std::endl;
+ abort();
+ }
+#endif
+}
+
+template<typename Traits>
+multi_type_vector<Traits>::~multi_type_vector()
+{
+ MDDS_MTV_TRACE(destructor);
+
+ delete_element_blocks(0, m_block_store.positions.size());
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::delete_element_block(size_type block_index)
+{
+ element_block_type* data = m_block_store.element_blocks[block_index];
+ if (!data)
+ // This block is empty.
+ return;
+
+ m_hdl_event.element_block_released(data);
+ block_funcs::delete_block(data);
+ m_block_store.element_blocks[block_index] = nullptr;
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::delete_element_blocks(size_type start, size_type end)
+{
+ for (size_type i = start; i < end; ++i)
+ delete_element_block(i);
+}
+
+template<typename Traits>
+template<typename T>
+void multi_type_vector<Traits>::get_impl(size_type pos, T& value) const
+{
+ size_type block_index = get_block_position(pos);
+ if (block_index == m_block_store.positions.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::get", __LINE__, pos, block_size(), size());
+
+ assert(block_index < m_block_store.element_blocks.size());
+ const element_block_type* data = m_block_store.element_blocks[block_index];
+ if (!data)
+ {
+ // empty cell block.
+ mdds_mtv_get_empty_value(value);
+ return;
+ }
+
+ size_type start_row = m_block_store.positions[block_index];
+ assert(pos >= start_row);
+ size_type offset = pos - start_row;
+ mdds_mtv_get_value(*data, offset, value);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::position_type multi_type_vector<Traits>::position(size_type pos)
+{
+ MDDS_MTV_TRACE_ARGS(accessor, "pos=" << pos);
+
+ if (pos == m_cur_size)
+ {
+ // This is a valid end position. Create a valid position object that
+ // represents a valid end position.
+ return position_type(end(), 0);
+ }
+
+ size_type block_index = get_block_position(pos);
+ if (block_index == m_block_store.positions.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::position", __LINE__, pos, block_size(), size());
+
+ size_type start_pos = m_block_store.positions[block_index];
+
+ iterator it = get_iterator(block_index);
+ return position_type(it, pos - start_pos);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::position_type multi_type_vector<Traits>::position(
+ const iterator& pos_hint, size_type pos)
+{
+ MDDS_MTV_TRACE_ARGS(accessor_with_pos_hint, "pos_hint=" << pos_hint << "; pos=" << pos);
+
+ if (pos == m_cur_size)
+ {
+ // This is a valid end position. Create a valid position object that
+ // represents a valid end position.
+ return position_type(end(), 0);
+ }
+
+ size_type block_index = get_block_position(pos_hint->__private_data, pos);
+ if (block_index == m_block_store.positions.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::position", __LINE__, pos, block_size(), size());
+
+ iterator it = get_iterator(block_index);
+ size_type start_pos = m_block_store.positions[block_index];
+ return position_type(it, pos - start_pos);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::const_position_type multi_type_vector<Traits>::position(size_type pos) const
+{
+ MDDS_MTV_TRACE_ARGS(accessor, "pos=" << pos);
+
+ if (pos == m_cur_size)
+ {
+ // This is a valid end position. Create a valid position object that
+ // represents a valid end position.
+ return const_position_type(cend(), 0);
+ }
+
+ size_type block_index = get_block_position(pos);
+ if (block_index == m_block_store.positions.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::position", __LINE__, pos, block_size(), size());
+
+ size_type start_pos = m_block_store.positions[block_index];
+
+ const_iterator it = get_const_iterator(block_index);
+ return const_position_type(it, pos - start_pos);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::const_position_type multi_type_vector<Traits>::position(
+ const const_iterator& pos_hint, size_type pos) const
+{
+ MDDS_MTV_TRACE_ARGS(accessor_with_pos_hint, "pos_hint=" << pos_hint << "; pos=" << pos);
+
+ if (pos == m_cur_size)
+ {
+ // This is a valid end position. Create a valid position object that
+ // represents a valid end position.
+ return const_position_type(cend(), 0);
+ }
+
+ size_type block_index = get_block_position(pos_hint->__private_data, pos);
+ if (block_index == m_block_store.positions.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::position", __LINE__, pos, block_size(), size());
+
+ const_iterator it = get_const_iterator(block_index);
+ size_type start_pos = m_block_store.positions[block_index];
+ return const_position_type(it, pos - start_pos);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::transfer(
+ size_type start_pos, size_type end_pos, multi_type_vector& dest, size_type dest_pos)
+{
+ MDDS_MTV_TRACE_ARGS(
+ mutator, "start_pos=" << start_pos << "; end_pos=" << end_pos << "; dest=?; dest_pos=" << dest_pos);
+
+ if (&dest == this)
+ throw invalid_arg_error("You cannot transfer between the same container.");
+
+ size_type block_index1 = get_block_position(start_pos);
+ if (block_index1 == m_block_store.positions.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::transfer", __LINE__, start_pos, block_size(), size());
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ std::ostringstream os_prev_block, os_prev_block_dest;
+ dump_blocks(os_prev_block);
+ dest.dump_blocks(os_prev_block_dest);
+#endif
+
+ iterator ret = transfer_impl(start_pos, end_pos, block_index1, dest, dest_pos);
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ std::ostringstream os_block, os_block_dest;
+ dump_blocks(os_block);
+ dest.dump_blocks(os_block_dest);
+
+ try
+ {
+ check_block_integrity();
+ dest.check_block_integrity();
+ }
+ catch (const mdds::integrity_error& e)
+ {
+ std::ostringstream os;
+ os << e.what() << std::endl;
+ os << std::endl
+ << "block integrity check failed in transfer (start_pos=" << start_pos << "; end_pos=" << end_pos
+ << "; dest_pos=" << dest_pos << ")" << std::endl;
+ os << std::endl << "previous block state (source):" << std::endl;
+ os << os_prev_block.str();
+ os << std::endl << "previous block state (destination):" << std::endl;
+ os << os_prev_block_dest.str();
+ os << std::endl << "altered block state (source):" << std::endl;
+ os << os_block.str();
+ os << std::endl << "altered block state (destination):" << std::endl;
+ os << os_block_dest.str();
+ std::cerr << os.str() << std::endl;
+ abort();
+ }
+#endif
+
+ return ret;
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::transfer(
+ const iterator& pos_hint, size_type start_pos, size_type end_pos, multi_type_vector& dest, size_type dest_pos)
+{
+ MDDS_MTV_TRACE_ARGS(
+ mutator_with_pos_hint, "pos_hint=" << pos_hint << "; start_pos=" << start_pos << "; end_pos=" << end_pos
+ << "; dest=?; dest_pos=" << dest_pos);
+
+ if (&dest == this)
+ throw invalid_arg_error("You cannot transfer between the same container.");
+
+ size_type block_index1 = get_block_position(pos_hint->__private_data, start_pos);
+ if (block_index1 == m_block_store.positions.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::transfer", __LINE__, start_pos, block_size(), size());
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ std::ostringstream os_prev_block, os_prev_block_dest;
+ dump_blocks(os_prev_block);
+ dest.dump_blocks(os_prev_block_dest);
+#endif
+
+ iterator ret = transfer_impl(start_pos, end_pos, block_index1, dest, dest_pos);
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ std::ostringstream os_block, os_block_dest;
+ dump_blocks(os_block);
+ dest.dump_blocks(os_block_dest);
+
+ try
+ {
+ check_block_integrity();
+ dest.check_block_integrity();
+ }
+ catch (const mdds::integrity_error& e)
+ {
+ std::ostringstream os;
+ os << e.what() << std::endl;
+ os << std::endl
+ << "block integrity check failed in transfer (start_pos=" << start_pos << "; end_pos=" << end_pos
+ << "; dest_pos=" << dest_pos << ")" << std::endl;
+ os << std::endl << "previous block state (source):" << std::endl;
+ os << os_prev_block.str();
+ os << std::endl << "previous block state (destination):" << std::endl;
+ os << os_prev_block_dest.str();
+ os << std::endl << "altered block state (source):" << std::endl;
+ os << os_block.str();
+ os << std::endl << "altered block state (destination):" << std::endl;
+ os << os_block_dest.str();
+ std::cerr << os.str() << std::endl;
+ abort();
+ }
+#endif
+
+ return ret;
+}
+
+template<typename Traits>
+template<typename T>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::set(size_type pos, const T& value)
+{
+ MDDS_MTV_TRACE_ARGS(mutator, "pos=" << pos << "; value=? (type=" << mdds_mtv_get_element_type(value) << ")");
+
+ size_type block_index = get_block_position(pos);
+ if (block_index == m_block_store.positions.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::set", __LINE__, pos, block_size(), size());
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ std::ostringstream os_prev_block;
+ dump_blocks(os_prev_block);
+#endif
+
+ iterator ret = set_impl(pos, block_index, value);
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ try
+ {
+ check_block_integrity();
+ }
+ catch (const mdds::integrity_error& e)
+ {
+ std::ostringstream os;
+ os << e.what() << std::endl;
+ os << "block integrity check failed in set (pos=" << pos << ")" << std::endl;
+ os << "previous block state:" << std::endl;
+ os << os_prev_block.str();
+ std::cerr << os.str() << std::endl;
+ abort();
+ }
+#endif
+
+ return ret;
+}
+
+template<typename Traits>
+template<typename T>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::set(
+ const iterator& pos_hint, size_type pos, const T& value)
+{
+ MDDS_MTV_TRACE_ARGS(
+ mutator_with_pos_hint,
+ "pos_hint=" << pos_hint << "; pos=" << pos << "; value=? (type=" << mdds_mtv_get_element_type(value) << ")");
+
+ size_type block_index = get_block_position(pos_hint->__private_data, pos);
+ if (block_index == m_block_store.positions.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::set", __LINE__, pos, block_size(), size());
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ std::ostringstream os_prev_block;
+ dump_blocks(os_prev_block);
+#endif
+
+ iterator ret = set_impl(pos, block_index, value);
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ try
+ {
+ check_block_integrity();
+ }
+ catch (const mdds::integrity_error& e)
+ {
+ std::ostringstream os;
+ os << e.what() << std::endl;
+ os << "block integrity check failed in set (pos=" << pos << ")" << std::endl;
+ os << "previous block state:" << std::endl;
+ os << os_prev_block.str();
+ std::cerr << os.str() << std::endl;
+ abort();
+ }
+#endif
+
+ return ret;
+}
+
+template<typename Traits>
+template<typename T>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::set(
+ size_type pos, const T& it_begin, const T& it_end)
+{
+ MDDS_MTV_TRACE_ARGS(
+ mutator, "pos=" << pos << "; it_begin=?; it_end=? (length=" << std::distance(it_begin, it_end) << ")");
+
+ auto res = mdds::mtv::detail::calc_input_end_position(it_begin, it_end, pos, m_cur_size);
+
+ if (!res.second)
+ return end();
+
+ size_type end_pos = res.first;
+ size_type block_index1 = get_block_position(pos);
+
+ if (block_index1 == m_block_store.positions.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::set", __LINE__, pos, block_size(), size());
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ std::ostringstream os_prev_block;
+ dump_blocks(os_prev_block);
+#endif
+
+ auto ret = set_cells_impl(pos, end_pos, block_index1, it_begin, it_end);
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ try
+ {
+ check_block_integrity();
+ }
+ catch (const mdds::integrity_error& e)
+ {
+ std::ostringstream os;
+ os << e.what() << std::endl;
+ os << "block integrity check failed in set (pos=" << pos << ")" << std::endl;
+ os << "previous block state:" << std::endl;
+ os << os_prev_block.str();
+ std::cerr << os.str() << std::endl;
+ abort();
+ }
+#endif
+
+ return ret;
+}
+
+template<typename Traits>
+template<typename T>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::set(
+ const iterator& pos_hint, size_type pos, const T& it_begin, const T& it_end)
+{
+ MDDS_MTV_TRACE_ARGS(
+ mutator_with_pos_hint, "pos_hint=" << pos_hint << "; pos=" << pos << "; it_begin=?; it_end=? (length="
+ << std::distance(it_begin, it_end) << ")");
+
+ auto res = mdds::mtv::detail::calc_input_end_position(it_begin, it_end, pos, m_cur_size);
+ if (!res.second)
+ return end();
+
+ size_type end_pos = res.first;
+ size_type block_index1 = get_block_position(pos_hint->__private_data, pos);
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ std::ostringstream os_prev_block;
+ dump_blocks(os_prev_block);
+#endif
+
+ auto ret = set_cells_impl(pos, end_pos, block_index1, it_begin, it_end);
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ try
+ {
+ check_block_integrity();
+ }
+ catch (const mdds::integrity_error& e)
+ {
+ std::ostringstream os;
+ os << e.what() << std::endl;
+ os << "block integrity check failed in set (pos=" << pos << ")" << std::endl;
+ os << "previous block state:" << std::endl;
+ os << os_prev_block.str();
+ std::cerr << os.str() << std::endl;
+ abort();
+ }
+#endif
+
+ return ret;
+}
+
+template<typename Traits>
+template<typename T>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::push_back(const T& value)
+{
+ MDDS_MTV_TRACE_ARGS(mutator, "value=? (type=" << mdds_mtv_get_element_type(value) << ")");
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ std::ostringstream os_prev_block;
+ dump_blocks(os_prev_block);
+#endif
+
+ auto ret = push_back_impl(value);
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ try
+ {
+ check_block_integrity();
+ }
+ catch (const mdds::integrity_error& e)
+ {
+ std::ostringstream os;
+ os << e.what() << std::endl;
+ os << "block integrity check failed in push_back" << std::endl;
+ os << "previous block state:" << std::endl;
+ os << os_prev_block.str();
+ std::cerr << os.str() << std::endl;
+ abort();
+ }
+#endif
+
+ return ret;
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::push_back_empty()
+{
+ MDDS_MTV_TRACE(mutator);
+
+ size_type block_index = m_block_store.positions.size();
+
+ if (!append_empty(1))
+ {
+ // Last empty block has been extended.
+ --block_index;
+ }
+
+ // Get the iterator of the last block.
+ return get_iterator(block_index);
+}
+
+template<typename Traits>
+template<typename T>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::insert(
+ size_type pos, const T& it_begin, const T& it_end)
+{
+ MDDS_MTV_TRACE_ARGS(
+ mutator, "pos=" << pos << "it_begin=?; it_end=? (length=" << std::distance(it_begin, it_end) << ")");
+
+ size_type block_index = get_block_position(pos);
+ if (block_index == m_block_store.positions.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::insert", __LINE__, pos, block_size(), size());
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ std::ostringstream os_prev_block;
+ dump_blocks(os_prev_block);
+#endif
+
+ iterator ret = insert_cells_impl(pos, block_index, it_begin, it_end);
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ try
+ {
+ check_block_integrity();
+ }
+ catch (const mdds::integrity_error& e)
+ {
+ std::ostringstream os;
+ os << e.what() << std::endl;
+ element_category_type cat = mdds_mtv_get_element_type(*it_begin);
+ os << "block integrity check failed in insert (pos=" << pos
+ << "; value-size=" << std::distance(it_begin, it_end) << "; value-type=" << cat << ")" << std::endl;
+ os << "previous block state:" << std::endl;
+ os << os_prev_block.str();
+ std::cerr << os.str() << std::endl;
+ abort();
+ }
+#endif
+
+ return ret;
+}
+
+template<typename Traits>
+template<typename T>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::insert(
+ const iterator& pos_hint, size_type pos, const T& it_begin, const T& it_end)
+{
+ MDDS_MTV_TRACE_ARGS(
+ mutator_with_pos_hint, "pos_hint=" << pos_hint << "; pos=" << pos << "; it_begin=?; it_end=? (length="
+ << std::distance(it_begin, it_end) << ")");
+
+ size_type block_index = get_block_position(pos_hint->__private_data, pos);
+ if (block_index == m_block_store.positions.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::insert", __LINE__, pos, block_size(), size());
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ std::ostringstream os_prev_block;
+ dump_blocks(os_prev_block);
+#endif
+
+ iterator ret = insert_cells_impl(pos, block_index, it_begin, it_end);
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ try
+ {
+ check_block_integrity();
+ }
+ catch (const mdds::integrity_error& e)
+ {
+ std::ostringstream os;
+ os << e.what() << std::endl;
+ element_category_type cat = mdds_mtv_get_element_type(*it_begin);
+ os << "block integrity check failed in insert (pos=" << pos
+ << "; value-size=" << std::distance(it_begin, it_end) << "; value-type=" << cat << ")" << std::endl;
+ os << "previous block state:" << std::endl;
+ os << os_prev_block.str();
+ std::cerr << os.str() << std::endl;
+ abort();
+ }
+#endif
+
+ return ret;
+}
+
+template<typename Traits>
+template<typename T>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::push_back_impl(const T& value)
+{
+ element_category_type cat = mdds_mtv_get_element_type(value);
+ element_block_type* last_data =
+ m_block_store.element_blocks.empty() ? nullptr : m_block_store.element_blocks.back();
+
+ if (!last_data || cat != get_block_type(*last_data))
+ {
+ // Either there is no block, or the last block is empty or of different
+ // type. Append a new block.
+ size_type block_index = m_block_store.positions.size();
+ size_type start_pos = m_cur_size;
+
+ m_block_store.push_back(start_pos, 1, nullptr);
+ create_new_block_with_new_cell(block_index, value);
+ ++m_cur_size;
+
+ return get_iterator(block_index);
+ }
+
+ assert(last_data);
+ assert(cat == get_block_type(*last_data));
+
+ // Append the new value to the last block.
+ size_type block_index = m_block_store.positions.size() - 1;
+
+ mdds_mtv_append_value(*last_data, value);
+ ++m_block_store.sizes.back();
+ ++m_cur_size;
+
+ return get_iterator(block_index);
+}
+
+template<typename Traits>
+mtv::element_t multi_type_vector<Traits>::get_type(size_type pos) const
+{
+ MDDS_MTV_TRACE_ARGS(accessor, "pos=" << pos);
+
+ size_type block_index = get_block_position(pos);
+ if (block_index == m_block_store.positions.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::get_type", __LINE__, pos, block_size(), size());
+
+ element_block_type* blk_data = m_block_store.element_blocks[block_index];
+ if (!blk_data)
+ return mtv::element_type_empty;
+
+ return mtv::get_block_type(*blk_data);
+}
+
+template<typename Traits>
+bool multi_type_vector<Traits>::is_empty(size_type pos) const
+{
+ MDDS_MTV_TRACE_ARGS(accessor, "pos=" << pos);
+
+ size_type block_index = get_block_position(pos);
+ if (block_index == m_block_store.positions.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::is_empty", __LINE__, pos, block_size(), size());
+
+ return m_block_store.element_blocks[block_index] == nullptr;
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::set_empty(
+ size_type start_pos, size_type end_pos)
+{
+ MDDS_MTV_TRACE_ARGS(mutator, "start_pos=" << start_pos << "; end_pos=" << end_pos);
+
+ size_type block_index1 = get_block_position(start_pos);
+ if (block_index1 == m_block_store.positions.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::set_empty", __LINE__, start_pos, block_size(), size());
+
+ return set_empty_impl(start_pos, end_pos, block_index1, true);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::set_empty(
+ const iterator& pos_hint, size_type start_pos, size_type end_pos)
+{
+ MDDS_MTV_TRACE_ARGS(
+ mutator_with_pos_hint, "pos_hint=" << pos_hint << "; start_pos=" << start_pos << "; end_pos=" << end_pos);
+
+ size_type block_index1 = get_block_position(pos_hint->__private_data, start_pos);
+ if (block_index1 == m_block_store.positions.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::set_empty", __LINE__, start_pos, block_size(), size());
+
+ return set_empty_impl(start_pos, end_pos, block_index1, true);
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::erase(size_type start_pos, size_type end_pos)
+{
+ MDDS_MTV_TRACE_ARGS(mutator, "start_pos=" << start_pos << "; end_pos=" << end_pos);
+
+ if (start_pos > end_pos)
+ throw std::out_of_range("Start row is larger than the end row.");
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ std::ostringstream os_prev_block;
+ dump_blocks(os_prev_block);
+#endif
+
+ erase_impl(start_pos, end_pos);
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ try
+ {
+ check_block_integrity();
+ }
+ catch (const mdds::integrity_error& e)
+ {
+ std::ostringstream os;
+ os << e.what() << std::endl;
+ os << "block integrity check failed in erase (" << start_pos << "-" << end_pos << ")" << std::endl;
+ os << "block integrity check failed in push_back" << std::endl;
+ os << "previous block state:" << std::endl;
+ os << os_prev_block.str();
+ std::cerr << os.str() << std::endl;
+ abort();
+ }
+#endif
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::insert_empty(size_type pos, size_type length)
+{
+ MDDS_MTV_TRACE_ARGS(mutator, "pos=" << pos << "; length=" << length);
+
+ if (!length)
+ // Nothing to insert.
+ return end();
+
+ size_type block_index = get_block_position(pos);
+ if (block_index == m_block_store.positions.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::insert_empty", __LINE__, pos, block_size(), size());
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ std::ostringstream os_prev_block;
+ dump_blocks(os_prev_block);
+#endif
+
+ iterator ret = insert_empty_impl(pos, block_index, length);
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ try
+ {
+ check_block_integrity();
+ }
+ catch (const mdds::integrity_error& e)
+ {
+ std::ostringstream os;
+ os << e.what() << std::endl;
+ os << "block integrity check failed in insert_empty (pos=" << pos << "; length=" << length << ")" << std::endl;
+ os << "previous block state:" << std::endl;
+ os << os_prev_block.str();
+ std::cerr << os.str() << std::endl;
+ abort();
+ }
+#endif
+
+ return ret;
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::insert_empty(
+ const iterator& pos_hint, size_type pos, size_type length)
+{
+ MDDS_MTV_TRACE_ARGS(mutator_with_pos_hint, "pos_hint=" << pos_hint << "; pos=" << pos << "; length=" << length);
+
+ if (!length)
+ // Nothing to insert.
+ return end();
+
+ size_type block_index = get_block_position(pos_hint->__private_data, pos);
+ if (block_index == m_block_store.positions.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::insert_empty", __LINE__, pos, block_size(), size());
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ std::ostringstream os_prev_block;
+ dump_blocks(os_prev_block);
+#endif
+
+ iterator ret = insert_empty_impl(pos, block_index, length);
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ try
+ {
+ check_block_integrity();
+ }
+ catch (const mdds::integrity_error& e)
+ {
+ std::ostringstream os;
+ os << e.what() << std::endl;
+ os << "block integrity check failed in insert_empty (pos=" << pos << "; length=" << length << ")" << std::endl;
+ os << "previous block state:" << std::endl;
+ os << os_prev_block.str();
+ std::cerr << os.str() << std::endl;
+ abort();
+ }
+#endif
+
+ return ret;
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::clear()
+{
+ MDDS_MTV_TRACE(mutator);
+
+ delete_element_blocks(0, m_block_store.element_blocks.size());
+ m_block_store.clear();
+ m_cur_size = 0;
+}
+
+template<typename Traits>
+template<typename T>
+bool multi_type_vector<Traits>::set_cells_precheck(
+ size_type pos, const T& it_begin, const T& it_end, size_type& end_pos)
+{
+ size_type length = std::distance(it_begin, it_end);
+ if (!length)
+ // empty data array. nothing to do.
+ return false;
+
+ end_pos = pos + length - 1;
+ if (end_pos >= m_cur_size)
+ throw std::out_of_range("Data array is too long.");
+
+ return true;
+}
+
+template<typename Traits>
+template<typename T>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::set_impl(
+ size_type pos, size_type block_index, const T& value)
+{
+ size_type start_row = m_block_store.positions[block_index];
+
+ size_type blk_size = m_block_store.sizes[block_index];
+ element_block_type* blk_data = m_block_store.element_blocks[block_index];
+ assert(blk_size > 0); // block size should never be zero at any time.
+
+ assert(pos >= start_row);
+ size_type pos_in_block = pos - start_row;
+ assert(pos_in_block < blk_size);
+
+ if (!blk_data)
+ {
+ // This is an empty block.
+ return set_cell_to_empty_block(block_index, pos_in_block, value);
+ }
+
+ element_category_type cat = mdds_mtv_get_element_type(value);
+
+ assert(blk_data);
+ element_category_type blk_cat = mdds::mtv::get_block_type(*blk_data);
+
+ if (blk_cat == cat)
+ {
+ // This block is of the same type as the cell being inserted.
+ size_type i = pos - start_row;
+ block_funcs::overwrite_values(*blk_data, i, 1);
+ mdds_mtv_set_value(*blk_data, i, value);
+ return get_iterator(block_index);
+ }
+
+ assert(blk_cat != cat);
+
+ if (pos == start_row)
+ {
+ // t|???|x--|???|b - Insertion point is at the start of the block.
+ if (blk_size == 1)
+ {
+ // t|???|x|???|b
+ return set_cell_to_non_empty_block_of_size_one(block_index, value);
+ }
+
+ assert(blk_size > 1);
+ bool blk_prev = is_previous_block_of_type(block_index, cat);
+ if (blk_prev)
+ {
+ // t|xxx|x--|???|b - Append to the previous block.
+ m_block_store.sizes[block_index] -= 1;
+ m_block_store.positions[block_index] += 1;
+ block_funcs::overwrite_values(*m_block_store.element_blocks[block_index], 0, 1);
+ block_funcs::erase(*m_block_store.element_blocks[block_index], 0);
+ m_block_store.sizes[block_index - 1] += 1;
+ mdds_mtv_append_value(*m_block_store.element_blocks[block_index - 1], value);
+ return get_iterator(block_index - 1);
+ }
+
+ // t|---|x--|???|b
+ set_cell_to_top_of_data_block(block_index, value);
+ return get_iterator(block_index);
+ }
+
+ if (pos < (start_row + blk_size - 1))
+ {
+ // t|???|-x-|???|b - Insertion point is in the middle of the block.
+ return set_cell_to_middle_of_block(block_index, pos_in_block, value);
+ }
+
+ // t|???|--x|???|b - Insertion point is at the end of the block.
+ assert(pos == (start_row + m_block_store.sizes[block_index] - 1));
+ assert(pos > start_row);
+ assert(m_block_store.sizes[block_index] > 1);
+
+ if (block_index == 0)
+ {
+ if (m_block_store.positions.size() == 1)
+ {
+ // t|--x|b - This is the only block.
+ set_cell_to_bottom_of_data_block(0, value);
+ iterator itr = end();
+ return --itr;
+ }
+
+ bool blk_next = is_next_block_of_type(block_index, cat);
+ if (!blk_next)
+ {
+ // t|--x|---|b - Next block is of different type.
+ set_cell_to_bottom_of_data_block(0, value);
+ iterator itr = begin();
+ return ++itr;
+ }
+
+ // t|--x|xxx|b - Next block is of the same type as the new value.
+
+ block_funcs::overwrite_values(*blk_data, blk_size - 1, 1);
+ block_funcs::erase(*blk_data, blk_size - 1);
+ m_block_store.sizes[block_index] -= 1;
+ mdds_mtv_prepend_value(*m_block_store.element_blocks[block_index + 1], value);
+ m_block_store.sizes[block_index + 1] += 1;
+ m_block_store.positions[block_index + 1] -= 1;
+
+ return get_iterator(block_index + 1);
+ }
+
+ assert(block_index > 0);
+
+ if (block_index == m_block_store.positions.size() - 1)
+ {
+ // t|???|--x|b - This is the last block.
+ set_cell_to_bottom_of_data_block(block_index, value);
+ iterator itr = end();
+ return --itr;
+ }
+
+ bool blk_next = is_next_block_of_type(block_index, cat);
+ if (!blk_next)
+ {
+ // t|???|--x|---|b - Next block is of different type than the new
+ // value's.
+ set_cell_to_bottom_of_data_block(block_index, value);
+ return get_iterator(block_index + 1);
+ }
+
+ // t|???|--x|xxx|b - The next block is of the same type as the new value.
+ block_funcs::overwrite_values(*blk_data, blk_size - 1, 1);
+ block_funcs::erase(*blk_data, blk_size - 1);
+ m_block_store.sizes[block_index] -= 1;
+ mdds_mtv_prepend_value(*m_block_store.element_blocks[block_index + 1], value);
+ m_block_store.sizes[block_index + 1] += 1;
+ m_block_store.positions[block_index + 1] -= 1;
+
+ return get_iterator(block_index + 1);
+}
+
+template<typename Traits>
+template<typename T>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::release_impl(
+ size_type pos, size_type block_index, T& value)
+{
+ const element_block_type* blk_data = m_block_store.element_blocks[block_index];
+ size_type start_pos = m_block_store.positions[block_index];
+
+ if (!blk_data)
+ {
+ // Empty cell block. There is no element to release.
+ mdds_mtv_get_empty_value(value);
+ return get_iterator(block_index);
+ }
+
+ assert(pos >= start_pos);
+ assert(blk_data); // data for non-empty blocks should never be nullptr.
+ size_type offset = pos - start_pos;
+ mdds_mtv_get_value(*blk_data, offset, value);
+
+ // Set the element slot empty without overwriting it.
+ return set_empty_in_single_block(pos, pos, block_index, false);
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::swap_impl(
+ multi_type_vector& other, size_type start_pos, size_type end_pos, size_type other_pos, size_type block_index1,
+ size_type block_index2, size_type dblock_index1, size_type dblock_index2)
+{
+ if (block_index1 == block_index2)
+ {
+ // Source range is in a single block.
+ if (dblock_index1 == dblock_index2)
+ {
+ // Destination range is also in a single block.
+ swap_single_block(other, start_pos, end_pos, other_pos, block_index1, dblock_index1);
+ }
+ else
+ {
+ // Source is single-, and destination is multi-blocks.
+ swap_single_to_multi_blocks(
+ other, start_pos, end_pos, other_pos, block_index1, dblock_index1, dblock_index2);
+ }
+ }
+ else if (dblock_index1 == dblock_index2)
+ {
+ // Destination range is over a single block. Switch source and destination.
+ size_type len = end_pos - start_pos + 1;
+ other.swap_single_to_multi_blocks(
+ *this, other_pos, other_pos + len - 1, start_pos, dblock_index1, block_index1, block_index2);
+ }
+ else
+ {
+ // Both source and destinations are multi-block.
+ swap_multi_to_multi_blocks(
+ other, start_pos, end_pos, other_pos, block_index1, block_index2, dblock_index1, dblock_index2);
+ }
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::swap_single_block(
+ multi_type_vector& other, size_type start_pos, size_type end_pos, size_type other_pos, size_type block_index,
+ size_type other_block_index)
+{
+ element_block_type* src_data = m_block_store.element_blocks[block_index];
+ element_block_type* dst_data = other.m_block_store.element_blocks[other_block_index];
+ size_type start_pos_in_block = m_block_store.positions[block_index];
+ size_type start_pos_in_other_block = other.m_block_store.positions[other_block_index];
+ element_category_type cat_src = mtv::element_type_empty;
+ element_category_type cat_dst = mtv::element_type_empty;
+
+ if (src_data)
+ cat_src = mtv::get_block_type(*src_data);
+ if (dst_data)
+ cat_dst = mtv::get_block_type(*dst_data);
+
+ size_t other_end_pos = other_pos + end_pos - start_pos;
+ size_t len = end_pos - start_pos + 1; // length of elements to swap.
+ size_type src_offset = start_pos - start_pos_in_block;
+ size_type dst_offset = other_pos - start_pos_in_other_block;
+
+ // length of the tail that will not be swapped.
+ size_type src_tail_len = m_block_store.sizes[block_index] - src_offset - len;
+
+ if (cat_src == cat_dst)
+ {
+ // Source and destination blocks are of the same type.
+ if (cat_src == mtv::element_type_empty)
+ // Both are empty blocks. Nothing to swap.
+ return;
+
+ block_funcs::swap_values(*src_data, *dst_data, src_offset, dst_offset, len);
+ return;
+ }
+
+ // Source and destination blocks are of different types.
+
+ if (cat_src == mtv::element_type_empty)
+ {
+ // Source is empty but destination is not. This is equivalent of transfer.
+ other.transfer_single_block(other_pos, other_end_pos, other_block_index, *this, start_pos);
+ // No update of local index vars needed.
+ return;
+ }
+
+ if (cat_dst == mtv::element_type_empty)
+ {
+ // Source is not empty but destination is. Use transfer.
+ transfer_single_block(start_pos, end_pos, block_index, other, other_pos);
+ // No update of local index vars needed.
+ return;
+ }
+
+ // Neither the source nor destination blocks are empty, and they are of different types.
+ if (src_offset == 0)
+ {
+ // Source range is at the top of a block.
+ if (src_tail_len == 0)
+ {
+ // the entire source block needs to be replaced.
+ std::unique_ptr<element_block_type, element_block_deleter> src_data_original(src_data);
+ m_hdl_event.element_block_released(src_data);
+ m_block_store.element_blocks[block_index] =
+ other.exchange_elements(*src_data_original, src_offset, other_block_index, dst_offset, len);
+ src_data = m_block_store.element_blocks[block_index];
+ m_hdl_event.element_block_acquired(src_data);
+
+ // Release elements in the source block to prevent double-deletion.
+ block_funcs::resize_block(*src_data_original, 0);
+ merge_with_adjacent_blocks(block_index);
+ return;
+ }
+
+ // Get the new elements from the other container.
+ std::unique_ptr<element_block_type, element_block_deleter> new_dst_data(
+ other.exchange_elements(*src_data, src_offset, other_block_index, dst_offset, len));
+
+ // Shrink the current block by erasing the top part.
+ block_funcs::erase(*src_data, 0, len);
+ m_block_store.positions[block_index] += len;
+ m_block_store.sizes[block_index] -= len;
+
+ bool blk_prev = is_previous_block_of_type(block_index, cat_dst);
+ if (blk_prev)
+ {
+ // Append the new elements to the previous block.
+ element_block_type* prev_data = m_block_store.element_blocks[block_index - 1];
+ block_funcs::append_block(*prev_data, *new_dst_data);
+ block_funcs::resize_block(*new_dst_data, 0); // prevent double-delete.
+ m_block_store.sizes[block_index - 1] += len;
+ }
+ else
+ {
+ // Insert a new block to store the new elements.
+ size_type position = m_block_store.positions[block_index] - len;
+ m_block_store.insert(block_index, position, len, nullptr);
+ m_block_store.element_blocks[block_index] = new_dst_data.release();
+ m_hdl_event.element_block_acquired(m_block_store.element_blocks[block_index]);
+ }
+ return;
+ }
+
+ // Get the new elements from the destination instance.
+ std::unique_ptr<element_block_type, element_block_deleter> data_from_dst(
+ other.exchange_elements(*src_data, src_offset, other_block_index, dst_offset, len));
+
+ if (src_tail_len == 0)
+ {
+ // Source range is at the bottom of a block.
+
+ // Shrink the current block.
+ block_funcs::resize_block(*src_data, src_offset);
+ m_block_store.sizes[block_index] = src_offset;
+
+ if (is_next_block_of_type(block_index, cat_dst))
+ {
+ // Merge with the next block.
+ element_block_type* next_data = m_block_store.element_blocks[block_index + 1];
+ block_funcs::prepend_values_from_block(*next_data, *data_from_dst, 0, len);
+ block_funcs::resize_block(*data_from_dst, 0); // prevent double-delete.
+ m_block_store.sizes[block_index + 1] += len;
+ m_block_store.positions[block_index + 1] -= len;
+ }
+ else
+ {
+ m_block_store.insert(block_index + 1, 0, len, nullptr);
+ m_block_store.calc_block_position(block_index + 1);
+ m_block_store.element_blocks[block_index + 1] = data_from_dst.release();
+ m_hdl_event.element_block_acquired(m_block_store.element_blocks[block_index + 1]);
+ }
+ return;
+ }
+
+ // Source range is in the middle of a block.
+ assert(src_offset && src_tail_len);
+ block_index = set_new_block_to_middle(block_index, src_offset, len, false);
+ m_block_store.element_blocks[block_index] = data_from_dst.release();
+ m_hdl_event.element_block_acquired(m_block_store.element_blocks[block_index]);
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::swap_single_to_multi_blocks(
+ multi_type_vector& other, size_type start_pos, size_type end_pos, size_type other_pos, size_type block_index,
+ size_type dst_block_index1, size_type dst_block_index2)
+{
+ element_block_type* src_data = m_block_store.element_blocks[block_index];
+ size_type start_pos_in_block = m_block_store.positions[block_index];
+ size_type dst_start_pos_in_block1 = other.m_block_store.positions[dst_block_index1];
+ size_type dst_start_pos_in_block2 = other.m_block_store.positions[dst_block_index2];
+
+ element_category_type cat_src = src_data ? mtv::get_block_type(*src_data) : mtv::element_type_empty;
+
+ size_type len = end_pos - start_pos + 1;
+
+ if (cat_src == mtv::element_type_empty)
+ {
+ // The source block is empty. Use transfer.
+ other.transfer_multi_blocks(
+ other_pos, other_pos + len - 1, dst_block_index1, dst_block_index2, *this, start_pos);
+ return;
+ }
+
+ size_type src_offset = start_pos - start_pos_in_block;
+ size_type dst_offset1 = other_pos - dst_start_pos_in_block1;
+ size_type dst_offset2 = other_pos + len - 1 - dst_start_pos_in_block2;
+
+ // length of the tail that will not be swapped.
+ size_type src_tail_len = m_block_store.sizes[block_index] - src_offset - len;
+
+ // Get the new elements from the other instance.
+ blocks_type new_blocks;
+ other.exchange_elements(
+ *src_data, src_offset, dst_block_index1, dst_offset1, dst_block_index2, dst_offset2, len, new_blocks);
+
+ new_blocks.check_integrity();
+
+ if (new_blocks.positions.empty())
+ throw general_error("multi_type_vector::swap_single_to_multi_blocks: failed to exchange elements.");
+
+ if (src_offset == 0)
+ {
+ // Source range is at the top of a block.
+
+ size_type src_position = m_block_store.positions[block_index];
+
+ if (src_tail_len == 0)
+ {
+ // the whole block needs to be replaced. Delete the block, but
+ // don't delete the managed elements the block contains since they
+ // have been transferred over to the destination block.
+ block_funcs::resize_block(*src_data, 0);
+ delete_element_block(block_index);
+ m_block_store.erase(block_index);
+ }
+ else
+ {
+ // Shrink the current block by erasing the top part.
+ block_funcs::erase(*src_data, 0, len);
+ m_block_store.sizes[block_index] -= len;
+ m_block_store.positions[block_index] += len;
+ }
+
+ insert_blocks_at(src_position, block_index, new_blocks);
+ merge_with_next_block(block_index + new_blocks.positions.size() - 1); // last block inserted.
+ if (block_index > 0)
+ merge_with_next_block(block_index - 1); // block before the first block inserted.
+
+ return;
+ }
+
+ size_type position = 0;
+
+ if (src_tail_len == 0)
+ {
+ // Source range is at the bottom of a block.
+
+ // Shrink the current block.
+ block_funcs::resize_block(*src_data, src_offset);
+ m_block_store.sizes[block_index] = src_offset;
+ position = m_block_store.positions[block_index] + m_block_store.sizes[block_index];
+ }
+ else
+ {
+ // Source range is in the middle of a block.
+ assert(src_offset && src_tail_len);
+
+ // This will create two new slots at block_index+1, the first of which
+ // we will immediately remove. The new blocks from the other
+ // container will be inserted at the removed slot.
+ set_new_block_to_middle(block_index, src_offset, len, false);
+ delete_element_block(block_index + 1);
+ m_block_store.erase(block_index + 1);
+ position = m_block_store.positions[block_index] + m_block_store.sizes[block_index];
+ }
+
+ insert_blocks_at(position, block_index + 1, new_blocks);
+ merge_with_next_block(block_index + new_blocks.positions.size()); // last block inserted.
+ merge_with_next_block(block_index); // block before the first block inserted.
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::swap_multi_to_multi_blocks(
+ multi_type_vector& other, size_type start_pos, size_type end_pos, size_type other_pos, size_type block_index1,
+ size_type block_index2, size_type dblock_index1, size_type dblock_index2)
+{
+ assert(block_index1 < block_index2);
+ assert(dblock_index1 < dblock_index2);
+
+ size_type start_pos_in_block1 = m_block_store.positions[block_index1];
+ size_type start_pos_in_block2 = m_block_store.positions[block_index2];
+ size_type start_pos_in_dblock1 = other.m_block_store.positions[dblock_index1];
+ size_type start_pos_in_dblock2 = other.m_block_store.positions[dblock_index2];
+
+ size_type len = end_pos - start_pos + 1;
+ size_type src_offset1 = start_pos - start_pos_in_block1;
+ size_type src_offset2 = end_pos - start_pos_in_block2;
+ size_type dst_offset1 = other_pos - start_pos_in_dblock1;
+ size_type dst_offset2 = other_pos + len - 1 - start_pos_in_dblock2;
+
+ blocks_to_transfer src_bucket, dst_bucket;
+ prepare_blocks_to_transfer(src_bucket, block_index1, src_offset1, block_index2, src_offset2);
+ other.prepare_blocks_to_transfer(dst_bucket, dblock_index1, dst_offset1, dblock_index2, dst_offset2);
+
+ size_type position = 0;
+ if (src_bucket.insert_index > 0)
+ {
+ size_type i = src_bucket.insert_index - 1;
+ position = m_block_store.positions[i] + m_block_store.sizes[i];
+ }
+ insert_blocks_at(position, src_bucket.insert_index, dst_bucket.blocks);
+
+ // Merge the boundary blocks in the source.
+ merge_with_next_block(src_bucket.insert_index + dst_bucket.blocks.positions.size() - 1);
+ if (src_bucket.insert_index > 0)
+ merge_with_next_block(src_bucket.insert_index - 1);
+
+ position = 0;
+ if (dst_bucket.insert_index > 0)
+ {
+ size_type i = dst_bucket.insert_index - 1;
+ position = other.m_block_store.positions[i] + other.m_block_store.sizes[i];
+ }
+ other.insert_blocks_at(position, dst_bucket.insert_index, src_bucket.blocks);
+
+ // Merge the boundary blocks in the destination.
+ other.merge_with_next_block(dst_bucket.insert_index + src_bucket.blocks.positions.size() - 1);
+ if (dst_bucket.insert_index > 0)
+ other.merge_with_next_block(dst_bucket.insert_index - 1);
+}
+
+template<typename Traits>
+template<typename T>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::insert_cells_impl(
+ size_type row, size_type block_index, const T& it_begin, const T& it_end)
+{
+ size_type start_row = m_block_store.positions[block_index];
+ size_type length = std::distance(it_begin, it_end);
+ if (!length)
+ // empty data array. nothing to do.
+ return end();
+
+ element_category_type cat = mdds_mtv_get_element_type(*it_begin);
+ element_block_type* blk_data = m_block_store.element_blocks[block_index];
+ if (!blk_data)
+ {
+ if (row == start_row)
+ {
+ // Insert into an empty block. Check the previos block (if exists)
+ // to see if the data can be appended to it if inserting at the top
+ // of the block.
+ bool blk0 = is_previous_block_of_type(block_index, cat);
+
+ if (blk0)
+ {
+ // Append to the previous block.
+ element_block_type* blk0_data = m_block_store.element_blocks[block_index - 1];
+ mdds_mtv_append_values(*blk0_data, *it_begin, it_begin, it_end);
+ m_block_store.sizes[block_index - 1] += length;
+ m_cur_size += length;
+ adjust_block_positions_func{}(m_block_store, block_index, length);
+
+ return get_iterator(block_index - 1);
+ }
+
+ // Just insert a new block before the current block.
+ size_type position = m_block_store.positions[block_index];
+ m_block_store.insert(block_index, position, length, nullptr);
+ m_block_store.element_blocks[block_index] = block_funcs::create_new_block(cat, 0);
+ blk_data = m_block_store.element_blocks[block_index];
+ m_hdl_event.element_block_acquired(blk_data);
+ mdds_mtv_assign_values(*blk_data, *it_begin, it_begin, it_end);
+ m_cur_size += length;
+ adjust_block_positions_func{}(m_block_store, block_index + 1, length);
+
+ return get_iterator(block_index);
+ }
+
+ insert_cells_to_middle(row, block_index, it_begin, it_end);
+ m_cur_size += length;
+
+ return get_iterator(block_index + 1);
+ }
+
+ assert(blk_data);
+ element_category_type blk_cat = mdds::mtv::get_block_type(*blk_data);
+ if (cat == blk_cat)
+ {
+ // Simply insert the new data series into existing block.
+ assert(it_begin != it_end);
+ mdds_mtv_insert_values(*blk_data, row - start_row, *it_begin, it_begin, it_end);
+ m_block_store.sizes[block_index] += length;
+ m_cur_size += length;
+ adjust_block_positions_func{}(m_block_store, block_index + 1, length);
+
+ return get_iterator(block_index);
+ }
+
+ assert(cat != blk_cat);
+ if (row == start_row)
+ {
+ // Check the previous block to see if we can append the data there.
+ bool blk0 = is_previous_block_of_type(block_index, cat);
+ if (blk0)
+ {
+ // Append to the previous block.
+ element_block_type* blk0_data = m_block_store.element_blocks[block_index - 1];
+ mdds_mtv_append_values(*blk0_data, *it_begin, it_begin, it_end);
+ m_block_store.sizes[block_index - 1] += length;
+ m_cur_size += length;
+ adjust_block_positions_func{}(m_block_store, block_index, length);
+
+ return get_iterator(block_index - 1);
+ }
+
+ // Just insert a new block before the current block.
+ m_block_store.insert(block_index, m_block_store.positions[block_index], length, nullptr);
+ m_block_store.element_blocks[block_index] = block_funcs::create_new_block(cat, 0);
+ m_hdl_event.element_block_acquired(blk_data);
+ blk_data = m_block_store.element_blocks[block_index];
+ mdds_mtv_assign_values(*blk_data, *it_begin, it_begin, it_end);
+ m_block_store.sizes[block_index] = length;
+ m_cur_size += length;
+ adjust_block_positions_func{}(m_block_store, block_index + 1, length);
+
+ return get_iterator(block_index);
+ }
+
+ insert_cells_to_middle(row, block_index, it_begin, it_end);
+ m_cur_size += length;
+
+ return get_iterator(block_index + 1);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::set_empty_impl(
+ size_type start_pos, size_type end_pos, size_type block_index1, bool overwrite)
+{
+ if (start_pos > end_pos)
+ throw std::out_of_range("Start row is larger than the end row.");
+
+ size_type block_index2 = get_block_position(end_pos, block_index1);
+ if (block_index2 == m_block_store.positions.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::set_empty_impl", __LINE__, end_pos, block_size(), size());
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ std::ostringstream os_prev_block;
+ dump_blocks(os_prev_block);
+#endif
+
+ iterator ret_it;
+ if (block_index1 == block_index2)
+ ret_it = set_empty_in_single_block(start_pos, end_pos, block_index1, overwrite);
+ else
+ ret_it = set_empty_in_multi_blocks(start_pos, end_pos, block_index1, block_index2, overwrite);
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ try
+ {
+ check_block_integrity();
+ }
+ catch (const mdds::integrity_error& e)
+ {
+ std::ostringstream os;
+ os << e.what() << std::endl;
+ os << "block integrity check failed in set_empty(start_pos=" << start_pos << "; end_pos=" << end_pos << ")"
+ << std::endl;
+ os << "previous block state:" << std::endl;
+ os << os_prev_block.str();
+ std::cerr << os.str() << std::endl;
+ abort();
+ }
+#endif
+ return ret_it;
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::set_empty_in_single_block(
+ size_type start_row, size_type end_row, size_type block_index, bool overwrite)
+{
+ // Range is within a single block.
+ element_block_type* blk_data = m_block_store.element_blocks[block_index];
+ if (!blk_data)
+ // This block is already empty. Do nothing.
+ return get_iterator(block_index);
+
+ size_type start_row_in_block = m_block_store.positions[block_index];
+ assert(start_row_in_block + m_block_store.sizes[block_index] >= 1);
+ size_type end_row_in_block = start_row_in_block + m_block_store.sizes[block_index] - 1;
+ size_type empty_block_size = end_row - start_row + 1;
+
+ if (start_row == start_row_in_block)
+ {
+ // start row coincides with the start of a block.
+
+ if (end_row == end_row_in_block)
+ return set_whole_block_empty(block_index, overwrite);
+
+ // Set the upper part of the block empty.
+ if (overwrite)
+ block_funcs::overwrite_values(*blk_data, 0, empty_block_size);
+
+ block_funcs::erase(*blk_data, 0, empty_block_size);
+ m_block_store.sizes[block_index] -= empty_block_size;
+
+ // Check if the preceding block (if exists) is also empty.
+ bool blk_prev = is_previous_block_of_type(block_index, mtv::element_type_empty);
+ if (blk_prev)
+ {
+ // Extend the previous empty block.
+ m_block_store.sizes[block_index - 1] += empty_block_size;
+ m_block_store.positions[block_index] += empty_block_size;
+ return get_iterator(block_index - 1);
+ }
+
+ // Insert a new empty block before the current one.
+ size_type block_position = m_block_store.positions[block_index];
+ m_block_store.positions[block_index] += empty_block_size;
+ m_block_store.insert(block_index, block_position, empty_block_size, nullptr);
+ return get_iterator(block_index);
+ }
+
+ if (end_row == end_row_in_block)
+ {
+ // end row equals the end of a block.
+ assert(start_row > start_row_in_block);
+
+ // Set the lower part of the block empty.
+ size_type start_pos = start_row - start_row_in_block;
+ if (overwrite)
+ block_funcs::overwrite_values(*blk_data, start_pos, empty_block_size);
+
+ block_funcs::erase(*blk_data, start_pos, empty_block_size);
+ m_block_store.sizes[block_index] -= empty_block_size;
+
+ // Check if the following block (if exists) is also empty.
+ bool blk_next = is_next_block_of_type(block_index, mtv::element_type_empty);
+ if (blk_next)
+ {
+ // Extend the next empty block to cover the new empty segment.
+ m_block_store.sizes[block_index + 1] += empty_block_size;
+ m_block_store.positions[block_index + 1] = start_row;
+ }
+ else
+ {
+ // Insert a new empty block after the current one.
+ m_block_store.insert(block_index + 1, start_row, empty_block_size, nullptr);
+ }
+
+ return get_iterator(block_index + 1);
+ }
+
+ // Empty the middle part of a block.
+ assert(end_row_in_block - end_row > 0);
+ set_new_block_to_middle(block_index, start_row - start_row_in_block, empty_block_size, overwrite);
+
+ return get_iterator(block_index + 1);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::set_empty_in_multi_blocks(
+ size_type start_row, size_type end_row, size_type block_index1, size_type block_index2, bool overwrite)
+{
+ assert(block_index1 < block_index2);
+ size_type start_row_in_block1 = m_block_store.positions[block_index1];
+ size_type start_row_in_block2 = m_block_store.positions[block_index2];
+
+ {
+ // Empty the lower part of the first block.
+ element_block_type* blk_data = m_block_store.element_blocks[block_index1];
+ if (blk_data)
+ {
+ if (start_row_in_block1 == start_row)
+ {
+ // Empty the whole block.
+
+ bool prev_empty = is_previous_block_of_type(block_index1, mtv::element_type_empty);
+ if (prev_empty)
+ {
+ // Previous block is empty. Move the start row to the
+ // first row of the previous block, and make the previous
+ // block 'block 1'.
+ start_row -= m_block_store.sizes[block_index1 - 1];
+ --block_index1;
+ }
+ else
+ {
+ // Make block 1 empty.
+ if (!overwrite)
+ block_funcs::resize_block(*blk_data, 0);
+
+ delete_element_block(block_index1);
+ }
+ }
+ else
+ {
+ // Empty the lower part.
+ size_type new_size = start_row - start_row_in_block1;
+ if (overwrite)
+ block_funcs::overwrite_values(*blk_data, new_size, m_block_store.sizes[block_index1] - new_size);
+
+ block_funcs::resize_block(*blk_data, new_size);
+ m_block_store.sizes[block_index1] = new_size;
+ }
+ }
+ else
+ {
+ // First block is already empty. Adjust the start row of the new
+ // empty range.
+ start_row = start_row_in_block1;
+ }
+ }
+
+ size_type end_block_to_erase = block_index2; // End block position is non-inclusive.
+
+ {
+ // Empty the upper part of the last block.
+ element_block_type* blk_data = m_block_store.element_blocks[block_index2];
+ size_type last_row_in_block = start_row_in_block2 + m_block_store.sizes[block_index2] - 1;
+
+ if (blk_data)
+ {
+ if (last_row_in_block == end_row)
+ {
+ // Delete the whole block.
+ ++end_block_to_erase;
+
+ // Check if the following block (if exists) is also empty.
+ bool next_empty = is_next_block_of_type(block_index2, mtv::element_type_empty);
+ if (next_empty)
+ {
+ // The following block is also empty.
+ end_row += m_block_store.sizes[block_index2 + 1];
+ ++end_block_to_erase;
+ }
+ }
+ else
+ {
+ // Empty the upper part.
+ size_type size_to_erase = end_row - start_row_in_block2 + 1;
+ if (overwrite)
+ block_funcs::overwrite_values(*blk_data, 0, size_to_erase);
+
+ block_funcs::erase(*blk_data, 0, size_to_erase);
+ m_block_store.sizes[block_index2] -= size_to_erase;
+ m_block_store.positions[block_index2] = start_row_in_block2 + size_to_erase;
+ }
+ }
+ else
+ {
+ // Last block is empty. Delete this block and adjust the end row
+ // of the new empty range.
+ ++end_block_to_erase;
+ end_row = last_row_in_block;
+ }
+ }
+
+ if (end_block_to_erase - block_index1 > 1)
+ {
+ // Remove all blocks in-between, from block_index1+1 to end_block_to_erase-1.
+
+ for (size_type i = block_index1 + 1; i < end_block_to_erase; ++i)
+ {
+ element_block_type* data = m_block_store.element_blocks[i];
+ if (!overwrite && data)
+ block_funcs::resize_block(*data, 0);
+
+ delete_element_block(i);
+ }
+
+ size_type n_erase_blocks = end_block_to_erase - block_index1 - 1;
+ m_block_store.erase(block_index1 + 1, n_erase_blocks);
+ }
+
+ element_block_type* blk_data = m_block_store.element_blocks[block_index1];
+ size_type empty_block_size = end_row - start_row + 1;
+ if (blk_data)
+ {
+ // Insert a new empty block after the first block.
+ m_block_store.insert(block_index1 + 1, start_row, empty_block_size, nullptr);
+ return get_iterator(block_index1 + 1);
+ }
+
+ // Current block is already empty. Just extend its size.
+ m_block_store.sizes[block_index1] = empty_block_size;
+ m_block_store.positions[block_index1] = start_row;
+ return get_iterator(block_index1);
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::erase_impl(size_type start_row, size_type end_row)
+{
+ assert(start_row <= end_row);
+
+ // Keep the logic similar to set_empty().
+
+ size_type block_pos1 = get_block_position(start_row);
+ if (block_pos1 == m_block_store.positions.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::erase_impl", __LINE__, start_row, block_size(), size());
+
+ size_type block_pos2 = get_block_position(end_row, block_pos1);
+ if (block_pos2 == m_block_store.positions.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::erase_impl", __LINE__, start_row, block_size(), size());
+
+ size_type start_row_in_block1 = m_block_store.positions[block_pos1];
+ size_type start_row_in_block2 = m_block_store.positions[block_pos2];
+
+ if (block_pos1 == block_pos2)
+ {
+ erase_in_single_block(start_row, end_row, block_pos1);
+ return;
+ }
+
+ assert(block_pos1 < block_pos2);
+
+ // Initially, we set to erase all blocks between the first and the last.
+ size_type index_erase_begin = block_pos1 + 1;
+ size_type index_erase_end = block_pos2;
+
+ // First, inspect the first block.
+ if (start_row_in_block1 == start_row)
+ {
+ // Erase the whole block.
+ --index_erase_begin;
+ }
+ else
+ {
+ // Erase the lower part of the first element block.
+ element_block_type* blk_data = m_block_store.element_blocks[block_pos1];
+ size_type new_size = start_row - start_row_in_block1;
+ if (blk_data)
+ {
+ // Shrink the element block.
+ block_funcs::overwrite_values(*blk_data, new_size, m_block_store.sizes[block_pos1] - new_size);
+ block_funcs::resize_block(*blk_data, new_size);
+ }
+ m_block_store.sizes[block_pos1] = new_size;
+ }
+
+ size_type adjust_block_offset = 0;
+
+ // Then inspect the last block.
+ size_type last_row_in_block = start_row_in_block2 + m_block_store.sizes[block_pos2] - 1;
+ if (last_row_in_block == end_row)
+ {
+ // Delete the whole block.
+ ++index_erase_end;
+ }
+ else
+ {
+ size_type size_to_erase = end_row - start_row_in_block2 + 1;
+ m_block_store.sizes[block_pos2] -= size_to_erase;
+ m_block_store.positions[block_pos2] = start_row;
+ element_block_type* blk_data = m_block_store.element_blocks[block_pos2];
+ if (blk_data)
+ {
+ // Erase the upper part.
+ block_funcs::overwrite_values(*blk_data, 0, size_to_erase);
+ block_funcs::erase(*blk_data, 0, size_to_erase);
+ }
+
+ adjust_block_offset = 1; // Exclude this block from later block position adjustment.
+ }
+
+ // Get the index of the block that sits before the blocks being erased.
+ block_pos1 = index_erase_begin;
+ if (block_pos1 > 0)
+ --block_pos1;
+
+ // Now, erase all blocks in between.
+ delete_element_blocks(index_erase_begin, index_erase_end);
+ m_block_store.erase(index_erase_begin, index_erase_end - index_erase_begin);
+ int64_t delta = end_row - start_row + 1;
+ m_cur_size -= delta;
+
+ if (m_block_store.positions.empty())
+ return;
+
+ // Adjust the positions of the blocks following the erased.
+ size_type adjust_pos = index_erase_begin;
+ adjust_pos += adjust_block_offset;
+ adjust_block_positions_func{}(m_block_store, adjust_pos, -delta);
+ merge_with_next_block(block_pos1);
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::erase_in_single_block(size_type start_pos, size_type end_pos, size_type block_index)
+{
+ // Range falls within the same block.
+ element_block_type* blk_data = m_block_store.element_blocks[block_index];
+ int64_t size_to_erase = end_pos - start_pos + 1;
+
+ if (blk_data)
+ {
+ // Erase data in the data block.
+ size_type offset = start_pos - m_block_store.positions[block_index];
+ block_funcs::overwrite_values(*blk_data, offset, size_to_erase);
+ block_funcs::erase(*blk_data, offset, size_to_erase);
+ }
+
+ m_block_store.sizes[block_index] -= size_to_erase;
+ m_cur_size -= size_to_erase;
+
+ if (m_block_store.sizes[block_index])
+ {
+ // Block still contains data. Bail out.
+ adjust_block_positions_func{}(m_block_store, block_index + 1, -size_to_erase);
+ return;
+ }
+
+ // Delete the current block since it has become empty.
+ delete_element_block(block_index);
+ m_block_store.erase(block_index);
+
+ if (block_index == 0)
+ {
+ // Deleted block was the first block.
+ adjust_block_positions_func{}(m_block_store, block_index, -size_to_erase);
+ return;
+ }
+
+ if (block_index >= m_block_store.positions.size())
+ // Deleted block was the last block.
+ return;
+
+ // Check the previous and next blocks to see if they should be merged.
+ element_block_type* prev_data = m_block_store.element_blocks[block_index - 1];
+ element_block_type* next_data = m_block_store.element_blocks[block_index];
+
+ if (prev_data)
+ {
+ // Previous block has data.
+ if (!next_data)
+ {
+ // Next block is empty. Nothing to do.
+ adjust_block_positions_func{}(m_block_store, block_index, -size_to_erase);
+ return;
+ }
+
+ element_category_type cat1 = mdds::mtv::get_block_type(*prev_data);
+ element_category_type cat2 = mdds::mtv::get_block_type(*next_data);
+
+ if (cat1 == cat2)
+ {
+ // Merge the two blocks.
+ block_funcs::append_block(*prev_data, *next_data);
+ m_block_store.sizes[block_index - 1] += m_block_store.sizes[block_index];
+ // Resize to 0 to prevent deletion of cells in case of managed cells.
+ block_funcs::resize_block(*next_data, 0);
+ delete_element_block(block_index);
+ m_block_store.erase(block_index);
+ }
+
+ adjust_block_positions_func{}(m_block_store, block_index, -size_to_erase);
+ }
+ else
+ {
+ // Previous block is empty.
+ if (next_data)
+ {
+ // Next block is not empty. Nothing to do.
+ adjust_block_positions_func{}(m_block_store, block_index, -size_to_erase);
+ return;
+ }
+
+ // Both blocks are empty. Simply increase the size of the previous
+ // block.
+ m_block_store.sizes[block_index - 1] += m_block_store.sizes[block_index];
+ delete_element_block(block_index);
+ m_block_store.erase(block_index);
+ adjust_block_positions_func{}(m_block_store, block_index, -size_to_erase);
+ }
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::insert_empty_impl(
+ size_type pos, size_type block_index, size_type length)
+{
+ assert(pos < m_cur_size);
+
+ element_block_type* blk_data = m_block_store.element_blocks[block_index];
+
+ if (!blk_data)
+ {
+ // Insertion point is already empty. Just expand its size and be done
+ // with it.
+ m_block_store.sizes[block_index] += length;
+ m_cur_size += length;
+ adjust_block_positions_func{}(m_block_store, block_index + 1, length);
+ return get_iterator(block_index);
+ }
+
+ size_type start_pos = m_block_store.positions[block_index];
+
+ if (start_pos == pos)
+ {
+ // Insertion point is at the top of an existing non-empty block.
+ bool blk_prev = is_previous_block_of_type(block_index, mtv::element_type_empty);
+ if (blk_prev)
+ {
+ // Previous block is empty. Expand the size of the previous block.
+ assert(!m_block_store.element_blocks[block_index - 1]);
+ m_block_store.sizes[block_index - 1] += length;
+ m_cur_size += length;
+ adjust_block_positions_func{}(m_block_store, block_index, length);
+ return get_iterator(block_index - 1);
+ }
+
+ // Insert a new empty block.
+ m_block_store.insert(block_index, start_pos, length, nullptr);
+ m_cur_size += length;
+ adjust_block_positions_func{}(m_block_store, block_index + 1, length);
+ return get_iterator(block_index);
+ }
+
+ assert(blk_data);
+ assert(pos > start_pos);
+
+ size_type size_blk_prev = pos - start_pos;
+ size_type size_blk_next = m_block_store.sizes[block_index] - size_blk_prev;
+
+ // Insert two new blocks below the current; one for the empty block being
+ // inserted, and the other for the lower part of the current non-empty
+ // block.
+ m_block_store.insert(block_index + 1, 2u);
+
+ m_block_store.sizes[block_index + 1] = length;
+ m_block_store.sizes[block_index + 2] = size_blk_next;
+
+ m_block_store.element_blocks[block_index + 2] =
+ block_funcs::create_new_block(mdds::mtv::get_block_type(*blk_data), 0);
+ element_block_type* next_data = m_block_store.element_blocks[block_index + 2];
+ m_hdl_event.element_block_acquired(next_data);
+
+ // Check if the previous block is the bigger one
+ if (size_blk_prev > size_blk_next)
+ {
+ // Upper (previous) block is larger than the lower (next) block. Copy
+ // the lower values to the next block.
+ block_funcs::assign_values_from_block(*next_data, *blk_data, size_blk_prev, size_blk_next);
+ block_funcs::resize_block(*blk_data, size_blk_prev);
+ m_block_store.sizes[block_index] = size_blk_prev;
+ }
+ else
+ {
+ // Lower (next) block is larger than the upper (previous) block. Copy
+ // the upper values to the "next" block.
+ block_funcs::assign_values_from_block(*next_data, *blk_data, 0, size_blk_prev);
+ m_block_store.sizes[block_index + 2] = size_blk_prev;
+
+ // Remove the copied values and push the rest to the top.
+ block_funcs::erase(*blk_data, 0, size_blk_prev);
+
+ // Set the size of the current block to its new size ( what is after the new block )
+ m_block_store.sizes[block_index] = size_blk_next;
+
+ // And now let's swap the blocks, but save the block position.
+ size_type position = m_block_store.positions[block_index];
+ m_block_store.swap(block_index, block_index + 2);
+ m_block_store.positions[block_index] = position;
+ }
+
+ m_cur_size += length;
+ m_block_store.calc_block_position(block_index + 1);
+ m_block_store.calc_block_position(block_index + 2);
+ adjust_block_positions_func{}(m_block_store, block_index + 3, length);
+
+ return get_iterator(block_index + 1);
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::insert_blocks_at(size_type position, size_type insert_pos, blocks_type& new_blocks)
+{
+ for (size_type i = 0; i < new_blocks.positions.size(); ++i)
+ {
+ new_blocks.positions[i] = position;
+ position += new_blocks.sizes[i];
+
+ const element_block_type* data = new_blocks.element_blocks[i];
+
+ if (data)
+ m_hdl_event.element_block_acquired(data);
+ }
+
+ m_block_store.insert(insert_pos, new_blocks);
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::prepare_blocks_to_transfer(
+ blocks_to_transfer& bucket, size_type block_index1, size_type offset1, size_type block_index2, size_type offset2)
+{
+ assert(block_index1 < block_index2);
+ assert(offset1 < m_block_store.sizes[block_index1]);
+ assert(offset2 < m_block_store.sizes[block_index2]);
+
+ block_slot_type block_first;
+ block_slot_type block_last;
+ size_type index_begin = block_index1 + 1;
+ size_type index_end = block_index2;
+
+ bucket.insert_index = block_index1 + 1;
+
+ if (offset1 == 0)
+ {
+ // The whole first block needs to be swapped.
+ --index_begin;
+ --bucket.insert_index;
+ }
+ else
+ {
+ // Copy the lower part of the block for transfer.
+ size_type blk_size = m_block_store.sizes[block_index1] - offset1;
+ block_first.size = blk_size;
+
+ element_block_type* blk_data1 = m_block_store.element_blocks[block_index1];
+ if (blk_data1)
+ {
+ block_first.element_block = block_funcs::create_new_block(mtv::get_block_type(*blk_data1), 0);
+ block_funcs::assign_values_from_block(*block_first.element_block, *blk_data1, offset1, blk_size);
+
+ // Shrink the existing block.
+ block_funcs::resize_block(*blk_data1, offset1);
+ }
+
+ m_block_store.sizes[block_index1] = offset1;
+ }
+
+ if (offset2 == m_block_store.sizes[block_index2] - 1)
+ {
+ // The entire last block needs to be swapped.
+ ++index_end;
+ }
+ else
+ {
+ // Copy the upper part of the last block for transfer.
+ size_type blk_size = offset2 + 1;
+ block_last.size = blk_size;
+ element_block_type* blk_data2 = m_block_store.element_blocks[block_index2];
+
+ if (blk_data2)
+ {
+ block_last.element_block = block_funcs::create_new_block(mtv::get_block_type(*blk_data2), 0);
+ block_funcs::assign_values_from_block(*block_last.element_block, *blk_data2, 0, blk_size);
+
+ // Shrink the existing block.
+ block_funcs::erase(*blk_data2, 0, blk_size);
+ }
+
+ m_block_store.positions[block_index2] += blk_size;
+ m_block_store.sizes[block_index2] -= blk_size;
+ }
+
+ // Copy all blocks into the bucket.
+ if (block_first.size)
+ bucket.blocks.push_back(block_first);
+
+ for (size_type i = index_begin; i < index_end; ++i)
+ {
+ element_block_type* data = m_block_store.element_blocks[i];
+ if (data)
+ m_hdl_event.element_block_released(data);
+
+ bucket.blocks.push_back(m_block_store.positions[i], m_block_store.sizes[i], m_block_store.element_blocks[i]);
+ }
+
+ if (block_last.size)
+ bucket.blocks.push_back(block_last);
+
+ // Remove the slots for these blocks (but don't delete the blocks).
+ m_block_store.erase(index_begin, index_end - index_begin);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::set_whole_block_empty(
+ size_type block_index, bool overwrite)
+{
+ element_block_type* blk_data = m_block_store.element_blocks[block_index];
+ if (!overwrite)
+ // Resize block to 0 before deleting, to prevent its elements from getting deleted.
+ block_funcs::resize_block(*blk_data, 0);
+
+ delete_element_block(block_index);
+
+ bool blk_prev = is_previous_block_of_type(block_index, mtv::element_type_empty);
+ bool blk_next = is_next_block_of_type(block_index, mtv::element_type_empty);
+
+ // Merge with adjacent block(s) if necessary.
+ if (blk_prev)
+ {
+ assert(!m_block_store.element_blocks[block_index - 1]);
+
+ if (blk_next)
+ {
+ // Both preceding and next blocks are empty.
+ assert(!m_block_store.element_blocks[block_index + 1]);
+
+ m_block_store.sizes[block_index - 1] +=
+ m_block_store.sizes[block_index] + m_block_store.sizes[block_index + 1];
+
+ // No need delete the current and next element blocks since they are both empty.
+ m_block_store.erase(block_index, 2);
+
+ return get_iterator(block_index - 1);
+ }
+
+ // Only the preceding block is empty. Merge the current block with the previous.
+ m_block_store.sizes[block_index - 1] += m_block_store.sizes[block_index];
+ m_block_store.erase(block_index);
+
+ return get_iterator(block_index - 1);
+ }
+ else if (blk_next)
+ {
+ assert(!m_block_store.element_blocks[block_index + 1]);
+
+ // Only the next block is empty. Merge the next block with the current.
+ m_block_store.sizes[block_index] += m_block_store.sizes[block_index + 1];
+ m_block_store.erase(block_index + 1);
+
+ return get_iterator(block_index);
+ }
+
+ return get_iterator(block_index);
+}
+
+template<typename Traits>
+template<typename T>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::set_cells_impl(
+ size_type row, size_type end_row, size_type block_index1, const T& it_begin, const T& it_end)
+{
+ size_type block_index2 = get_block_position(end_row, block_index1);
+ if (block_index2 == m_block_store.positions.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::set_cells_impl", __LINE__, end_row, block_size(), size());
+
+ if (block_index1 == block_index2)
+ {
+ // The whole data array will fit in a single block.
+ return set_cells_to_single_block(row, end_row, block_index1, it_begin, it_end);
+ }
+
+ return set_cells_to_multi_blocks(row, end_row, block_index1, block_index2, it_begin, it_end);
+}
+
+template<typename Traits>
+template<typename T>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::set_cells_to_single_block(
+ size_type start_row, size_type end_row, size_type block_index, const T& it_begin, const T& it_end)
+{
+ assert(it_begin != it_end);
+ assert(!m_block_store.positions.empty());
+
+ element_category_type cat = mdds_mtv_get_element_type(*it_begin);
+ size_type start_row_in_block = m_block_store.positions[block_index];
+ size_type data_length = std::distance(it_begin, it_end);
+ element_block_type* blk_data = m_block_store.element_blocks[block_index];
+
+ if (blk_data && mdds::mtv::get_block_type(*blk_data) == cat)
+ {
+ // t|---|xxx|---|b - Simple overwrite.
+ size_type offset = start_row - start_row_in_block;
+ block_funcs::overwrite_values(*blk_data, offset, data_length);
+ if (!offset && data_length == m_block_store.sizes[block_index])
+ // Overwrite the whole block. It's faster to use assign_values.
+ mdds_mtv_assign_values(*blk_data, *it_begin, it_begin, it_end);
+ else
+ mdds_mtv_set_values(*blk_data, offset, *it_begin, it_begin, it_end);
+
+ return get_iterator(block_index);
+ }
+
+ size_type end_row_in_block = start_row_in_block + m_block_store.sizes[block_index] - 1;
+ if (start_row == start_row_in_block)
+ {
+ if (end_row == end_row_in_block)
+ {
+ // t|???|xxx|???|b - Replace the entire current block, but first try
+ // to see if the values can be appended to the previous block.
+ if (append_to_prev_block(block_index, cat, end_row - start_row + 1, it_begin, it_end))
+ {
+ delete_element_block(block_index);
+ m_block_store.erase(block_index);
+
+ // Check if we need to merge it with the next block.
+ --block_index;
+ merge_with_next_block(block_index);
+
+ return get_iterator(block_index);
+ }
+
+ // Replace the whole block.
+ if (blk_data)
+ {
+ m_hdl_event.element_block_released(blk_data);
+ block_funcs::delete_block(blk_data);
+ }
+
+ m_block_store.element_blocks[block_index] = block_funcs::create_new_block(cat, 0);
+ blk_data = m_block_store.element_blocks[block_index];
+ m_hdl_event.element_block_acquired(blk_data);
+ mdds_mtv_assign_values(*blk_data, *it_begin, it_begin, it_end);
+ merge_with_next_block(block_index);
+ return get_iterator(block_index);
+ }
+
+ // t|???|xxx--|???|b - Replace the upper part of the block. Shrink the
+ // current block first.
+ size_type length = end_row_in_block - end_row;
+ m_block_store.sizes[block_index] = length;
+
+ if (blk_data)
+ {
+ // Erase the upper part of the data from the current element block.
+ std::unique_ptr<element_block_type, element_block_deleter> new_data(
+ block_funcs::create_new_block(mdds::mtv::get_block_type(*blk_data), 0));
+
+ if (!new_data)
+ throw std::logic_error("failed to create a new element block.");
+
+ size_type pos = end_row - start_row_in_block + 1;
+ block_funcs::assign_values_from_block(*new_data, *blk_data, pos, length);
+ block_funcs::overwrite_values(*blk_data, 0, pos);
+
+ block_funcs::resize_block(*blk_data, 0); // to prevent deletion of elements
+ block_funcs::delete_block(blk_data);
+ m_block_store.element_blocks[block_index] = new_data.release();
+
+ // We intentionally don't trigger element block events here.
+ }
+
+ length = end_row - start_row + 1;
+ if (append_to_prev_block(block_index, cat, length, it_begin, it_end))
+ {
+ // The new values have been successfully appended to the previous block.
+ m_block_store.positions[block_index] += length;
+ return get_iterator(block_index - 1);
+ }
+
+ // Insert a new block before the current block, and populate it with
+ // the new data.
+ size_type position = m_block_store.positions[block_index];
+ m_block_store.positions[block_index] += length;
+ m_block_store.insert(block_index, position, length, nullptr);
+ m_block_store.element_blocks[block_index] = block_funcs::create_new_block(cat, 0);
+ blk_data = m_block_store.element_blocks[block_index];
+ m_hdl_event.element_block_acquired(blk_data);
+ m_block_store.sizes[block_index] = length;
+ mdds_mtv_assign_values(*blk_data, *it_begin, it_begin, it_end);
+
+ return get_iterator(block_index);
+ }
+
+ assert(start_row > start_row_in_block);
+ if (end_row == end_row_in_block)
+ {
+ // t|???|--xxx|???|b - Shrink the end of the current block and insert a
+ // new block for the new data series after the current block.
+ size_type new_size = start_row - start_row_in_block;
+ m_block_store.sizes[block_index] = new_size;
+
+ if (blk_data)
+ {
+ block_funcs::overwrite_values(*blk_data, new_size, data_length);
+ block_funcs::resize_block(*blk_data, new_size);
+ }
+
+ new_size = end_row - start_row + 1; // size of the data array being inserted.
+
+ if (block_index < m_block_store.positions.size() - 1)
+ {
+ // t|???|--xxx|???|b - There is a block after the current block..
+ // There is a block (or more) after the current block. Check the next block.
+ bool blk_next = is_next_block_of_type(block_index, cat);
+ if (blk_next)
+ {
+ // t|???|--xxx|xxx|b - Prepend it to the next block.
+ mdds_mtv_prepend_values(*m_block_store.element_blocks[block_index + 1], *it_begin, it_begin, it_end);
+ m_block_store.sizes[block_index + 1] += new_size;
+ m_block_store.positions[block_index + 1] -= new_size;
+ return get_iterator(block_index + 1);
+ }
+
+ // t|???|--xxx|---|b - Next block has a different data type. Do the
+ // normal insertion.
+ m_block_store.insert(block_index + 1, 0, new_size, nullptr);
+ m_block_store.calc_block_position(block_index + 1);
+ m_block_store.element_blocks[block_index + 1] = block_funcs::create_new_block(cat, 0);
+ blk_data = m_block_store.element_blocks[block_index + 1];
+ m_hdl_event.element_block_acquired(blk_data);
+ mdds_mtv_assign_values(*blk_data, *it_begin, it_begin, it_end);
+
+ return get_iterator(block_index + 1);
+ }
+
+ // t|???|--xxx|b - Last block.
+ assert(block_index == m_block_store.positions.size() - 1);
+
+ m_block_store.push_back(m_cur_size - new_size, new_size, nullptr);
+ m_block_store.element_blocks.back() = block_funcs::create_new_block(cat, 0);
+ blk_data = m_block_store.element_blocks.back();
+ m_hdl_event.element_block_acquired(blk_data);
+ mdds_mtv_assign_values(*blk_data, *it_begin, it_begin, it_end);
+
+ return get_iterator(block_index + 1);
+ }
+
+ // t|???|-xx-|???|b - New values will be in the middle of the current block.
+ assert(start_row_in_block < start_row && end_row < end_row_in_block);
+
+ block_index = set_new_block_to_middle(block_index, start_row - start_row_in_block, end_row - start_row + 1, true);
+
+ m_block_store.element_blocks[block_index] = block_funcs::create_new_block(cat, 0);
+ blk_data = m_block_store.element_blocks[block_index];
+ m_hdl_event.element_block_acquired(blk_data);
+ mdds_mtv_assign_values(*blk_data, *it_begin, it_begin, it_end);
+
+ return get_iterator(block_index);
+}
+
+template<typename Traits>
+template<typename T>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::set_cells_to_multi_blocks(
+ size_type start_row, size_type end_row, size_type block_index1, size_type block_index2, const T& it_begin,
+ const T& it_end)
+{
+ assert(block_index1 < block_index2);
+ assert(it_begin != it_end);
+ assert(!m_block_store.positions.empty());
+
+ element_block_type* blk1_data = m_block_store.element_blocks[block_index1];
+ if (blk1_data)
+ {
+ return set_cells_to_multi_blocks_block1_non_empty(
+ start_row, end_row, block_index1, block_index2, it_begin, it_end);
+ }
+
+ // Block 1 is empty.
+ assert(!blk1_data);
+
+ return set_cells_to_multi_blocks_block1_non_equal(start_row, end_row, block_index1, block_index2, it_begin, it_end);
+}
+
+template<typename Traits>
+template<typename T>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::set_cells_to_multi_blocks_block1_non_equal(
+ size_type start_row, size_type end_row, size_type block_index1, size_type block_index2, const T& it_begin,
+ const T& it_end)
+{
+ element_category_type cat = mdds_mtv_get_element_type(*it_begin);
+ element_block_type* blk1_data = m_block_store.element_blocks[block_index1];
+ element_block_type* blk2_data = m_block_store.element_blocks[block_index2];
+
+ size_type start_row_in_block1 = m_block_store.positions[block_index1];
+ size_type start_row_in_block2 = m_block_store.positions[block_index2];
+ size_type length = std::distance(it_begin, it_end);
+ size_type offset = start_row - start_row_in_block1;
+ size_type end_row_in_block2 = start_row_in_block2 + m_block_store.sizes[block_index2] - 1;
+
+ // Initially set to erase blocks between block 1 and block 2 non-inclusive at either end.
+ size_type index_erase_begin = block_index1 + 1;
+ size_type index_erase_end = block_index2;
+
+ // Create the new data block first.
+ block_slot_type data_blk(start_row, length);
+
+ bool blk0_copied = false;
+ if (offset == 0)
+ {
+ // Remove block 1.
+ --index_erase_begin;
+
+ // Check the type of the previous block (block 0) if exists.
+ if (block_index1 > 0)
+ {
+ element_block_type* blk0_data = m_block_store.element_blocks[block_index1 - 1];
+ if (blk0_data && cat == mdds::mtv::get_block_type(*blk0_data))
+ {
+ // Transfer the whole data from block 0 to data block.
+ data_blk.element_block = blk0_data;
+ m_block_store.element_blocks[block_index1 - 1] = nullptr;
+
+ data_blk.size += m_block_store.sizes[block_index1 - 1];
+ data_blk.position = m_block_store.positions[block_index1 - 1];
+
+ --index_erase_begin;
+ blk0_copied = true;
+ }
+ }
+ }
+ else
+ {
+ // Shrink block 1 by the end.
+ if (blk1_data)
+ {
+ size_type n = m_block_store.sizes[block_index1] - offset;
+ block_funcs::overwrite_values(*blk1_data, offset, n);
+ block_funcs::resize_block(*blk1_data, offset);
+ }
+ m_block_store.sizes[block_index1] = offset;
+ }
+
+ if (blk0_copied)
+ {
+ mdds_mtv_append_values(*data_blk.element_block, *it_begin, it_begin, it_end);
+ }
+ else
+ {
+ data_blk.element_block = block_funcs::create_new_block(cat, 0);
+ m_hdl_event.element_block_acquired(data_blk.element_block);
+ mdds_mtv_assign_values(*data_blk.element_block, *it_begin, it_begin, it_end);
+ }
+
+ if (end_row == end_row_in_block2)
+ {
+ // Remove block 2.
+ ++index_erase_end;
+
+ if (block_index2 + 1 < m_block_store.positions.size())
+ {
+ // There is at least one block after block 2.
+ element_block_type* blk3_data = m_block_store.element_blocks[block_index2 + 1];
+ if (blk3_data && mdds::mtv::get_block_type(*blk3_data) == cat)
+ {
+ // Merge the whole block 3 with the new data. Remove block 3
+ // afterward. Resize block 3 to zero to prevent invalid free.
+ block_funcs::append_block(*data_blk.element_block, *blk3_data);
+ block_funcs::resize_block(*blk3_data, 0);
+ data_blk.size += m_block_store.sizes[block_index2 + 1];
+ ++index_erase_end;
+ }
+ }
+ }
+ else
+ {
+ bool erase_upper = true;
+
+ if (blk2_data)
+ {
+ element_category_type blk_cat2 = mdds::mtv::get_block_type(*blk2_data);
+ if (blk_cat2 == cat)
+ {
+ // Merge the lower part of block 2 with the new data, and erase
+ // block 2. Resize block 2 to avoid invalid free on the copied
+ // portion of the block.
+ size_type copy_pos = end_row - start_row_in_block2 + 1;
+ size_type size_to_copy = end_row_in_block2 - end_row;
+ block_funcs::append_values_from_block(*data_blk.element_block, *blk2_data, copy_pos, size_to_copy);
+ block_funcs::resize_block(*blk2_data, copy_pos);
+ data_blk.size += size_to_copy;
+
+ ++index_erase_end;
+ erase_upper = false;
+ }
+ }
+
+ if (erase_upper)
+ {
+ // Erase the upper part of block 2.
+ size_type size_to_erase = end_row - start_row_in_block2 + 1;
+
+ if (blk2_data)
+ {
+ block_funcs::overwrite_values(*blk2_data, 0, size_to_erase);
+ block_funcs::erase(*blk2_data, 0, size_to_erase);
+ }
+
+ m_block_store.sizes[block_index2] -= size_to_erase;
+ m_block_store.positions[block_index2] += size_to_erase;
+ }
+ }
+
+ size_type insert_pos = index_erase_begin;
+
+ // Remove the in-between blocks first.
+ delete_element_blocks(index_erase_begin, index_erase_end);
+ m_block_store.erase(index_erase_begin, index_erase_end - index_erase_begin);
+
+ // Insert the new data block.
+
+ m_block_store.insert(insert_pos, data_blk.position, data_blk.size, data_blk.element_block);
+ return get_iterator(insert_pos);
+}
+
+template<typename Traits>
+template<typename T>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::set_cells_to_multi_blocks_block1_non_empty(
+ size_type start_row, size_type end_row, size_type block_index1, size_type block_index2, const T& it_begin,
+ const T& it_end)
+{
+ size_type start_row_in_block1 = m_block_store.positions[block_index1];
+ size_type start_row_in_block2 = m_block_store.positions[block_index2];
+
+ element_category_type cat = mdds_mtv_get_element_type(*it_begin);
+ element_block_type* blk1_data = m_block_store.element_blocks[block_index1];
+ assert(blk1_data);
+ element_category_type blk_cat1 = mdds::mtv::get_block_type(*blk1_data);
+
+ if (blk_cat1 == cat)
+ {
+ size_type length = std::distance(it_begin, it_end);
+ size_type offset = start_row - start_row_in_block1;
+ size_type end_row_in_block2 = start_row_in_block2 + m_block_store.sizes[block_index2] - 1;
+
+ // Initially set to erase blocks between block 1 and block 2 non-inclusive at either end.
+ size_type index_erase_begin = block_index1 + 1;
+ size_type index_erase_end = block_index2;
+
+ // Extend the first block to store the new data set.
+
+ // Shrink it first to remove the old values, then append new values.
+ block_funcs::overwrite_values(*blk1_data, offset, m_block_store.sizes[block_index1] - offset);
+ block_funcs::resize_block(*blk1_data, offset);
+ mdds_mtv_append_values(*blk1_data, *it_begin, it_begin, it_end);
+ m_block_store.sizes[block_index1] = offset + length;
+
+ element_block_type* blk2_data = m_block_store.element_blocks[block_index2];
+
+ if (end_row == end_row_in_block2)
+ {
+ // Data overlaps the entire block 2. Erase it.
+ ++index_erase_end;
+ }
+ else if (blk2_data)
+ {
+ element_category_type blk_cat2 = mdds::mtv::get_block_type(*blk2_data);
+
+ if (blk_cat2 == cat)
+ {
+ // Copy the lower (non-overwritten) part of block 2 to block 1,
+ // and remove the whole block 2. Resize block 2 to zero first to
+ // prevent the transferred / overwritten cells from being
+ // deleted on block deletion.
+ size_type data_length = end_row_in_block2 - end_row;
+ size_type begin_pos = end_row - start_row_in_block2 + 1;
+ block_funcs::append_values_from_block(*blk1_data, *blk2_data, begin_pos, data_length);
+ block_funcs::overwrite_values(*blk2_data, 0, begin_pos);
+ block_funcs::resize_block(*blk2_data, 0);
+ m_block_store.sizes[block_index1] += data_length;
+ ++index_erase_end;
+ }
+ else
+ {
+ // Erase the upper part of block 2.
+ size_type size_to_erase = end_row - start_row_in_block2 + 1;
+ block_funcs::erase(*blk2_data, 0, size_to_erase);
+ m_block_store.sizes[block_index2] -= size_to_erase;
+ m_block_store.positions[block_index2] += size_to_erase;
+ }
+ }
+ else
+ {
+ // Last block is empty.
+ size_type size_to_erase = end_row - start_row_in_block2 + 1;
+ m_block_store.sizes[block_index2] -= size_to_erase;
+ m_block_store.positions[block_index2] += size_to_erase;
+ }
+
+ delete_element_blocks(index_erase_begin, index_erase_end);
+ m_block_store.erase(index_erase_begin, index_erase_end - index_erase_begin);
+
+ return get_iterator(block_index1);
+ }
+
+ // The first block type is different.
+ assert(blk_cat1 != cat);
+
+ return set_cells_to_multi_blocks_block1_non_equal(start_row, end_row, block_index1, block_index2, it_begin, it_end);
+}
+
+template<typename Traits>
+template<typename T>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::set_cell_to_empty_block(
+ size_type block_index, size_type pos_in_block, const T& cell)
+{
+ assert(!m_block_store.element_blocks[block_index]); // In this call, the current block is an empty block.
+
+ if (block_index == 0)
+ {
+ // Topmost block.
+ if (m_block_store.positions.size() == 1)
+ {
+ // this is the only block.
+ assert(m_block_store.sizes[block_index] == m_cur_size);
+ if (m_cur_size == 1)
+ {
+ // This column is allowed to have only one row!
+ assert(pos_in_block == 0);
+ create_new_block_with_new_cell(block_index, cell);
+ return begin();
+ }
+
+ // block has multiple rows.
+ if (pos_in_block == 0)
+ {
+ // Insert into the first cell in this block.
+ m_block_store.sizes[block_index] -= 1;
+ assert(m_block_store.sizes[block_index] > 0);
+
+ m_block_store.positions.emplace(m_block_store.positions.begin(), 0);
+ m_block_store.sizes.emplace(m_block_store.sizes.begin(), 1);
+ m_block_store.element_blocks.emplace(m_block_store.element_blocks.begin(), nullptr);
+
+ create_new_block_with_new_cell(0, cell);
+
+ m_block_store.positions[1] = 1;
+ return begin();
+ }
+
+ if (size_type& blk_size = m_block_store.sizes[block_index]; pos_in_block == blk_size - 1)
+ {
+ // Insert into the last cell in block.
+ blk_size -= 1;
+ assert(blk_size > 0);
+
+ m_block_store.positions.push_back(blk_size);
+ m_block_store.sizes.push_back(1);
+ m_block_store.element_blocks.push_back(nullptr);
+
+ create_new_block_with_new_cell(block_index + 1, cell);
+ iterator ret = end();
+ return --ret;
+ }
+
+ // Insert into the middle of the block.
+ return set_cell_to_middle_of_block(block_index, pos_in_block, cell);
+ }
+
+ // This topmost empty block is followed by a non-empty block.
+
+ if (pos_in_block == 0)
+ {
+ // t|x |???|b
+ assert(block_index < m_block_store.positions.size() - 1);
+
+ if (m_block_store.sizes[block_index] == 1)
+ {
+ // t|x|???| - Top empty block with only one cell size.
+ element_category_type cat = mdds_mtv_get_element_type(cell);
+ bool blk_next = is_next_block_of_type(block_index, cat);
+ if (blk_next)
+ {
+ // t|x|xxx|b - Remove this top empty block of size 1, and
+ // prepend the cell to the next block.
+ delete_element_block(block_index);
+ m_block_store.erase(block_index);
+ m_block_store.sizes[block_index] += 1;
+ m_block_store.positions[block_index] -= 1;
+ mdds_mtv_prepend_value(*m_block_store.element_blocks[block_index], cell);
+ }
+ else
+ {
+ // t|x|---|b
+ create_new_block_with_new_cell(block_index, cell);
+ }
+ }
+ else
+ {
+ // Shrink this topmost block by one and set the new value above it as a new block of size 1.
+ assert(block_index == 0);
+ assert(m_block_store.sizes[0] > 1);
+ m_block_store.sizes[0] -= 1;
+ m_block_store.positions[0] = 1;
+
+ m_block_store.positions.emplace(m_block_store.positions.begin(), 0);
+ m_block_store.sizes.emplace(m_block_store.sizes.begin(), 1);
+ m_block_store.element_blocks.emplace(m_block_store.element_blocks.begin(), nullptr);
+ create_new_block_with_new_cell(0, cell);
+ }
+
+ return begin();
+ }
+
+ if (pos_in_block == m_block_store.sizes[block_index] - 1)
+ {
+ // Set the cell to the last position of the block, immediately above
+ // a non-empty block.
+ element_category_type cat = mdds_mtv_get_element_type(cell);
+ bool blk_next = is_next_block_of_type(block_index, cat);
+ if (blk_next)
+ {
+ assert(m_block_store.sizes[block_index] > 1);
+
+ // Shrink this empty block by one, and prepend the cell to the next block.
+ m_block_store.sizes[block_index] -= 1;
+ m_block_store.sizes[block_index + 1] += 1;
+ m_block_store.positions[block_index + 1] -= 1;
+ mdds_mtv_prepend_value(*m_block_store.element_blocks[block_index + 1], cell);
+ }
+ else
+ {
+ // Shrink the current empty block by one, and create a new block of size 1 to store the new value.
+ m_block_store.sizes[block_index] -= 1;
+ m_block_store.insert(block_index + 1, 1);
+ m_block_store.calc_block_position(block_index + 1);
+ m_block_store.sizes[block_index + 1] = 1;
+
+ create_new_block_with_new_cell(block_index + 1, cell);
+ }
+
+ return get_iterator(block_index + 1);
+ }
+
+ // Inserting into the middle of an empty block.
+ return set_cell_to_middle_of_block(block_index, pos_in_block, cell);
+ }
+
+ // This empty block is right below a non-empty block.
+ assert(block_index > 0 && m_block_store.element_blocks[block_index - 1] != nullptr);
+
+ if (pos_in_block == 0)
+ {
+ // Set the value to the top of the block, right below a non-empty block.
+ element_category_type blk_cat_prev = mdds::mtv::get_block_type(*m_block_store.element_blocks[block_index - 1]);
+ element_category_type cat = mdds_mtv_get_element_type(cell);
+
+ if (blk_cat_prev == cat)
+ {
+ // Extend the previous block by one to insert this cell.
+ if (m_block_store.sizes[block_index] == 1)
+ {
+ // Check if we need to merge with the following block.
+ if (block_index == m_block_store.positions.size() - 1)
+ {
+ // Last block. Delete this block and extend the previous
+ // block by one.
+ delete_element_block(block_index);
+ m_block_store.pop_back();
+ append_cell_to_block(block_index - 1, cell);
+ }
+ else
+ {
+ // Block exists below.
+ bool blk_next = is_next_block_of_type(block_index, blk_cat_prev);
+ if (blk_next)
+ {
+ // Empty block must not be followed by another empty block.
+ assert(m_block_store.element_blocks[block_index + 1]);
+
+ // We need to merge the previous and next blocks, then
+ // delete the current and next blocks. Be sure to
+ // resize the next block to zero to prevent the
+ // transferred cells to be deleted.
+
+ // Check if the next block is bigger.
+ if (m_block_store.sizes[block_index - 1] < m_block_store.sizes[block_index + 1])
+ {
+ // Prepend the new item to the next block, then
+ // prepend the content of the previous block and
+ // release both previous and current blocks.
+
+ size_type position = m_block_store.positions[block_index - 1];
+
+ element_block_type* data = m_block_store.element_blocks[block_index];
+ element_block_type* prev_data = m_block_store.element_blocks[block_index - 1];
+ element_block_type* next_data = m_block_store.element_blocks[block_index + 1];
+
+ // Increase the size of block and prepend the new cell
+ m_block_store.sizes[block_index + 1] += 1;
+ mdds_mtv_prepend_value(*next_data, cell);
+
+ // Preprend the content of previous block to the next block.
+ size_type prev_size = m_block_store.sizes[block_index - 1];
+ block_funcs::prepend_values_from_block(*next_data, *prev_data, 0, prev_size);
+ m_block_store.sizes[block_index + 1] += prev_size;
+ m_block_store.positions[block_index + 1] = position;
+
+ // Resize the previous block to zero
+ block_funcs::resize_block(*prev_data, 0);
+ m_hdl_event.element_block_released(prev_data);
+
+ // Release both blocks which are no longer used
+ block_funcs::delete_block(data);
+ block_funcs::delete_block(prev_data);
+
+ // Remove the previous and current blocks.
+ m_block_store.erase(block_index - 1, 2);
+ }
+ else
+ {
+ // Be sure to resize the next block to zero to prevent the
+ // transferred cells to be deleted.
+ m_block_store.sizes[block_index - 1] += 1 + m_block_store.sizes[block_index + 1];
+ element_block_type* data = m_block_store.element_blocks[block_index];
+ element_block_type* data_prev = m_block_store.element_blocks[block_index - 1];
+ element_block_type* data_next = m_block_store.element_blocks[block_index + 1];
+ mdds_mtv_append_value(*data_prev, cell);
+ block_funcs::append_block(*data_prev, *data_next);
+ block_funcs::resize_block(*data_next, 0);
+ m_hdl_event.element_block_released(data_next);
+ block_funcs::delete_block(data);
+ block_funcs::delete_block(data_next);
+ m_block_store.erase(block_index, 2);
+ }
+ }
+ else
+ {
+ // Ignore the next block. Just extend the previous block.
+ delete_element_block(block_index);
+ m_block_store.erase(block_index);
+ append_cell_to_block(block_index - 1, cell);
+ }
+ }
+ }
+ else
+ {
+ // Extend the previous block to append the cell.
+ assert(m_block_store.sizes[block_index] > 1);
+ m_block_store.sizes[block_index] -= 1;
+ m_block_store.positions[block_index] += 1;
+ append_cell_to_block(block_index - 1, cell);
+ }
+
+ return get_iterator(block_index - 1);
+ }
+ else
+ {
+ // t|---|x |???|b - Cell type is different from the previous block's.
+ if (m_block_store.sizes[block_index] == 1)
+ {
+ // t|---|x|???|b
+ if (block_index == m_block_store.positions.size() - 1)
+ {
+ // t|---|x|b - There is no more block below. Simply turn this empty block into a non-empty one.
+ create_new_block_with_new_cell(block_index, cell);
+ }
+ else
+ {
+ // Check the type of the following non-empty block.
+ assert(block_index < m_block_store.positions.size() - 1);
+ bool blk_next = is_next_block_of_type(block_index, cat);
+ if (blk_next)
+ {
+ // t|---|x|xxx|b - Remove this empty block, and prepend the cell to the next block.
+ m_block_store.sizes[block_index + 1] += 1;
+ m_block_store.positions[block_index + 1] -= 1;
+ mdds_mtv_prepend_value(*m_block_store.element_blocks[block_index + 1], cell);
+ delete_element_block(block_index);
+ m_block_store.erase(block_index);
+ }
+ else
+ {
+ // t|---|x|---|b - Simply turn this empty block into a
+ // non-empty one.
+ create_new_block_with_new_cell(block_index, cell);
+ }
+ }
+ }
+ else
+ {
+ // t|---|x |???|b
+
+ size_type new_block_size = m_block_store.sizes[block_index] - 1;
+ size_type new_block_position = m_block_store.positions[block_index] + 1;
+ m_block_store.sizes[block_index] = 1;
+ create_new_block_with_new_cell(block_index, cell);
+ m_block_store.insert(block_index + 1, new_block_position, new_block_size, nullptr);
+ }
+
+ return get_iterator(block_index);
+ }
+ }
+ else if (pos_in_block == m_block_store.sizes[block_index] - 1)
+ {
+ // t|???| x|???|b - New cell is set to the end of the current block.
+ assert(m_block_store.sizes[block_index] > 1);
+ if (block_index == m_block_store.positions.size() - 1)
+ {
+ // t|???| x|b - The current block is the last block.
+ m_block_store.sizes[block_index] -= 1;
+ m_block_store.push_back(0, 1, nullptr);
+ m_block_store.calc_block_position(block_index + 1);
+ create_new_block_with_new_cell(block_index + 1, cell);
+
+ iterator it = end();
+ return --it;
+ }
+ else
+ {
+ // t|???| x|???|b - A non-empty block exists below.
+ element_category_type cat = mdds_mtv_get_element_type(cell);
+ bool blk_next = is_next_block_of_type(block_index, cat);
+ if (blk_next)
+ {
+ // t|???| x|xxx|b - Shrink this empty block and extend the next block.
+ m_block_store.sizes[block_index] -= 1;
+ m_block_store.sizes[block_index + 1] += 1;
+ m_block_store.positions[block_index + 1] -= 1;
+ mdds_mtv_prepend_value(*m_block_store.element_blocks[block_index + 1], cell);
+ }
+ else
+ {
+ // t|???| x|---|b - Shrink this block by one and insert a new block for the new cell.
+ m_block_store.sizes[block_index] -= 1;
+ m_block_store.insert(block_index + 1, 0, 1, nullptr);
+ m_block_store.calc_block_position(block_index + 1);
+ create_new_block_with_new_cell(block_index + 1, cell);
+ }
+
+ return get_iterator(block_index + 1);
+ }
+ }
+
+ // New cell is somewhere in the middle of an empty block.
+ return set_cell_to_middle_of_block(block_index, pos_in_block, cell);
+}
+
+template<typename Traits>
+template<typename T>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::set_cell_to_non_empty_block_of_size_one(
+ size_type block_index, const T& cell)
+{
+ assert(m_block_store.sizes[block_index] == 1);
+ assert(m_block_store.element_blocks[block_index]);
+ element_category_type cat = mdds_mtv_get_element_type(cell);
+ assert(mdds::mtv::get_block_type(*m_block_store.element_blocks[block_index]) != cat);
+
+ if (block_index == 0)
+ {
+ // t|x|???|b - topmost block of size 1.
+ if (block_index == m_block_store.positions.size() - 1)
+ {
+ // t|x|b - This is the only block.
+ create_new_block_with_new_cell(block_index, cell);
+ return begin();
+ }
+
+ // There is a block below.
+ bool blk_next = is_next_block_of_type(block_index, cat);
+ if (!blk_next)
+ {
+ // t|x|---|b - Next block is of different type.
+ create_new_block_with_new_cell(block_index, cell);
+ return begin();
+ }
+
+ // t|x|xxx|b - Delete this block and prepend the cell to the next block.
+ m_block_store.sizes[block_index + 1] += 1;
+ m_block_store.positions[block_index + 1] -= 1;
+ mdds_mtv_prepend_value(*m_block_store.element_blocks[block_index + 1], cell);
+ delete_element_block(block_index);
+ m_block_store.erase(block_index);
+
+ return begin();
+ }
+
+ assert(block_index > 0);
+
+ if (block_index == m_block_store.positions.size() - 1)
+ {
+ // t|???|x|b - This is the last block and another block exists above.
+ element_block_type* prev_data = m_block_store.element_blocks[block_index - 1];
+ if (!prev_data || mdds::mtv::get_block_type(*prev_data) != cat)
+ {
+ // t|---|x|b - The previous block is of different type.
+ create_new_block_with_new_cell(block_index, cell);
+ }
+ else
+ {
+ // t|xxx|x|b - Append the cell to the previous block and remove the current one.
+ mdds_mtv_append_value(*m_block_store.element_blocks[block_index - 1], cell);
+ m_block_store.sizes[block_index - 1] += 1;
+ delete_element_block(block_index);
+ m_block_store.erase(block_index);
+ }
+
+ iterator itr = end();
+ return --itr;
+ }
+
+ // Remove the current block, and check if the cell can be append to the
+ // previous block, or prepended to the following block. Also check if the
+ // blocks above and below need to be combined.
+
+ if (!m_block_store.element_blocks[block_index - 1])
+ {
+ // t| |x|???|b - Previous block is empty.
+ if (!m_block_store.element_blocks[block_index + 1])
+ {
+ // t| |x| |b - Next block is empty too.
+ create_new_block_with_new_cell(block_index, cell);
+ return get_iterator(block_index);
+ }
+
+ // Previous block is empty, but the next block is not.
+ element_category_type blk_cat_next = mdds::mtv::get_block_type(*m_block_store.element_blocks[block_index + 1]);
+
+ if (blk_cat_next == cat)
+ {
+ // t| |x|xxx|b - Next block is of the same type as the new value.
+ delete_element_block(block_index);
+ m_block_store.erase(block_index);
+ m_block_store.sizes[block_index] += 1;
+ m_block_store.positions[block_index] -= 1;
+ mdds_mtv_prepend_value(*m_block_store.element_blocks[block_index], cell);
+ return get_iterator(block_index);
+ }
+
+ // t| |x|---|b
+ assert(blk_cat_next != cat);
+ create_new_block_with_new_cell(block_index, cell);
+ return get_iterator(block_index);
+ }
+
+ if (!m_block_store.element_blocks[block_index + 1])
+ {
+ // t|---|x| |b - Next block is empty and the previous block is not.
+ element_block_type* prev_data = m_block_store.element_blocks[block_index - 1];
+ assert(prev_data);
+ element_category_type prev_cat = mdds::mtv::get_block_type(*prev_data);
+
+ if (prev_cat == cat)
+ {
+ // t|xxx|x| |b - Append to the previous block.
+ m_block_store.sizes[block_index - 1] += 1;
+ mdds_mtv_append_value(*prev_data, cell);
+ delete_element_block(block_index);
+ m_block_store.erase(block_index);
+ return get_iterator(block_index - 1);
+ }
+
+ // Just overwrite the current block.
+ create_new_block_with_new_cell(block_index, cell);
+ return get_iterator(block_index);
+ }
+
+ // t|???|x|???|b - Neither previous nor next blocks are empty.
+ element_block_type* prev_data = m_block_store.element_blocks[block_index - 1];
+ element_block_type* next_data = m_block_store.element_blocks[block_index + 1];
+ assert(prev_data);
+ assert(next_data);
+ element_category_type prev_cat = mdds::mtv::get_block_type(*prev_data);
+ element_category_type next_cat = mdds::mtv::get_block_type(*next_data);
+
+ if (prev_cat == next_cat)
+ {
+ if (prev_cat == cat)
+ {
+ // t|xxx|x|xxx|b - All three blocks are of the same type. Merge all
+ // three blocks.
+ m_block_store.sizes[block_index - 1] += 1 + m_block_store.sizes[block_index + 1];
+ mdds_mtv_append_value(*prev_data, cell);
+ block_funcs::append_block(*prev_data, *next_data);
+ block_funcs::resize_block(*next_data, 0); // to prevent deletion of managed cells on block deletion
+
+ // Delete the current and next blocks.
+ delete_element_block(block_index);
+ delete_element_block(block_index + 1);
+ m_block_store.erase(block_index, 2);
+
+ return get_iterator(block_index - 1);
+ }
+
+ // t|---|x|---|b - Just overwrite the current block.
+ create_new_block_with_new_cell(block_index, cell);
+ return get_iterator(block_index);
+ }
+
+ assert(prev_cat != next_cat);
+
+ if (prev_cat == cat)
+ {
+ // t|xxx|x|---|b - Append to the previous block.
+ m_block_store.sizes[block_index - 1] += 1;
+ mdds_mtv_append_value(*m_block_store.element_blocks[block_index - 1], cell);
+ delete_element_block(block_index);
+ m_block_store.erase(block_index);
+ return get_iterator(block_index - 1);
+ }
+
+ if (next_cat == cat)
+ {
+ // t|---|x|xxx|b - Prepend to the next block.
+ m_block_store.sizes[block_index + 1] += 1;
+ m_block_store.positions[block_index + 1] -= 1;
+ mdds_mtv_prepend_value(*m_block_store.element_blocks[block_index + 1], cell);
+ delete_element_block(block_index);
+ m_block_store.erase(block_index);
+ return get_iterator(block_index);
+ }
+
+ // t|---|x|+++|b - Just overwrite the current block.
+ create_new_block_with_new_cell(block_index, cell);
+ return get_iterator(block_index);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::size_type multi_type_vector<Traits>::size() const
+{
+ MDDS_MTV_TRACE(accessor);
+
+ return m_cur_size;
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::size_type multi_type_vector<Traits>::block_size() const
+{
+ MDDS_MTV_TRACE(accessor);
+
+ return m_block_store.positions.size();
+}
+
+template<typename Traits>
+bool multi_type_vector<Traits>::empty() const
+{
+ MDDS_MTV_TRACE(accessor);
+
+ return m_block_store.positions.empty();
+}
+
+template<typename Traits>
+template<typename T>
+void multi_type_vector<Traits>::get(size_type pos, T& value) const
+{
+ MDDS_MTV_TRACE_ARGS(accessor, "pos=" << pos << "; value=? (type=" << mdds_mtv_get_element_type(value) << ")");
+ get_impl(pos, value);
+}
+
+template<typename Traits>
+template<typename T>
+T multi_type_vector<Traits>::get(size_type pos) const
+{
+ MDDS_MTV_TRACE_ARGS(accessor, "pos=" << pos);
+
+ T cell;
+ get_impl(pos, cell);
+ return cell;
+}
+
+template<typename Traits>
+template<typename T>
+T multi_type_vector<Traits>::release(size_type pos)
+{
+ MDDS_MTV_TRACE_ARGS(mutator, "pos=" << pos);
+
+ size_type block_index = get_block_position(pos);
+ if (block_index == m_block_store.positions.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::release", __LINE__, pos, block_size(), size());
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ std::ostringstream os_prev_block;
+ dump_blocks(os_prev_block);
+#endif
+
+ T value;
+ release_impl(pos, block_index, value);
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ try
+ {
+ check_block_integrity();
+ }
+ catch (const mdds::integrity_error& e)
+ {
+ std::ostringstream os;
+ os << e.what() << std::endl;
+ os << "block integrity check failed in release(pos=" << pos << ")" << std::endl;
+ os << "previous block state:" << std::endl;
+ os << os_prev_block.str();
+ std::cerr << os.str() << std::endl;
+ abort();
+ }
+#endif
+
+ return value;
+}
+
+template<typename Traits>
+template<typename T>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::release(size_type pos, T& value)
+{
+ MDDS_MTV_TRACE_ARGS(mutator, "pos=" << pos << "; value=? (type=" << mdds_mtv_get_element_type(value) << ")");
+
+ size_type block_index = get_block_position(pos);
+ if (block_index == m_block_store.positions.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::release", __LINE__, pos, block_size(), size());
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ std::ostringstream os_prev_block;
+ dump_blocks(os_prev_block);
+#endif
+
+ auto ret = release_impl(pos, block_index, value);
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ try
+ {
+ check_block_integrity();
+ }
+ catch (const mdds::integrity_error& e)
+ {
+ std::ostringstream os;
+ os << e.what() << std::endl;
+ os << "block integrity check failed in release(pos=" << pos << ")" << std::endl;
+ os << "previous block state:" << std::endl;
+ os << os_prev_block.str();
+ std::cerr << os.str() << std::endl;
+ abort();
+ }
+#endif
+
+ return ret;
+}
+
+template<typename Traits>
+template<typename T>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::release(
+ const iterator& pos_hint, size_type pos, T& value)
+{
+ MDDS_MTV_TRACE_ARGS(
+ mutator_with_pos_hint,
+ "pos_hint=" << pos_hint << "; pos=" << pos << "; value=? (type=" << mdds_mtv_get_element_type(value) << ")");
+
+ size_type block_index = get_block_position(pos_hint->__private_data, pos);
+ if (block_index == m_block_store.positions.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::release", __LINE__, pos, block_size(), size());
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ std::ostringstream os_prev_block;
+ dump_blocks(os_prev_block);
+#endif
+
+ auto ret = release_impl(pos, block_index, value);
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ try
+ {
+ check_block_integrity();
+ }
+ catch (const mdds::integrity_error& e)
+ {
+ std::ostringstream os;
+ os << e.what() << std::endl;
+ os << "block integrity check failed in release(pos=" << pos << ")" << std::endl;
+ os << "previous block state:" << std::endl;
+ os << os_prev_block.str();
+ std::cerr << os.str() << std::endl;
+ abort();
+ }
+#endif
+
+ return ret;
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::release()
+{
+ MDDS_MTV_TRACE(mutator);
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ std::ostringstream os_prev_block;
+ dump_blocks(os_prev_block);
+#endif
+
+ for (auto* data : m_block_store.element_blocks)
+ {
+ if (!data)
+ continue;
+
+ block_funcs::resize_block(*data, 0);
+ m_hdl_event.element_block_released(data);
+ block_funcs::delete_block(data);
+ }
+
+ m_block_store.clear();
+ m_cur_size = 0;
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ try
+ {
+ check_block_integrity();
+ }
+ catch (const mdds::integrity_error& e)
+ {
+ std::ostringstream os;
+ os << e.what() << std::endl;
+ os << "block integrity check failed in release()" << std::endl;
+ os << "previous block state:" << std::endl;
+ os << os_prev_block.str();
+ std::cerr << os.str() << std::endl;
+ abort();
+ }
+#endif
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::release_range(
+ size_type start_pos, size_type end_pos)
+{
+ MDDS_MTV_TRACE_ARGS(mutator, "start_pos=" << start_pos << "; end_pos=" << end_pos);
+
+ size_type block_index1 = get_block_position(start_pos);
+ if (block_index1 == m_block_store.positions.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::release_range", __LINE__, start_pos, block_size(), size());
+
+ return set_empty_impl(start_pos, end_pos, block_index1, false);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::release_range(
+ const iterator& pos_hint, size_type start_pos, size_type end_pos)
+{
+ MDDS_MTV_TRACE_ARGS(
+ mutator_with_pos_hint, "pos_hint=" << pos_hint << "; start_pos=" << start_pos << "; end_pos=" << end_pos);
+
+ size_type block_index1 = get_block_position(pos_hint->__private_data, start_pos);
+ if (block_index1 == m_block_store.positions.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::release_range", __LINE__, start_pos, block_size(), size());
+
+ return set_empty_impl(start_pos, end_pos, block_index1, false);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::begin()
+{
+ MDDS_MTV_TRACE(accessor);
+
+ return iterator(
+ {m_block_store.positions.begin(), m_block_store.sizes.begin(), m_block_store.element_blocks.begin()},
+ {m_block_store.positions.end(), m_block_store.sizes.end(), m_block_store.element_blocks.end()}, this, 0);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::end()
+{
+ MDDS_MTV_TRACE(accessor);
+
+ return iterator(
+ {m_block_store.positions.end(), m_block_store.sizes.end(), m_block_store.element_blocks.end()},
+ {m_block_store.positions.end(), m_block_store.sizes.end(), m_block_store.element_blocks.end()}, this,
+ m_block_store.positions.size());
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::const_iterator multi_type_vector<Traits>::begin() const
+{
+ MDDS_MTV_TRACE(accessor);
+
+ return cbegin();
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::const_iterator multi_type_vector<Traits>::end() const
+{
+ MDDS_MTV_TRACE(accessor);
+
+ return cend();
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::const_iterator multi_type_vector<Traits>::cbegin() const
+{
+ MDDS_MTV_TRACE(accessor);
+
+ return const_iterator(
+ {m_block_store.positions.cbegin(), m_block_store.sizes.cbegin(), m_block_store.element_blocks.cbegin()},
+ {m_block_store.positions.cend(), m_block_store.sizes.cend(), m_block_store.element_blocks.cend()}, this, 0);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::const_iterator multi_type_vector<Traits>::cend() const
+{
+ MDDS_MTV_TRACE(accessor);
+
+ return const_iterator(
+ {m_block_store.positions.cend(), m_block_store.sizes.cend(), m_block_store.element_blocks.cend()},
+ {m_block_store.positions.cend(), m_block_store.sizes.cend(), m_block_store.element_blocks.cend()}, this,
+ m_block_store.positions.size());
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::reverse_iterator multi_type_vector<Traits>::rbegin()
+{
+ MDDS_MTV_TRACE(accessor);
+
+ return reverse_iterator(
+ {m_block_store.positions.rbegin(), m_block_store.sizes.rbegin(), m_block_store.element_blocks.rbegin()},
+ {m_block_store.positions.rend(), m_block_store.sizes.rend(), m_block_store.element_blocks.rend()}, this, 0);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::reverse_iterator multi_type_vector<Traits>::rend()
+{
+ MDDS_MTV_TRACE(accessor);
+
+ return reverse_iterator(
+ {m_block_store.positions.rend(), m_block_store.sizes.rend(), m_block_store.element_blocks.rend()},
+ {m_block_store.positions.rend(), m_block_store.sizes.rend(), m_block_store.element_blocks.rend()}, this, 0);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::const_reverse_iterator multi_type_vector<Traits>::rbegin() const
+{
+ MDDS_MTV_TRACE(accessor);
+
+ return crbegin();
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::const_reverse_iterator multi_type_vector<Traits>::rend() const
+{
+ MDDS_MTV_TRACE(accessor);
+
+ return crend();
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::const_reverse_iterator multi_type_vector<Traits>::crbegin() const
+{
+ MDDS_MTV_TRACE(accessor);
+
+ return const_reverse_iterator(
+ {m_block_store.positions.rbegin(), m_block_store.sizes.rbegin(), m_block_store.element_blocks.rbegin()},
+ {m_block_store.positions.rend(), m_block_store.sizes.rend(), m_block_store.element_blocks.rend()}, this, 0);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::const_reverse_iterator multi_type_vector<Traits>::crend() const
+{
+ MDDS_MTV_TRACE(accessor);
+
+ return const_reverse_iterator(
+ {m_block_store.positions.rend(), m_block_store.sizes.rend(), m_block_store.element_blocks.rend()},
+ {m_block_store.positions.rend(), m_block_store.sizes.rend(), m_block_store.element_blocks.rend()}, this, 0);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::size_type multi_type_vector<Traits>::get_block_position(
+ size_type row, size_type start_block_index) const
+{
+ if (row >= m_cur_size || start_block_index >= m_block_store.positions.size())
+ return m_block_store.positions.size();
+
+ auto it0 = m_block_store.positions.begin();
+ std::advance(it0, start_block_index);
+
+ auto it = std::lower_bound(it0, m_block_store.positions.end(), row);
+
+ if (it == m_block_store.positions.end() || *it != row)
+ {
+ // Binary search has overshot by one block. Move back one.
+ assert(it != it0);
+ --it;
+ }
+
+ size_type pos = std::distance(it0, it) + start_block_index;
+ assert(*it <= row);
+ assert(row < *it + m_block_store.sizes[pos]);
+ return pos;
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::size_type multi_type_vector<Traits>::get_block_position(
+ const typename value_type::private_data& pos_data, size_type row) const
+{
+ size_type block_index = 0;
+ if (pos_data.parent == this && pos_data.block_index < m_block_store.positions.size())
+ block_index = pos_data.block_index;
+
+ size_type start_row = m_block_store.positions[block_index];
+
+ if (row < start_row)
+ {
+ // Position hint is past the insertion position.
+ // Walk back if that seems efficient.
+ if (row > start_row / 2)
+ {
+ for (size_type i = block_index; i > 0;)
+ {
+ --i;
+ start_row = m_block_store.positions[i];
+ if (row >= start_row)
+ {
+ // Row is in this block.
+ return i;
+ }
+ // Specified row is not in this block.
+ }
+ assert(start_row == 0);
+ }
+ // Otherwise reset.
+ block_index = 0;
+ }
+ return get_block_position(row, block_index);
+}
+
+template<typename Traits>
+template<typename T>
+void multi_type_vector<Traits>::create_new_block_with_new_cell(size_type block_index, const T& cell)
+{
+ element_block_type* data = m_block_store.element_blocks[block_index];
+ if (data)
+ {
+ m_hdl_event.element_block_released(data);
+ block_funcs::delete_block(data);
+ }
+
+ // New cell block with size 1.
+ data = mdds_mtv_create_new_block(1, cell);
+ if (!data)
+ throw general_error("Failed to create new block.");
+
+ m_hdl_event.element_block_acquired(data);
+
+ m_block_store.element_blocks[block_index] = data;
+}
+
+template<typename Traits>
+template<typename T>
+void multi_type_vector<Traits>::append_cell_to_block(size_type block_index, const T& cell)
+{
+ m_block_store.sizes[block_index] += 1;
+ mdds_mtv_append_value(*m_block_store.element_blocks[block_index], cell);
+}
+
+template<typename Traits>
+template<typename T>
+bool multi_type_vector<Traits>::append_to_prev_block(
+ size_type block_index, element_category_type cat, size_type length, const T& it_begin, const T& it_end)
+{
+ bool blk_prev = is_previous_block_of_type(block_index, cat);
+ if (!blk_prev)
+ return false;
+
+ // Append to the previous block.
+ mdds_mtv_append_values(*m_block_store.element_blocks[block_index - 1], *it_begin, it_begin, it_end);
+ m_block_store.sizes[block_index - 1] += length;
+ return true;
+}
+
+template<typename Traits>
+template<typename T>
+void multi_type_vector<Traits>::insert_cells_to_middle(
+ size_type row, size_type block_index, const T& it_begin, const T& it_end)
+{
+ size_type start_row = m_block_store.positions[block_index];
+ size_type length = std::distance(it_begin, it_end);
+ element_category_type cat = mdds_mtv_get_element_type(*it_begin);
+ element_block_type* blk_data = m_block_store.element_blocks[block_index];
+
+ // Insert two new blocks after the specified block position.
+ size_type n1 = row - start_row;
+ size_type n2 = m_block_store.sizes[block_index] - n1;
+ m_block_store.insert(block_index + 1, 2u);
+
+ m_block_store.sizes[block_index] = n1;
+ m_block_store.sizes[block_index + 1] = length;
+ m_block_store.sizes[block_index + 2] = n2;
+ m_block_store.calc_block_position(block_index + 1);
+ m_block_store.calc_block_position(block_index + 2);
+
+ // block for data series.
+ m_block_store.element_blocks[block_index + 1] = block_funcs::create_new_block(cat, 0);
+ element_block_type* blk2_data = m_block_store.element_blocks[block_index + 1];
+ m_hdl_event.element_block_acquired(blk2_data);
+ mdds_mtv_assign_values(*blk2_data, *it_begin, it_begin, it_end);
+
+ if (blk_data)
+ {
+ element_category_type blk_cat = mdds::mtv::get_block_type(*blk_data);
+
+ // block to hold data from the lower part of the existing block.
+ m_block_store.element_blocks[block_index + 2] = block_funcs::create_new_block(blk_cat, 0);
+ element_block_type* blk3_data = m_block_store.element_blocks[block_index + 2];
+ m_hdl_event.element_block_acquired(blk3_data);
+
+ // Transfer the lower part of the current block to the new block.
+ size_type offset = row - start_row;
+ block_funcs::assign_values_from_block(*blk3_data, *blk_data, offset, n2);
+ block_funcs::resize_block(*blk_data, m_block_store.sizes[block_index]);
+ }
+
+ adjust_block_positions_func{}(m_block_store, block_index + 3, length);
+}
+
+template<typename Traits>
+template<typename T>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::set_cell_to_middle_of_block(
+ size_type block_index, size_type pos_in_block, const T& cell)
+{
+ block_index = set_new_block_to_middle(block_index, pos_in_block, 1, true);
+ create_new_block_with_new_cell(block_index, cell);
+
+ // Return the iterator referencing the inserted block.
+ return get_iterator(block_index);
+}
+
+template<typename Traits>
+template<typename T>
+void multi_type_vector<Traits>::set_cell_to_top_of_data_block(size_type block_index, const T& cell)
+{
+ // t|---|x--|???|b
+
+ m_block_store.sizes[block_index] -= 1;
+ size_type position = m_block_store.positions[block_index];
+ m_block_store.positions[block_index] += 1;
+
+ element_block_type* data = m_block_store.element_blocks[block_index];
+ if (data)
+ {
+ block_funcs::overwrite_values(*data, 0, 1);
+ block_funcs::erase(*data, 0);
+ }
+
+ m_block_store.insert(block_index, position, 1, nullptr);
+ create_new_block_with_new_cell(block_index, cell);
+}
+
+template<typename Traits>
+template<typename T>
+void multi_type_vector<Traits>::set_cell_to_bottom_of_data_block(size_type block_index, const T& cell)
+{
+ // Erase the last value of the block.
+ assert(block_index < m_block_store.positions.size());
+ element_block_type* blk_data = m_block_store.element_blocks[block_index];
+ size_type& blk_size = m_block_store.sizes[block_index];
+ if (blk_data)
+ {
+ block_funcs::overwrite_values(*blk_data, blk_size - 1, 1);
+ block_funcs::erase(*blk_data, blk_size - 1);
+ }
+ blk_size -= 1;
+
+ // Insert a new block of size one with the new value.
+ m_block_store.insert(block_index + 1, 0, 1, nullptr);
+ m_block_store.calc_block_position(block_index + 1);
+ create_new_block_with_new_cell(block_index + 1, cell);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::transfer_impl(
+ size_type start_pos, size_type end_pos, size_type block_index1, multi_type_vector& dest, size_type dest_pos)
+{
+ if (start_pos > end_pos)
+ {
+ std::ostringstream os;
+ os << "multi_type_vector::transfer_impl: start position is larger than the end position. (start=";
+ os << start_pos << ", end=" << end_pos << ")";
+ throw std::out_of_range(os.str());
+ }
+
+ size_type block_index2 = get_block_position(end_pos, block_index1);
+ if (block_index2 == m_block_store.positions.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::transfer_impl", __LINE__, end_pos, block_size(), size());
+
+ size_type len = end_pos - start_pos + 1;
+ size_type last_dest_pos = dest_pos + len - 1;
+
+ // Make sure the destination container is large enough.
+ if (last_dest_pos >= dest.size())
+ throw std::out_of_range("Destination vector is too small for the elements being transferred.");
+
+ if (block_index1 == block_index2)
+ {
+ // All elements are in the same block.
+ return transfer_single_block(start_pos, end_pos, block_index1, dest, dest_pos);
+ }
+
+ return transfer_multi_blocks(start_pos, end_pos, block_index1, block_index2, dest, dest_pos);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::transfer_single_block(
+ size_type start_pos, size_type end_pos, size_type block_index1, multi_type_vector& dest, size_type dest_pos)
+{
+ size_type len = end_pos - start_pos + 1;
+ size_type last_dest_pos = dest_pos + len - 1;
+
+ // All elements are in the same block.
+ element_block_type* src_data = m_block_store.element_blocks[block_index1];
+ size_type start_pos_in_block1 = m_block_store.positions[block_index1];
+
+ // Empty the region in the destination instance where the source elements
+ // are to be transferred to. This also ensures that the destination region
+ // consists of a single block.
+ iterator it_dest_blk = dest.set_empty(dest_pos, last_dest_pos);
+
+ if (!src_data)
+ return get_iterator(block_index1);
+
+ element_category_type cat = get_block_type(*src_data);
+
+ size_type dest_block_index = it_dest_blk->__private_data.block_index;
+ element_block_type* dst_data = dest.m_block_store.element_blocks[dest_block_index];
+
+ size_type dest_pos_in_block = dest_pos - it_dest_blk->position;
+ if (dest_pos_in_block == 0)
+ {
+ // Copy to the top part of the destination block.
+
+ assert(!dst_data); // should be already emptied.
+ size_type dst_size = dest.m_block_store.sizes[dest_block_index];
+ if (len < dst_size)
+ {
+ // Shrink the existing block and insert a new block before it.
+ size_type position = dest.m_block_store.positions[dest_block_index];
+ dest.m_block_store.positions[dest_block_index] += len;
+ dest.m_block_store.sizes[dest_block_index] -= len;
+ dest.m_block_store.insert(dest_block_index, position, len, nullptr);
+ }
+ }
+ else if (dest_pos_in_block + len - 1 == it_dest_blk->size - 1)
+ {
+ // Copy to the bottom part of destination block.
+
+ // Insert a new block below current, and shrink the current block.
+ dest.m_block_store.sizes[dest_block_index] -= len;
+ dest.m_block_store.insert(dest_block_index + 1, 0, len, nullptr);
+ dest.m_block_store.calc_block_position(dest_block_index + 1);
+ ++dest_block_index; // Must point to the new copied block.
+ }
+ else
+ {
+ // Copy to the middle of the destination block.
+
+ // Insert two new blocks below current.
+ size_type blk2_size = dest.m_block_store.sizes[dest_block_index] - dest_pos_in_block - len;
+ dest.m_block_store.insert(dest_block_index + 1, 2);
+ dest.m_block_store.sizes[dest_block_index] = dest_pos_in_block;
+ dest.m_block_store.sizes[dest_block_index + 1] = len;
+ dest.m_block_store.sizes[dest_block_index + 2] = blk2_size;
+
+ dest.m_block_store.calc_block_position(dest_block_index + 1);
+ dest.m_block_store.calc_block_position(dest_block_index + 2);
+
+ ++dest_block_index; // Must point to the new copied block.
+ }
+
+ assert(dest.m_block_store.sizes[dest_block_index] == len);
+ size_type offset = start_pos - start_pos_in_block1;
+ if (offset == 0 && len == m_block_store.sizes[block_index1])
+ {
+ // Just move the whole element block.
+ dest.m_block_store.element_blocks[dest_block_index] = src_data;
+ dest.m_hdl_event.element_block_acquired(src_data);
+
+ m_hdl_event.element_block_released(src_data);
+ m_block_store.element_blocks[block_index1] = nullptr;
+
+ dest.merge_with_adjacent_blocks(dest_block_index);
+ size_type start_pos_offset = merge_with_adjacent_blocks(block_index1);
+ if (start_pos_offset)
+ {
+ // Merged with the previous block. Adjust the return block position.
+ --block_index1;
+ start_pos_in_block1 -= start_pos_offset;
+ }
+
+ return get_iterator(block_index1);
+ }
+
+ dest.m_block_store.element_blocks[dest_block_index] = block_funcs::create_new_block(cat, 0);
+ dst_data = dest.m_block_store.element_blocks[dest_block_index];
+ assert(dst_data);
+ dest.m_hdl_event.element_block_acquired(dst_data);
+
+ // Shallow-copy the elements to the destination block.
+ block_funcs::assign_values_from_block(*dst_data, *src_data, offset, len);
+ dest.merge_with_adjacent_blocks(dest_block_index);
+
+ // Set the source range empty without overwriting the elements.
+ return set_empty_in_single_block(start_pos, end_pos, block_index1, false);
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::size_type multi_type_vector<Traits>::merge_with_adjacent_blocks(
+ size_type block_index)
+{
+ assert(!m_block_store.positions.empty());
+ assert(block_index < m_block_store.positions.size());
+
+ if (block_index == 0)
+ {
+ // No previous block.
+ merge_with_next_block(block_index);
+ return 0;
+ }
+
+ size_type size_prev = m_block_store.sizes[block_index - 1];
+ element_block_type* prev_data = m_block_store.element_blocks[block_index - 1];
+ element_block_type* blk_data = m_block_store.element_blocks[block_index];
+ bool has_next = block_index < (m_block_store.element_blocks.size() - 1);
+ element_block_type* next_data = has_next ? m_block_store.element_blocks[block_index + 1] : nullptr;
+
+ // Check the previous block.
+ if (prev_data)
+ {
+ // Previous block has data.
+ element_category_type cat_prev = mtv::get_block_type(*prev_data);
+ if (!blk_data || cat_prev != mtv::get_block_type(*blk_data))
+ {
+ // Current block is empty or is of different type from the previous one.
+ merge_with_next_block(block_index);
+ return 0;
+ }
+
+ // Previous and current blocks are of the same type.
+ if (next_data && cat_prev == get_block_type(*next_data))
+ {
+ // Merge all three blocks.
+ m_block_store.sizes[block_index - 1] +=
+ m_block_store.sizes[block_index] + m_block_store.sizes[block_index + 1];
+ block_funcs::append_block(*prev_data, *blk_data);
+ block_funcs::append_block(*prev_data, *next_data);
+
+ // Avoid overwriting the transferred elements.
+ block_funcs::resize_block(*blk_data, 0);
+ block_funcs::resize_block(*next_data, 0);
+
+ delete_element_block(block_index);
+ delete_element_block(block_index + 1);
+
+ m_block_store.erase(block_index, 2);
+ return size_prev;
+ }
+
+ // Merge only the previous and current blocks.
+ bool merged = merge_with_next_block(block_index - 1);
+ if (!merged)
+ assert(!"Blocks were not merged!");
+
+ return size_prev;
+ }
+
+ assert(!prev_data); // Previous block is empty.
+
+ if (blk_data)
+ {
+ // Current block is not empty. Check with the next block.
+ merge_with_next_block(block_index);
+ return 0;
+ }
+
+ // Previous and current blocks are both empty.
+ assert(!blk_data);
+
+ if (has_next && !next_data)
+ {
+ // Next block is empty too. Merge all three.
+ m_block_store.sizes[block_index - 1] += m_block_store.sizes[block_index] + m_block_store.sizes[block_index + 1];
+
+ m_block_store.erase(block_index, 2);
+
+ return size_prev;
+ }
+
+ // Next block is not empty, or does not exist. Merge the current block with the previous one.
+ bool merged = merge_with_next_block(block_index - 1);
+ if (!merged)
+ assert(!"Blocks were not merged!");
+
+ return size_prev;
+}
+
+template<typename Traits>
+bool multi_type_vector<Traits>::merge_with_next_block(size_type block_index)
+{
+ assert(!m_block_store.positions.empty());
+ assert(block_index < m_block_store.positions.size());
+
+ if (block_index >= m_block_store.positions.size() - 1)
+ // No more block below this one.
+ return false;
+
+ // Block exists below.
+ element_block_type* blk_data = m_block_store.element_blocks[block_index];
+ element_block_type* next_data = m_block_store.element_blocks[block_index + 1];
+
+ if (!blk_data)
+ {
+ // Empty block. Merge only if the next block is also empty.
+ if (next_data)
+ // Next block is not empty.
+ return false;
+
+ // Merge the two blocks.
+ m_block_store.sizes[block_index] += m_block_store.sizes[block_index + 1];
+ m_block_store.erase(block_index + 1);
+ return true;
+ }
+
+ if (!next_data)
+ return false;
+
+ if (mdds::mtv::get_block_type(*blk_data) != mdds::mtv::get_block_type(*next_data))
+ // Block types differ. Don't merge.
+ return false;
+
+ // Merge it with the next block.
+ block_funcs::append_block(*blk_data, *next_data);
+ block_funcs::resize_block(*next_data, 0);
+ m_block_store.sizes[block_index] += m_block_store.sizes[block_index + 1];
+ delete_element_block(block_index + 1);
+ m_block_store.erase(block_index + 1);
+ return true;
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::size_type multi_type_vector<Traits>::set_new_block_to_middle(
+ size_type block_index, size_type offset, size_type new_block_size, bool overwrite)
+{
+ assert(block_index < m_block_store.positions.size());
+
+ // First, insert two new blocks after the current block.
+ size_type lower_block_size = m_block_store.sizes[block_index] - offset - new_block_size;
+ m_block_store.insert(block_index + 1, 2);
+ m_block_store.sizes[block_index + 1] = new_block_size; // empty block.
+ m_block_store.sizes[block_index + 2] = lower_block_size;
+
+ element_block_type* blk_data = m_block_store.element_blocks[block_index];
+ if (blk_data)
+ {
+ size_type lower_data_start = offset + new_block_size;
+ assert(m_block_store.sizes[block_index + 2] == lower_block_size);
+ element_category_type cat = mtv::get_block_type(*blk_data);
+ m_block_store.element_blocks[block_index + 2] = block_funcs::create_new_block(cat, 0);
+ m_hdl_event.element_block_acquired(m_block_store.element_blocks[block_index + 2]);
+
+ // Try to copy the fewer amount of data to the new non-empty block.
+ if (offset > lower_block_size)
+ {
+ // Keep the upper values in the current block and copy the lower
+ // values to the new non-empty block.
+ block_funcs::assign_values_from_block(
+ *m_block_store.element_blocks[block_index + 2], *blk_data, lower_data_start, lower_block_size);
+
+ if (overwrite)
+ {
+ // Overwrite cells that will become empty.
+ block_funcs::overwrite_values(*blk_data, offset, new_block_size);
+ }
+
+ // Shrink the current data block.
+ block_funcs::resize_block(*blk_data, offset);
+ m_block_store.sizes[block_index] = offset;
+ m_block_store.sizes[block_index + 2] = lower_block_size;
+ }
+ else
+ {
+ // Keep the lower values in the current block and copy the upper
+ // values to the new non-empty block (blk_lower), and swap the two
+ // later.
+ element_block_type* blk_lower_data = m_block_store.element_blocks[block_index + 2];
+ block_funcs::assign_values_from_block(*blk_lower_data, *blk_data, 0, offset);
+ m_block_store.sizes[block_index + 2] = offset;
+
+ if (overwrite)
+ {
+ // Overwrite cells that will become empty.
+ block_funcs::overwrite_values(*blk_data, offset, new_block_size);
+ }
+
+ // Remove the upper and middle values and push the rest to the top.
+ block_funcs::erase(*blk_data, 0, lower_data_start);
+
+ // Set the size of the current block to its new size ( what is after the new block )
+ m_block_store.sizes[block_index] = lower_block_size;
+ m_block_store.sizes[block_index + 2] = offset;
+
+ // And now let's swap the blocks, while preserving the position of the original block.
+ size_type position = m_block_store.positions[block_index];
+ m_block_store.swap(block_index, block_index + 2);
+ m_block_store.positions[block_index] = position;
+ }
+ }
+ else
+ {
+ // There is no data, we just need to update the size of the block
+ m_block_store.sizes[block_index] = offset;
+ }
+
+ // Re-calculate the block positions.
+ m_block_store.calc_block_position(block_index + 1);
+ m_block_store.calc_block_position(block_index + 2);
+
+ return block_index + 1;
+}
+
+template<typename Traits>
+bool multi_type_vector<Traits>::is_previous_block_of_type(size_type block_index, element_category_type cat) const
+{
+ if (block_index == 0)
+ // No previous block.
+ return false;
+
+ const element_block_type* data = m_block_store.element_blocks[block_index - 1];
+ if (data)
+ return cat == mdds::mtv::get_block_type(*data);
+
+ return cat == mtv::element_type_empty;
+}
+
+template<typename Traits>
+bool multi_type_vector<Traits>::is_next_block_of_type(size_type block_index, element_category_type cat) const
+{
+ if (block_index == m_block_store.positions.size() - 1)
+ // No next block.
+ return false;
+
+ const element_block_type* data = m_block_store.element_blocks[block_index + 1];
+ if (data)
+ return cat == mdds::mtv::get_block_type(*data);
+
+ return cat == mtv::element_type_empty;
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::element_block_type* multi_type_vector<Traits>::exchange_elements(
+ const element_block_type& src_data, size_type src_offset, size_type dst_index, size_type dst_offset, size_type len)
+{
+ assert(dst_index < m_block_store.positions.size());
+ element_block_type* dst_blk_data = m_block_store.element_blocks[dst_index];
+ assert(dst_blk_data);
+ size_type dst_blk_size = m_block_store.sizes[dst_index];
+ element_category_type cat_src = mtv::get_block_type(src_data);
+ bool blk_next = is_next_block_of_type(dst_index, cat_src);
+
+ if (dst_offset == 0)
+ {
+ // Set elements to the top of the destination block.
+ bool blk_prev = is_previous_block_of_type(dst_index, cat_src);
+
+ if (dst_blk_size == len)
+ {
+ // The whole block will get replaced.
+ std::unique_ptr<element_block_type, element_block_deleter> data(dst_blk_data);
+ m_hdl_event.element_block_released(dst_blk_data);
+ m_block_store.element_blocks[dst_index] =
+ nullptr; // Prevent its deletion when the parent block gets deleted.
+ dst_blk_data = nullptr;
+
+ if (blk_prev)
+ {
+ // Append to the previous block. Remove the current block.
+ element_block_type* dst_prev_data = m_block_store.element_blocks[dst_index - 1];
+ block_funcs::append_values_from_block(*dst_prev_data, src_data, src_offset, len);
+ m_block_store.sizes[dst_index - 1] += len;
+
+ size_type dst_erase_size = 1;
+
+ // no need to call delete_block since dst_blk_data is null.
+
+ if (blk_next)
+ {
+ // Apend elements from the next block too.
+ element_block_type* dst_next_data = m_block_store.element_blocks[dst_index + 1];
+ block_funcs::append_block(*dst_prev_data, *dst_next_data);
+ m_block_store.sizes[dst_index - 1] += m_block_store.sizes[dst_index + 1];
+ ++dst_erase_size;
+ delete_element_block(dst_index + 1);
+ }
+
+ m_block_store.erase(dst_index, dst_erase_size);
+ return data.release();
+ }
+
+ // Check the next block to see if we need to merge.
+ if (blk_next)
+ {
+ // We need to merge with the next block. Remove the current
+ // block and use the next block to store the new elements as
+ // well as the existing ones.
+ element_block_type* dst_next_data = m_block_store.element_blocks[dst_index + 1];
+ block_funcs::prepend_values_from_block(*dst_next_data, src_data, src_offset, len);
+ m_block_store.positions[dst_index + 1] -= len;
+ m_block_store.sizes[dst_index + 1] += len;
+ m_block_store.erase(dst_index);
+ }
+ else
+ {
+ dst_blk_data = block_funcs::create_new_block(cat_src, 0);
+ m_block_store.element_blocks[dst_index] = dst_blk_data;
+ m_hdl_event.element_block_acquired(dst_blk_data);
+ assert(dst_blk_data && dst_blk_data != data.get());
+ block_funcs::assign_values_from_block(*dst_blk_data, src_data, src_offset, len);
+ }
+
+ // Return this data block as-is.
+ return data.release();
+ }
+
+ // New block to send back to the caller.
+ std::unique_ptr<element_block_type, element_block_deleter> data(nullptr);
+
+ if (dst_blk_data)
+ {
+ element_category_type cat_dst = mtv::get_block_type(*dst_blk_data);
+ data.reset(block_funcs::create_new_block(cat_dst, 0));
+
+ // We need to keep the tail elements of the current block.
+ block_funcs::assign_values_from_block(*data, *dst_blk_data, 0, len);
+ block_funcs::erase(*dst_blk_data, 0, len);
+ }
+
+ size_type position = m_block_store.positions[dst_index];
+ m_block_store.positions[dst_index] += len;
+ m_block_store.sizes[dst_index] -= len;
+
+ if (blk_prev)
+ {
+ // Append the new elements to the previous block.
+ element_block_type* dst_prev_data = m_block_store.element_blocks[dst_index - 1];
+ block_funcs::append_values_from_block(*dst_prev_data, src_data, src_offset, len);
+ m_block_store.sizes[dst_index - 1] += len;
+ }
+ else
+ {
+ // Insert a new block to house the new elements.
+ m_block_store.insert(dst_index, position, len, nullptr);
+ dst_blk_data = block_funcs::create_new_block(cat_src, 0);
+ m_block_store.element_blocks[dst_index] = dst_blk_data;
+ m_hdl_event.element_block_acquired(dst_blk_data);
+ block_funcs::assign_values_from_block(*dst_blk_data, src_data, src_offset, len);
+ }
+
+ return data.release();
+ }
+
+ // New block to send back to the caller.
+ std::unique_ptr<element_block_type, element_block_deleter> data(nullptr);
+
+ if (dst_blk_data)
+ {
+ // Copy the elements of the current block to the block being returned.
+ element_category_type cat_dst = mtv::get_block_type(*dst_blk_data);
+ data.reset(block_funcs::create_new_block(cat_dst, 0));
+ block_funcs::assign_values_from_block(*data, *dst_blk_data, dst_offset, len);
+ }
+
+ assert(dst_offset > 0);
+ size_type dst_end_pos = dst_offset + len;
+
+ if (dst_end_pos == dst_blk_size)
+ {
+ // The new elements will replace the lower part of the block.
+ assert(dst_blk_data);
+ block_funcs::resize_block(*dst_blk_data, dst_offset);
+ m_block_store.sizes[dst_index] = dst_offset;
+
+ if (blk_next)
+ {
+ // Merge with the next block.
+ element_block_type* dst_next_data = m_block_store.element_blocks[dst_index + 1];
+ block_funcs::prepend_values_from_block(*dst_next_data, src_data, src_offset, len);
+ m_block_store.positions[dst_index + 1] -= len;
+ m_block_store.sizes[dst_index + 1] += len;
+ }
+ else
+ {
+ // Insert a new block to store the new elements.
+ size_type position = m_block_store.positions[dst_index] + dst_offset;
+ m_block_store.insert(dst_index + 1, position, len, nullptr);
+ m_block_store.element_blocks[dst_index + 1] = block_funcs::create_new_block(cat_src, 0);
+ dst_blk_data = m_block_store.element_blocks[dst_index + 1];
+ assert(dst_blk_data);
+ m_hdl_event.element_block_acquired(dst_blk_data);
+ block_funcs::assign_values_from_block(*dst_blk_data, src_data, src_offset, len);
+ }
+ }
+ else
+ {
+ // The new elements will replace the middle of the block.
+ assert(dst_end_pos < m_block_store.sizes[dst_index]);
+ dst_index = set_new_block_to_middle(dst_index, dst_offset, len, false);
+ assert(m_block_store.sizes[dst_index] == len);
+ m_block_store.element_blocks[dst_index] = block_funcs::create_new_block(cat_src, 0);
+ dst_blk_data = m_block_store.element_blocks[dst_index];
+ assert(dst_blk_data);
+ m_hdl_event.element_block_acquired(dst_blk_data);
+ block_funcs::assign_values_from_block(*dst_blk_data, src_data, src_offset, len);
+ }
+
+ return data.release();
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::exchange_elements(
+ const element_block_type& src_blk, size_type src_offset, size_type dst_index1, size_type dst_offset1,
+ size_type dst_index2, size_type dst_offset2, size_type len, blocks_type& new_blocks)
+{
+ assert(dst_index1 < dst_index2);
+ assert(dst_offset1 < m_block_store.sizes[dst_index1]);
+ assert(dst_offset2 < m_block_store.sizes[dst_index2]);
+
+ blocks_to_transfer bucket;
+ prepare_blocks_to_transfer(bucket, dst_index1, dst_offset1, dst_index2, dst_offset2);
+
+ m_block_store.insert(bucket.insert_index, 0, len, nullptr);
+ if (bucket.insert_index > 0)
+ m_block_store.calc_block_position(bucket.insert_index);
+
+ m_block_store.element_blocks[bucket.insert_index] = block_funcs::create_new_block(mtv::get_block_type(src_blk), 0);
+
+ element_block_type* blk_data = m_block_store.element_blocks[bucket.insert_index];
+
+ m_hdl_event.element_block_acquired(blk_data);
+ block_funcs::assign_values_from_block(*blk_data, src_blk, src_offset, len);
+ merge_with_adjacent_blocks(bucket.insert_index);
+
+ new_blocks.swap(bucket.blocks);
+}
+
+template<typename Traits>
+bool multi_type_vector<Traits>::append_empty(size_type len)
+{
+ // Append empty cells.
+ if (m_block_store.positions.empty())
+ {
+ // No existing block. Create a new one.
+ assert(m_cur_size == 0);
+ m_block_store.push_back(0, len, nullptr);
+ m_cur_size = len;
+ return true;
+ }
+
+ bool new_block_added = false;
+
+ element_block_type* last_data = m_block_store.element_blocks.back();
+
+ if (!last_data)
+ {
+ // Last block is empty. Just increase its size.
+ m_block_store.sizes.back() += len;
+ }
+ else
+ {
+ // Append a new empty block.
+ m_block_store.push_back(m_cur_size, len, nullptr);
+ new_block_added = true;
+ }
+
+ m_cur_size += len;
+
+ return new_block_added;
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::resize(size_type new_size)
+{
+ MDDS_MTV_TRACE_ARGS(mutator, "new_size=" << new_size);
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ std::ostringstream os_prev_block;
+ dump_blocks(os_prev_block);
+#endif
+
+ resize_impl(new_size);
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ try
+ {
+ check_block_integrity();
+ }
+ catch (const mdds::integrity_error& e)
+ {
+ std::ostringstream os;
+ os << e.what() << std::endl;
+ os << "block integrity check failed in resize (new-size=" << new_size << ")" << std::endl;
+ os << "previous block state:" << std::endl;
+ os << os_prev_block.str();
+ std::cerr << os.str() << std::endl;
+ abort();
+ }
+#endif
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::resize_impl(size_type new_size)
+{
+ if (new_size == m_cur_size)
+ return;
+
+ if (!new_size)
+ {
+ clear();
+ return;
+ }
+
+ if (new_size > m_cur_size)
+ {
+ // Append empty cells.
+ append_empty(new_size - m_cur_size);
+ return;
+ }
+
+ assert(new_size < m_cur_size && new_size > 0);
+
+ // Find out in which block the new end row will be.
+ size_type new_end_row = new_size - 1;
+ size_type block_index = get_block_position(new_end_row);
+ if (block_index == m_block_store.positions.size())
+ mdds::mtv::detail::throw_block_position_not_found(
+ "multi_type_vector::resize", __LINE__, new_end_row, block_size(), size());
+
+ element_block_type* data = m_block_store.element_blocks[block_index];
+ size_type start_row_in_block = m_block_store.positions[block_index];
+ size_type end_row_in_block = start_row_in_block + m_block_store.sizes[block_index] - 1;
+
+ if (new_end_row < end_row_in_block)
+ {
+ // Shrink the size of the current block.
+ size_type new_block_size = new_end_row - start_row_in_block + 1;
+ if (data)
+ {
+ block_funcs::overwrite_values(*data, new_end_row + 1, end_row_in_block - new_end_row);
+ block_funcs::resize_block(*data, new_block_size);
+ }
+ m_block_store.sizes[block_index] = new_block_size;
+ }
+
+ // Remove all blocks below the current one.
+ delete_element_blocks(block_index + 1, m_block_store.element_blocks.size());
+ size_type len = m_block_store.element_blocks.size() - block_index - 1;
+ m_block_store.erase(block_index + 1, len);
+ m_cur_size = new_size;
+}
+
+template<typename Traits>
+typename multi_type_vector<Traits>::iterator multi_type_vector<Traits>::transfer_multi_blocks(
+ size_type start_pos, size_type end_pos, size_type block_index1, size_type block_index2, multi_type_vector& dest,
+ size_type dest_pos)
+{
+ assert(block_index1 < block_index2);
+ size_type start_pos_in_block1 = m_block_store.positions[block_index1];
+ size_type start_pos_in_block2 = m_block_store.positions[block_index2];
+
+ size_type len = end_pos - start_pos + 1;
+ size_type last_dest_pos = dest_pos + len - 1;
+
+ // Empty the region in the destination container where the elements
+ // are to be transferred to. This ensures that the destination region
+ // consists of a single block.
+ iterator it_dest_blk = dest.set_empty(dest_pos, last_dest_pos);
+
+ size_type dest_block_index = it_dest_blk->__private_data.block_index;
+ size_type dest_pos_in_block = dest_pos - it_dest_blk->position;
+ assert(!dest.m_block_store.element_blocks[dest_block_index]); // should be already emptied.
+
+ size_type block_len = block_index2 - block_index1 + 1;
+
+ // Create slots for new blocks in the destination.
+
+ size_type dest_block_index1 = dest_block_index;
+
+ if (dest_pos_in_block == 0)
+ {
+ // Copy to the top part of destination block.
+ if (len < dest.m_block_store.sizes[dest_block_index])
+ {
+ // Shrink the existing block and insert slots for the new blocks before it.
+ dest.m_block_store.sizes[dest_block_index] -= len;
+ dest.m_block_store.positions[dest_block_index] += len;
+ dest.m_block_store.insert(dest_block_index, block_len);
+ }
+ else
+ {
+ // Destination block is exactly of the length of the elements being transferred.
+ dest.delete_element_block(dest_block_index);
+ dest.m_block_store.sizes[dest_block_index] = 0;
+ if (block_len > 1)
+ dest.m_block_store.insert(dest_block_index, block_len - 1);
+ }
+ }
+ else if (dest_pos_in_block + len - 1 == it_dest_blk->size - 1)
+ {
+ // Copy to the bottom part of destination block. Insert slots for new
+ // blocks below current, and shrink the current block.
+ dest.m_block_store.insert(dest_block_index + 1, block_len);
+ dest.m_block_store.sizes[dest_block_index] -= len;
+
+ ++dest_block_index1;
+ }
+ else
+ {
+ // Copy to the middle of the destination block. Insert slots for the
+ // new blocks (plus one extra for the bottom empty block) below the
+ // current block.
+ size_type blk2_size = dest.m_block_store.sizes[dest_block_index] - dest_pos_in_block - len;
+ dest.m_block_store.insert(dest_block_index + 1, block_len + 1);
+ assert(dest.m_block_store.positions.size() > dest_block_index + block_len + 1);
+ dest.m_block_store.sizes[dest_block_index] = dest_pos_in_block;
+
+ // Re-calculate the size and position of the lower part of the destination block.
+ dest.m_block_store.positions[dest_block_index + block_len + 1] =
+ dest.m_block_store.calc_next_block_position(dest_block_index) + len;
+ dest.m_block_store.sizes[dest_block_index + block_len + 1] = blk2_size;
+
+ ++dest_block_index1;
+ }
+
+ size_type del_index1 = block_index1, del_index2 = block_index2;
+
+ // Now that the new slots have been created, start transferring the blocks.
+
+ // Transfer the first block.
+ size_type offset = start_pos - start_pos_in_block1;
+ if (offset)
+ {
+ // Transfer the lower part of the first block.
+
+ assert(dest.m_block_store.sizes[dest_block_index1] == 0);
+ dest.m_block_store.sizes[dest_block_index1] = m_block_store.sizes[block_index1] - offset;
+ if (dest_block_index1 > 0)
+ dest.m_block_store.calc_block_position(dest_block_index1);
+
+ if (m_block_store.element_blocks[block_index1])
+ {
+ element_block_type* blk_data1 = m_block_store.element_blocks[block_index1];
+ element_category_type cat = mtv::get_block_type(*blk_data1);
+ dest.m_block_store.element_blocks[dest_block_index1] = block_funcs::create_new_block(cat, 0);
+ element_block_type* dst_data1 = dest.m_block_store.element_blocks[dest_block_index1];
+ assert(dst_data1);
+ dest.m_hdl_event.element_block_acquired(dst_data1);
+
+ // Shallow-copy the elements to the destination block, and shrink
+ // the source block to remove the transferred elements.
+ block_funcs::assign_values_from_block(
+ *dst_data1, *blk_data1, offset, m_block_store.sizes[block_index1] - offset);
+ block_funcs::resize_block(*blk_data1, offset);
+ }
+
+ m_block_store.sizes[block_index1] = offset;
+ ++del_index1; // Retain this block.
+ }
+ else
+ {
+ // Just move the whole block over.
+ element_block_type* data = m_block_store.element_blocks[block_index1];
+ dest.m_block_store.element_blocks[dest_block_index1] = data;
+ dest.m_block_store.sizes[dest_block_index1] = m_block_store.sizes[block_index1];
+ dest.m_block_store.calc_block_position(dest_block_index1);
+
+ if (data)
+ {
+ dest.m_hdl_event.element_block_acquired(data);
+ m_hdl_event.element_block_released(data);
+ m_block_store.element_blocks[block_index1] = nullptr;
+ }
+
+ m_block_store.sizes[block_index1] = 0;
+ }
+
+ if (block_len > 2)
+ {
+ size_type position = dest.m_block_store.calc_next_block_position(dest_block_index1);
+
+ for (size_type i = 0; i < block_len - 2; ++i)
+ {
+ size_type src_block_pos = block_index1 + 1 + i;
+ size_type dest_block_pos = dest_block_index1 + 1 + i;
+ assert(dest.m_block_store.sizes[dest_block_pos] == 0);
+
+ element_block_type* data = m_block_store.element_blocks[src_block_pos];
+ dest.m_block_store.element_blocks[dest_block_pos] = data;
+ dest.m_block_store.sizes[dest_block_pos] = m_block_store.sizes[src_block_pos];
+ dest.m_block_store.positions[dest_block_pos] = position;
+ position += m_block_store.sizes[src_block_pos];
+ m_block_store.sizes[src_block_pos] = 0;
+
+ if (data)
+ {
+ dest.m_hdl_event.element_block_acquired(data);
+ m_hdl_event.element_block_released(data);
+ m_block_store.element_blocks[src_block_pos] = nullptr;
+ }
+ }
+ }
+
+ // Transfer the last block.
+ if (block_len > 1)
+ {
+ size_type size_to_trans = end_pos - start_pos_in_block2 + 1;
+ size_type dest_block_pos = dest_block_index1 + block_len - 1;
+ assert(dest.m_block_store.sizes[dest_block_pos] == 0);
+
+ element_block_type* blk_data2 = m_block_store.element_blocks[block_index2];
+
+ if (size_to_trans < m_block_store.sizes[block_index2])
+ {
+ // Transfer the upper part of this block.
+ assert(dest_block_pos > 0);
+ dest.m_block_store.calc_block_position(dest_block_pos);
+ dest.m_block_store.sizes[dest_block_pos] = size_to_trans;
+
+ if (blk_data2)
+ {
+ element_category_type cat = mtv::get_block_type(*blk_data2);
+ dest.m_block_store.element_blocks[dest_block_pos] = block_funcs::create_new_block(cat, 0);
+ element_block_type* blk_dst_data = dest.m_block_store.element_blocks[dest_block_pos];
+ dest.m_hdl_event.element_block_acquired(blk_dst_data);
+
+ block_funcs::assign_values_from_block(*blk_dst_data, *blk_data2, 0, size_to_trans);
+ block_funcs::erase(*blk_data2, 0, size_to_trans);
+ }
+
+ m_block_store.positions[block_index2] += size_to_trans;
+ m_block_store.sizes[block_index2] -= size_to_trans;
+ --del_index2; // Retain this block.
+ }
+ else
+ {
+ // Just move the whole block over.
+ dest.m_block_store.sizes[dest_block_pos] = m_block_store.sizes[block_index2];
+ dest.m_block_store.element_blocks[dest_block_pos] = m_block_store.element_blocks[block_index2];
+ dest.m_block_store.calc_block_position(dest_block_pos);
+
+ if (blk_data2)
+ {
+ dest.m_hdl_event.element_block_acquired(blk_data2);
+ m_hdl_event.element_block_released(blk_data2);
+ m_block_store.element_blocks[block_index2] = nullptr;
+ }
+
+ m_block_store.sizes[block_index2] = 0;
+ }
+ }
+
+ // Now that all the elements have been transferred, check the bordering
+ // blocks in the destination and merge them as needed.
+ if (block_len > 1)
+ dest.merge_with_adjacent_blocks(dest_block_index1 + block_len - 1);
+ dest.merge_with_adjacent_blocks(dest_block_index1);
+
+ // Delete all transferred blocks, and replace it with one empty block.
+ if (del_index2 < del_index1)
+ {
+ // No blocks will be deleted. See if we can just extend one of the
+ // neighboring empty blocks.
+
+ element_block_type* blk1_data = m_block_store.element_blocks[block_index1];
+ element_block_type* blk2_data = m_block_store.element_blocks[block_index2];
+
+ if (!blk1_data)
+ {
+ assert(blk2_data);
+
+ // Block 1 is empty. Extend this block downward.
+ m_block_store.sizes[block_index1] += len;
+ return get_iterator(block_index1);
+ }
+
+ if (!blk2_data)
+ {
+ assert(blk1_data);
+
+ // Block 2 is empty. Extend this block upward.
+ m_block_store.sizes[block_index2] += len;
+ m_block_store.positions[block_index2] -= len;
+ return get_iterator(block_index2);
+ }
+
+ // Neither block1 nor block2 are empty. Just insert a new empty block
+ // between them. After the insertion, the old block2 position becomes
+ // the position of the inserted block.
+ m_block_store.insert(block_index2, 0, len, nullptr);
+ m_block_store.calc_block_position(block_index2);
+
+ // No need to adjust local index vars
+ return get_iterator(block_index2);
+ }
+
+ if (del_index1 > 0 && !m_block_store.element_blocks[del_index1 - 1])
+ {
+ // The block before the first block to be deleted is empty. Simply
+ // extend that block to cover the deleted block segment.
+ m_block_store.sizes[del_index1 - 1] += len;
+ }
+ else
+ {
+ // Block before is not empty (or doesn't exist). Keep the first slot,
+ // and erase the rest.
+ m_block_store.sizes[del_index1] = len; // Insert an empty
+ ++del_index1;
+ }
+
+ size_type ret_block_index = del_index1 - 1;
+
+ if (del_index2 >= del_index1)
+ {
+ size_type n_del_blocks = del_index2 - del_index1 + 1;
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ for (size_type i = del_index1; i <= del_index2; ++i)
+ {
+ // All slots to be erased should have zero size
+ assert(m_block_store.sizes[i] == 0);
+ }
+#endif
+ m_block_store.erase(del_index1, n_del_blocks);
+ }
+
+ // The block pointed to by ret_block_index is guaranteed to be empty by
+ // this point.
+ assert(!m_block_store.element_blocks[ret_block_index]);
+
+ // Merging with the previous block never happens.
+ size_type start_pos_offset = merge_with_adjacent_blocks(ret_block_index);
+ (void)start_pos_offset; // avoid unused variable compiler warning.
+ assert(!start_pos_offset);
+
+ m_block_store.calc_block_position(ret_block_index);
+ return get_iterator(ret_block_index);
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::swap(multi_type_vector& other)
+{
+ MDDS_MTV_TRACE_ARGS(mutator, "other=?");
+
+ std::swap(m_hdl_event, other.m_hdl_event);
+ std::swap(m_cur_size, other.m_cur_size);
+ m_block_store.swap(other.m_block_store);
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::swap(
+ size_type start_pos, size_type end_pos, multi_type_vector& other, size_type other_pos)
+{
+ MDDS_MTV_TRACE_ARGS(
+ mutator, "start_pos=" << start_pos << "; end_pos=" << end_pos << "; other=?; other_pos=" << other_pos);
+
+ if (start_pos > end_pos)
+ throw std::out_of_range("multi_type_vector::swap: start position is larger than the end position!");
+
+ size_type other_end_pos = other_pos + end_pos - start_pos;
+
+ if (end_pos >= m_cur_size || other_end_pos >= other.m_cur_size)
+ throw std::out_of_range("multi_type_vector::swap: end position is out of bound!");
+
+ size_type block_index1 = get_block_position(start_pos);
+ if (block_index1 == m_block_store.positions.size())
+ throw std::out_of_range("multi_type_vector::swap: start block position in source not found!");
+
+ size_type block_index2 = get_block_position(end_pos, block_index1);
+ if (block_index2 == m_block_store.positions.size())
+ throw std::out_of_range("multi_type_vector::swap: end block position in source not found!");
+
+ size_type dest_block_index1 = other.get_block_position(other_pos);
+ if (dest_block_index1 == other.m_block_store.positions.size())
+ throw std::out_of_range("multi_type_vector::swap: start block position in destination not found!");
+
+ size_type dest_block_index2 = other.get_block_position(other_end_pos, dest_block_index1);
+ if (dest_block_index2 == other.m_block_store.positions.size())
+ throw std::out_of_range("multi_type_vector::swap: end block position in destination not found!");
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ std::ostringstream os_prev_block, os_prev_block_other;
+ dump_blocks(os_prev_block);
+ other.dump_blocks(os_prev_block_other);
+#endif
+
+ swap_impl(other, start_pos, end_pos, other_pos, block_index1, block_index2, dest_block_index1, dest_block_index2);
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ std::ostringstream os_block, os_block_other;
+ dump_blocks(os_block);
+ other.dump_blocks(os_block_other);
+
+ try
+ {
+ check_block_integrity();
+ other.check_block_integrity();
+ }
+ catch (const mdds::integrity_error& e)
+ {
+ std::ostringstream os;
+ os << e.what() << std::endl;
+ os << std::endl
+ << "block integrity check failed in swap (start_pos=" << start_pos << "; end_pos=" << end_pos
+ << "; other_pos=" << other_pos << ")" << std::endl;
+ os << std::endl << "previous block state (source):" << std::endl;
+ os << os_prev_block.str();
+ os << std::endl << "previous block state (destination):" << std::endl;
+ os << os_prev_block_other.str();
+ os << std::endl << "altered block state (source):" << std::endl;
+ os << os_block.str();
+ os << std::endl << "altered block state (destination):" << std::endl;
+ os << os_block_other.str();
+ std::cerr << os.str() << std::endl;
+ abort();
+ }
+#endif
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::shrink_to_fit()
+{
+ MDDS_MTV_TRACE(mutator);
+
+ for (auto* data : m_block_store.element_blocks)
+ {
+ if (data)
+ block_funcs::shrink_to_fit(*data);
+ }
+}
+
+template<typename Traits>
+bool multi_type_vector<Traits>::operator==(const multi_type_vector& other) const
+{
+ MDDS_MTV_TRACE_ARGS(accessor, "other=?");
+
+ if (this == &other)
+ // Comparing to self is always equal.
+ return true;
+
+ if (m_cur_size != other.m_cur_size)
+ // Row sizes differ.
+ return false;
+
+ return m_block_store.equals(other.m_block_store);
+}
+
+template<typename Traits>
+bool multi_type_vector<Traits>::operator!=(const multi_type_vector& other) const
+{
+ MDDS_MTV_TRACE_ARGS(accessor, "other=?");
+
+ return !operator==(other);
+}
+
+template<typename Traits>
+multi_type_vector<Traits>& multi_type_vector<Traits>::operator=(const multi_type_vector& other)
+{
+ MDDS_MTV_TRACE_ARGS(mutator, "other=? (copy)");
+
+ multi_type_vector assigned(other);
+ swap(assigned);
+ return *this;
+}
+
+template<typename Traits>
+multi_type_vector<Traits>& multi_type_vector<Traits>::operator=(multi_type_vector&& other)
+{
+ MDDS_MTV_TRACE_ARGS(mutator, "other=? (move)");
+
+ multi_type_vector assigned(std::move(other));
+ swap(assigned);
+ return *this;
+}
+
+template<typename Traits>
+template<typename T>
+mtv::element_t multi_type_vector<Traits>::get_element_type(const T& elem)
+{
+ return mdds_mtv_get_element_type(elem);
+}
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+
+template<typename Traits>
+void multi_type_vector<Traits>::dump_blocks(std::ostream& os) const
+{
+ os << "--- blocks" << std::endl;
+ std::ios_base::fmtflags origflags = os.flags();
+ for (size_type i = 0, n = m_block_store.positions.size(); i < n; ++i)
+ {
+ size_type pos = m_block_store.positions[i];
+ size_type size = m_block_store.sizes[i];
+ const element_block_type* data = m_block_store.element_blocks[i];
+ element_category_type cat = mtv::element_type_empty;
+ if (data)
+ cat = mtv::get_block_type(*data);
+ os << " block " << i << ": position=" << pos << " size=" << size << " type=" << std::dec << cat
+ << " data=" << std::hex << data << std::endl;
+ }
+ os.setf(origflags);
+}
+
+template<typename Traits>
+void multi_type_vector<Traits>::check_block_integrity() const
+{
+ m_block_store.check_integrity();
+
+ if (m_block_store.positions.empty())
+ // Nothing to check.
+ return;
+
+ if (m_block_store.sizes.size() == 1 && m_block_store.sizes[0] == 0)
+ throw mdds::integrity_error("block should never be zero sized!");
+
+ if (m_block_store.positions[0] != 0u)
+ {
+ std::ostringstream os;
+ os << "position of the first block should be zero!" << std::endl;
+
+ dump_blocks(os);
+ mdds::integrity_error(os.str());
+ }
+
+ const element_block_type* data_prev = m_block_store.element_blocks[0];
+ element_category_type cat_prev = data_prev ? mtv::get_block_type(*data_prev) : mtv::element_type_empty;
+
+ size_type cur_position = m_block_store.sizes[0];
+ size_type total_size = m_block_store.sizes[0];
+
+ for (size_type i = 1, n = m_block_store.positions.size(); i < n; ++i)
+ {
+ size_type this_size = m_block_store.sizes[i];
+ if (this_size == 0)
+ throw mdds::integrity_error("block should never be zero sized!");
+
+ if (m_block_store.positions[i] != cur_position)
+ {
+ std::ostringstream os;
+ os << "position of the current block is wrong! (expected=" << cur_position
+ << "; actual=" << m_block_store.positions[i] << ")" << std::endl;
+
+ dump_blocks(os);
+ throw mdds::integrity_error(os.str());
+ }
+
+ element_category_type cat = mtv::element_type_empty;
+
+ const element_block_type* data = m_block_store.element_blocks[i];
+
+ if (data)
+ {
+ cat = mtv::get_block_type(*data);
+
+ if (block_funcs::size(*data) != this_size)
+ throw mdds::integrity_error("block size cache and the actual element block size differ!");
+ }
+
+ if (cat_prev == cat)
+ {
+ std::ostringstream os;
+ os << "Two adjacent blocks should never be of the same type." << std::endl;
+ dump_blocks(os);
+ mdds::integrity_error(os.str());
+ }
+
+ data_prev = data;
+ cat_prev = cat;
+
+ total_size += this_size;
+ cur_position += this_size;
+ }
+
+ if (total_size != m_cur_size)
+ {
+ std::ostringstream os;
+ os << "Current size does not equal the total sizes of all blocks." << std::endl;
+ os << "current size=" << m_cur_size << " total block size=" << total_size << std::endl;
+ dump_blocks(os);
+ mdds::integrity_error(os.str());
+ }
+}
+
+#endif
+
+}}} // namespace mdds::mtv::soa
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/mdds/multi_type_vector/standard_element_blocks.hpp b/include/mdds/multi_type_vector/standard_element_blocks.hpp
new file mode 100644
index 0000000..a045ce8
--- /dev/null
+++ b/include/mdds/multi_type_vector/standard_element_blocks.hpp
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * Copyright (c) 2022 Kohei Yoshida
+ *
+ * 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, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ ************************************************************************/
+#pragma once
+
+#include "types.hpp"
+#include "util.hpp"
+#include "block_funcs.hpp"
+#include "macro.hpp"
+
+namespace mdds { namespace mtv {
+
+constexpr element_t element_type_boolean = element_type_reserved_start;
+constexpr element_t element_type_int8 = element_type_reserved_start + 1;
+constexpr element_t element_type_uint8 = element_type_reserved_start + 2;
+constexpr element_t element_type_int16 = element_type_reserved_start + 3;
+constexpr element_t element_type_uint16 = element_type_reserved_start + 4;
+constexpr element_t element_type_int32 = element_type_reserved_start + 5;
+constexpr element_t element_type_uint32 = element_type_reserved_start + 6;
+constexpr element_t element_type_int64 = element_type_reserved_start + 7;
+constexpr element_t element_type_uint64 = element_type_reserved_start + 8;
+constexpr element_t element_type_float = element_type_reserved_start + 9;
+constexpr element_t element_type_double = element_type_reserved_start + 10;
+constexpr element_t element_type_string = element_type_reserved_start + 11;
+
+using boolean_element_block = default_element_block<element_type_boolean, bool>;
+using int8_element_block = default_element_block<element_type_int8, int8_t>;
+using uint8_element_block = default_element_block<element_type_uint8, uint8_t>;
+using int16_element_block = default_element_block<element_type_int16, int16_t>;
+using uint16_element_block = default_element_block<element_type_uint16, uint16_t>;
+using int32_element_block = default_element_block<element_type_int32, int32_t>;
+using uint32_element_block = default_element_block<element_type_uint32, uint32_t>;
+using int64_element_block = default_element_block<element_type_int64, int64_t>;
+using uint64_element_block = default_element_block<element_type_uint64, uint64_t>;
+using float_element_block = default_element_block<element_type_float, float>;
+using double_element_block = default_element_block<element_type_double, double>;
+using string_element_block = default_element_block<element_type_string, std::string>;
+
+MDDS_MTV_DEFINE_ELEMENT_CALLBACKS(bool, element_type_boolean, false, boolean_element_block)
+MDDS_MTV_DEFINE_ELEMENT_CALLBACKS(int8_t, element_type_int8, 0, int8_element_block)
+MDDS_MTV_DEFINE_ELEMENT_CALLBACKS(uint8_t, element_type_uint8, 0, uint8_element_block)
+MDDS_MTV_DEFINE_ELEMENT_CALLBACKS(int16_t, element_type_int16, 0, int16_element_block)
+MDDS_MTV_DEFINE_ELEMENT_CALLBACKS(uint16_t, element_type_uint16, 0, uint16_element_block)
+MDDS_MTV_DEFINE_ELEMENT_CALLBACKS(int32_t, element_type_int32, 0, int32_element_block)
+MDDS_MTV_DEFINE_ELEMENT_CALLBACKS(uint32_t, element_type_uint32, 0, uint32_element_block)
+MDDS_MTV_DEFINE_ELEMENT_CALLBACKS(int64_t, element_type_int64, 0, int64_element_block)
+MDDS_MTV_DEFINE_ELEMENT_CALLBACKS(uint64_t, element_type_uint64, 0, uint64_element_block)
+MDDS_MTV_DEFINE_ELEMENT_CALLBACKS(float, element_type_float, 0.0, float_element_block)
+MDDS_MTV_DEFINE_ELEMENT_CALLBACKS(double, element_type_double, 0.0, double_element_block)
+MDDS_MTV_DEFINE_ELEMENT_CALLBACKS(std::string, element_type_string, std::string(), string_element_block)
+
+struct standard_element_blocks_traits : public default_traits
+{
+ using block_funcs = element_block_funcs<
+ boolean_element_block, int8_element_block, uint8_element_block, int16_element_block, uint16_element_block,
+ int32_element_block, uint32_element_block, int64_element_block, uint64_element_block, float_element_block,
+ double_element_block, string_element_block>;
+};
+
+}} // namespace mdds::mtv
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/mdds/multi_type_vector/types.hpp b/include/mdds/multi_type_vector/types.hpp
new file mode 100644
index 0000000..c51f31e
--- /dev/null
+++ b/include/mdds/multi_type_vector/types.hpp
@@ -0,0 +1,1011 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * Copyright (c) 2021 Kohei Yoshida
+ *
+ * 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, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ ************************************************************************/
+
+#ifndef INCLUDED_MDDS_MULTI_TYPE_VECTOR_TYPES_2_HPP
+#define INCLUDED_MDDS_MULTI_TYPE_VECTOR_TYPES_2_HPP
+
+#include "../global.hpp"
+#include "./types_util.hpp"
+
+#include <algorithm>
+#include <cassert>
+#include <memory>
+#include <cstdint>
+#include <vector>
+
+#if defined(MDDS_UNIT_TEST) || defined(MDDS_MULTI_TYPE_VECTOR_DEBUG)
+#include <iostream>
+#include <sstream>
+using std::cerr;
+using std::cout;
+using std::endl;
+#endif
+
+namespace mdds { namespace mtv {
+
+using element_t = int;
+
+constexpr element_t element_type_empty = -1;
+
+constexpr element_t element_type_reserved_start = 0;
+constexpr element_t element_type_reserved_end = 49;
+
+constexpr element_t element_type_user_start = 50;
+
+/**
+ * Loop-unrolling factor with optional SIMD feature.
+ *
+ * In each enumerator value, the first byte contains the loop-unrolling factor
+ * (either 0, 4, 8, 16 or 32), while the second byte stores SIMD flags.
+ */
+enum class lu_factor_t : int
+{
+ none = 0,
+ lu4 = 4,
+ lu8 = 8,
+ lu16 = 16,
+ lu32 = 32,
+ sse2_x64 = 1 << 8,
+ sse2_x64_lu4 = 1 << 8 | 4,
+ sse2_x64_lu8 = 1 << 8 | 8,
+ sse2_x64_lu16 = 1 << 8 | 16,
+ avx2_x64 = 2 << 8,
+ avx2_x64_lu4 = 2 << 8 | 4,
+ avx2_x64_lu8 = 2 << 8 | 8,
+};
+
+/**
+ * Type of traced method.
+ *
+ * An <code>accessor</code> in this context is a method whose call alone does
+ * not mutate the state of the container. All const methods are accessors.
+ * Note that some non-const methods that return non-const references to
+ * internal data are still considered accessors.
+ *
+ * A <code>mutator</code> is a method that, when called, may change the state
+ * of the stored data immediately.
+ *
+ * The <code>accessor_with_pos_hint</code> label signifies an accessor that
+ * takes a position hint as its first argument. Likewise,
+ * <code>mutator_with_pos_hint</code> signifies a mutator that takes a
+ * position hint as its first argument.
+ *
+ * The <code>constructor</code> and <code>destructor</code> labels are
+ * hopefully self-explanatory.
+ */
+enum class trace_method_t : int
+{
+ unspecified = 0,
+ accessor = 1,
+ accessor_with_pos_hint = 1 << 8 | 1,
+ mutator = 2,
+ mutator_with_pos_hint = 1 << 8 | 2,
+ constructor = 3,
+ destructor = 4
+};
+
+/**
+ * Struct containing the information about each traced method.
+ */
+struct trace_method_properties_t
+{
+ trace_method_t type = trace_method_t::unspecified;
+
+ /**
+ * Memory address of the container instance the traced method belongs to.
+ * This is essentially the <code>this</code> pointer inside the traced
+ * method.
+ */
+ const void* instance = nullptr;
+
+ /** Name of the method. */
+ const char* function_name = nullptr;
+
+ /**
+ * String containing the argument names as well as their values if
+ * available.
+ */
+ std::string function_args;
+
+ /** Path of the file where the method body is. */
+ const char* filepath = nullptr;
+
+ /** Line number of the first line of the traced method body. */
+ int line_number = -1;
+};
+
+/**
+ * Generic exception used for errors specific to element block operations.
+ */
+class element_block_error : public mdds::general_error
+{
+public:
+ element_block_error(const std::string& msg) : mdds::general_error(msg)
+ {}
+};
+
+class base_element_block;
+element_t get_block_type(const base_element_block&);
+
+/**
+ * Non-template common base type necessary for blocks of all types to be
+ * stored in a single container.
+ */
+class base_element_block
+{
+ friend element_t get_block_type(const base_element_block&);
+
+protected:
+ element_t type;
+ base_element_block(element_t _t) : type(_t)
+ {}
+};
+
+/**
+ * Vector that delays deleting from the front of the vector, which avoids
+ * O(n^2) memory move operations when code needs to delete items from one
+ * element block and add to another element block.
+ */
+template<typename T, typename Allocator = std::allocator<T>>
+class delayed_delete_vector
+{
+ typedef std::vector<T, Allocator> store_type;
+ store_type m_vec;
+ size_t m_front_offset = 0; // number of elements removed from front of array
+public:
+ typedef typename store_type::value_type value_type;
+ typedef typename store_type::size_type size_type;
+ typedef typename store_type::difference_type difference_type;
+ typedef typename store_type::reference reference;
+ typedef typename store_type::const_reference const_reference;
+ typedef typename store_type::pointer pointer;
+ typedef typename store_type::const_pointer const_pointer;
+ typedef typename store_type::iterator iterator;
+ typedef typename store_type::reverse_iterator reverse_iterator;
+ typedef typename store_type::const_iterator const_iterator;
+ typedef typename store_type::const_reverse_iterator const_reverse_iterator;
+
+ delayed_delete_vector() : m_vec()
+ {}
+
+ delayed_delete_vector(size_t n, const T& val) : m_vec(n, val)
+ {}
+
+ delayed_delete_vector(size_t n) : m_vec(n)
+ {}
+
+ template<typename InputIt>
+ delayed_delete_vector(InputIt first, InputIt last) : m_vec(first, last)
+ {}
+
+ iterator begin() noexcept
+ {
+ return m_vec.begin() + m_front_offset;
+ }
+
+ iterator end() noexcept
+ {
+ return m_vec.end();
+ }
+
+ const_iterator begin() const noexcept
+ {
+ return m_vec.begin() + m_front_offset;
+ }
+
+ const_iterator end() const noexcept
+ {
+ return m_vec.end();
+ }
+
+ reverse_iterator rbegin() noexcept
+ {
+ return m_vec.rbegin();
+ }
+
+ const_reverse_iterator rbegin() const noexcept
+ {
+ return m_vec.rbegin();
+ }
+
+ reverse_iterator rend() noexcept
+ {
+ return m_vec.rend() - m_front_offset;
+ }
+
+ const_reverse_iterator rend() const noexcept
+ {
+ return m_vec.rend() - m_front_offset;
+ }
+
+ reference operator[](size_type pos)
+ {
+ return m_vec[pos + m_front_offset];
+ }
+
+ const_reference operator[](size_type pos) const
+ {
+ return m_vec[pos + m_front_offset];
+ }
+
+ reference at(size_type pos)
+ {
+ return m_vec.at(pos + m_front_offset);
+ }
+
+ const_reference at(size_type pos) const
+ {
+ return m_vec.at(pos + m_front_offset);
+ }
+
+ void push_back(const T& value)
+ {
+ m_vec.push_back(value);
+ }
+
+ iterator insert(iterator pos, const T& value)
+ {
+ return m_vec.insert(pos, value);
+ }
+
+ iterator insert(const_iterator pos, T&& value)
+ {
+ return m_vec.insert(pos, std::move(value));
+ }
+
+ template<typename InputIt>
+ void insert(iterator pos, InputIt first, InputIt last)
+ {
+ m_vec.insert(pos, first, last);
+ }
+
+ void resize(size_type count)
+ {
+ clear_removed();
+ m_vec.resize(count);
+ }
+
+ iterator erase(iterator pos)
+ {
+ if (pos == m_vec.begin() + m_front_offset)
+ {
+ ++m_front_offset;
+ return m_vec.begin() + m_front_offset;
+ }
+ else
+ return m_vec.erase(pos);
+ }
+
+ iterator erase(iterator first, iterator last)
+ {
+ return m_vec.erase(first, last);
+ }
+
+ size_type capacity() const noexcept
+ {
+ return m_vec.capacity();
+ }
+
+ void shrink_to_fit()
+ {
+ clear_removed();
+ m_vec.shrink_to_fit();
+ }
+
+ void reserve(size_type new_cap)
+ {
+ clear_removed();
+ m_vec.reserve(new_cap);
+ }
+
+ size_type size() const
+ {
+ return m_vec.size() - m_front_offset;
+ }
+
+ template<typename InputIt>
+ void assign(InputIt first, InputIt last)
+ {
+ clear_removed();
+ m_vec.assign(first, last);
+ }
+
+ T* data()
+ {
+ return m_vec.data() + m_front_offset;
+ }
+
+ const T* data() const
+ {
+ return m_vec.data() + m_front_offset;
+ }
+
+private:
+ void clear_removed()
+ {
+ m_vec.erase(m_vec.begin(), m_vec.begin() + m_front_offset);
+ m_front_offset = 0;
+ }
+};
+
+namespace detail {
+
+template<>
+struct is_std_vector_bool_store<delayed_delete_vector<bool>>
+{
+ using type = std::true_type;
+};
+
+} // namespace detail
+
+template<typename T>
+bool operator==(const delayed_delete_vector<T>& lhs, const delayed_delete_vector<T>& rhs)
+{
+ return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
+}
+
+template<typename Self, element_t TypeId, typename ValueT, template<typename, typename> class StoreT>
+class element_block : public base_element_block
+{
+public:
+ using store_type = StoreT<ValueT, std::allocator<ValueT>>;
+ static constexpr element_t block_type = TypeId;
+
+protected:
+ store_type m_array;
+
+ element_block() : base_element_block(TypeId)
+ {}
+ element_block(size_t n) : base_element_block(TypeId), m_array(n)
+ {}
+ element_block(size_t n, const ValueT& val) : base_element_block(TypeId), m_array(n, val)
+ {}
+
+ template<typename Iter>
+ element_block(const Iter& it_begin, const Iter& it_end) : base_element_block(TypeId), m_array(it_begin, it_end)
+ {}
+
+public:
+ typedef typename store_type::iterator iterator;
+ typedef typename store_type::reverse_iterator reverse_iterator;
+ typedef typename store_type::const_iterator const_iterator;
+ typedef typename store_type::const_reverse_iterator const_reverse_iterator;
+ typedef ValueT value_type;
+
+private:
+ template<bool Mutable>
+ class base_range_type
+ {
+ using block_type = mdds::detail::mutable_t<base_element_block, Mutable>;
+ block_type& m_blk;
+
+ public:
+ using iter_type = std::conditional_t<Mutable, iterator, const_iterator>;
+
+ base_range_type(block_type& blk) : m_blk(blk)
+ {}
+
+ iter_type begin()
+ {
+ return element_block::begin(m_blk);
+ }
+
+ iter_type end()
+ {
+ return element_block::end(m_blk);
+ }
+ };
+
+public:
+ using range_type = base_range_type<true>;
+ using const_range_type = base_range_type<false>;
+
+ bool operator==(const Self& r) const
+ {
+ return m_array == r.m_array;
+ }
+
+ bool operator!=(const Self& r) const
+ {
+ return !operator==(r);
+ }
+
+ static const value_type& at(const base_element_block& block, typename store_type::size_type pos)
+ {
+ return get(block).m_array.at(pos);
+ }
+
+ static value_type& at(base_element_block& block, typename store_type::size_type pos)
+ {
+ return get(block).m_array.at(pos);
+ }
+
+ static value_type* data(base_element_block& block)
+ {
+ return get(block).m_array.data();
+ }
+
+ static typename store_type::size_type size(const base_element_block& block)
+ {
+ return get(block).m_array.size();
+ }
+
+ static iterator begin(base_element_block& block)
+ {
+ return get(block).m_array.begin();
+ }
+
+ static iterator end(base_element_block& block)
+ {
+ return get(block).m_array.end();
+ }
+
+ static const_iterator begin(const base_element_block& block)
+ {
+ return get(block).m_array.begin();
+ }
+
+ static const_iterator end(const base_element_block& block)
+ {
+ return get(block).m_array.end();
+ }
+
+ static const_iterator cbegin(const base_element_block& block)
+ {
+ return get(block).m_array.begin();
+ }
+
+ static const_iterator cend(const base_element_block& block)
+ {
+ return get(block).m_array.end();
+ }
+
+ static reverse_iterator rbegin(base_element_block& block)
+ {
+ return get(block).m_array.rbegin();
+ }
+
+ static reverse_iterator rend(base_element_block& block)
+ {
+ return get(block).m_array.rend();
+ }
+
+ static const_reverse_iterator rbegin(const base_element_block& block)
+ {
+ return get(block).m_array.rbegin();
+ }
+
+ static const_reverse_iterator rend(const base_element_block& block)
+ {
+ return get(block).m_array.rend();
+ }
+
+ static const_reverse_iterator crbegin(const base_element_block& block)
+ {
+ return get(block).m_array.rbegin();
+ }
+
+ static const_reverse_iterator crend(const base_element_block& block)
+ {
+ return get(block).m_array.rend();
+ }
+
+ static const_range_type range(const base_element_block& block)
+ {
+ return const_range_type(block);
+ }
+
+ static range_type range(base_element_block& block)
+ {
+ return range_type(block);
+ }
+
+ static Self& get(base_element_block& block)
+ {
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ if (get_block_type(block) != TypeId)
+ {
+ std::ostringstream os;
+ os << "incorrect block type: expected block type=" << TypeId
+ << ", passed block type=" << get_block_type(block);
+ throw general_error(os.str());
+ }
+#endif
+ return static_cast<Self&>(block);
+ }
+
+ static const Self& get(const base_element_block& block)
+ {
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+ if (get_block_type(block) != TypeId)
+ {
+ std::ostringstream os;
+ os << "incorrect block type: expected block type=" << TypeId
+ << ", passed block type=" << get_block_type(block);
+ throw general_error(os.str());
+ }
+#endif
+ return static_cast<const Self&>(block);
+ }
+
+ static void set_value(base_element_block& blk, size_t pos, const ValueT& val)
+ {
+ get(blk).m_array[pos] = val;
+ }
+
+ static void get_value(const base_element_block& blk, size_t pos, ValueT& val)
+ {
+ val = get(blk).m_array[pos];
+ }
+
+ static value_type get_value(const base_element_block& blk, size_t pos)
+ {
+ return get(blk).m_array[pos];
+ }
+
+ static void append_value(base_element_block& blk, const ValueT& val)
+ {
+ get(blk).m_array.push_back(val);
+ }
+
+ static void prepend_value(base_element_block& blk, const ValueT& val)
+ {
+ store_type& blk2 = get(blk).m_array;
+ blk2.insert(blk2.begin(), val);
+ }
+
+ static Self* create_block(size_t init_size)
+ {
+ return new Self(init_size);
+ }
+
+ static void delete_block(const base_element_block* p)
+ {
+ delete static_cast<const Self*>(p);
+ }
+
+ static void resize_block(base_element_block& blk, size_t new_size)
+ {
+ store_type& st = get(blk).m_array;
+ st.resize(new_size);
+
+ // Test if the vector's capacity is larger than twice its current
+ // size, and if so, shrink its capacity to free up some memory.
+ if (new_size < (detail::get_block_capacity(st) / 2))
+ detail::shrink_to_fit(st);
+ }
+
+#ifdef MDDS_UNIT_TEST
+ static void print_block(const base_element_block& blk)
+ {
+ const store_type& blk2 = get(blk).m_array;
+ for (const auto& val : blk2)
+ std::cout << val << " ";
+
+ std::cout << std::endl;
+ }
+#else
+ static void print_block(const base_element_block&)
+ {}
+#endif
+
+ static void erase_value(base_element_block& blk, size_t pos)
+ {
+ store_type& blk2 = get(blk).m_array;
+ blk2.erase(blk2.begin() + pos);
+ }
+
+ static void erase_values(base_element_block& blk, size_t pos, size_t size)
+ {
+ store_type& blk2 = get(blk).m_array;
+ blk2.erase(blk2.begin() + pos, blk2.begin() + pos + size);
+ }
+
+ static void append_block(base_element_block& dest, const base_element_block& src)
+ {
+ store_type& d = get(dest).m_array;
+ const store_type& s = get(src).m_array;
+ d.insert(d.end(), s.begin(), s.end());
+ }
+
+ static void append_values_from_block(
+ base_element_block& dest, const base_element_block& src, size_t begin_pos, size_t len)
+ {
+ store_type& d = get(dest).m_array;
+ const store_type& s = get(src).m_array;
+ std::pair<const_iterator, const_iterator> its = get_iterator_pair(s, begin_pos, len);
+ detail::reserve(d, d.size() + len);
+ d.insert(d.end(), its.first, its.second);
+ }
+
+ static void assign_values_from_block(
+ base_element_block& dest, const base_element_block& src, size_t begin_pos, size_t len)
+ {
+ store_type& d = get(dest).m_array;
+ const store_type& s = get(src).m_array;
+ std::pair<const_iterator, const_iterator> its = get_iterator_pair(s, begin_pos, len);
+ d.assign(its.first, its.second);
+ }
+
+ static void prepend_values_from_block(
+ base_element_block& dest, const base_element_block& src, size_t begin_pos, size_t len)
+ {
+ store_type& d = get(dest).m_array;
+ const store_type& s = get(src).m_array;
+ std::pair<const_iterator, const_iterator> its = get_iterator_pair(s, begin_pos, len);
+ detail::reserve(d, d.size() + len);
+ d.insert(d.begin(), its.first, its.second);
+ }
+
+ static void swap_values(base_element_block& blk1, base_element_block& blk2, size_t pos1, size_t pos2, size_t len)
+ {
+ store_type& st1 = get(blk1).m_array;
+ store_type& st2 = get(blk2).m_array;
+ assert(pos1 + len <= st1.size());
+ assert(pos2 + len <= st2.size());
+
+ typename store_type::iterator it1 = st1.begin(), it2 = st2.begin();
+ std::advance(it1, pos1);
+ std::advance(it2, pos2);
+
+ for (size_t i = 0; i < len; ++i, ++it1, ++it2)
+ {
+ value_type v1 = *it1, v2 = *it2;
+ *it1 = v2;
+ *it2 = v1;
+ }
+ }
+
+ static bool equal_block(const base_element_block& left, const base_element_block& right)
+ {
+ return get(left) == get(right);
+ }
+
+ template<typename Iter>
+ static void set_values(base_element_block& block, size_t pos, const Iter& it_begin, const Iter& it_end)
+ {
+ store_type& d = get(block).m_array;
+ typename store_type::iterator it_dest = d.begin();
+ std::advance(it_dest, pos);
+ for (Iter it = it_begin; it != it_end; ++it, ++it_dest)
+ *it_dest = *it;
+ }
+
+ template<typename Iter>
+ static void append_values(base_element_block& block, const Iter& it_begin, const Iter& it_end)
+ {
+ store_type& d = get(block).m_array;
+ typename store_type::iterator it = d.end();
+ d.insert(it, it_begin, it_end);
+ }
+
+ template<typename Iter>
+ static void prepend_values(base_element_block& block, const Iter& it_begin, const Iter& it_end)
+ {
+ store_type& d = get(block).m_array;
+ d.insert(d.begin(), it_begin, it_end);
+ }
+
+ template<typename Iter>
+ static void assign_values(base_element_block& dest, const Iter& it_begin, const Iter& it_end)
+ {
+ store_type& d = get(dest).m_array;
+ d.assign(it_begin, it_end);
+ }
+
+ template<typename Iter>
+ static void insert_values(base_element_block& block, size_t pos, const Iter& it_begin, const Iter& it_end)
+ {
+ store_type& blk = get(block).m_array;
+ blk.insert(blk.begin() + pos, it_begin, it_end);
+ }
+
+ static size_t capacity(const base_element_block& block)
+ {
+ const store_type& blk = get(block).m_array;
+ return detail::get_block_capacity(blk);
+ }
+
+ static void reserve(base_element_block& block, std::size_t size)
+ {
+ store_type& blk = get(block).m_array;
+ detail::reserve(blk, size);
+ }
+
+ static void shrink_to_fit(base_element_block& block)
+ {
+ store_type& blk = get(block).m_array;
+ detail::shrink_to_fit(blk);
+ }
+
+private:
+ static std::pair<const_iterator, const_iterator> get_iterator_pair(
+ const store_type& array, size_t begin_pos, size_t len)
+ {
+ assert(begin_pos + len <= array.size());
+ const_iterator it = array.begin();
+ std::advance(it, begin_pos);
+ const_iterator it_end = it;
+ std::advance(it_end, len);
+ return std::pair<const_iterator, const_iterator>(it, it_end);
+ }
+};
+
+template<typename Self, element_t TypeId, typename ValueT, template<typename, typename> class StoreT>
+class copyable_element_block : public element_block<Self, TypeId, ValueT, StoreT>
+{
+ using base_type = element_block<Self, TypeId, ValueT, StoreT>;
+
+protected:
+ copyable_element_block() : base_type()
+ {}
+ copyable_element_block(size_t n) : base_type(n)
+ {}
+ copyable_element_block(size_t n, const ValueT& val) : base_type(n, val)
+ {}
+
+ template<typename Iter>
+ copyable_element_block(const Iter& it_begin, const Iter& it_end) : base_type(it_begin, it_end)
+ {}
+
+public:
+ using base_type::get;
+
+ static Self* clone_block(const base_element_block& blk)
+ {
+ // Use copy constructor to copy the data.
+ return new Self(get(blk));
+ }
+};
+
+template<typename Self, element_t TypeId, typename ValueT, template<typename, typename> class StoreT>
+class noncopyable_element_block : public element_block<Self, TypeId, ValueT, StoreT>
+{
+ using base_type = element_block<Self, TypeId, ValueT, StoreT>;
+
+protected:
+ noncopyable_element_block() : base_type()
+ {}
+ noncopyable_element_block(size_t n) : base_type(n)
+ {}
+ noncopyable_element_block(size_t n, const ValueT& val) : base_type(n, val)
+ {}
+
+ template<typename Iter>
+ noncopyable_element_block(const Iter& it_begin, const Iter& it_end) : base_type(it_begin, it_end)
+ {}
+
+public:
+ noncopyable_element_block(const noncopyable_element_block&) = delete;
+ noncopyable_element_block& operator=(const noncopyable_element_block&) = delete;
+
+ static Self* clone_block(const base_element_block&)
+ {
+ throw element_block_error("attempted to clone a noncopyable element block.");
+ }
+};
+
+/**
+ * Get the numerical block type ID from a given element block instance.
+ *
+ * @param blk element block instance
+ *
+ * @return numerical value representing the ID of a element block.
+ */
+inline element_t get_block_type(const base_element_block& blk)
+{
+ return blk.type;
+}
+
+/**
+ * Template for default, unmanaged element block for use in
+ * multi_type_vector.
+ */
+template<element_t TypeId, typename ValueT, template<typename, typename> class StoreT = delayed_delete_vector>
+struct default_element_block
+ : public copyable_element_block<default_element_block<TypeId, ValueT, StoreT>, TypeId, ValueT, StoreT>
+{
+ using self_type = default_element_block<TypeId, ValueT, StoreT>;
+ using base_type = copyable_element_block<self_type, TypeId, ValueT, StoreT>;
+
+ default_element_block() : base_type()
+ {}
+ default_element_block(size_t n) : base_type(n)
+ {}
+ default_element_block(size_t n, const ValueT& val) : base_type(n, val)
+ {}
+
+ template<typename Iter>
+ default_element_block(const Iter& it_begin, const Iter& it_end) : base_type(it_begin, it_end)
+ {}
+
+ static self_type* create_block_with_value(size_t init_size, const ValueT& val)
+ {
+ return new self_type(init_size, val);
+ }
+
+ template<typename Iter>
+ static self_type* create_block_with_values(const Iter& it_begin, const Iter& it_end)
+ {
+ return new self_type(it_begin, it_end);
+ }
+
+ static void overwrite_values(base_element_block&, size_t, size_t)
+ {
+ // Do nothing.
+ }
+};
+
+/**
+ * Template for element block that stores pointers to objects whose life
+ * cycles are managed by the block.
+ */
+template<element_t TypeId, typename ValueT, template<typename, typename> class StoreT = delayed_delete_vector>
+struct managed_element_block
+ : public copyable_element_block<managed_element_block<TypeId, ValueT, StoreT>, TypeId, ValueT*, StoreT>
+{
+ using self_type = managed_element_block<TypeId, ValueT, StoreT>;
+ using base_type = copyable_element_block<self_type, TypeId, ValueT*, StoreT>;
+
+ using base_type::get;
+ using base_type::m_array;
+ using base_type::reserve;
+ using base_type::set_value;
+
+ managed_element_block() : base_type()
+ {}
+ managed_element_block(size_t n) : base_type(n)
+ {}
+ managed_element_block(const managed_element_block& r)
+ {
+ detail::reserve(m_array, r.m_array.size());
+ for (const auto& v : r.m_array)
+ m_array.push_back(new ValueT(*v));
+ }
+
+ template<typename Iter>
+ managed_element_block(const Iter& it_begin, const Iter& it_end) : base_type(it_begin, it_end)
+ {}
+
+ ~managed_element_block()
+ {
+ std::for_each(m_array.begin(), m_array.end(), std::default_delete<ValueT>());
+ }
+
+ static self_type* create_block_with_value(size_t init_size, ValueT* val)
+ {
+ // Managed blocks don't support initialization with value.
+ if (init_size > 1)
+ throw general_error("You can't create a managed block with initial value.");
+
+ std::unique_ptr<self_type> blk = std::make_unique<self_type>(init_size);
+ if (init_size == 1)
+ set_value(*blk, 0, val);
+
+ return blk.release();
+ }
+
+ template<typename Iter>
+ static self_type* create_block_with_values(const Iter& it_begin, const Iter& it_end)
+ {
+ return new self_type(it_begin, it_end);
+ }
+
+ static void overwrite_values(base_element_block& block, size_t pos, size_t len)
+ {
+ managed_element_block& blk = get(block);
+ typename managed_element_block::store_type::iterator it = blk.m_array.begin() + pos;
+ typename managed_element_block::store_type::iterator it_end = it + len;
+ std::for_each(it, it_end, std::default_delete<ValueT>());
+ }
+};
+
+template<element_t TypeId, typename ValueT, template<typename, typename> class StoreT = delayed_delete_vector>
+struct noncopyable_managed_element_block
+ : public noncopyable_element_block<
+ noncopyable_managed_element_block<TypeId, ValueT, StoreT>, TypeId, ValueT*, StoreT>
+{
+ using self_type = noncopyable_managed_element_block<TypeId, ValueT, StoreT>;
+ using base_type = noncopyable_element_block<self_type, TypeId, ValueT*, StoreT>;
+
+ using base_type::get;
+ using base_type::m_array;
+ using base_type::set_value;
+
+ noncopyable_managed_element_block() : base_type()
+ {}
+ noncopyable_managed_element_block(size_t n) : base_type(n)
+ {}
+
+ template<typename Iter>
+ noncopyable_managed_element_block(const Iter& it_begin, const Iter& it_end) : base_type(it_begin, it_end)
+ {}
+
+ ~noncopyable_managed_element_block()
+ {
+ std::for_each(m_array.begin(), m_array.end(), std::default_delete<ValueT>());
+ }
+
+ static self_type* create_block_with_value(size_t init_size, ValueT* val)
+ {
+ // Managed blocks don't support initialization with value.
+ if (init_size > 1)
+ throw general_error("You can't create a managed block with initial value.");
+
+ std::unique_ptr<self_type> blk = std::make_unique<self_type>(init_size);
+ if (init_size == 1)
+ set_value(*blk, 0, val);
+
+ return blk.release();
+ }
+
+ template<typename Iter>
+ static self_type* create_block_with_values(const Iter& it_begin, const Iter& it_end)
+ {
+ return new self_type(it_begin, it_end);
+ }
+
+ static void overwrite_values(base_element_block& block, size_t pos, size_t len)
+ {
+ noncopyable_managed_element_block& blk = get(block);
+ typename noncopyable_managed_element_block::store_type::iterator it = blk.m_array.begin() + pos;
+ typename noncopyable_managed_element_block::store_type::iterator it_end = it + len;
+ std::for_each(it, it_end, std::default_delete<ValueT>());
+ }
+};
+
+namespace detail {
+
+template<typename Blk>
+bool get_block_element_at(const mdds::mtv::base_element_block& data, size_t offset, std::true_type)
+{
+ auto it = Blk::cbegin(data);
+ std::advance(it, offset);
+ return *it;
+}
+
+template<typename Blk>
+typename Blk::value_type get_block_element_at(const mdds::mtv::base_element_block& data, size_t offset, std::false_type)
+{
+ return Blk::at(data, offset);
+}
+
+template<typename Blk>
+typename Blk::value_type get_block_element_at(const mdds::mtv::base_element_block& data, size_t offset)
+{
+ typename mdds::mtv::detail::has_std_vector_bool_store<Blk>::type v;
+ return get_block_element_at<Blk>(data, offset, v);
+}
+
+} // namespace detail
+
+}} // namespace mdds::mtv
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/mdds/multi_type_vector/types_util.hpp b/include/mdds/multi_type_vector/types_util.hpp
new file mode 100644
index 0000000..f4f3330
--- /dev/null
+++ b/include/mdds/multi_type_vector/types_util.hpp
@@ -0,0 +1,165 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * Copyright (c) 2022 Kohei Yoshida
+ *
+ * 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, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ ************************************************************************/
+
+#pragma once
+
+#include <vector>
+
+namespace mdds { namespace mtv { namespace detail {
+
+template<typename T>
+struct has_capacity_method
+{
+ using yes_type = char;
+ using no_type = int;
+
+ template<typename U, std::size_t (U::*)() const>
+ struct test_has_method
+ {
+ };
+
+ template<typename U>
+ static yes_type test(test_has_method<U, &U::capacity>*);
+ template<typename U>
+ static no_type test(...);
+
+ using type = std::conditional_t<sizeof(test<T>(0)) == sizeof(yes_type), std::true_type, std::false_type>;
+};
+
+template<typename T>
+std::size_t get_block_capacity(const T& blk, std::true_type)
+{
+ return blk.capacity();
+}
+
+template<typename T>
+std::size_t get_block_capacity(const T&, std::false_type)
+{
+ return 0;
+}
+
+template<typename T>
+std::size_t get_block_capacity(const T& blk)
+{
+ typename has_capacity_method<T>::type v;
+ return get_block_capacity(blk, v);
+}
+
+template<typename T>
+struct has_reserve_method
+{
+ using yes_type = char;
+ using no_type = int;
+
+ template<typename U, void (U::*)(typename T::size_type)>
+ struct test_has_method
+ {
+ };
+
+ template<typename U>
+ static yes_type test(test_has_method<U, &U::reserve>*);
+ template<typename U>
+ static no_type test(...);
+
+ using type = std::conditional_t<sizeof(test<T>(0)) == sizeof(yes_type), std::true_type, std::false_type>;
+};
+
+template<typename T>
+void reserve(T& blk, typename T::size_type size, std::true_type)
+{
+ return blk.reserve(size);
+}
+
+template<typename T>
+void reserve(T&, typename T::size_type, std::false_type)
+{}
+
+template<typename T>
+void reserve(T& blk, typename T::size_type size)
+{
+ typename has_reserve_method<T>::type v;
+ reserve(blk, size, v);
+}
+
+template<typename T>
+struct has_shrink_to_fit_method
+{
+ using yes_type = char;
+ using no_type = int;
+
+ template<typename U, void (U::*)()>
+ struct test_has_method
+ {
+ };
+
+ template<typename U>
+ static yes_type test(test_has_method<U, &U::shrink_to_fit>*);
+ template<typename U>
+ static no_type test(...);
+
+ using type = std::conditional_t<sizeof(test<T>(0)) == sizeof(yes_type), std::true_type, std::false_type>;
+};
+
+template<typename T>
+void shrink_to_fit(T& blk, std::true_type)
+{
+ return blk.shrink_to_fit();
+}
+
+template<typename T>
+void shrink_to_fit(T&, std::false_type)
+{}
+
+template<typename T>
+void shrink_to_fit(T& blk)
+{
+ typename has_shrink_to_fit_method<T>::type v;
+ shrink_to_fit(blk, v);
+}
+
+template<typename T>
+struct is_std_vector_bool_store
+{
+ using type = std::false_type;
+};
+
+template<>
+struct is_std_vector_bool_store<std::vector<bool>>
+{
+ using type = std::true_type;
+};
+
+template<typename Blk>
+struct has_std_vector_bool_store
+{
+ using type = typename is_std_vector_bool_store<typename Blk::store_type>::type;
+};
+
+}}} // namespace mdds::mtv::detail
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/mdds/multi_type_vector/util.hpp b/include/mdds/multi_type_vector/util.hpp
new file mode 100644
index 0000000..929eea1
--- /dev/null
+++ b/include/mdds/multi_type_vector/util.hpp
@@ -0,0 +1,264 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * Copyright (c) 2021 Kohei Yoshida
+ *
+ * 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, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ ************************************************************************/
+
+#ifndef INCLUDED_MDDS_MULTI_TYPE_VECTOR_DIR_UTIL_HPP
+#define INCLUDED_MDDS_MULTI_TYPE_VECTOR_DIR_UTIL_HPP
+
+#include "./block_funcs.hpp"
+
+#include <sstream>
+
+namespace mdds { namespace mtv {
+
+/**
+ * Empty event function handler structure, used when no custom function
+ * handler is specified.
+ */
+struct empty_event_func
+{
+ /**
+ * Callback function for element block acquisition events. This gets called
+ * whenever the container acquires a new element block either as a result of
+ * a new element block creation or a transfer of an existing element block
+ * from another container.
+ *
+ * @param block pointer to the acquired element block instance.
+ */
+ void element_block_acquired(const base_element_block* block)
+ {
+ (void)block;
+ }
+
+ /**
+ * Callback function for element block release events. This gets called
+ * whenever the container releases an existing element block either because
+ * the block is about to be deleted or to be transferred to another
+ * container.
+ *
+ * @param block pointer to the element block instance being released.
+ */
+ void element_block_released(const base_element_block* block)
+ {
+ (void)block;
+ }
+};
+
+/**
+ * Default trait to be used when no custom trait is specified.
+ */
+struct default_traits
+{
+ /**
+ * Class or struct type that contains callback functions for element block
+ * events as its member functions.
+ */
+ using event_func = empty_event_func;
+
+ /**
+ * Static value specifying the loop-unrolling factor to use for the block
+ * position adjustment function. This must be a const expression.
+ */
+ static constexpr lu_factor_t loop_unrolling = lu_factor_t::lu16;
+
+ /**
+ * Type that contains block functions used throughout the multi_type_vector
+ * implementation. The user must overwrite this type to specify one or more
+ * block types as template arguments to element_block_funcs. Alternatively,
+ * you may be interested in using standard_element_blocks_traits which
+ * already supports the pre-defined block types for the optional standard
+ * data types.
+ */
+ using block_funcs = element_block_funcs<>;
+};
+
+namespace detail {
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+
+template<typename T, typename = void>
+struct has_trace : std::false_type
+{
+};
+
+template<typename T>
+struct has_trace<T, decltype((void)T::trace)> : std::true_type
+{
+};
+
+template<typename Traits>
+struct call_trace
+{
+ int& call_depth;
+
+ call_trace(int& _call_depth) : call_depth(_call_depth)
+ {
+ ++call_depth;
+ }
+ ~call_trace() noexcept
+ {
+ --call_depth;
+ }
+
+ void call(std::false_type, const ::mdds::mtv::trace_method_properties_t&) const
+ {
+ // sink
+ }
+
+ void call(std::true_type, const ::mdds::mtv::trace_method_properties_t& props) const
+ {
+ // In case of recursive calls, only trace the first encountered method.
+ if (call_depth <= 1)
+ Traits::trace(props);
+ }
+
+ void operator()(const ::mdds::mtv::trace_method_properties_t& props) const
+ {
+ call(has_trace<Traits>{}, props);
+ }
+};
+
+#endif
+
+inline void throw_block_position_not_found(
+ const char* method_sig, int line, size_t pos, size_t block_size, size_t container_size)
+{
+ std::ostringstream os;
+ os << method_sig << "#" << line << ": block position not found! (logical pos=" << pos
+ << ", block size=" << block_size << ", logical size=" << container_size << ")";
+ throw std::out_of_range(os.str());
+}
+
+/**
+ * Given a pair of iterators comprising the input value sequence, and a
+ * desired logical insertion position, calculate the position of the last
+ * input value. Also check if the input value sequence is empty.
+ *
+ * @exception std::out_of_range if the end position is greater than the last
+ * allowed position of the destination storage.
+ *
+ * @param it_begin iterator pointing to the first element of the input value
+ * sequence.
+ * @param it_end iterator point to the position past the last element of the
+ * input value sequence.
+ * @param pos desired insertion position.
+ * @param total_size total logical size of the destination storage.
+ *
+ * @return position of the last input value (first) and a flag on whether or
+ * not the input value sequence is empty (second).
+ */
+template<typename _T, typename _SizeT>
+std::pair<_SizeT, bool> calc_input_end_position(const _T& it_begin, const _T& it_end, _SizeT pos, _SizeT total_size)
+{
+ using ret_type = std::pair<_SizeT, bool>;
+
+ _SizeT length = std::distance(it_begin, it_end);
+ if (!length)
+ // empty data array. nothing to do.
+ return ret_type(0, false);
+
+ _SizeT end_pos = pos + length - 1;
+ if (end_pos >= total_size)
+ throw std::out_of_range("Input data sequence is too long.");
+
+ return ret_type(end_pos, true);
+}
+
+template<typename T>
+T advance_position(const T& pos, int steps)
+{
+ T ret = pos;
+
+ if (steps > 0)
+ {
+ while (steps > 0)
+ {
+ if (ret.second + steps < ret.first->size)
+ {
+ // element is still in the same block.
+ ret.second += steps;
+ break;
+ }
+ else
+ {
+ steps -= static_cast<int>(ret.first->size - ret.second);
+ ++ret.first;
+ ret.second = 0;
+ }
+ }
+ }
+ else
+ {
+ while (steps < 0)
+ {
+ if (static_cast<int>(ret.second) >= -steps)
+ {
+ ret.second += steps;
+ break;
+ }
+ else
+ {
+ steps += static_cast<int>(ret.second + 1);
+ --ret.first;
+ ret.second = ret.first->size - 1;
+ }
+ }
+ }
+
+ return ret;
+}
+
+} // namespace detail
+
+}} // namespace mdds::mtv
+
+#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
+
+#define MDDS_MTV_TRACE(method_type) \
+ ::mdds::mtv::detail::call_trace<Traits> mdds_mtv_ct(m_trace_call_depth); \
+ mdds_mtv_ct({trace_method_t::method_type, this, __func__, "", __FILE__, __LINE__})
+
+#define MDDS_MTV_TRACE_ARGS(method_type, stream) \
+ ::mdds::mtv::detail::call_trace<Traits> mdds_mtv_ct(m_trace_call_depth); \
+ do \
+ { \
+ std::ostringstream _os_; \
+ _os_ << stream; \
+ mdds_mtv_ct({trace_method_t::method_type, this, __func__, _os_.str(), __FILE__, __LINE__}); \
+ } while (false)
+
+#else
+
+#define MDDS_MTV_TRACE(...)
+
+#define MDDS_MTV_TRACE_ARGS(...)
+
+#endif
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/mdds/multi_type_vector_itr.hpp b/include/mdds/multi_type_vector_itr.hpp
new file mode 100644
index 0000000..d261e3d
--- /dev/null
+++ b/include/mdds/multi_type_vector_itr.hpp
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * Copyright (c) 2021 Kohei Yoshida
+ *
+ * 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, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ ************************************************************************/
+
+#ifndef INCLUDED_MDDS_MULTI_TYPE_VECTOR_ITR_HPP
+#define INCLUDED_MDDS_MULTI_TYPE_VECTOR_ITR_HPP
+
+#include "./multi_type_vector/aos/iterator.hpp"
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/mdds/multi_type_vector_macro.hpp b/include/mdds/multi_type_vector_macro.hpp
new file mode 100644
index 0000000..be7e697
--- /dev/null
+++ b/include/mdds/multi_type_vector_macro.hpp
@@ -0,0 +1,33 @@
+/*************************************************************************
+ *
+ * Copyright (c) 2012-2021 Kohei Yoshida
+ *
+ * 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, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ ************************************************************************/
+
+#ifndef INCLUDED_MDDS_MULTI_TYPE_VECTOR_MACRO_HPP
+#define INCLUDED_MDDS_MULTI_TYPE_VECTOR_MACRO_HPP
+
+#include "./multi_type_vector/macro.hpp"
+
+#endif
diff --git a/include/mdds/multi_type_vector_types.hpp b/include/mdds/multi_type_vector_types.hpp
new file mode 100644
index 0000000..3339558
--- /dev/null
+++ b/include/mdds/multi_type_vector_types.hpp
@@ -0,0 +1,33 @@
+/*************************************************************************
+ *
+ * Copyright (c) 2012-2018 Kohei Yoshida
+ *
+ * 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, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ ************************************************************************/
+
+#ifndef INCLUDED_MDDS_MULTI_TYPE_VECTOR_TYPES_HPP
+#define INCLUDED_MDDS_MULTI_TYPE_VECTOR_TYPES_HPP
+
+#include "./multi_type_vector/types.hpp"
+
+#endif
diff --git a/include/mdds/node.hpp b/include/mdds/node.hpp
new file mode 100644
index 0000000..e94fcda
--- /dev/null
+++ b/include/mdds/node.hpp
@@ -0,0 +1,490 @@
+/*************************************************************************
+ *
+ * Copyright (c) 2008-2014 Kohei Yoshida
+ *
+ * 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, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ ************************************************************************/
+
+#ifndef __MDDS_NODE_HXX__
+#define __MDDS_NODE_HXX__
+
+#include <iostream>
+#include <vector>
+#include <cassert>
+
+#include <boost/intrusive_ptr.hpp>
+
+namespace mdds { namespace __st {
+
+#ifdef MDDS_DEBUG_NODE_BASE
+size_t node_instance_count = 0;
+#endif
+
+struct node_base
+{
+ node_base* parent; /// parent nonleaf_node
+ bool is_leaf;
+
+ node_base(bool _is_leaf) : parent(nullptr), is_leaf(_is_leaf)
+ {}
+ node_base(const node_base& r) : parent(nullptr), is_leaf(r.is_leaf)
+ {}
+};
+
+template<typename T>
+struct nonleaf_node : public node_base
+{
+ typedef typename T::nonleaf_value_type nonleaf_value_type;
+ typedef typename T::fill_nonleaf_value_handler fill_nonleaf_value_handler;
+ typedef typename T::init_handler init_handler;
+ typedef typename T::dispose_handler dispose_handler;
+#ifdef MDDS_UNIT_TEST
+ typedef typename T::to_string_handler to_string_handler;
+#endif
+ nonleaf_value_type value_nonleaf;
+
+ node_base* left; /// left child nonleaf_node
+ node_base* right; /// right child nonleaf_node
+
+private:
+ fill_nonleaf_value_handler _hdl_fill_nonleaf;
+ init_handler _hdl_init;
+ dispose_handler _hdl_dispose;
+#ifdef MDDS_UNIT_TEST
+ to_string_handler _hdl_to_string;
+#endif
+
+public:
+ nonleaf_node() : node_base(false), left(nullptr), right(nullptr)
+ {
+ _hdl_init(*this);
+ }
+
+ /**
+ * When copying nonleaf_node, only the stored values should be copied.
+ * Connections to the parent, left and right nodes must not be copied.
+ */
+ nonleaf_node(const nonleaf_node& r) : node_base(r), left(nullptr), right(nullptr)
+ {
+ value_nonleaf = r.value_nonleaf;
+ }
+
+ /**
+ * Like the copy constructor, only the stored values should be copied.
+ */
+ nonleaf_node& operator=(const nonleaf_node& r)
+ {
+ if (this == &r)
+ // assignment to self.
+ return *this;
+
+ value_nonleaf = r.value_nonleaf;
+ return *this;
+ }
+
+ ~nonleaf_node()
+ {
+ dispose();
+ }
+
+ void dispose()
+ {
+ _hdl_dispose(*this);
+ }
+
+ bool equals(const nonleaf_node& r) const
+ {
+ return value_nonleaf == r.value_nonleaf;
+ }
+
+ void fill_nonleaf_value(const node_base* left_node, const node_base* right_node)
+ {
+ _hdl_fill_nonleaf(*this, left_node, right_node);
+ }
+
+#ifdef MDDS_UNIT_TEST
+ void dump_value() const
+ {
+ ::std::cout << _hdl_to_string(*this);
+ }
+
+ ::std::string to_string() const
+ {
+ return _hdl_to_string(*this);
+ }
+#endif
+};
+
+template<typename T>
+struct node : public node_base
+{
+ typedef ::boost::intrusive_ptr<node> node_ptr;
+
+ typedef typename T::leaf_value_type leaf_value_type;
+ typedef typename T::init_handler init_handler;
+ typedef typename T::dispose_handler dispose_handler;
+#ifdef MDDS_UNIT_TEST
+ typedef typename T::to_string_handler to_string_handler;
+#endif
+
+ static size_t get_instance_count()
+ {
+#ifdef MDDS_DEBUG_NODE_BASE
+ return node_instance_count;
+#else
+ return 0;
+#endif
+ }
+
+ leaf_value_type value_leaf;
+
+ node_ptr prev; /// previous sibling leaf node.
+ node_ptr next; /// next sibling leaf node.
+
+ size_t refcount;
+
+private:
+ init_handler _hdl_init;
+ dispose_handler _hdl_dispose;
+#ifdef MDDS_UNIT_TEST
+ to_string_handler _hdl_to_string;
+#endif
+
+public:
+ node() : node_base(true), refcount(0)
+ {
+#ifdef MDDS_DEBUG_NODE_BASE
+ ++node_instance_count;
+#endif
+ _hdl_init(*this);
+ }
+
+ /**
+ * When copying node, only the stored values should be copied.
+ * Connections to the parent, left and right nodes must not be copied.
+ */
+ node(const node& r) : node_base(r), refcount(0)
+ {
+#ifdef MDDS_DEBUG_NODE_BASE
+ ++node_instance_count;
+#endif
+ value_leaf = r.value_leaf;
+ }
+
+ /**
+ * Like the copy constructor, only the stored values should be copied.
+ */
+ node& operator=(const node& r)
+ {
+ if (this == &r)
+ // assignment to self.
+ return *this;
+
+ value_leaf = r.value_leaf;
+ return *this;
+ }
+
+ ~node()
+ {
+#ifdef MDDS_DEBUG_NODE_BASE
+ --node_instance_count;
+#endif
+ dispose();
+ }
+
+ void dispose()
+ {
+ _hdl_dispose(*this);
+ }
+
+ bool equals(const node& r) const
+ {
+ return value_leaf == r.value_leaf;
+ }
+
+#ifdef MDDS_UNIT_TEST
+ void dump_value() const
+ {
+ ::std::cout << _hdl_to_string(*this);
+ }
+
+ ::std::string to_string() const
+ {
+ return _hdl_to_string(*this);
+ }
+#endif
+};
+
+template<typename T>
+inline void intrusive_ptr_add_ref(node<T>* p)
+{
+ ++p->refcount;
+}
+
+template<typename T>
+inline void intrusive_ptr_release(node<T>* p)
+{
+ --p->refcount;
+ if (!p->refcount)
+ delete p;
+}
+
+template<typename T>
+void link_nodes(typename node<T>::node_ptr& left, typename node<T>::node_ptr& right)
+{
+ left->next = right;
+ right->prev = left;
+}
+
+template<typename T>
+class tree_builder
+{
+public:
+ typedef mdds::__st::node<T> leaf_node;
+ typedef typename mdds::__st::node<T>::node_ptr leaf_node_ptr;
+ typedef mdds::__st::nonleaf_node<T> nonleaf_node;
+ typedef std::vector<nonleaf_node> nonleaf_node_pool_type;
+
+ tree_builder(nonleaf_node_pool_type& pool) : m_pool(pool), m_pool_pos(pool.begin()), m_pool_pos_end(pool.end())
+ {}
+
+ nonleaf_node* build(const leaf_node_ptr& left_leaf_node)
+ {
+ if (!left_leaf_node)
+ // The left leaf node is empty. Nothing to build.
+ return nullptr;
+
+ leaf_node_ptr node1 = left_leaf_node;
+
+ std::vector<nonleaf_node*> node_list;
+ while (true)
+ {
+ leaf_node_ptr node2 = node1->next;
+ nonleaf_node* parent_node = make_parent_node(node1.get(), node2.get());
+ node_list.push_back(parent_node);
+
+ if (!node2 || !node2->next)
+ // no more nodes. Break out of the loop.
+ break;
+
+ node1 = node2->next;
+ }
+
+ return build_tree_non_leaf(node_list);
+ }
+
+private:
+ nonleaf_node* make_parent_node(node_base* node1, node_base* node2)
+ {
+ assert(m_pool_pos != m_pool_pos_end);
+
+ nonleaf_node* parent_node = &(*m_pool_pos);
+ ++m_pool_pos;
+ node1->parent = parent_node;
+ parent_node->left = node1;
+ if (node2)
+ {
+ node2->parent = parent_node;
+ parent_node->right = node2;
+ }
+
+ parent_node->fill_nonleaf_value(node1, node2);
+ return parent_node;
+ }
+
+ nonleaf_node* build_tree_non_leaf(const std::vector<nonleaf_node*>& node_list)
+ {
+ size_t node_count = node_list.size();
+ if (node_count == 1)
+ {
+ return node_list.front();
+ }
+ else if (node_count == 0)
+ return nullptr;
+
+ std::vector<nonleaf_node*> new_node_list;
+ nonleaf_node* node1 = nullptr;
+ typename std::vector<nonleaf_node*>::const_iterator it = node_list.begin();
+ typename std::vector<nonleaf_node*>::const_iterator it_end = node_list.end();
+ for (bool even_itr = false; it != it_end; ++it, even_itr = !even_itr)
+ {
+ if (even_itr)
+ {
+ nonleaf_node* node2 = *it;
+ nonleaf_node* parent_node = make_parent_node(node1, node2);
+ new_node_list.push_back(parent_node);
+ node1 = nullptr;
+ node2 = nullptr;
+ }
+ else
+ node1 = *it;
+ }
+
+ if (node1)
+ {
+ // Un-paired node still needs a parent...
+ nonleaf_node* parent_node = make_parent_node(node1, nullptr);
+ new_node_list.push_back(parent_node);
+ }
+
+ // Move up one level, and do the same procedure until the root node is reached.
+ return build_tree_non_leaf(new_node_list);
+ }
+
+ nonleaf_node_pool_type& m_pool;
+ typename nonleaf_node_pool_type::iterator m_pool_pos;
+ typename nonleaf_node_pool_type::iterator m_pool_pos_end;
+};
+
+template<typename T>
+void disconnect_all_nodes(node<T>* p)
+{
+ if (!p)
+ return;
+
+ p->prev.reset();
+ p->next.reset();
+ p->parent = nullptr;
+}
+
+template<typename T>
+void disconnect_leaf_nodes(node<T>* left_node, node<T>* right_node)
+{
+ if (!left_node || !right_node)
+ return;
+
+ // Go through all leaf nodes, and disconnect their links.
+ node<T>* cur_node = left_node;
+ do
+ {
+ node<T>* next_node = cur_node->next.get();
+ disconnect_all_nodes(cur_node);
+ cur_node = next_node;
+ } while (cur_node != right_node);
+
+ disconnect_all_nodes(right_node);
+}
+
+template<typename T>
+size_t count_leaf_nodes(const node<T>* left_end, const node<T>* right_end)
+{
+ size_t leaf_count = 1;
+ const node<T>* p = left_end;
+ const node<T>* p_end = right_end;
+ for (; p != p_end; p = p->next.get(), ++leaf_count)
+ ;
+
+ return leaf_count;
+}
+
+inline size_t count_needed_nonleaf_nodes(size_t leaf_count)
+{
+ size_t nonleaf_count = 0;
+ while (true)
+ {
+ if (leaf_count == 1)
+ break;
+
+ if ((leaf_count % 2) == 1)
+ // Add one to make it an even number.
+ ++leaf_count;
+
+ leaf_count /= 2;
+ nonleaf_count += leaf_count;
+ }
+
+ return nonleaf_count;
+}
+
+#ifdef MDDS_UNIT_TEST
+
+template<typename _Leaf, typename _NonLeaf>
+class tree_dumper
+{
+ typedef std::vector<const node_base*> node_list_type;
+
+public:
+ static size_t dump(const node_base* root_node)
+ {
+ if (!root_node)
+ return 0;
+
+ node_list_type node_list;
+ node_list.push_back(root_node);
+ return dump_layer(node_list, 0);
+ }
+
+private:
+ static size_t dump_layer(const node_list_type& node_list, unsigned int level)
+ {
+ using ::std::cout;
+ using ::std::endl;
+
+ if (node_list.empty())
+ return 0;
+
+ size_t node_count = node_list.size();
+
+ bool is_leaf = node_list.front()->is_leaf;
+ cout << "level " << level << " (" << (is_leaf ? "leaf" : "non-leaf") << ")" << endl;
+
+ node_list_type new_list;
+ typename node_list_type::const_iterator it = node_list.begin(), it_end = node_list.end();
+ for (; it != it_end; ++it)
+ {
+ const node_base* p = *it;
+ if (!p)
+ {
+ cout << "(x) ";
+ continue;
+ }
+
+ if (p->is_leaf)
+ static_cast<const _Leaf*>(p)->dump_value();
+ else
+ static_cast<const _NonLeaf*>(p)->dump_value();
+
+ if (p->is_leaf)
+ continue;
+
+ if (static_cast<const _NonLeaf*>(p)->left)
+ {
+ new_list.push_back(static_cast<const _NonLeaf*>(p)->left);
+ if (static_cast<const _NonLeaf*>(p)->right)
+ new_list.push_back(static_cast<const _NonLeaf*>(p)->right);
+ }
+ }
+ cout << endl;
+
+ if (!new_list.empty())
+ node_count += dump_layer(new_list, level + 1);
+
+ return node_count;
+ }
+};
+
+#endif
+
+}} // namespace mdds::__st
+
+#endif
diff --git a/include/mdds/point_quad_tree.hpp b/include/mdds/point_quad_tree.hpp
new file mode 100644
index 0000000..01c930d
--- /dev/null
+++ b/include/mdds/point_quad_tree.hpp
@@ -0,0 +1,1576 @@
+/*************************************************************************
+ *
+ * Copyright (c) 2010-2015 Kohei Yoshida
+ *
+ * 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, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ ************************************************************************/
+
+#ifndef INCLUDED_MDDS_POINT_QUAD_TREE_HPP
+#define INCLUDED_MDDS_POINT_QUAD_TREE_HPP
+
+#include "mdds/quad_node.hpp"
+
+#include <cstdlib>
+#include <cassert>
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <vector>
+#include <memory>
+
+#define DEBUG_POINT_QUAD_TREE 0
+
+namespace mdds {
+
+template<typename _PairType>
+void ensure_order(_PairType& v)
+{
+ if (v.first > v.second)
+ ::std::swap(v.first, v.second);
+}
+
+template<typename _Key, typename _NodeType, typename _Inserter>
+void search_region_node(const _NodeType* p, _Key x1, _Key y1, _Key x2, _Key y2, _Inserter& result)
+{
+ using namespace std;
+
+ if (!p)
+ return;
+
+ search_region_space_t region = ::mdds::get_search_region_space(p, x1, y1, x2, y2);
+
+ switch (region)
+ {
+ case region_center:
+ result(p);
+ search_region_node(p->northeast.get(), x1, y1, x2, y2, result);
+ search_region_node(p->northwest.get(), x1, y1, x2, y2, result);
+ search_region_node(p->southeast.get(), x1, y1, x2, y2, result);
+ search_region_node(p->southwest.get(), x1, y1, x2, y2, result);
+ break;
+ case region_east:
+ search_region_node(p->northwest.get(), x1, y1, x2, y2, result);
+ search_region_node(p->southwest.get(), x1, y1, x2, y2, result);
+ break;
+ case region_north:
+ search_region_node(p->southeast.get(), x1, y1, x2, y2, result);
+ search_region_node(p->southwest.get(), x1, y1, x2, y2, result);
+ break;
+ case region_northeast:
+ search_region_node(p->southwest.get(), x1, y1, x2, y2, result);
+ break;
+ case region_northwest:
+ search_region_node(p->southeast.get(), x1, y1, x2, y2, result);
+ break;
+ case region_south:
+ search_region_node(p->northeast.get(), x1, y1, x2, y2, result);
+ search_region_node(p->northwest.get(), x1, y1, x2, y2, result);
+ break;
+ case region_southeast:
+ search_region_node(p->northwest.get(), x1, y1, x2, y2, result);
+ break;
+ case region_southwest:
+ search_region_node(p->northeast.get(), x1, y1, x2, y2, result);
+ break;
+ case region_west:
+ search_region_node(p->northeast.get(), x1, y1, x2, y2, result);
+ search_region_node(p->southeast.get(), x1, y1, x2, y2, result);
+ break;
+ default:
+ throw general_error("unknown search region");
+ }
+}
+
+template<typename _Key, typename _Value>
+class point_quad_tree
+{
+private:
+ class search_result_inserter;
+
+public:
+ typedef _Key key_type;
+ typedef _Value value_type;
+ typedef size_t size_type;
+ typedef ::std::vector<value_type> data_array_type;
+
+ class data_not_found : public ::std::exception
+ {
+ };
+
+private:
+ struct node;
+ typedef ::boost::intrusive_ptr<node> node_ptr;
+
+ struct node : quad_node_base<node_ptr, node, key_type>
+ {
+ value_type data;
+ node(key_type _x, key_type _y, value_type _data) : quad_node_base<node_ptr, node, key_type>(_x, _y), data(_data)
+ {}
+
+ node(const node& r) : quad_node_base<node_ptr, node, key_type>(r), data(r.data)
+ {}
+
+ void dispose()
+ {}
+
+ bool operator==(const node& r) const
+ {
+ return quad_node_base<node_ptr, node, key_type>::operator==(r) && data == r.data;
+ }
+
+ node& operator=(const node& r)
+ {
+ quad_node_base<node_ptr, node, key_type>::operator=(r);
+ data = r.data;
+ return *this;
+ }
+ };
+
+ typedef ::std::vector<node_ptr> reinsert_tree_array_type;
+ typedef ::std::pair<key_type, key_type> key_range_type;
+
+public:
+ /**
+ * Node wrapper to allow read-only access to the internal quad node
+ * structure.
+ */
+ class node_access
+ {
+ friend class point_quad_tree<_Key, _Value>;
+
+ public:
+ node_access northeast() const
+ {
+ return node_access(mp->northeast.get());
+ }
+ node_access northwest() const
+ {
+ return node_access(mp->northwest.get());
+ }
+ node_access southeast() const
+ {
+ return node_access(mp->southeast.get());
+ }
+ node_access southwest() const
+ {
+ return node_access(mp->southwest.get());
+ }
+
+ value_type data() const
+ {
+ return mp->data;
+ }
+ key_type x() const
+ {
+ return mp->x;
+ }
+ key_type y() const
+ {
+ return mp->y;
+ }
+
+ operator bool() const
+ {
+ return mp != nullptr;
+ }
+ bool operator==(const node_access& r) const
+ {
+ return mp == r.mp;
+ }
+
+ node_access() : mp(nullptr)
+ {}
+ ~node_access()
+ {}
+
+ private:
+ node_access(const node* p) : mp(p)
+ {}
+
+ private:
+ const node* mp;
+ };
+
+ struct point
+ {
+ key_type x;
+ key_type y;
+ point(key_type _x, key_type _y) : x(_x), y(_y)
+ {}
+ point() : x(0), y(0)
+ {}
+ };
+
+ class search_results
+ {
+ friend class search_result_inserter;
+
+ typedef std::vector<const node*> res_nodes_type;
+ typedef std::shared_ptr<res_nodes_type> res_nodes_ptr;
+
+ public:
+ class const_iterator
+ {
+ friend class point_quad_tree<_Key, _Value>::search_results;
+ typedef typename point_quad_tree<_Key, _Value>::point point;
+ typedef typename point_quad_tree<_Key, _Value>::value_type parent_value_type;
+
+ public:
+ // Iterator traits
+ typedef std::pair<point, parent_value_type> value_type;
+ typedef value_type* pointer;
+ typedef value_type& reference;
+ typedef ptrdiff_t difference_type;
+ typedef ::std::bidirectional_iterator_tag iterator_category;
+
+ const_iterator(res_nodes_ptr& ptr) : mp_res_nodes(ptr), m_end_pos(false)
+ {}
+
+ const_iterator(const const_iterator& r)
+ : mp_res_nodes(r.mp_res_nodes), m_cur_pos(r.m_cur_pos), m_cur_value(r.m_cur_value),
+ m_end_pos(r.m_end_pos)
+ {}
+
+ const_iterator& operator=(const const_iterator& r)
+ {
+ mp_res_nodes = r.mp_res_nodes;
+ m_cur_pos = r.m_cur_pos;
+ m_cur_value = r.m_cur_value;
+ m_end_pos = r.m_end_pos;
+ return *this;
+ }
+
+ bool operator==(const const_iterator& r) const
+ {
+ if (mp_res_nodes)
+ {
+ // Non-empty result set.
+ return mp_res_nodes.get() == r.mp_res_nodes.get() && m_cur_pos == r.m_cur_pos &&
+ m_end_pos == r.m_end_pos;
+ }
+
+ // Empty result set.
+ if (r.mp_res_nodes)
+ return false;
+
+ return m_end_pos == r.m_end_pos;
+ }
+
+ bool operator!=(const const_iterator& r) const
+ {
+ return !operator==(r);
+ }
+
+ const value_type& operator*() const
+ {
+ return m_cur_value;
+ }
+
+ const value_type* operator->() const
+ {
+ return get_current_value();
+ }
+
+ const value_type* operator++()
+ {
+ // The only difference between the last data position and the
+ // end iterator position must be the value of m_end_pos;
+ // m_cur_pos needs to point to the last data position even
+ // when the iterator is at the end-of-iterator position.
+
+ typename res_nodes_type::const_iterator cur_pos = m_cur_pos;
+ if (++cur_pos == mp_res_nodes->end())
+ {
+ m_end_pos = true;
+ return nullptr;
+ }
+ m_cur_pos = cur_pos;
+ update_current_value();
+ return operator->();
+ }
+
+ const value_type* operator--()
+ {
+ if (m_end_pos)
+ {
+ m_end_pos = false;
+ return get_current_value();
+ }
+ --m_cur_pos;
+ update_current_value();
+ return get_current_value();
+ }
+
+ private:
+ void move_to_front()
+ {
+ if (!mp_res_nodes)
+ {
+ // Empty data set.
+ m_end_pos = true;
+ return;
+ }
+
+ m_cur_pos = mp_res_nodes->begin();
+ m_end_pos = false;
+ update_current_value();
+ }
+
+ void move_to_end()
+ {
+ m_end_pos = true;
+ if (!mp_res_nodes)
+ // Empty data set.
+ return;
+
+ m_cur_pos = mp_res_nodes->end();
+ --m_cur_pos; // Keep the position at the last data position.
+ }
+
+ void update_current_value()
+ {
+ const node* p = *m_cur_pos;
+ m_cur_value.first = point(p->x, p->y);
+ m_cur_value.second = p->data;
+ }
+
+ const value_type* get_current_value() const
+ {
+ return &m_cur_value;
+ }
+
+ private:
+ res_nodes_ptr mp_res_nodes;
+ typename res_nodes_type::const_iterator m_cur_pos;
+ value_type m_cur_value;
+ bool m_end_pos : 1;
+ };
+
+ search_results() : mp_res_nodes(static_cast<res_nodes_type*>(nullptr))
+ {}
+ search_results(const search_results& r) : mp_res_nodes(r.mp_res_nodes)
+ {}
+
+ typename search_results::const_iterator begin()
+ {
+ typename search_results::const_iterator itr(mp_res_nodes);
+ itr.move_to_front();
+ return itr;
+ }
+
+ typename search_results::const_iterator end()
+ {
+ typename search_results::const_iterator itr(mp_res_nodes);
+ itr.move_to_end();
+ return itr;
+ }
+
+ private:
+ void push_back(const node* p)
+ {
+ if (!mp_res_nodes)
+ mp_res_nodes.reset(new res_nodes_type);
+ mp_res_nodes->push_back(p);
+ }
+
+ private:
+ res_nodes_ptr mp_res_nodes;
+ };
+
+ point_quad_tree();
+ point_quad_tree(const point_quad_tree& r);
+ ~point_quad_tree();
+
+ /**
+ * Insert a new data at specified coordinates. It overwrites existing
+ * data in case one exists at the specified coordinates.
+ *
+ * @param x x coordinate of new data position
+ * @param y y coordinate of new data position
+ * @param data data being inserted at the specified coordinates.
+ */
+ void insert(key_type x, key_type y, value_type data);
+
+ /**
+ * Perform region search (aka window search), that is, find all points
+ * that fall within specified rectangular region. The boundaries are
+ * inclusive.
+ *
+ * @param x1 left coordinate of the search region
+ * @param y1 top coordinate of the search region
+ * @param x2 right coordinate of the search region
+ * @param y2 bottom coordinate of the search region
+ * @param result this array will contain all data found without specified
+ * region.
+ */
+ void search_region(key_type x1, key_type y1, key_type x2, key_type y2, data_array_type& result) const;
+
+ /**
+ * Perform region search (aka window search), that is, find all points
+ * that fall within specified rectangular region. The boundaries are
+ * inclusive.
+ *
+ * @param x1 left coordinate of the search region
+ * @param y1 top coordinate of the search region
+ * @param x2 right coordinate of the search region
+ * @param y2 bottom coordinate of the search region
+ *
+ * @return search result object containing all data found within the
+ * specified region.
+ */
+ search_results search_region(key_type x1, key_type y1, key_type x2, key_type y2) const;
+
+ /**
+ * Find data at specified coordinates. If no data exists at the specified
+ * coordinates, this method throws a
+ * <code>point_quad_tree::data_not_found</code> exception.
+ *
+ * @param x x coordinate
+ * @param y y coordinate
+ *
+ * @return data found at the specified coordinates.
+ */
+ value_type find(key_type x, key_type y) const;
+
+ /**
+ * Remove data from specified coordinates. This method does nothing if no
+ * data exists at the specified coordinates.
+ *
+ * @param x x coordinate
+ * @param y y coordinate
+ */
+ void remove(key_type x, key_type y);
+
+ /**
+ * Swap the internal state with another instance.
+ *
+ * @param r another instance to swap internals with.
+ */
+ void swap(point_quad_tree& r);
+
+ /**
+ * Remove all stored data.
+ */
+ void clear();
+
+ /**
+ * Check whether or not the container is empty.
+ *
+ * @return bool true if empty, false otherwise.
+ */
+ bool empty() const;
+
+ /**
+ * Get the number of stored data.
+ *
+ * @return the number of data currently stored in the container.
+ */
+ size_t size() const;
+
+ /**
+ * Get read-only access to the internal quad node tree.
+ *
+ * @return root node
+ */
+ node_access get_node_access() const;
+
+ point_quad_tree& operator=(const point_quad_tree& r);
+
+ bool operator==(const point_quad_tree& r) const;
+
+ bool operator!=(const point_quad_tree& r) const
+ {
+ return !operator==(r);
+ }
+
+#ifdef MDDS_UNIT_TEST
+public:
+#else
+private:
+#endif
+ /**
+ * Data stored in each node. Used for verification of data stored in tree
+ * during unit testing.
+ */
+ struct node_data
+ {
+ key_type x;
+ key_type y;
+ value_type data;
+ node_data(key_type _x, key_type _y, value_type _data) : x(_x), y(_y), data(_data)
+ {}
+ node_data(const node_data& r) : x(r.x), y(r.y), data(r.data)
+ {}
+
+ bool operator==(const node_data& r) const
+ {
+ return (x == r.x) && (y == r.y) && (data == r.data);
+ }
+
+ bool operator!=(const node_data& r) const
+ {
+ return !operator==(r);
+ }
+
+ struct sorter
+ {
+ bool operator()(const node_data& left, const node_data& right) const
+ {
+ if (left.x != right.x)
+ return left.x < right.x;
+ if (left.y != right.y)
+ return left.y < right.y;
+ return left.data < right.data;
+ }
+ };
+ };
+
+ static bool equals(::std::vector<node_data>& v1, ::std::vector<node_data>& v2);
+
+ bool verify_data(::std::vector<node_data>& expected) const;
+
+ bool verify_node_iterator(const node_access& nac) const;
+ static bool verify_node_iterator(const node_access& nac, const node* p);
+
+ void get_all_stored_data(::std::vector<node_data>& stored_data) const;
+
+ void dump_tree_svg(const ::std::string& fpath) const;
+
+private:
+ class array_inserter
+ {
+ public:
+ array_inserter(data_array_type& result) : m_result(result)
+ {}
+
+ void operator()(const node* p)
+ {
+ m_result.push_back(p->data);
+ }
+
+ private:
+ data_array_type& m_result;
+ };
+
+ class search_result_inserter
+ {
+ public:
+ search_result_inserter(search_results& result) : m_result(result)
+ {}
+
+ void operator()(const node* p)
+ {
+ m_result.push_back(p);
+ }
+
+ private:
+ search_results& m_result;
+ };
+
+ class data_inserter
+ {
+ public:
+ data_inserter(point_quad_tree& db) : m_db(db)
+ {}
+
+ void operator()(const node_data& v)
+ {
+ m_db.insert(v.x, v.y, v.data);
+ }
+
+ private:
+ point_quad_tree& m_db;
+ };
+
+ struct node_distance
+ {
+ node_quadrant_t quad;
+ key_type dist;
+ node_ptr node;
+
+ node_distance() : quad(quad_unspecified), dist(0), node(nullptr)
+ {}
+ node_distance(node_quadrant_t _quad, key_type _dist, const node_ptr& _node)
+ : quad(_quad), dist(_dist), node(_node)
+ {}
+ };
+
+ node_ptr find_node(key_type x, key_type y) const;
+ const node* find_node_ptr(key_type x, key_type y) const;
+ node_ptr find_replacement_node(key_type x, key_type y, const node_ptr& delete_node) const;
+
+ void find_candidate_in_quad(
+ key_type x, key_type y, node_distance& dx_node, node_distance& dy_node, node_distance& min_city_block_node,
+ const node_ptr& delete_node, node_quadrant_t quad) const;
+
+ void adjust_quad(
+ const key_range_type& hatched_xrange, const key_range_type& hatched_yrange, node_ptr quad_root, direction_t dir,
+ reinsert_tree_array_type& insert_list);
+
+ void set_new_root(
+ const key_range_type& hatched_xrange, const key_range_type& hatched_yrange, node_ptr& quad_root,
+ node_quadrant_t dir, reinsert_tree_array_type& insert_list);
+
+ void insert_node(node_ptr& dest, node_ptr& node);
+ void reinsert_tree(node_ptr& dest, node_ptr& root);
+ void reinsert_tree(node_ptr& dest, node_quadrant_t quad, node_ptr& root);
+ void clear_all_nodes();
+ void dump_node_svg(const node* p, ::std::ofstream& file) const;
+ void count_all_nodes(const node* p, size_t& node_count) const;
+ void insert_data_from(const point_quad_tree& r);
+ void get_all_stored_data(const node* p, ::std::vector<node_data>& stored_data) const;
+
+private:
+ node_ptr m_root;
+
+ key_range_type m_xrange;
+ key_range_type m_yrange;
+};
+
+template<typename _Key, typename _Value>
+point_quad_tree<_Key, _Value>::point_quad_tree() : m_root(nullptr), m_xrange(0, 0), m_yrange(0, 0)
+{}
+
+template<typename _Key, typename _Value>
+point_quad_tree<_Key, _Value>::point_quad_tree(const point_quad_tree& r)
+ : m_root(nullptr), m_xrange(0, 0), m_yrange(0, 0)
+{
+ insert_data_from(r);
+}
+
+template<typename _Key, typename _Value>
+point_quad_tree<_Key, _Value>::~point_quad_tree()
+{
+ clear_all_nodes();
+}
+
+template<typename _Key, typename _Value>
+void point_quad_tree<_Key, _Value>::insert(key_type x, key_type y, value_type data)
+{
+ m_xrange.first = ::std::min(m_xrange.first, x);
+ m_xrange.second = ::std::max(m_xrange.second, x);
+ m_yrange.first = ::std::min(m_yrange.first, y);
+ m_yrange.second = ::std::max(m_yrange.second, y);
+
+ if (!m_root)
+ {
+ // The very first node.
+ m_root.reset(new node(x, y, data));
+ return;
+ }
+
+ node_ptr cur_node = m_root;
+ while (true)
+ {
+ if (cur_node->x == x && cur_node->y == y)
+ {
+ // Replace the current data with this, and we are done!
+ cur_node->data = data;
+ return;
+ }
+
+ node_quadrant_t quad = cur_node->get_quadrant(x, y);
+ switch (quad)
+ {
+ case quad_northeast:
+ if (cur_node->northeast)
+ cur_node = cur_node->northeast;
+ else
+ {
+ cur_node->northeast.reset(new node(x, y, data));
+ cur_node->northeast->parent = cur_node;
+ return;
+ }
+ break;
+ case quad_northwest:
+ if (cur_node->northwest)
+ cur_node = cur_node->northwest;
+ else
+ {
+ cur_node->northwest.reset(new node(x, y, data));
+ cur_node->northwest->parent = cur_node;
+ return;
+ }
+ break;
+ case quad_southeast:
+ if (cur_node->southeast)
+ cur_node = cur_node->southeast;
+ else
+ {
+ cur_node->southeast.reset(new node(x, y, data));
+ cur_node->southeast->parent = cur_node;
+ return;
+ }
+ break;
+ case quad_southwest:
+ if (cur_node->southwest)
+ cur_node = cur_node->southwest;
+ else
+ {
+ cur_node->southwest.reset(new node(x, y, data));
+ cur_node->southwest->parent = cur_node;
+ return;
+ }
+ break;
+ default:
+ throw general_error("unknown quadrant");
+ }
+ }
+ assert(!"This should never be reached.");
+}
+
+template<typename _Key, typename _Value>
+void point_quad_tree<_Key, _Value>::search_region(
+ key_type x1, key_type y1, key_type x2, key_type y2, data_array_type& result) const
+{
+ using namespace std;
+ const node* p = m_root.get();
+ array_inserter _inserter(result);
+ ::mdds::search_region_node(p, x1, y1, x2, y2, _inserter);
+}
+
+template<typename _Key, typename _Value>
+typename point_quad_tree<_Key, _Value>::search_results point_quad_tree<_Key, _Value>::search_region(
+ key_type x1, key_type y1, key_type x2, key_type y2) const
+{
+ using namespace std;
+ search_results result;
+ const node* p = m_root.get();
+ search_result_inserter _inserter(result);
+ ::mdds::search_region_node(p, x1, y1, x2, y2, _inserter);
+ return result;
+}
+
+template<typename _Key, typename _Value>
+typename point_quad_tree<_Key, _Value>::value_type point_quad_tree<_Key, _Value>::find(key_type x, key_type y) const
+{
+ const node* p = find_node_ptr(x, y);
+ if (!p)
+ throw data_not_found();
+ return p->data;
+}
+
+template<typename _Key, typename _Value>
+void point_quad_tree<_Key, _Value>::remove(key_type x, key_type y)
+{
+ using namespace std;
+ node_ptr delete_node = find_node(x, y);
+ if (!delete_node)
+ // No node exists at this coordinate.
+ return;
+
+#if DEBUG_POINT_QUAD_TREE
+ cout << "found the node to be removed at " << delete_node->x << "," << delete_node->y << " (" << *delete_node->data
+ << ")" << endl;
+#endif
+
+ // Check if this is a leaf node, in which case we can just delete it
+ // without further processing.
+ if (delete_node->leaf())
+ {
+#if DEBUG_POINT_QUAD_TREE
+ cout << "deleting a leaf node." << endl;
+#endif
+ if (delete_node.get() == m_root.get())
+ m_root.reset();
+ else
+ disconnect_node_from_parent(delete_node);
+ delete_node.reset();
+ return;
+ }
+
+ node_ptr repl_node = find_replacement_node(x, y, delete_node);
+ if (!repl_node)
+ // Non-leaf node should have at least one replacement candidate.
+ throw general_error("failed to find a replacement candidate node.");
+
+ node_quadrant_t repl_quad = delete_node->get_quadrant(repl_node->x, repl_node->y);
+
+ key_range_type xrange(delete_node->x, repl_node->x);
+ key_range_type yrange(delete_node->y, repl_node->y);
+ ensure_order(xrange);
+ ensure_order(yrange);
+ reinsert_tree_array_type insert_list;
+
+ // Call the quadrant where the replacement node is quadrant I. Adjust the
+ // quadrants adjacent to quadrant I first, then adjust quadrant I
+ // afterwards.
+ switch (repl_quad)
+ {
+ case quad_northeast:
+ adjust_quad(xrange, yrange, delete_node->northwest, dir_south, insert_list);
+ adjust_quad(xrange, yrange, delete_node->southeast, dir_west, insert_list);
+ set_new_root(xrange, yrange, delete_node->northeast, quad_southwest, insert_list);
+ break;
+ case quad_northwest:
+ adjust_quad(xrange, yrange, delete_node->northeast, dir_south, insert_list);
+ adjust_quad(xrange, yrange, delete_node->southwest, dir_east, insert_list);
+ set_new_root(xrange, yrange, delete_node->northwest, quad_southeast, insert_list);
+ break;
+ case quad_southeast:
+ adjust_quad(xrange, yrange, delete_node->northeast, dir_west, insert_list);
+ adjust_quad(xrange, yrange, delete_node->southwest, dir_north, insert_list);
+ set_new_root(xrange, yrange, delete_node->southeast, quad_northwest, insert_list);
+ break;
+ case quad_southwest:
+ adjust_quad(xrange, yrange, delete_node->northwest, dir_east, insert_list);
+ adjust_quad(xrange, yrange, delete_node->southeast, dir_north, insert_list);
+ set_new_root(xrange, yrange, delete_node->southwest, quad_northeast, insert_list);
+ break;
+ default:
+ throw general_error("quadrant for the replacement node is unspecified.");
+ }
+
+ // Reinsert all child nodes from the replacement node into the node to be
+ // "deleted".
+ switch (repl_quad)
+ {
+ case quad_northeast:
+ case quad_southwest:
+ {
+ node_ptr root = repl_node->northwest;
+ repl_node->northwest.reset();
+ reinsert_tree(delete_node, quad_northwest, root);
+
+ root = repl_node->southeast;
+ repl_node->southeast.reset();
+ reinsert_tree(delete_node, quad_southeast, root);
+ }
+ break;
+ case quad_northwest:
+ case quad_southeast:
+ {
+ node_ptr root = repl_node->northeast;
+ repl_node->northeast.reset();
+ reinsert_tree(delete_node, quad_northeast, root);
+
+ root = repl_node->southwest;
+ repl_node->southwest.reset();
+ reinsert_tree(delete_node, quad_southwest, root);
+ }
+ break;
+ default:
+ throw general_error("quadrant for the replacement node is unspecified.");
+ }
+
+ // Finally, replace the node to be removed with the replacement node.
+ delete_node->x = repl_node->x;
+ delete_node->y = repl_node->y;
+ delete_node->data = repl_node->data;
+
+ // Reset the parent node.
+ delete_node->parent = repl_node->parent;
+ repl_node->parent.reset();
+
+ switch (repl_quad)
+ {
+ case quad_northeast:
+ delete_node->northeast = repl_node->northeast;
+ repl_node->northeast.reset();
+ break;
+ case quad_northwest:
+ delete_node->northwest = repl_node->northwest;
+ repl_node->northwest.reset();
+ break;
+ case quad_southeast:
+ delete_node->southeast = repl_node->southeast;
+ repl_node->southeast.reset();
+ break;
+ case quad_southwest:
+ delete_node->southwest = repl_node->southwest;
+ repl_node->southwest.reset();
+ break;
+ default:
+ throw general_error("quadrant for the replacement node is unspecified.");
+ }
+
+ // Lastly, re-insert all those trees that have been cut during the quad
+ // adjustment into the new root.
+ typename reinsert_tree_array_type::iterator itr = insert_list.begin(), itr_end = insert_list.end();
+ for (; itr != itr_end; ++itr)
+ reinsert_tree(delete_node, *itr);
+}
+
+template<typename _Key, typename _Value>
+void point_quad_tree<_Key, _Value>::swap(point_quad_tree& r)
+{
+ m_root.swap(r.m_root);
+ ::std::swap(m_xrange, r.m_xrange);
+ ::std::swap(m_yrange, r.m_yrange);
+}
+
+template<typename _Key, typename _Value>
+void point_quad_tree<_Key, _Value>::clear()
+{
+ clear_all_nodes();
+}
+
+template<typename _Key, typename _Value>
+bool point_quad_tree<_Key, _Value>::empty() const
+{
+ return (m_root.get() == nullptr);
+}
+
+template<typename _Key, typename _Value>
+size_t point_quad_tree<_Key, _Value>::size() const
+{
+ size_t node_count = 0;
+ count_all_nodes(m_root.get(), node_count);
+ return node_count;
+}
+
+template<typename _Key, typename _Value>
+typename point_quad_tree<_Key, _Value>::node_access point_quad_tree<_Key, _Value>::get_node_access() const
+{
+ return node_access(m_root.get());
+}
+
+template<typename _Key, typename _Value>
+point_quad_tree<_Key, _Value>& point_quad_tree<_Key, _Value>::operator=(const point_quad_tree& r)
+{
+ m_xrange = key_range_type(0, 0);
+ m_yrange = key_range_type(0, 0);
+ clear_all_nodes();
+ insert_data_from(r);
+ return *this;
+}
+
+template<typename _Key, typename _Value>
+bool point_quad_tree<_Key, _Value>::operator==(const point_quad_tree& r) const
+{
+ ::std::vector<node_data> v1, v2;
+ get_all_stored_data(v1);
+ r.get_all_stored_data(v2);
+ return equals(v1, v2);
+}
+
+template<typename _Key, typename _Value>
+void point_quad_tree<_Key, _Value>::dump_tree_svg(const ::std::string& fpath) const
+{
+ using namespace std;
+ ofstream file(fpath.c_str());
+ file << "<svg width=\"60cm\" height=\"60cm\" viewBox=\"-2 -2 202 202\" xmlns=\"http://www.w3.org/2000/svg\" "
+ "version=\"1.1\">"
+ << endl;
+ file << "<defs>"
+ << " <marker id=\"Triangle\""
+ << " viewBox=\"0 0 10 10\" refX=\"10\" refY=\"5\" "
+ << " markerUnits=\"strokeWidth\""
+ << " markerWidth=\"9\" markerHeight=\"6\""
+ << " orient=\"auto\">"
+ << " <path d=\"M 0 0 L 10 5 L 0 10 z\" />"
+ << " </marker>"
+ << "</defs>" << endl;
+
+ file << "<path d=\"M 0 0 L 0 " << m_yrange.second + 1
+ << "\" stroke=\"blue\" stroke-width=\"0.2\" marker-end=\"url(#Triangle)\"/>" << endl;
+ file << "<path d=\"M 0 0 L " << m_xrange.second + 1
+ << " 0\" stroke=\"blue\" stroke-width=\"0.2\" marker-end=\"url(#Triangle)\"/>" << endl;
+ dump_node_svg(m_root.get(), file);
+ file << "</svg>" << endl;
+}
+
+template<typename _NodePtr>
+void draw_svg_arrow(::std::ofstream& file, const _NodePtr start, const _NodePtr end)
+{
+ using namespace std;
+ file << "<g stroke=\"red\" marker-end=\"url(#Triangle)\">" << endl;
+ file << "<line x1=\"" << start->x << "\" y1=\"" << start->y << "\" x2=\"" << end->x << "\" y2=\"" << end->y
+ << "\" stroke-width=\"0.2\"/>" << endl;
+ file << "</g>" << endl;
+}
+
+template<typename _Key, typename _Value>
+void point_quad_tree<_Key, _Value>::dump_node_svg(const node* p, ::std::ofstream& file) const
+{
+ using namespace std;
+
+ if (!p)
+ return;
+
+ file << "<circle cx=\"" << p->x << "\" cy=\"" << p->y << "\" r=\"0.1\""
+ << " fill=\"black\" stroke=\"black\"/>" << endl;
+ file << "<text x=\"" << p->x + 1 << "\" y=\"" << p->y + 2 << "\" font-size=\"1.2\" fill=\"black\">" << *p->data
+ << " (" << p->x << "," << p->y << ")</text>" << endl;
+
+ if (p->northwest)
+ draw_svg_arrow<const node*>(file, p, p->northwest.get());
+
+ if (p->northeast)
+ draw_svg_arrow<const node*>(file, p, p->northeast.get());
+
+ if (p->southwest)
+ draw_svg_arrow<const node*>(file, p, p->southwest.get());
+
+ if (p->southeast)
+ draw_svg_arrow<const node*>(file, p, p->southeast.get());
+
+ dump_node_svg(p->northeast.get(), file);
+ dump_node_svg(p->northwest.get(), file);
+ dump_node_svg(p->southeast.get(), file);
+ dump_node_svg(p->southwest.get(), file);
+}
+
+template<typename _Key, typename _Value>
+bool point_quad_tree<_Key, _Value>::equals(::std::vector<node_data>& v1, ::std::vector<node_data>& v2)
+{
+ using namespace std;
+
+ if (v1.size() != v2.size())
+ return false;
+
+ sort(v1.begin(), v1.end(), typename node_data::sorter());
+ sort(v2.begin(), v2.end(), typename node_data::sorter());
+
+ typename vector<node_data>::const_iterator itr1 = v1.begin(), itr1_end = v1.end(), itr2 = v2.begin(),
+ itr2_end = v2.end();
+
+ for (; itr1 != itr1_end; ++itr1, ++itr2)
+ {
+ if (itr2 == itr2_end)
+ return false;
+
+ if (*itr1 != *itr2)
+ return false;
+ }
+ if (itr2 != itr2_end)
+ return false;
+
+ return true;
+}
+
+template<typename _Key, typename _Value>
+void point_quad_tree<_Key, _Value>::get_all_stored_data(::std::vector<node_data>& stored_data) const
+{
+ stored_data.clear();
+ if (!m_root)
+ return;
+
+ get_all_stored_data(m_root.get(), stored_data);
+}
+
+template<typename _Key, typename _Value>
+void point_quad_tree<_Key, _Value>::count_all_nodes(const node* p, size_t& node_count) const
+{
+ if (!p)
+ return;
+
+ ++node_count;
+
+ count_all_nodes(p->northeast.get(), node_count);
+ count_all_nodes(p->northwest.get(), node_count);
+ count_all_nodes(p->southeast.get(), node_count);
+ count_all_nodes(p->southwest.get(), node_count);
+}
+
+template<typename _Key, typename _Value>
+void point_quad_tree<_Key, _Value>::insert_data_from(const point_quad_tree& r)
+{
+ using namespace std;
+ vector<node_data> all_data;
+ r.get_all_stored_data(all_data);
+ for_each(all_data.begin(), all_data.end(), data_inserter(*this));
+}
+
+template<typename _Key, typename _Value>
+bool point_quad_tree<_Key, _Value>::verify_data(::std::vector<node_data>& expected) const
+{
+ ::std::vector<node_data> stored;
+ get_all_stored_data(stored);
+ return equals(stored, expected);
+}
+
+template<typename _Key, typename _Value>
+bool point_quad_tree<_Key, _Value>::verify_node_iterator(const node_access& nac) const
+{
+ return verify_node_iterator(nac, m_root.get());
+}
+
+template<typename _Key, typename _Value>
+bool point_quad_tree<_Key, _Value>::verify_node_iterator(const node_access& nac, const node* p)
+{
+ if (!nac)
+ return (p == nullptr);
+
+ if (!p)
+ return false;
+
+ if (!verify_node_iterator(nac.northeast(), p->northeast.get()))
+ return false;
+ if (!verify_node_iterator(nac.northwest(), p->northwest.get()))
+ return false;
+ if (!verify_node_iterator(nac.southeast(), p->southeast.get()))
+ return false;
+ if (!verify_node_iterator(nac.southwest(), p->southwest.get()))
+ return false;
+
+ return true;
+}
+
+template<typename _Key, typename _Value>
+void point_quad_tree<_Key, _Value>::get_all_stored_data(const node* p, ::std::vector<node_data>& stored_data) const
+{
+ if (!p)
+ return;
+
+ stored_data.push_back(node_data(p->x, p->y, p->data));
+
+ get_all_stored_data(p->northeast.get(), stored_data);
+ get_all_stored_data(p->northwest.get(), stored_data);
+ get_all_stored_data(p->southeast.get(), stored_data);
+ get_all_stored_data(p->southwest.get(), stored_data);
+}
+
+template<typename _Key, typename _Value>
+typename point_quad_tree<_Key, _Value>::node_ptr point_quad_tree<_Key, _Value>::find_node(key_type x, key_type y) const
+{
+ node_ptr cur_node = m_root;
+ while (cur_node)
+ {
+ if (cur_node->x == x && cur_node->y == y)
+ {
+ // Found the node.
+ return cur_node;
+ }
+
+ node_quadrant_t quad = cur_node->get_quadrant(x, y);
+ switch (quad)
+ {
+ case quad_northeast:
+ if (!cur_node->northeast)
+ return node_ptr();
+ cur_node = cur_node->northeast;
+ break;
+ case quad_northwest:
+ if (!cur_node->northwest)
+ return node_ptr();
+ cur_node = cur_node->northwest;
+ break;
+ case quad_southeast:
+ if (!cur_node->southeast)
+ return node_ptr();
+ cur_node = cur_node->southeast;
+ break;
+ case quad_southwest:
+ if (!cur_node->southwest)
+ return node_ptr();
+ cur_node = cur_node->southwest;
+ break;
+ default:
+ throw general_error("unknown quadrant");
+ }
+ }
+ return node_ptr();
+}
+
+template<typename _Key, typename _Value>
+const typename point_quad_tree<_Key, _Value>::node* point_quad_tree<_Key, _Value>::find_node_ptr(
+ key_type x, key_type y) const
+{
+ const node* cur_node = m_root.get();
+ while (cur_node)
+ {
+ if (cur_node->x == x && cur_node->y == y)
+ {
+ // Found the node.
+ return cur_node;
+ }
+
+ node_quadrant_t quad = cur_node->get_quadrant(x, y);
+ switch (quad)
+ {
+ case quad_northeast:
+ if (!cur_node->northeast)
+ return nullptr;
+ cur_node = cur_node->northeast.get();
+ break;
+ case quad_northwest:
+ if (!cur_node->northwest)
+ return nullptr;
+ cur_node = cur_node->northwest.get();
+ break;
+ case quad_southeast:
+ if (!cur_node->southeast)
+ return nullptr;
+ cur_node = cur_node->southeast.get();
+ break;
+ case quad_southwest:
+ if (!cur_node->southwest)
+ return nullptr;
+ cur_node = cur_node->southwest.get();
+ break;
+ default:
+ throw general_error("unknown quadrant");
+ }
+ }
+ return nullptr;
+}
+
+template<typename _Key, typename _Value>
+typename point_quad_tree<_Key, _Value>::node_ptr point_quad_tree<_Key, _Value>::find_replacement_node(
+ key_type x, key_type y, const node_ptr& delete_node) const
+{
+ using namespace std;
+
+ // Try to get a replacement candidate in each quadrant.
+ node_distance dx_node, dy_node, min_city_block_node;
+
+#if DEBUG_POINT_QUAD_TREE
+ cout << "northeast" << endl;
+#endif
+ find_candidate_in_quad(x, y, dx_node, dy_node, min_city_block_node, delete_node, quad_northeast);
+
+#if DEBUG_POINT_QUAD_TREE
+ cout << "northwest" << endl;
+#endif
+ find_candidate_in_quad(x, y, dx_node, dy_node, min_city_block_node, delete_node, quad_northwest);
+
+#if DEBUG_POINT_QUAD_TREE
+ cout << "southwest" << endl;
+#endif
+ find_candidate_in_quad(x, y, dx_node, dy_node, min_city_block_node, delete_node, quad_southwest);
+
+#if DEBUG_POINT_QUAD_TREE
+ cout << "southeast" << endl;
+#endif
+ find_candidate_in_quad(x, y, dx_node, dy_node, min_city_block_node, delete_node, quad_southeast);
+
+ // Check Criterion 1.
+
+#if DEBUG_POINT_QUAD_TREE
+ if (dx_node.node)
+ cout << "node closest to x axis: " << *dx_node.node->data << " (dx=" << dx_node.dist << ")" << endl;
+
+ if (dy_node.node)
+ cout << "node closest to y axis: " << *dy_node.node->data << " (dy=" << dy_node.dist << ")" << endl;
+#endif
+
+ if (dx_node.node == dy_node.node && ((dx_node.quad == quad_northwest) || (dx_node.quad == quad_southeast)))
+ {
+#if DEBUG_POINT_QUAD_TREE
+ cout << "node that satisfies Criterion 1: " << *dx_node.node->data << endl;
+#endif
+ return dx_node.node;
+ }
+ else
+ {
+#if DEBUG_POINT_QUAD_TREE
+ cout << "unable to find node that satisfies Criterion 1." << endl;
+#endif
+ }
+
+ // Move on to Criterion 2.
+
+ if (min_city_block_node.node)
+ {
+#if DEBUG_POINT_QUAD_TREE
+ cout << "node that satisfies Criterion 2: " << *min_city_block_node.node->data
+ << " (dist=" << min_city_block_node.dist << ")" << endl;
+#endif
+ return min_city_block_node.node;
+ }
+
+ return node_ptr();
+}
+
+template<typename _Key, typename _Value>
+void point_quad_tree<_Key, _Value>::find_candidate_in_quad(
+ key_type x, key_type y, node_distance& dx_node, node_distance& dy_node, node_distance& min_city_block_node,
+ const node_ptr& delete_node, node_quadrant_t quad) const
+{
+ using namespace std;
+
+ node_ptr repl_node = delete_node->get_quadrant_node(quad);
+ if (!repl_node)
+ {
+ // No candidate in this quadrant.
+#if DEBUG_POINT_QUAD_TREE
+ cout << " no candidate in this quadrant" << endl;
+#endif
+ return;
+ }
+
+ node_quadrant_t oppo_quad = opposite(quad);
+ while (repl_node->has_quadrant_node(oppo_quad))
+ repl_node = repl_node->get_quadrant_node(oppo_quad);
+
+#if DEBUG_POINT_QUAD_TREE
+ cout << " candidate: " << repl_node->x << "," << repl_node->y << " (" << *repl_node->data << ")" << endl;
+#endif
+
+ // Calculate its distance to each of the borders.
+ key_type dx = repl_node->x > x ? repl_node->x - x : x - repl_node->x;
+ key_type dy = repl_node->y > y ? repl_node->y - y : y - repl_node->y;
+#if DEBUG_POINT_QUAD_TREE
+ cout << " dx = " << dx << ", dy = " << dy << endl;
+#endif
+
+ if (!dx_node.node || dx_node.dist > dx)
+ dx_node = node_distance(quad, dx, repl_node);
+ if (!dy_node.node || dy_node.dist > dy)
+ dy_node = node_distance(quad, dy, repl_node);
+
+ if (!min_city_block_node.node || min_city_block_node.dist > (dx + dy))
+ min_city_block_node = node_distance(quad_unspecified, dx + dy, repl_node);
+}
+
+template<typename _Key, typename _Value>
+void point_quad_tree<_Key, _Value>::adjust_quad(
+ const key_range_type& hatched_xrange, const key_range_type& hatched_yrange, node_ptr quad_root, direction_t dir,
+ reinsert_tree_array_type& insert_list)
+{
+ using namespace std;
+
+ if (!quad_root)
+ return;
+
+#if DEBUG_POINT_QUAD_TREE
+ cout << "adjust_quad: checking " << *quad_root->data << " (" << quad_root->x << "," << quad_root->y << ")" << endl;
+#endif
+
+ if ((hatched_xrange.first <= quad_root->x && quad_root->x <= hatched_xrange.second) ||
+ (hatched_yrange.first <= quad_root->y && quad_root->y <= hatched_yrange.second))
+ {
+#if DEBUG_POINT_QUAD_TREE
+ cout << " " << *quad_root->data << " is in the hatched region" << endl;
+#endif
+ // Insert the whole tree, including the root, into the insert list.
+ disconnect_node_from_parent(quad_root);
+ quad_root->parent.reset();
+ insert_list.push_back(quad_root);
+ return;
+ }
+
+ switch (dir)
+ {
+ case dir_east:
+ adjust_quad(hatched_xrange, hatched_yrange, quad_root->northeast, dir_east, insert_list);
+ adjust_quad(hatched_xrange, hatched_yrange, quad_root->southeast, dir_east, insert_list);
+ break;
+ case dir_north:
+ adjust_quad(hatched_xrange, hatched_yrange, quad_root->northeast, dir_north, insert_list);
+ adjust_quad(hatched_xrange, hatched_yrange, quad_root->northwest, dir_north, insert_list);
+ break;
+ case dir_south:
+ adjust_quad(hatched_xrange, hatched_yrange, quad_root->southeast, dir_south, insert_list);
+ adjust_quad(hatched_xrange, hatched_yrange, quad_root->southwest, dir_south, insert_list);
+ break;
+ case dir_west:
+ adjust_quad(hatched_xrange, hatched_yrange, quad_root->northwest, dir_west, insert_list);
+ adjust_quad(hatched_xrange, hatched_yrange, quad_root->southwest, dir_west, insert_list);
+ break;
+ default:;
+ }
+}
+
+template<typename _Key, typename _Value>
+void point_quad_tree<_Key, _Value>::set_new_root(
+ const key_range_type& hatched_xrange, const key_range_type& hatched_yrange, node_ptr& quad_root,
+ node_quadrant_t dir, reinsert_tree_array_type& insert_list)
+{
+ node_ptr cur_node = quad_root;
+ while (cur_node)
+ {
+ switch (dir)
+ {
+ case quad_northeast:
+ adjust_quad(hatched_xrange, hatched_yrange, cur_node->southeast, dir_east, insert_list);
+ adjust_quad(hatched_xrange, hatched_yrange, cur_node->northwest, dir_north, insert_list);
+ break;
+ case quad_northwest:
+ adjust_quad(hatched_xrange, hatched_yrange, cur_node->northeast, dir_north, insert_list);
+ adjust_quad(hatched_xrange, hatched_yrange, cur_node->southwest, dir_west, insert_list);
+ break;
+ case quad_southeast:
+ adjust_quad(hatched_xrange, hatched_yrange, cur_node->northeast, dir_east, insert_list);
+ adjust_quad(hatched_xrange, hatched_yrange, cur_node->southwest, dir_south, insert_list);
+ break;
+ case quad_southwest:
+ adjust_quad(hatched_xrange, hatched_yrange, cur_node->northwest, dir_west, insert_list);
+ adjust_quad(hatched_xrange, hatched_yrange, cur_node->southeast, dir_south, insert_list);
+ break;
+ default:
+ throw general_error("unspecified quadrant");
+ }
+ cur_node = cur_node->get_quadrant_node(dir);
+ }
+}
+
+template<typename _Key, typename _Value>
+void point_quad_tree<_Key, _Value>::insert_node(node_ptr& dest, node_ptr& this_node)
+{
+ node_ptr cur_node = dest;
+ while (true)
+ {
+ if (cur_node->x == this_node->x && cur_node->y == this_node->y)
+ {
+ // When inserting a node instance directly (likely as part of tree
+ // re-insertion), we are not supposed to have another node at
+ // identical position.
+ throw general_error("node with identical position encountered.");
+ }
+
+ node_quadrant_t quad = cur_node->get_quadrant(this_node->x, this_node->y);
+ switch (quad)
+ {
+ case quad_northeast:
+ if (cur_node->northeast)
+ cur_node = cur_node->northeast;
+ else
+ {
+ cur_node->northeast = this_node;
+ this_node->parent = cur_node;
+ return;
+ }
+ break;
+ case quad_northwest:
+ if (cur_node->northwest)
+ cur_node = cur_node->northwest;
+ else
+ {
+ cur_node->northwest = this_node;
+ this_node->parent = cur_node;
+ return;
+ }
+ break;
+ case quad_southeast:
+ if (cur_node->southeast)
+ cur_node = cur_node->southeast;
+ else
+ {
+ cur_node->southeast = this_node;
+ this_node->parent = cur_node;
+ return;
+ }
+ break;
+ case quad_southwest:
+ if (cur_node->southwest)
+ cur_node = cur_node->southwest;
+ else
+ {
+ cur_node->southwest = this_node;
+ this_node->parent = cur_node;
+ return;
+ }
+ break;
+ default:
+ throw general_error("unknown quadrant");
+ }
+ }
+ assert(!"This should never be reached.");
+}
+
+template<typename _Key, typename _Value>
+void point_quad_tree<_Key, _Value>::reinsert_tree(node_ptr& dest, node_ptr& root)
+{
+ assert(dest); // Destination node should not be null.
+
+ if (!root)
+ // Nothing to re-insert. Bail out.
+ return;
+
+ if (root->northeast)
+ {
+ reinsert_tree(dest, root->northeast);
+ root->northeast.reset();
+ }
+ if (root->northwest)
+ {
+ reinsert_tree(dest, root->northwest);
+ root->northwest.reset();
+ }
+ if (root->southeast)
+ {
+ reinsert_tree(dest, root->southeast);
+ root->southeast.reset();
+ }
+ if (root->southwest)
+ {
+ reinsert_tree(dest, root->southwest);
+ root->southwest.reset();
+ }
+
+ root->parent.reset();
+ insert_node(dest, root);
+}
+
+template<typename _Key, typename _Value>
+void point_quad_tree<_Key, _Value>::reinsert_tree(node_ptr& dest, node_quadrant_t quad, node_ptr& root)
+{
+ if (!root)
+ // Nothing to re-insert. Bail out.
+ return;
+
+ switch (quad)
+ {
+ case quad_northeast:
+ if (dest->northeast)
+ reinsert_tree(dest->northeast, root);
+ else
+ {
+ dest->northeast = root;
+ root->parent = dest;
+ }
+ break;
+ case quad_northwest:
+ if (dest->northwest)
+ reinsert_tree(dest->northwest, root);
+ else
+ {
+ dest->northwest = root;
+ root->parent = dest;
+ }
+ break;
+ case quad_southeast:
+ if (dest->southeast)
+ reinsert_tree(dest->southeast, root);
+ else
+ {
+ dest->southeast = root;
+ root->parent = dest;
+ }
+ break;
+ case quad_southwest:
+ if (dest->southwest)
+ reinsert_tree(dest->southwest, root);
+ else
+ {
+ dest->southwest = root;
+ root->parent = dest;
+ }
+ break;
+ default:
+ throw general_error("reinsert_tree: quadrant unspecified");
+ }
+}
+
+template<typename _Key, typename _Value>
+void point_quad_tree<_Key, _Value>::clear_all_nodes()
+{
+ ::mdds::disconnect_all_nodes(m_root);
+ m_root.reset();
+}
+
+} // namespace mdds
+
+#endif
diff --git a/include/mdds/quad_node.hpp b/include/mdds/quad_node.hpp
new file mode 100644
index 0000000..d69efb3
--- /dev/null
+++ b/include/mdds/quad_node.hpp
@@ -0,0 +1,375 @@
+/*************************************************************************
+ *
+ * Copyright (c) 2010 Kohei Yoshida
+ *
+ * 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, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ ************************************************************************/
+
+#ifndef __MDDS_QUAD_NODE_HPP__
+#define __MDDS_QUAD_NODE_HPP__
+
+#include "mdds/global.hpp"
+
+#include <cassert>
+
+#include <boost/intrusive_ptr.hpp>
+
+namespace mdds {
+
+#ifdef MDDS_DEBUG_NODE_BASE
+size_t node_instance_count = 0;
+inline size_t get_node_instance_count()
+{
+ return node_instance_count;
+}
+#endif
+
+/**
+ * NW | NE
+ * -----|-----
+ * SW | SE
+ */
+enum node_quadrant_t
+{
+ quad_northeast,
+ quad_northwest,
+ quad_southeast,
+ quad_southwest,
+ quad_unspecified
+};
+
+/**
+ * NW | N | NE
+ * -----|-----|-----
+ * W | C | E
+ * -----|-----|-----
+ * SW | S | SE
+ */
+enum search_region_space_t
+{
+ region_northwest,
+ region_north,
+ region_northeast,
+ region_east,
+ region_southeast,
+ region_south,
+ region_southwest,
+ region_west,
+ region_center
+};
+
+/**
+ * N
+ * |
+ * |
+ * W -----+----- E
+ * |
+ * |
+ * S
+ */
+enum direction_t
+{
+ dir_north,
+ dir_west,
+ dir_south,
+ dir_east
+};
+
+inline node_quadrant_t opposite(node_quadrant_t quad)
+{
+ switch (quad)
+ {
+ case quad_northeast:
+ return quad_southwest;
+ case quad_northwest:
+ return quad_southeast;
+ case quad_southeast:
+ return quad_northwest;
+ case quad_southwest:
+ return quad_northeast;
+ case quad_unspecified:
+ default:;
+ }
+ return quad_unspecified;
+}
+
+template<typename _NodePtr, typename _NodeType, typename _Key>
+struct quad_node_base
+{
+ typedef _Key key_type;
+ typedef _NodePtr node_ptr;
+ typedef _NodeType node_type;
+
+ size_t refcount;
+
+ node_ptr parent;
+ node_ptr northeast;
+ node_ptr northwest;
+ node_ptr southeast;
+ node_ptr southwest;
+
+ key_type x;
+ key_type y;
+
+ quad_node_base(key_type _x, key_type _y)
+ : refcount(0), parent(nullptr), northeast(nullptr), northwest(nullptr), southeast(nullptr), southwest(nullptr),
+ x(_x), y(_y)
+ {
+#ifdef MDDS_DEBUG_NODE_BASE
+ ++node_instance_count;
+#endif
+ }
+
+ /**
+ * When copying node, only the stored values should be copied.
+ * Connections to the parent and the neighboring nodes must not be copied.
+ */
+ quad_node_base(const quad_node_base& r)
+ : refcount(0), parent(nullptr), northeast(nullptr), northwest(nullptr), southeast(nullptr), southwest(nullptr),
+ x(r.x), y(r.y)
+ {
+#ifdef MDDS_DEBUG_NODE_BASE
+ ++node_instance_count;
+#endif
+ }
+
+ bool leaf() const
+ {
+ return !northeast && !northwest && !southeast && !southwest;
+ }
+
+ bool operator==(const quad_node_base& r) const
+ {
+ return x == r.x && y == r.y;
+ }
+
+ /**
+ * Like the copy constructor, only the stored values should be copied.
+ */
+ quad_node_base& operator=(const quad_node_base& r)
+ {
+ if (this == &r)
+ // assignment to self.
+ return *this;
+
+ x = r.x;
+ y = r.y;
+ return *this;
+ }
+
+ ~quad_node_base()
+ {
+#ifdef MDDS_DEBUG_NODE_BASE
+ --node_instance_count;
+#endif
+ static_cast<node_type*>(this)->dispose();
+ }
+
+ /**
+ * Return the quadrant of specified point in reference to this node.
+ *
+ * @return quadrant where the other node is located in reference to this
+ * node.
+ */
+ node_quadrant_t get_quadrant(key_type other_x, key_type other_y) const
+ {
+ if (other_x < x)
+ // west
+ return other_y < y ? quad_northwest : quad_southwest;
+
+ // east
+ return other_y < y ? quad_northeast : quad_southeast;
+ }
+
+ bool has_quadrant_node(node_quadrant_t quad) const
+ {
+ switch (quad)
+ {
+ case quad_northeast:
+ return northeast.get() != nullptr;
+ case quad_northwest:
+ return northwest.get() != nullptr;
+ case quad_southeast:
+ return southeast.get() != nullptr;
+ case quad_southwest:
+ return southwest.get() != nullptr;
+ default:
+ throw general_error("unknown quadrant type");
+ }
+ return false;
+ }
+
+ node_ptr get_quadrant_node(node_quadrant_t quad) const
+ {
+ node_ptr ret;
+ switch (quad)
+ {
+ case quad_northeast:
+ ret = northeast;
+ break;
+ case quad_northwest:
+ ret = northwest;
+ break;
+ case quad_southeast:
+ ret = southeast;
+ break;
+ case quad_southwest:
+ ret = southwest;
+ break;
+ default:
+ throw general_error("unknown quadrant type");
+ }
+ return ret;
+ }
+};
+
+template<typename _NodePtr, typename _NodeType, typename _Key>
+inline void intrusive_ptr_add_ref(::mdds::quad_node_base<_NodePtr, _NodeType, _Key>* p)
+{
+ ++p->refcount;
+}
+
+template<typename _NodePtr, typename _NodeType, typename _Key>
+inline void intrusive_ptr_release(::mdds::quad_node_base<_NodePtr, _NodeType, _Key>* p)
+{
+ --p->refcount;
+ if (!p->refcount)
+ delete p;
+}
+
+template<typename _NodePtr>
+void disconnect_node_from_parent(_NodePtr p)
+{
+ if (!p || !p->parent)
+ // Nothing to do.
+ return;
+
+ _NodePtr& parent = p->parent;
+ if (parent->northeast && parent->northeast == p)
+ {
+ parent->northeast.reset();
+ }
+ else if (parent->northwest && parent->northwest == p)
+ {
+ parent->northwest.reset();
+ }
+ else if (parent->southwest && parent->southwest == p)
+ {
+ parent->southwest.reset();
+ }
+ else if (parent->southeast && parent->southeast == p)
+ {
+ parent->southeast.reset();
+ }
+ else
+ throw general_error("parent node doesn't lead to the deleted node.");
+}
+
+template<typename _NodePtr>
+void disconnect_all_nodes(_NodePtr p)
+{
+ if (!p)
+ return;
+
+ if (p->northeast)
+ {
+ disconnect_all_nodes(p->northeast);
+ p->northeast.reset();
+ }
+
+ if (p->northwest)
+ {
+ disconnect_all_nodes(p->northwest);
+ p->northwest.reset();
+ }
+
+ if (p->southeast)
+ {
+ disconnect_all_nodes(p->southeast);
+ p->southeast.reset();
+ }
+
+ if (p->southwest)
+ {
+ disconnect_all_nodes(p->southwest);
+ p->southwest.reset();
+ }
+
+ p->parent.reset();
+}
+
+template<typename _NodeType, typename _Key>
+search_region_space_t get_search_region_space(_NodeType* p, _Key x1, _Key y1, _Key x2, _Key y2)
+{
+ typedef _Key key_type;
+
+ key_type x = p->x, y = p->y;
+ if (x < x1)
+ {
+ // western region
+ if (y < y1)
+ {
+ return region_northwest;
+ }
+ else if (y1 <= y && y <= y2)
+ {
+ return region_west;
+ }
+
+ assert(y2 < y);
+ return region_southwest;
+ }
+ else if (x1 <= x && x <= x2)
+ {
+ // central region
+ if (y < y1)
+ {
+ return region_north;
+ }
+ else if (y1 <= y && y <= y2)
+ {
+ return region_center;
+ }
+
+ assert(y2 < y);
+ return region_south;
+ }
+
+ // eastern region
+ assert(x2 < x);
+ if (y < y1)
+ {
+ return region_northeast;
+ }
+ else if (y1 <= y && y <= y2)
+ {
+ return region_east;
+ }
+
+ assert(y2 < y);
+ return region_southeast;
+}
+
+} // namespace mdds
+
+#endif
diff --git a/include/mdds/ref_pair.hpp b/include/mdds/ref_pair.hpp
new file mode 100644
index 0000000..ea91ef7
--- /dev/null
+++ b/include/mdds/ref_pair.hpp
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * Copyright (c) 2020 Kohei Yoshida
+ *
+ * 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, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ ************************************************************************/
+
+#ifndef INCLUDED_MDDS_REF_PAIR_HPP
+#define INCLUDED_MDDS_REF_PAIR_HPP
+
+#include <type_traits>
+
+namespace mdds { namespace detail {
+
+template<typename T1, typename T2>
+struct ref_pair
+{
+ using first_type = typename std::add_lvalue_reference<T1>::type;
+ using second_type = typename std::add_lvalue_reference<T2>::type;
+
+ first_type first;
+ second_type second;
+
+ ref_pair(first_type _first, second_type _second) : first(_first), second(_second)
+ {}
+
+ ref_pair(const ref_pair& other) = default;
+
+ bool operator==(const std::pair<typename std::decay<T1>::type, typename std::decay<T2>::type>& other) const
+ {
+ return first == other.first && second == other.second;
+ }
+
+ bool operator!=(const std::pair<typename std::decay<T1>::type, typename std::decay<T2>::type>& other) const
+ {
+ return !operator==(other);
+ }
+
+ bool operator==(const ref_pair& other) const
+ {
+ return first == other.first && second == other.second;
+ }
+
+ bool operator!=(const ref_pair& other) const
+ {
+ return !operator==(other);
+ }
+
+ ref_pair* operator->()
+ {
+ return this;
+ }
+};
+
+}} // namespace mdds::detail
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/mdds/rtree.hpp b/include/mdds/rtree.hpp
new file mode 100644
index 0000000..b21ed8f
--- /dev/null
+++ b/include/mdds/rtree.hpp
@@ -0,0 +1,830 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * Copyright (c) 2018 Kohei Yoshida
+ *
+ * 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, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ ************************************************************************/
+
+#ifndef INCLUDED_MDDS_RTREE_HPP
+#define INCLUDED_MDDS_RTREE_HPP
+
+#include <deque>
+#include <vector>
+#include <cstdlib>
+#include <string>
+#include <unordered_set>
+#include <unordered_map>
+#include <functional>
+
+namespace mdds {
+
+namespace detail { namespace rtree {
+
+struct default_rtree_traits
+{
+ /**
+ * Number of dimensions in bounding rectangles.
+ */
+ static constexpr size_t dimensions = 2;
+
+ /**
+ * Minimum number of child nodes that must be present in each directory
+ * node. Exception is the root node, which is allowed to have less than
+ * the minimum number of nodes, but only when it's a leaf directory node.
+ */
+ static constexpr size_t min_node_size = 40;
+
+ /**
+ * Maximum number of child nodes that each directory node is allowed to
+ * have. There are no exceptions to this rule.
+ */
+ static constexpr size_t max_node_size = 100;
+
+ /**
+ * Maximum depth a tree is allowed to have.
+ */
+ static constexpr size_t max_tree_depth = 100;
+
+ /**
+ * A flag to determine whether or not to perform forced reinsertion when a
+ * directory node overflows, before attempting to split the node.
+ */
+ static constexpr bool enable_forced_reinsertion = true;
+
+ /**
+ * Number of nodes to get re-inserted during forced reinsertion. This
+ * should be roughly 30% of max_node_size + 1, and should not be greater
+ * than max_node_size - min_node_size + 1.
+ */
+ static constexpr size_t reinsertion_size = 30;
+};
+
+struct integrity_check_properties
+{
+ /**
+ * When true, the integrity check will throw an exception on the first
+ * validation failure. When false, it will run through the entire tree
+ * and report all encountered validation failures then throw an exception
+ * if there is at least one failure.
+ */
+ bool throw_on_first_error = true;
+
+ /**
+ * When true, a node containing children less than the minimum node size
+ * will be treated as an error.
+ */
+ bool error_on_min_node_size = true;
+};
+
+enum class node_type
+{
+ unspecified,
+ directory_leaf,
+ directory_nonleaf,
+ value
+};
+
+enum class export_tree_type
+{
+ /**
+ * Textural representation of a tree structure. Indent levels represent
+ * depths, and each line represents a single node.
+ */
+ formatted_node_properties,
+
+ /**
+ * The extents of all directory and value nodes are exported as Wavefront
+ * .obj format. Only 2 dimensional trees are supported for now.
+ *
+ * For a 2-dimensional tree, each depth is represented by a 2D plane
+ * filled with rectangles representing the extents of either value or
+ * directory nodes at that depth level. The depth planes are then stacked
+ * vertically.
+ */
+ extent_as_obj,
+
+ /**
+ * The extents of all directory and value nodes are exported as a scalable
+ * vector graphics (SVG) format. Only 2 dimensional trees are supported.
+ */
+ extent_as_svg
+};
+
+enum class search_type
+{
+ /**
+ * Pick up all objects whose extents overlap with the specified search
+ * extent.
+ */
+ overlap,
+
+ /**
+ * Pick up all objects whose extents exactly match the specified search
+ * extent.
+ */
+ match,
+};
+
+template<typename _NodePtrT>
+struct ptr_to_string;
+
+}} // namespace detail::rtree
+
+template<typename KeyT, typename ValueT, typename Traits = detail::rtree::default_rtree_traits>
+class rtree
+{
+ using traits_type = Traits;
+
+ constexpr static size_t max_dist_size = traits_type::max_node_size - traits_type::min_node_size * 2 + 2;
+
+public:
+ using key_type = KeyT;
+ using value_type = ValueT;
+
+ struct point_type
+ {
+ key_type d[traits_type::dimensions];
+
+ point_type();
+ point_type(std::initializer_list<key_type> vs);
+
+ std::string to_string() const;
+
+ bool operator==(const point_type& other) const;
+ bool operator!=(const point_type& other) const;
+ };
+
+ struct extent_type
+ {
+ point_type start;
+ point_type end;
+
+ extent_type();
+ extent_type(const point_type& _start, const point_type& _end);
+
+ std::string to_string() const;
+
+ bool is_point() const;
+
+ bool operator==(const extent_type& other) const;
+ bool operator!=(const extent_type& other) const;
+
+ /**
+ * Determine whether or not the specified point lies within this
+ * extent.
+ *
+ * @param pt point to query with.
+ *
+ * @return true if the point lies within this extent, or false
+ * otherwise.
+ */
+ bool contains(const point_type& pt) const;
+
+ /**
+ * Determine whether or not the specified extent lies <i>entirely</i>
+ * within this extent.
+ *
+ * @param bb extent to query with.
+ *
+ * @return true if the specified extent lies entirely within this
+ * extent, or otherwise false.
+ */
+ bool contains(const extent_type& bb) const;
+
+ /**
+ * Determine whether or not the specified extent overlaps with this
+ * extent either partially or fully.
+ *
+ * @param bb extent to query with.
+ *
+ * @return true if the specified extent overlaps with this extent, or
+ * otherwise false.
+ */
+ bool intersects(const extent_type& bb) const;
+
+ /**
+ * Determine whether or not another bounding box is within this
+ * bounding box and shares a part of its boundaries.
+ */
+ bool contains_at_boundary(const extent_type& other) const;
+ };
+
+ using node_type = detail::rtree::node_type;
+ using export_tree_type = detail::rtree::export_tree_type;
+ using search_type = detail::rtree::search_type;
+ using integrity_check_properties = detail::rtree::integrity_check_properties;
+
+ struct node_properties
+ {
+ node_type type;
+ extent_type extent;
+ };
+
+private:
+ struct node;
+ struct directory_node;
+
+ /**
+ * This class is intentionally only movable and non-copyable, to prevent
+ * accidental copying of its object. To "copy" this class, you must use
+ * its clone() method explicitly.
+ */
+ struct node_store
+ {
+ node_type type;
+ extent_type extent;
+ node_store* parent;
+ node* node_ptr;
+ size_t count;
+
+ bool valid_pointer;
+
+ node_store(const node_store&) = delete;
+ node_store& operator=(const node_store&) = delete;
+
+ node_store();
+ node_store(node_store&& r);
+ node_store(node_type _type, const extent_type& _extent, node* _node_ptr);
+ ~node_store();
+
+ node_store clone() const;
+
+ static node_store create_leaf_directory_node();
+ static node_store create_nonleaf_directory_node();
+ static node_store create_value_node(const extent_type& extent, value_type&& v);
+ static node_store create_value_node(const extent_type& extent, const value_type& v);
+
+ node_store& operator=(node_store&& other);
+
+ /**
+ * Re-calculate the extent based on its current children.
+ *
+ * @return true if the extent has changed, false otherwise.
+ */
+ bool pack();
+
+ /**
+ * Re-calculate the extent of the parent nodes recursively all the way
+ * up to the root node.
+ */
+ void pack_upward();
+
+ bool is_directory() const;
+ bool is_root() const;
+ bool exceeds_capacity() const;
+
+ void swap(node_store& other);
+
+ /**
+ * Have all its child nodes update their parent pointers if the memory
+ * location of this node has been invalidated. Run the tree
+ * recursively until no more invalid pointers have been found.
+ */
+ void reset_parent_pointers_of_children();
+
+ /**
+ * Update its parent pointer and all its child nodes' parent pointers
+ * if the memory location of this node has been invalidated. Run the
+ * tree recursively until no more invalid pointers have been found.
+ */
+ void reset_parent_pointers();
+
+ directory_node* get_directory_node();
+ const directory_node* get_directory_node() const;
+
+ bool erase_child(const node_store* p);
+ };
+
+ using dir_store_type = std::deque<node_store>;
+
+ struct dir_store_segment
+ {
+ typename dir_store_type::iterator begin;
+ typename dir_store_type::iterator end;
+ size_t size;
+
+ dir_store_segment() : size(0)
+ {}
+
+ dir_store_segment(
+ typename dir_store_type::iterator _begin, typename dir_store_type::iterator _end, size_t _size)
+ : begin(std::move(_begin)), end(std::move(_end)), size(_size)
+ {}
+ };
+
+ struct distribution
+ {
+ dir_store_segment g1;
+ dir_store_segment g2;
+
+ distribution(size_t dist, dir_store_type& nodes)
+ {
+ g1.begin = nodes.begin();
+ g1.end = g1.begin;
+ g1.size = traits_type::min_node_size - 1 + dist;
+ std::advance(g1.end, g1.size);
+
+ g2.begin = g1.end;
+ g2.end = nodes.end();
+ g2.size = nodes.size() - g1.size;
+ }
+ };
+
+ struct node
+ {
+ node();
+ node(const node&) = delete;
+ ~node();
+ };
+
+ struct value_node : public node
+ {
+ value_type value;
+
+ value_node() = delete;
+ value_node(value_type&& _value);
+ value_node(const value_type& _value);
+ ~value_node();
+ };
+
+ /**
+ * Directory node can either contain all value nodes, or all directory
+ * nodes as its child nodes.
+ */
+ struct directory_node : public node
+ {
+ dir_store_type children;
+
+ directory_node(const directory_node&) = delete;
+ directory_node& operator=(const directory_node&) = delete;
+
+ directory_node();
+ ~directory_node();
+
+ bool erase(const node_store* p);
+
+ node_store* get_child_with_minimal_overlap(const extent_type& bb);
+ node_store* get_child_with_minimal_area_enlargement(const extent_type& bb);
+
+ extent_type calc_extent() const;
+ key_type calc_overlap_cost(const extent_type& bb) const;
+ bool has_leaf_directory() const;
+ };
+
+ struct orphan_node_entry
+ {
+ node_store ns;
+ size_t depth;
+ };
+
+ using orphan_node_entries_type = std::deque<orphan_node_entry>;
+
+ struct insertion_point
+ {
+ node_store* ns;
+ size_t depth;
+ };
+
+public:
+ template<typename NS>
+ class search_results_base
+ {
+ friend class rtree;
+
+ protected:
+ using node_store_type = NS;
+
+ struct entry
+ {
+ node_store_type* ns;
+ size_t depth;
+
+ entry(node_store_type* _ns, size_t _depth);
+ };
+
+ using store_type = std::vector<entry>;
+ store_type m_store;
+
+ void add_node_store(node_store_type* ns, size_t depth);
+ };
+
+ class const_iterator;
+ class iterator;
+ class search_results;
+
+ class const_search_results : public search_results_base<const node_store>
+ {
+ using base_type = search_results_base<const node_store>;
+ using base_type::m_store;
+
+ public:
+ const_iterator cbegin() const;
+ const_iterator cend() const;
+ const_iterator begin() const;
+ const_iterator end() const;
+ };
+
+ class search_results : public search_results_base<node_store>
+ {
+ using base_type = search_results_base<node_store>;
+ using base_type::m_store;
+
+ public:
+ iterator begin();
+ iterator end();
+ };
+
+ template<typename _SelfIter, typename _StoreIter, typename _ValueT>
+ class iterator_base
+ {
+ public:
+ using store_iterator_type = _StoreIter;
+ using self_iterator_type = _SelfIter;
+
+ protected:
+ store_iterator_type m_pos;
+
+ iterator_base(store_iterator_type pos);
+
+ public:
+ // iterator traits
+ using value_type = _ValueT;
+ using pointer = value_type*;
+ using reference = value_type&;
+ using difference_type = std::ptrdiff_t;
+ using iterator_category = std::bidirectional_iterator_tag;
+
+ bool operator==(const self_iterator_type& other) const;
+ bool operator!=(const self_iterator_type& other) const;
+
+ self_iterator_type& operator++();
+ self_iterator_type operator++(int);
+
+ self_iterator_type& operator--();
+ self_iterator_type operator--(int);
+
+ const extent_type& extent() const;
+ size_t depth() const;
+ };
+
+ class const_iterator
+ : public iterator_base<
+ const_iterator, typename const_search_results::store_type::const_iterator, const rtree::value_type>
+ {
+ using base_type = iterator_base<
+ const_iterator, typename const_search_results::store_type::const_iterator, const rtree::value_type>;
+
+ using store_type = typename const_search_results::store_type;
+ using base_type::m_pos;
+ using typename base_type::store_iterator_type;
+
+ friend class rtree;
+
+ const_iterator(store_iterator_type pos);
+
+ public:
+ using typename base_type::value_type;
+
+ value_type& operator*() const
+ {
+ return static_cast<const value_node*>(m_pos->ns->node_ptr)->value;
+ }
+
+ value_type* operator->() const
+ {
+ return &operator*();
+ }
+ };
+
+ class iterator : public iterator_base<iterator, typename search_results::store_type::iterator, rtree::value_type>
+ {
+ using base_type = iterator_base<iterator, typename search_results::store_type::iterator, rtree::value_type>;
+
+ using store_type = typename const_search_results::store_type;
+ using base_type::m_pos;
+ using typename base_type::store_iterator_type;
+
+ friend class rtree;
+
+ iterator(store_iterator_type pos);
+
+ public:
+ using typename base_type::value_type;
+
+ value_type& operator*()
+ {
+ return static_cast<value_node*>(m_pos->ns->node_ptr)->value;
+ }
+
+ value_type* operator->()
+ {
+ return &operator*();
+ }
+ };
+
+ /**
+ * Loader optimized for loading a large number of value objects. A
+ * resultant tree will have a higher chance of being well balanced than if
+ * the value objects were inserted individually into the tree.
+ */
+ class bulk_loader
+ {
+ dir_store_type m_store;
+
+ public:
+ bulk_loader();
+
+ void insert(const extent_type& extent, value_type&& value);
+ void insert(const extent_type& extent, const value_type& value);
+
+ void insert(const point_type& position, value_type&& value);
+ void insert(const point_type& position, const value_type& value);
+
+ rtree pack();
+
+ private:
+ void pack_level(dir_store_type& store, size_t depth);
+
+ void insert_impl(const extent_type& extent, value_type&& value);
+ void insert_impl(const extent_type& extent, const value_type& value);
+ };
+
+ rtree();
+ rtree(rtree&& other);
+ rtree(const rtree& other);
+
+private:
+ rtree(node_store&& root); // only used internally by bulk_loader.
+
+public:
+ ~rtree();
+
+ rtree& operator=(const rtree& other);
+
+ rtree& operator=(rtree&& other);
+
+ /**
+ * Insert a new value associated with a bounding box. The new value
+ * object will be moved into the container.
+ *
+ * @param extent bounding box associated with the value.
+ * @param value value being inserted.
+ */
+ void insert(const extent_type& extent, value_type&& value);
+
+ /**
+ * Insert a new value associated with a bounding box. A copy of the new
+ * value object will be placed into the container.
+ *
+ * @param extent bounding box associated with the value.
+ * @param value value being inserted.
+ */
+ void insert(const extent_type& extent, const value_type& value);
+
+ /**
+ * Insert a new value associated with a point. The new value object will
+ * be moved into the container.
+ *
+ * @param position point associated with the value.
+ * @param value value being inserted.
+ */
+ void insert(const point_type& position, value_type&& value);
+
+ /**
+ * Insert a new value associated with a point. A copy of the new value
+ * object will be placed into the container.
+ *
+ * @param position point associated with the value.
+ * @param value value being inserted.
+ */
+ void insert(const point_type& position, const value_type& value);
+
+ /**
+ * Search the tree and collect all value objects whose extents either
+ * contain the specified point, or exactly match the specified point.
+ *
+ * @param pt reference point to use for the search.
+ * @param st search type that determines the satisfying condition of the
+ * search with respect to the reference point.
+ *
+ * @return collection of all value objects that satisfy the specified
+ * search condition. This collection is immutable.
+ */
+ const_search_results search(const point_type& pt, search_type st) const;
+
+ /**
+ * Search the tree and collect all value objects whose extents either
+ * contain the specified point, or exactly match the specified point.
+ *
+ * @param pt reference point to use for the search.
+ * @param st search type that determines the satisfying condition of the
+ * search with respect to the reference point.
+ *
+ * @return collection of all value objects that satisfy the specified
+ * search condition. This collection is mutable.
+ */
+ search_results search(const point_type& pt, search_type st);
+
+ /**
+ * Search the tree and collect all value objects whose extents either
+ * overlaps with the specified extent, or exactly match the specified
+ * extent.
+ *
+ * @param extent reference extent to use for the search.
+ * @param st search type that determines the satisfying condition of the
+ * search with respect to the reference extent.
+ *
+ * @return collection of all value objects that satisfy the specified
+ * search condition. This collection is immutable.
+ */
+ const_search_results search(const extent_type& extent, search_type st) const;
+
+ /**
+ * Search the tree and collect all value objects whose extents either
+ * overlaps with the specified extent, or exactly match the specified
+ * extent.
+ *
+ * @param extent reference extent to use for the search.
+ * @param st search type that determines the satisfying condition of the
+ * search with respect to the reference extent.
+ *
+ * @return collection of all value objects that satisfy the specified
+ * search condition. This collection is mutable.
+ */
+ search_results search(const extent_type& extent, search_type st);
+
+ /**
+ * Erase the value object referenced by the iterator passed to this
+ * method.
+ *
+ * <p>The iterator object will become invalid if the call results in an
+ * erasure of a value.</p>
+ *
+ * @param pos iterator that refernces the value object to erase.
+ */
+ void erase(const const_iterator& pos);
+
+ /**
+ * Erase the value object referenced by the iterator passed to this
+ * method.
+ *
+ * <p>The iterator object will become invalid if the call results in an
+ * erasure of a value.</p>
+ *
+ * @param pos iterator that refernces the value object to erase.
+ */
+ void erase(const iterator& pos);
+
+ /**
+ * Get the minimum bounding extent of the root node of the tree. The
+ * extent returned from this method is the minimum extent that contains
+ * the extents of all objects stored in the tree.
+ *
+ * @return immutable reference to the extent of the root node of the tree.
+ */
+ const extent_type& extent() const;
+
+ /**
+ * Check whether or not the tree stores any objects.
+ *
+ * @return true if the tree is empty, otherwise false.
+ */
+ bool empty() const;
+
+ /**
+ * Return the number of value nodes currently stored in the tree.
+ *
+ * @return number of value nodes currently in the tree.
+ */
+ size_t size() const;
+
+ /**
+ * Swap the content of the tree with another instance.
+ *
+ * @param other another instance to swap the content with.
+ */
+ void swap(rtree& other);
+
+ /**
+ * Empty the entire container.
+ */
+ void clear();
+
+ /**
+ * Walk down the entire tree depth first.
+ *
+ * @param func function or function object that gets called at each node in
+ * the tree.
+ */
+ template<typename FuncT>
+ void walk(FuncT func) const;
+
+ /**
+ * Check the integrity of the entire tree structure.
+ *
+ * @param props specify how the check is to be performed.
+ *
+ * @exception integrity_error if the integrity check fails.
+ */
+ void check_integrity(const integrity_check_properties& props) const;
+
+ /**
+ * Export the structure of a tree in textural format.
+ *
+ * @param mode specify the format in which to represent the structure of a
+ * tree.
+ *
+ * @return string representation of the tree structure.
+ */
+ std::string export_tree(export_tree_type mode) const;
+
+private:
+ void insert_impl(const point_type& start, const point_type& end, value_type&& value);
+ void insert_impl(const point_type& start, const point_type& end, const value_type& value);
+
+ void erase_impl(const node_store* ns, size_t depth);
+
+ /**
+ * Build and return a callable function object that you can call in order
+ * to convert node's memory location to a normalized unique string still
+ * representative of that node.
+ */
+ detail::rtree::ptr_to_string<const node_store*> build_ptr_to_string_map() const;
+
+ std::string export_tree_formatted() const;
+ std::string export_tree_extent_as_obj() const;
+ std::string export_tree_extent_as_svg() const;
+
+ void insert(node_store&& ns, std::unordered_set<size_t>* reinserted_depths);
+ void insert_dir(node_store&& ns, size_t max_depth);
+
+ /**
+ * Split an overfilled node. The node to split is expected to have
+ * exactly M+1 child nodes where M is the maximum number of child nodes a
+ * single node is allowed to have.
+ *
+ * @param ns node to split.
+ */
+ void split_node(node_store* ns);
+
+ void perform_forced_reinsertion(node_store* ns, std::unordered_set<size_t>& reinserted_depth);
+
+ /**
+ * Determine the best dimension to split by, and sort the child nodes by
+ * that dimension.
+ *
+ * @param children child nodes to sort.
+ */
+ void sort_dir_store_by_split_dimension(dir_store_type& children);
+
+ void sort_dir_store_by_dimension(size_t dim, dir_store_type& children);
+
+ size_t pick_optimal_distribution(dir_store_type& children) const;
+
+ insertion_point find_leaf_directory_node_for_insertion(const extent_type& bb);
+ node_store* find_nonleaf_directory_node_for_insertion(const extent_type& bb, size_t max_depth);
+
+ template<typename FuncT>
+ void descend_with_func(FuncT func) const;
+
+ using search_condition_type = std::function<bool(const node_store&)>;
+
+ template<typename _ResT>
+ void search_descend(
+ size_t depth, const search_condition_type& dir_cond, const search_condition_type& value_cond,
+ typename _ResT::node_store_type& ns, _ResT& results) const;
+
+ void shrink_tree_upward(node_store* ns, const extent_type& bb_affected);
+
+private:
+ node_store m_root;
+};
+
+} // namespace mdds
+
+#include "rtree_def.inl"
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/mdds/rtree_def.inl b/include/mdds/rtree_def.inl
new file mode 100644
index 0000000..d7c4fa7
--- /dev/null
+++ b/include/mdds/rtree_def.inl
@@ -0,0 +1,2625 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * Copyright (c) 2018 Kohei Yoshida
+ *
+ * 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, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ ************************************************************************/
+
+#include "mdds/global.hpp"
+
+#include <stdexcept>
+#include <iostream>
+#include <sstream>
+#include <memory>
+#include <cassert>
+#include <algorithm>
+#include <cmath>
+
+namespace mdds {
+
+namespace detail { namespace rtree {
+
+template<typename T>
+using remove_cvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
+
+inline const char* to_string(node_type nt)
+{
+ switch (nt)
+ {
+ case node_type::unspecified:
+ return "unspecified";
+ case node_type::directory_leaf:
+ return "directory-leaf";
+ case node_type::directory_nonleaf:
+ return "directory-nonleaf";
+ case node_type::value:
+ return "value";
+ }
+
+ return "???";
+}
+
+inline size_t calc_optimal_segment_size_for_pack(size_t init_size, size_t min_size, size_t max_size, size_t value_count)
+{
+ // Keep increasing the size until the remainder becomes at least the min size.
+ size_t final_size = init_size;
+ for (; final_size < max_size; ++final_size)
+ {
+ size_t mod = value_count % final_size;
+ if (!mod || mod >= min_size)
+ return final_size;
+ }
+
+ // If increasing the size doesn't work, then decrease it.
+ final_size = init_size;
+ for (--final_size; min_size < final_size; --final_size)
+ {
+ size_t mod = value_count % final_size;
+ if (!mod || mod >= min_size)
+ return final_size;
+ }
+
+ // Nothing has worked. Use the original value.
+ return init_size;
+}
+
+template<typename Extent>
+auto calc_linear_intersection(size_t dim, const Extent& bb1, const Extent& bb2)
+ -> remove_cvref_t<decltype(bb1.start.d[0])>
+{
+ using key_type = remove_cvref_t<decltype(bb1.start.d[0])>;
+
+ key_type start1 = bb1.start.d[dim], end1 = bb1.end.d[dim];
+ key_type start2 = bb2.start.d[dim], end2 = bb2.end.d[dim];
+
+ // Ensure that start1 < start2.
+
+ if (start1 > start2)
+ {
+ std::swap(start1, start2);
+ std::swap(end1, end2);
+ }
+
+ assert(start1 <= start2);
+
+ if (end1 < start2)
+ {
+ // 1 : |------|
+ // 2 : |-------|
+
+ // These two are not intersected at all. Bail out.
+ return key_type();
+ }
+
+ if (end1 < end2)
+ {
+ // 1 : |---------|
+ // 2 : |----------|
+
+ return end1 - start2;
+ }
+
+ // 1 : |--------------|
+ // 2 : |-----|
+
+ return end2 - start2;
+}
+
+template<typename Extent>
+bool intersects(const Extent& bb1, const Extent& bb2)
+{
+ constexpr size_t dim_size = sizeof(bb1.start.d) / sizeof(bb1.start.d[0]);
+ using key_type = remove_cvref_t<decltype(bb1.start.d[0])>;
+
+ for (size_t dim = 0; dim < dim_size; ++dim)
+ {
+ key_type start1 = bb1.start.d[dim], end1 = bb1.end.d[dim];
+ key_type start2 = bb2.start.d[dim], end2 = bb2.end.d[dim];
+
+ // Ensure that start1 <= start2.
+
+ if (start1 > start2)
+ {
+ std::swap(start1, start2);
+ std::swap(end1, end2);
+ }
+
+ assert(start1 <= start2);
+
+ if (end1 < start2)
+ {
+ // 1 : |------|
+ // 2 : |-------|
+
+ // These two are not intersected at all. Bail out.
+ return false;
+ }
+ }
+
+ return true;
+}
+
+template<typename Extent>
+auto calc_intersection(const Extent& bb1, const Extent& bb2) -> remove_cvref_t<decltype(bb1.start.d[0])>
+{
+ constexpr size_t dim_size = sizeof(bb1.start.d) / sizeof(bb1.start.d[0]);
+ static_assert(dim_size > 0, "Dimension cannot be zero.");
+ using key_type = remove_cvref_t<decltype(bb1.start.d[0])>;
+
+ key_type total_volume = calc_linear_intersection<Extent>(0, bb1, bb2);
+ if (!total_volume)
+ return key_type();
+
+ for (size_t dim = 1; dim < dim_size; ++dim)
+ {
+ key_type segment_len = calc_linear_intersection<Extent>(dim, bb1, bb2);
+ if (!segment_len)
+ return key_type();
+
+ total_volume *= segment_len;
+ }
+
+ return total_volume;
+}
+
+template<typename Extent>
+bool enlarge_extent_to_fit(Extent& parent, const Extent& child)
+{
+ constexpr size_t dim_size = sizeof(parent.start.d) / sizeof(parent.start.d[0]);
+ static_assert(dim_size > 0, "Dimension cannot be zero.");
+ bool enlarged = false;
+
+ for (size_t dim = 0; dim < dim_size; ++dim)
+ {
+ if (child.start.d[dim] < parent.start.d[dim])
+ {
+ parent.start.d[dim] = child.start.d[dim];
+ enlarged = true;
+ }
+
+ if (parent.end.d[dim] < child.end.d[dim])
+ {
+ parent.end.d[dim] = child.end.d[dim];
+ enlarged = true;
+ }
+ }
+
+ return enlarged;
+}
+
+template<typename Extent>
+auto calc_area(const Extent& bb) -> remove_cvref_t<decltype(bb.start.d[0])>
+{
+ constexpr size_t dim_size = sizeof(bb.start.d) / sizeof(bb.start.d[0]);
+ static_assert(dim_size > 0, "Dimension cannot be zero.");
+ using key_type = remove_cvref_t<decltype(bb.start.d[0])>;
+
+ key_type area = bb.end.d[0] - bb.start.d[0];
+ for (size_t dim = 1; dim < dim_size; ++dim)
+ area *= bb.end.d[dim] - bb.start.d[dim];
+
+ return area;
+}
+
+template<typename Pt>
+auto calc_square_distance(const Pt& pt1, const Pt& pt2) -> remove_cvref_t<decltype(pt1.d[0])>
+{
+ constexpr size_t dim_size = sizeof(pt1.d) / sizeof(pt1.d[0]);
+ static_assert(dim_size > 0, "Dimension cannot be zero.");
+ using key_type = remove_cvref_t<decltype(pt1.d[0])>;
+
+ key_type dist = key_type();
+ for (size_t dim = 0; dim < dim_size; ++dim)
+ {
+ key_type v1 = pt1.d[dim], v2 = pt2.d[dim];
+
+ if (v1 > v2)
+ std::swap(v1, v2); // ensure that v1 <= v2.
+
+ assert(v1 <= v2);
+
+ key_type diff = v2 - v1;
+ dist += diff * diff;
+ }
+
+ return dist;
+}
+
+/**
+ * The margin here is defined as the sum of the lengths of the edges of a
+ * bounding box, per the original paper on R*-tree. It's half-margin
+ * because it only adds one of the two edges in each dimension.
+ */
+template<typename Extent>
+auto calc_half_margin(const Extent& bb) -> remove_cvref_t<decltype(bb.start.d[0])>
+{
+ constexpr size_t dim_size = sizeof(bb.start.d) / sizeof(bb.start.d[0]);
+ static_assert(dim_size > 0, "Dimension cannot be zero.");
+ using key_type = remove_cvref_t<decltype(bb.start.d[0])>;
+
+ key_type margin = bb.end.d[0] - bb.start.d[0];
+ for (size_t dim = 1; dim < dim_size; ++dim)
+ margin += bb.end.d[dim] - bb.start.d[dim];
+
+ return margin;
+}
+
+/**
+ * Area enlargement is calculated by calculating the area of the enlarged
+ * box subtracted by the area of the original box prior to the enlargement.
+ *
+ * @param bb_host bounding box of the area receiving the new object.
+ * @param bb_guest bounding of the new object being inserted.
+ *
+ * @return quantity of the area enlargement.
+ */
+template<typename Extent>
+auto calc_area_enlargement(const Extent& bb_host, const Extent& bb_guest)
+ -> remove_cvref_t<decltype(bb_host.start.d[0])>
+{
+ constexpr size_t dim_size = sizeof(bb_host.start.d) / sizeof(bb_host.start.d[0]);
+ static_assert(dim_size > 0, "Dimension cannot be zero.");
+ using key_type = remove_cvref_t<decltype(bb_host.start.d[0])>;
+ using extent = Extent;
+
+ // Calculate the original area.
+ key_type original_area = calc_area<Extent>(bb_host);
+
+ // Enlarge the box for the new object if needed.
+ extent bb_host_enlarged = bb_host; // make a copy.
+ bool enlarged = enlarge_extent_to_fit<Extent>(bb_host_enlarged, bb_guest);
+ if (!enlarged)
+ // Area enlargement did not take place.
+ return key_type();
+
+ key_type enlarged_area = calc_area<Extent>(bb_host_enlarged);
+
+ return enlarged_area - original_area;
+}
+
+template<typename Iter>
+auto calc_extent(Iter it, Iter it_end) -> decltype(it->extent)
+{
+ auto bb = it->extent;
+ for (++it; it != it_end; ++it)
+ enlarge_extent_to_fit(bb, it->extent);
+
+ return bb;
+}
+
+template<typename Extent>
+auto get_center_point(const Extent& extent) -> decltype(extent.start)
+{
+ constexpr size_t dim_size = sizeof(extent.start.d) / sizeof(extent.start.d[0]);
+ static_assert(dim_size > 0, "Dimension cannot be zero.");
+ using point_type = decltype(extent.start);
+ using key_type = decltype(extent.start.d[0]);
+
+ point_type ret;
+
+ static const key_type two = 2;
+
+ for (size_t dim = 0; dim < dim_size; ++dim)
+ ret.d[dim] = (extent.end.d[dim] + extent.start.d[dim]) / two;
+
+ return ret;
+}
+
+template<typename KeyT>
+struct min_value_pos
+{
+ KeyT value = KeyT();
+ size_t pos = 0;
+ size_t count = 0;
+
+ void assign(KeyT new_value, size_t new_pos)
+ {
+ if (count)
+ {
+ // Assign only if it's less than the current value.
+ if (new_value < value)
+ {
+ value = new_value;
+ pos = new_pos;
+ }
+ }
+ else
+ {
+ // The very first value. Just take it.
+ value = new_value;
+ pos = new_pos;
+ }
+
+ ++count;
+ }
+};
+
+template<typename KeyT>
+struct reinsertion_bucket
+{
+ using key_type = KeyT;
+
+ key_type distance;
+ size_t src_pos;
+};
+
+template<typename _NodePtrT>
+struct ptr_to_string
+{
+ using node_ptr_type = _NodePtrT;
+ using node_ptr_map_type = std::unordered_map<node_ptr_type, std::string>;
+
+ node_ptr_map_type node_ptr_map;
+
+ std::string operator()(node_ptr_type np) const
+ {
+ auto it = node_ptr_map.find(np);
+ return (it == node_ptr_map.end()) ? "(*, *)" : it->second;
+ }
+
+ ptr_to_string()
+ {
+ static_assert(std::is_pointer<node_ptr_type>::value, "Node pointer type must be a real pointer type.");
+ }
+
+ ptr_to_string(const ptr_to_string&) = delete;
+ ptr_to_string(ptr_to_string&& other) : node_ptr_map(std::move(other.node_ptr_map))
+ {}
+};
+
+}} // namespace detail::rtree
+
+template<typename KeyT, typename ValueT, typename Traits>
+rtree<KeyT, ValueT, Traits>::point_type::point_type()
+{
+ // Initialize the point values with the key type's default value.
+ key_type* p = d;
+ key_type* p_end = p + traits_type::dimensions;
+
+ for (; p != p_end; ++p)
+ *p = key_type{};
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+rtree<KeyT, ValueT, Traits>::point_type::point_type(std::initializer_list<key_type> vs)
+{
+ // Initialize the point values with the key type's default value.
+ key_type* dst = d;
+ key_type* dst_end = dst + traits_type::dimensions;
+
+ for (const key_type& v : vs)
+ {
+ if (dst == dst_end)
+ throw std::range_error("number of elements exceeds the dimension size.");
+
+ *dst = v;
+ ++dst;
+ }
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+std::string rtree<KeyT, ValueT, Traits>::point_type::to_string() const
+{
+ std::ostringstream os;
+
+ os << "(";
+ for (size_t i = 0; i < traits_type::dimensions; ++i)
+ {
+ if (i > 0)
+ os << ", ";
+ os << d[i];
+ }
+ os << ")";
+
+ return os.str();
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+bool rtree<KeyT, ValueT, Traits>::point_type::operator==(const point_type& other) const
+{
+ const key_type* left = d;
+ const key_type* right = other.d;
+ const key_type* left_end = left + traits_type::dimensions;
+
+ for (; left != left_end; ++left, ++right)
+ {
+ if (*left != *right)
+ return false;
+ }
+
+ return true;
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+bool rtree<KeyT, ValueT, Traits>::point_type::operator!=(const point_type& other) const
+{
+ return !operator==(other);
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+rtree<KeyT, ValueT, Traits>::extent_type::extent_type()
+{}
+
+template<typename KeyT, typename ValueT, typename Traits>
+rtree<KeyT, ValueT, Traits>::extent_type::extent_type(const point_type& _start, const point_type& _end)
+ : start(_start), end(_end)
+{}
+
+template<typename KeyT, typename ValueT, typename Traits>
+std::string rtree<KeyT, ValueT, Traits>::extent_type::to_string() const
+{
+ std::ostringstream os;
+ os << start.to_string();
+
+ if (!is_point())
+ os << " - " << end.to_string();
+
+ return os.str();
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+bool rtree<KeyT, ValueT, Traits>::extent_type::is_point() const
+{
+ return start == end;
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+bool rtree<KeyT, ValueT, Traits>::extent_type::operator==(const extent_type& other) const
+{
+ return start == other.start && end == other.end;
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+bool rtree<KeyT, ValueT, Traits>::extent_type::operator!=(const extent_type& other) const
+{
+ return !operator==(other);
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+bool rtree<KeyT, ValueT, Traits>::extent_type::contains(const point_type& pt) const
+{
+ for (size_t dim = 0; dim < traits_type::dimensions; ++dim)
+ {
+ if (pt.d[dim] < start.d[dim] || end.d[dim] < pt.d[dim])
+ return false;
+ }
+
+ return true;
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+bool rtree<KeyT, ValueT, Traits>::extent_type::contains(const extent_type& bb) const
+{
+ for (size_t dim = 0; dim < traits_type::dimensions; ++dim)
+ {
+ if (bb.start.d[dim] < start.d[dim] || end.d[dim] < bb.end.d[dim])
+ return false;
+ }
+
+ return true;
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+bool rtree<KeyT, ValueT, Traits>::extent_type::intersects(const extent_type& bb) const
+{
+ return detail::rtree::intersects(bb, *this);
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+bool rtree<KeyT, ValueT, Traits>::extent_type::contains_at_boundary(const extent_type& bb) const
+{
+ for (size_t dim = 0; dim < traits_type::dimensions; ++dim)
+ {
+ if (start.d[dim] == bb.start.d[dim] || bb.end.d[dim] == end.d[dim])
+ return true;
+ }
+
+ return false;
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+rtree<KeyT, ValueT, Traits>::node_store::node_store()
+ : type(node_type::unspecified), parent(nullptr), node_ptr(nullptr), count(0), valid_pointer(true)
+{}
+
+template<typename KeyT, typename ValueT, typename Traits>
+rtree<KeyT, ValueT, Traits>::node_store::node_store(node_store&& r)
+ : type(r.type), extent(r.extent), parent(r.parent), node_ptr(r.node_ptr), count(r.count),
+ valid_pointer(r.valid_pointer)
+{
+ r.type = node_type::unspecified;
+ r.extent = extent_type();
+ r.parent = nullptr;
+ r.node_ptr = nullptr;
+ r.count = 0;
+ r.valid_pointer = true;
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+rtree<KeyT, ValueT, Traits>::node_store::node_store(node_type _type, const extent_type& _extent, node* _node_ptr)
+ : type(_type), extent(_extent), parent(nullptr), node_ptr(_node_ptr), count(0), valid_pointer(true)
+{}
+
+template<typename KeyT, typename ValueT, typename Traits>
+rtree<KeyT, ValueT, Traits>::node_store::~node_store()
+{
+ if (node_ptr)
+ {
+ switch (type)
+ {
+ case node_type::directory_leaf:
+ case node_type::directory_nonleaf:
+ delete static_cast<directory_node*>(node_ptr);
+ break;
+ case node_type::value:
+ delete static_cast<value_node*>(node_ptr);
+ break;
+ case node_type::unspecified:
+ default:
+ assert(!"node::~node: unknown node type!");
+ }
+ }
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+typename rtree<KeyT, ValueT, Traits>::node_store rtree<KeyT, ValueT, Traits>::node_store::clone() const
+{
+ auto func_copy_dir = [this](node_store& cloned, const directory_node* src) {
+ directory_node* dir = cloned.get_directory_node();
+ assert(dir);
+ for (const node_store& ns : src->children)
+ dir->children.push_back(ns.clone());
+
+ cloned.count = count;
+ cloned.extent = extent;
+ };
+
+ switch (type)
+ {
+ case node_type::directory_leaf:
+ {
+ const directory_node* src = static_cast<const directory_node*>(node_ptr);
+ node_store cloned = create_leaf_directory_node();
+ func_copy_dir(cloned, src);
+ return cloned;
+ }
+ case node_type::directory_nonleaf:
+ {
+ const directory_node* src = static_cast<const directory_node*>(node_ptr);
+ node_store cloned = create_nonleaf_directory_node();
+ func_copy_dir(cloned, src);
+ return cloned;
+ }
+ case node_type::value:
+ {
+ const value_node* vn = static_cast<const value_node*>(node_ptr);
+ return create_value_node(extent, vn->value);
+ }
+ case node_type::unspecified:
+ default:
+ assert(!"node::~node: unknown node type!");
+ }
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+typename rtree<KeyT, ValueT, Traits>::node_store rtree<KeyT, ValueT, Traits>::node_store::create_leaf_directory_node()
+{
+ node_store ret(node_type::directory_leaf, extent_type(), new directory_node);
+ ret.valid_pointer = false;
+ return ret;
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+typename rtree<KeyT, ValueT, Traits>::node_store rtree<
+ KeyT, ValueT, Traits>::node_store::create_nonleaf_directory_node()
+{
+ node_store ret(node_type::directory_nonleaf, extent_type(), new directory_node);
+ ret.valid_pointer = false;
+ return ret;
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+typename rtree<KeyT, ValueT, Traits>::node_store rtree<KeyT, ValueT, Traits>::node_store::create_value_node(
+ const extent_type& extent, value_type&& v)
+{
+ node_store ret(node_type::value, extent, new value_node(std::move(v)));
+ return ret;
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+typename rtree<KeyT, ValueT, Traits>::node_store rtree<KeyT, ValueT, Traits>::node_store::create_value_node(
+ const extent_type& extent, const value_type& v)
+{
+ node_store ret(node_type::value, extent, new value_node(v));
+ return ret;
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+typename rtree<KeyT, ValueT, Traits>::node_store& rtree<KeyT, ValueT, Traits>::node_store::operator=(node_store&& other)
+{
+ node_store tmp(std::move(other));
+ swap(tmp);
+ return *this;
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+bool rtree<KeyT, ValueT, Traits>::node_store::pack()
+{
+ const directory_node* dir = get_directory_node();
+ if (!dir)
+ return false;
+
+ const dir_store_type& children = dir->children;
+ if (children.empty())
+ {
+ // This node has no children. Reset the bounding box to empty.
+ extent_type new_box;
+ bool changed = new_box != extent;
+ extent = new_box;
+ return changed;
+ }
+
+ extent_type new_box = dir->calc_extent();
+ bool changed = new_box != extent;
+ extent = new_box; // update the bounding box.
+ return changed;
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+void rtree<KeyT, ValueT, Traits>::node_store::pack_upward()
+{
+ bool propagate = true;
+ for (node_store* p = parent; propagate && p; p = p->parent)
+ propagate = p->pack();
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+bool rtree<KeyT, ValueT, Traits>::node_store::is_directory() const
+{
+ switch (type)
+ {
+ case node_type::directory_leaf:
+ case node_type::directory_nonleaf:
+ return true;
+ default:;
+ }
+
+ return false;
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+bool rtree<KeyT, ValueT, Traits>::node_store::is_root() const
+{
+ return parent == nullptr;
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+bool rtree<KeyT, ValueT, Traits>::node_store::exceeds_capacity() const
+{
+ if (type != node_type::directory_leaf)
+ return false;
+
+ return count > traits_type::max_node_size;
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+void rtree<KeyT, ValueT, Traits>::node_store::swap(node_store& other)
+{
+ std::swap(type, other.type);
+ std::swap(extent, other.extent);
+ std::swap(parent, other.parent);
+ std::swap(node_ptr, other.node_ptr);
+ std::swap(count, other.count);
+ std::swap(valid_pointer, other.valid_pointer);
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+void rtree<KeyT, ValueT, Traits>::node_store::reset_parent_pointers_of_children()
+{
+ if (valid_pointer)
+ return;
+
+ directory_node* dir = get_directory_node();
+ if (!dir)
+ return;
+
+ for (node_store& ns : dir->children)
+ {
+ ns.parent = this;
+ ns.reset_parent_pointers_of_children();
+ }
+
+ valid_pointer = true;
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+void rtree<KeyT, ValueT, Traits>::node_store::reset_parent_pointers()
+{
+ valid_pointer = false;
+ reset_parent_pointers_of_children();
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+typename rtree<KeyT, ValueT, Traits>::directory_node* rtree<KeyT, ValueT, Traits>::node_store::get_directory_node()
+{
+ if (!is_directory())
+ return nullptr;
+
+ return static_cast<directory_node*>(node_ptr);
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+const typename rtree<KeyT, ValueT, Traits>::directory_node* rtree<
+ KeyT, ValueT, Traits>::node_store::get_directory_node() const
+{
+ if (!is_directory())
+ return nullptr;
+
+ return static_cast<const directory_node*>(node_ptr);
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+bool rtree<KeyT, ValueT, Traits>::node_store::erase_child(const node_store* p)
+{
+ if (!is_directory())
+ return false;
+
+ directory_node* dir = static_cast<directory_node*>(node_ptr);
+ bool erased = dir->erase(p);
+ if (erased)
+ --count;
+
+ assert(count == dir->children.size());
+ return erased;
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+rtree<KeyT, ValueT, Traits>::node::node()
+{}
+
+template<typename KeyT, typename ValueT, typename Traits>
+rtree<KeyT, ValueT, Traits>::node::~node()
+{}
+
+template<typename KeyT, typename ValueT, typename Traits>
+rtree<KeyT, ValueT, Traits>::value_node::value_node(value_type&& _value) : value(std::move(_value))
+{}
+
+template<typename KeyT, typename ValueT, typename Traits>
+rtree<KeyT, ValueT, Traits>::value_node::value_node(const value_type& _value) : value(_value)
+{}
+
+template<typename KeyT, typename ValueT, typename Traits>
+rtree<KeyT, ValueT, Traits>::value_node::~value_node()
+{}
+
+template<typename KeyT, typename ValueT, typename Traits>
+rtree<KeyT, ValueT, Traits>::directory_node::directory_node()
+{}
+
+template<typename KeyT, typename ValueT, typename Traits>
+rtree<KeyT, ValueT, Traits>::directory_node::~directory_node()
+{}
+
+template<typename KeyT, typename ValueT, typename Traits>
+bool rtree<KeyT, ValueT, Traits>::directory_node::erase(const node_store* ns)
+{
+ auto it = std::find_if(
+ children.begin(), children.end(), [ns](const node_store& this_ns) -> bool { return &this_ns == ns; });
+
+ if (it == children.end())
+ return false;
+
+ // NB: std::deque::erase invalidates all elements when the erased element
+ // is somwhere in the middle. But if the erased element is either the
+ // first or the last element, only the erased element becomes invalidated.
+
+ std::size_t pos = std::distance(children.begin(), it);
+ bool all_valid = pos == 0 || pos == children.size() - 1;
+
+ it = children.erase(it);
+
+ if (!all_valid)
+ {
+ for (node_store& this_ns : children)
+ this_ns.valid_pointer = false;
+ }
+
+ return true;
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+typename rtree<KeyT, ValueT, Traits>::node_store* rtree<
+ KeyT, ValueT, Traits>::directory_node::get_child_with_minimal_overlap(const extent_type& bb)
+{
+ key_type min_overlap = key_type();
+ key_type min_area_enlargement = key_type();
+ key_type min_area = key_type();
+
+ node_store* dst = nullptr;
+
+ for (node_store& ns : children)
+ {
+ directory_node* dir = static_cast<directory_node*>(ns.node_ptr);
+ key_type overlap = dir->calc_overlap_cost(bb);
+ key_type area_enlargement = detail::rtree::calc_area_enlargement<extent_type>(ns.extent, bb);
+ key_type area = detail::rtree::calc_area<extent_type>(ns.extent);
+
+ bool pick_this = false;
+
+ if (!dst)
+ pick_this = true;
+ else if (overlap < min_overlap)
+ // Pick the entry with the smaller overlap cost increase.
+ pick_this = true;
+ else if (area_enlargement < min_area_enlargement)
+ // Pick the entry with the smaller area enlargment.
+ pick_this = true;
+ else if (area < min_area)
+ // Resolve ties by picking the one with on the smaller area
+ // rectangle.
+ pick_this = true;
+
+ if (pick_this)
+ {
+ min_overlap = overlap;
+ min_area_enlargement = area_enlargement;
+ min_area = area;
+ dst = &ns;
+ }
+ }
+
+ return dst;
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+typename rtree<KeyT, ValueT, Traits>::node_store* rtree<
+ KeyT, ValueT, Traits>::directory_node::get_child_with_minimal_area_enlargement(const extent_type& bb)
+{
+ // Compare the costs of area enlargements.
+ key_type min_cost = key_type();
+ key_type min_area = key_type();
+
+ node_store* dst = nullptr;
+
+ for (node_store& ns : children)
+ {
+ key_type cost = detail::rtree::calc_area_enlargement(ns.extent, bb);
+ key_type area = detail::rtree::calc_area(ns.extent);
+
+ bool pick_this = false;
+
+ if (!dst)
+ pick_this = true;
+ else if (cost < min_cost)
+ // Pick the entry with the smaller area enlargment.
+ pick_this = true;
+ else if (area < min_area)
+ // Resolve ties by picking the one with on the smaller area
+ // rectangle.
+ pick_this = true;
+
+ if (pick_this)
+ {
+ min_cost = cost;
+ min_area = area;
+ dst = &ns;
+ }
+ }
+
+ return dst;
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+typename rtree<KeyT, ValueT, Traits>::extent_type rtree<KeyT, ValueT, Traits>::directory_node::calc_extent() const
+{
+ auto it = children.cbegin(), ite = children.cend();
+
+ extent_type box;
+ if (it != ite)
+ box = detail::rtree::calc_extent(it, ite);
+
+ return box;
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+typename rtree<KeyT, ValueT, Traits>::key_type rtree<KeyT, ValueT, Traits>::directory_node::calc_overlap_cost(
+ const extent_type& bb) const
+{
+ key_type overlap_cost = key_type();
+
+ for (const node_store& ns : children)
+ overlap_cost += detail::rtree::calc_intersection<extent_type>(ns.extent, bb);
+
+ return overlap_cost;
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+bool rtree<KeyT, ValueT, Traits>::directory_node::has_leaf_directory() const
+{
+ for (const auto& ns : children)
+ {
+ if (ns.type == node_type::directory_leaf)
+ return true;
+ }
+
+ return false;
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+template<typename NS>
+void rtree<KeyT, ValueT, Traits>::search_results_base<NS>::add_node_store(node_store_type* ns, size_t depth)
+{
+ m_store.emplace_back(ns, depth);
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+template<typename NS>
+rtree<KeyT, ValueT, Traits>::search_results_base<NS>::entry::entry(node_store_type* _ns, size_t _depth)
+ : ns(_ns), depth(_depth)
+{}
+
+template<typename KeyT, typename ValueT, typename Traits>
+template<typename _SelfIter, typename _StoreIter, typename _ValueT>
+rtree<KeyT, ValueT, Traits>::iterator_base<_SelfIter, _StoreIter, _ValueT>::iterator_base(store_iterator_type pos)
+ : m_pos(std::move(pos))
+{}
+
+template<typename KeyT, typename ValueT, typename Traits>
+template<typename _SelfIter, typename _StoreIter, typename _ValueT>
+bool rtree<KeyT, ValueT, Traits>::iterator_base<_SelfIter, _StoreIter, _ValueT>::operator==(
+ const self_iterator_type& other) const
+{
+ return m_pos == other.m_pos;
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+template<typename _SelfIter, typename _StoreIter, typename _ValueT>
+bool rtree<KeyT, ValueT, Traits>::iterator_base<_SelfIter, _StoreIter, _ValueT>::operator!=(
+ const self_iterator_type& other) const
+{
+ return !operator==(other);
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+template<typename _SelfIter, typename _StoreIter, typename _ValueT>
+typename rtree<KeyT, ValueT, Traits>::template iterator_base<_SelfIter, _StoreIter, _ValueT>::self_iterator_type& rtree<
+ KeyT, ValueT, Traits>::iterator_base<_SelfIter, _StoreIter, _ValueT>::operator++()
+{
+ ++m_pos;
+ return static_cast<self_iterator_type&>(*this);
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+template<typename _SelfIter, typename _StoreIter, typename _ValueT>
+typename rtree<KeyT, ValueT, Traits>::template iterator_base<_SelfIter, _StoreIter, _ValueT>::self_iterator_type rtree<
+ KeyT, ValueT, Traits>::iterator_base<_SelfIter, _StoreIter, _ValueT>::operator++(int)
+{
+ self_iterator_type ret(m_pos);
+ ++m_pos;
+ return ret;
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+template<typename _SelfIter, typename _StoreIter, typename _ValueT>
+typename rtree<KeyT, ValueT, Traits>::template iterator_base<_SelfIter, _StoreIter, _ValueT>::self_iterator_type& rtree<
+ KeyT, ValueT, Traits>::iterator_base<_SelfIter, _StoreIter, _ValueT>::operator--()
+{
+ --m_pos;
+ return static_cast<self_iterator_type&>(*this);
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+template<typename _SelfIter, typename _StoreIter, typename _ValueT>
+typename rtree<KeyT, ValueT, Traits>::template iterator_base<_SelfIter, _StoreIter, _ValueT>::self_iterator_type rtree<
+ KeyT, ValueT, Traits>::iterator_base<_SelfIter, _StoreIter, _ValueT>::operator--(int)
+{
+ self_iterator_type ret(m_pos);
+ --m_pos;
+ return ret;
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+template<typename _SelfIter, typename _StoreIter, typename _ValueT>
+const typename rtree<KeyT, ValueT, Traits>::extent_type& rtree<KeyT, ValueT, Traits>::iterator_base<
+ _SelfIter, _StoreIter, _ValueT>::extent() const
+{
+ return m_pos->ns->extent;
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+template<typename _SelfIter, typename _StoreIter, typename _ValueT>
+size_t rtree<KeyT, ValueT, Traits>::iterator_base<_SelfIter, _StoreIter, _ValueT>::depth() const
+{
+ return m_pos->depth;
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+typename rtree<KeyT, ValueT, Traits>::const_iterator rtree<KeyT, ValueT, Traits>::const_search_results::cbegin() const
+{
+ return const_iterator(m_store.cbegin());
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+typename rtree<KeyT, ValueT, Traits>::const_iterator rtree<KeyT, ValueT, Traits>::const_search_results::cend() const
+{
+ return const_iterator(m_store.cend());
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+typename rtree<KeyT, ValueT, Traits>::const_iterator rtree<KeyT, ValueT, Traits>::const_search_results::begin() const
+{
+ return const_iterator(m_store.cbegin());
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+typename rtree<KeyT, ValueT, Traits>::const_iterator rtree<KeyT, ValueT, Traits>::const_search_results::end() const
+{
+ return const_iterator(m_store.cend());
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+typename rtree<KeyT, ValueT, Traits>::iterator rtree<KeyT, ValueT, Traits>::search_results::begin()
+{
+ return iterator(m_store.begin());
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+typename rtree<KeyT, ValueT, Traits>::iterator rtree<KeyT, ValueT, Traits>::search_results::end()
+{
+ return iterator(m_store.end());
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+rtree<KeyT, ValueT, Traits>::const_iterator::const_iterator(store_iterator_type pos) : base_type(std::move(pos))
+{}
+
+template<typename KeyT, typename ValueT, typename Traits>
+rtree<KeyT, ValueT, Traits>::iterator::iterator(store_iterator_type pos) : base_type(std::move(pos))
+{}
+
+template<typename KeyT, typename ValueT, typename Traits>
+rtree<KeyT, ValueT, Traits>::bulk_loader::bulk_loader()
+{}
+
+template<typename KeyT, typename ValueT, typename Traits>
+void rtree<KeyT, ValueT, Traits>::bulk_loader::insert(const extent_type& extent, value_type&& value)
+{
+ insert_impl(extent, std::move(value));
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+void rtree<KeyT, ValueT, Traits>::bulk_loader::insert(const extent_type& extent, const value_type& value)
+{
+ insert_impl(extent, value);
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+void rtree<KeyT, ValueT, Traits>::bulk_loader::insert(const point_type& position, value_type&& value)
+{
+ insert_impl(extent_type({position, position}), std::move(value));
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+void rtree<KeyT, ValueT, Traits>::bulk_loader::insert(const point_type& position, const value_type& value)
+{
+ insert_impl(extent_type({position, position}), value);
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+void rtree<KeyT, ValueT, Traits>::bulk_loader::insert_impl(const extent_type& extent, value_type&& value)
+{
+ node_store ns_value = node_store::create_value_node(extent, std::move(value));
+ m_store.emplace_back(std::move(ns_value));
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+void rtree<KeyT, ValueT, Traits>::bulk_loader::insert_impl(const extent_type& extent, const value_type& value)
+{
+ node_store ns_value = node_store::create_value_node(extent, value);
+ m_store.emplace_back(std::move(ns_value));
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+rtree<KeyT, ValueT, Traits> rtree<KeyT, ValueT, Traits>::bulk_loader::pack()
+{
+ size_t depth = 0;
+ for (; m_store.size() > traits_type::max_node_size; ++depth)
+ pack_level(m_store, depth);
+
+ // By this point, the number of directory nodes should have been reduced
+ // below the max node size. Create a root directory and store them there.
+
+ assert(m_store.size() <= traits_type::max_node_size);
+
+ node_store root = node_store::create_leaf_directory_node();
+ if (depth > 0)
+ root.type = node_type::directory_nonleaf;
+
+ directory_node* dir = root.get_directory_node();
+ assert(dir);
+ dir->children.swap(m_store);
+
+ root.count = dir->children.size();
+ root.pack();
+
+ rtree tree(std::move(root));
+ return tree;
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+void rtree<KeyT, ValueT, Traits>::bulk_loader::pack_level(dir_store_type& store, size_t depth)
+{
+ assert(!store.empty());
+
+ float n_total_node = std::ceil(store.size() / float(traits_type::max_node_size));
+ float n_splits_per_dim = std::ceil(std::pow(n_total_node, 1.0f / float(traits_type::dimensions)));
+
+ // The first dimension will start with one segment.
+ std::vector<dir_store_segment> segments;
+ segments.emplace_back(store.begin(), store.end(), store.size());
+
+ for (size_t dim = 0; dim < traits_type::dimensions; ++dim)
+ {
+ if (segments[0].size <= traits_type::max_node_size)
+ break;
+
+ std::vector<dir_store_segment> next_segments;
+
+ for (dir_store_segment& seg : segments)
+ {
+ assert(seg.size == size_t(std::distance(seg.begin, seg.end)));
+
+ if (seg.size <= traits_type::max_node_size)
+ {
+ next_segments.push_back(seg);
+ continue;
+ }
+
+ // Sort by the current dimension key.
+ std::sort(seg.begin, seg.end, [dim](const node_store& left, const node_store& right) -> bool {
+ // Compare the middle points.
+ float left_key = (left.extent.end.d[dim] + left.extent.start.d[dim]) / 2.0f;
+ float right_key = (right.extent.end.d[dim] + right.extent.start.d[dim]) / 2.0f;
+
+ return left_key < right_key;
+ });
+
+ // Size of each segment in this dimension splits.
+ size_t segment_size = detail::rtree::calc_optimal_segment_size_for_pack(
+ std::ceil(seg.size / n_splits_per_dim), traits_type::min_node_size, traits_type::max_node_size,
+ seg.size);
+
+ size_t n_cur_segment = 0;
+ auto begin = seg.begin;
+ for (auto it = begin; it != seg.end; ++it, ++n_cur_segment)
+ {
+ if (n_cur_segment == segment_size)
+ {
+ // Push a new segment.
+ next_segments.emplace_back(begin, it, n_cur_segment);
+ begin = it;
+ n_cur_segment = 0;
+ }
+ }
+
+ if (begin != seg.end)
+ {
+ size_t n = std::distance(begin, seg.end);
+ next_segments.emplace_back(begin, seg.end, n);
+ }
+ }
+
+#ifdef MDDS_RTREE_DEBUG
+ size_t test_total = 0;
+ for (const auto& seg : next_segments)
+ test_total += seg.size;
+
+ if (test_total != store.size())
+ throw std::logic_error("The total combined segment sizes must equal the size of the inserted values!");
+#endif
+ segments.swap(next_segments);
+ }
+
+#ifdef MDDS_RTREE_DEBUG
+ // Check the final segment.
+ size_t test_total = 0;
+ for (const auto& seg : segments)
+ test_total += seg.size;
+
+ if (test_total != store.size())
+ throw std::logic_error("The total combined segment sizes must equal the size of the inserted values!");
+#endif
+
+ assert(!segments.empty());
+
+ // Create a set of directory nodes from the current segments.
+ dir_store_type next_store;
+ for (dir_store_segment& seg : segments)
+ {
+ node_store ns = node_store::create_leaf_directory_node();
+ if (depth > 0)
+ ns.type = node_type::directory_nonleaf;
+
+ directory_node* dir = ns.get_directory_node();
+ assert(dir); // this better not be null since we know it's a directory node.
+
+ for (auto it = seg.begin; it != seg.end; ++it)
+ dir->children.push_back(std::move(*it));
+
+ ns.count = dir->children.size();
+ ns.pack();
+ next_store.push_back(std::move(ns));
+ }
+
+ store.swap(next_store);
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+rtree<KeyT, ValueT, Traits>::rtree() : m_root(node_store::create_leaf_directory_node())
+{
+ static_assert(
+ traits_type::min_node_size <= traits_type::max_node_size / 2,
+ "Minimum node size must be less than half of the maximum node size.");
+
+ static_assert(
+ traits_type::reinsertion_size <= (traits_type::max_node_size - traits_type::min_node_size + 1),
+ "Reinsertion size is too large.");
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+rtree<KeyT, ValueT, Traits>::rtree(rtree&& other) : m_root(std::move(other.m_root))
+{
+ // The root node must be a valid directory at all times.
+ other.m_root = node_store::create_leaf_directory_node();
+
+ // Since the moved root has its memory location changed, we need to update
+ // the parent pointers in its child nodes.
+ m_root.reset_parent_pointers();
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+rtree<KeyT, ValueT, Traits>::rtree(const rtree& other) : m_root(other.m_root.clone())
+{
+ m_root.reset_parent_pointers();
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+rtree<KeyT, ValueT, Traits>::rtree(node_store&& root) : m_root(std::move(root))
+{
+ m_root.reset_parent_pointers();
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+rtree<KeyT, ValueT, Traits>::~rtree()
+{}
+
+template<typename KeyT, typename ValueT, typename Traits>
+rtree<KeyT, ValueT, Traits>& rtree<KeyT, ValueT, Traits>::operator=(const rtree& other)
+{
+ rtree tmp(other);
+ tmp.swap(*this);
+ return *this;
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+rtree<KeyT, ValueT, Traits>& rtree<KeyT, ValueT, Traits>::operator=(rtree&& other)
+{
+ rtree tmp(std::move(other));
+ tmp.swap(*this);
+ return *this;
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+void rtree<KeyT, ValueT, Traits>::insert(const extent_type& extent, value_type&& value)
+{
+ insert_impl(extent.start, extent.end, std::move(value));
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+void rtree<KeyT, ValueT, Traits>::insert(const extent_type& extent, const value_type& value)
+{
+ insert_impl(extent.start, extent.end, value);
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+void rtree<KeyT, ValueT, Traits>::insert(const point_type& position, value_type&& value)
+{
+ insert_impl(position, position, std::move(value));
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+void rtree<KeyT, ValueT, Traits>::insert(const point_type& position, const value_type& value)
+{
+ insert_impl(position, position, value);
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+void rtree<KeyT, ValueT, Traits>::insert_impl(const point_type& start, const point_type& end, value_type&& value)
+{
+ extent_type bb(start, end);
+ node_store new_ns = node_store::create_value_node(bb, std::move(value));
+
+ std::unordered_set<size_t> reinserted_depths;
+ insert(std::move(new_ns), &reinserted_depths);
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+void rtree<KeyT, ValueT, Traits>::insert_impl(const point_type& start, const point_type& end, const value_type& value)
+{
+ extent_type bb(start, end);
+ node_store new_ns = node_store::create_value_node(bb, value);
+
+ std::unordered_set<size_t> reinserted_depths;
+ insert(std::move(new_ns), &reinserted_depths);
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+void rtree<KeyT, ValueT, Traits>::insert(node_store&& ns, std::unordered_set<size_t>* reinserted_depths)
+{
+ extent_type ns_box = ns.extent;
+
+ insertion_point insert_pt = find_leaf_directory_node_for_insertion(ns_box);
+ node_store* dir_ns = insert_pt.ns;
+ size_t depth = insert_pt.depth;
+
+ assert(dir_ns);
+ assert(dir_ns->type == node_type::directory_leaf);
+ directory_node* dir = static_cast<directory_node*>(dir_ns->node_ptr);
+
+ // Insert the new value to this node.
+ ns.parent = insert_pt.ns;
+ dir->children.push_back(std::move(ns));
+ ++dir_ns->count;
+
+ if (dir_ns->exceeds_capacity())
+ {
+ if (traits_type::enable_forced_reinsertion)
+ {
+ if (reinserted_depths && !reinserted_depths->count(depth))
+ {
+ // We perform forced re-insertion exactly once per depth level.
+ reinserted_depths->insert(depth);
+ perform_forced_reinsertion(dir_ns, *reinserted_depths);
+ }
+ else
+ split_node(dir_ns);
+ }
+ else
+ split_node(dir_ns);
+
+ return;
+ }
+
+ if (dir_ns->count == 1)
+ dir_ns->extent = ns_box;
+ else
+ detail::rtree::enlarge_extent_to_fit<extent_type>(dir_ns->extent, ns_box);
+
+ extent_type bb = dir_ns->extent; // grab the parent bounding box.
+
+ // Propagate the bounding box update up the tree all the way to the root.
+ for (dir_ns = dir_ns->parent; dir_ns; dir_ns = dir_ns->parent)
+ {
+ assert(dir_ns->count > 0);
+ detail::rtree::enlarge_extent_to_fit<extent_type>(dir_ns->extent, bb);
+ }
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+void rtree<KeyT, ValueT, Traits>::insert_dir(node_store&& ns, size_t max_depth)
+{
+ assert(ns.is_directory());
+ extent_type ns_box = ns.extent;
+ node_store* dir_ns = find_nonleaf_directory_node_for_insertion(ns_box, max_depth);
+ assert(dir_ns);
+ assert(dir_ns->type == node_type::directory_nonleaf);
+ directory_node* dir = static_cast<directory_node*>(dir_ns->node_ptr);
+
+ // Insert the new directory to this node.
+ ns.parent = dir_ns;
+ ns.valid_pointer = false;
+ dir->children.push_back(std::move(ns));
+ ++dir_ns->count;
+ dir->children.back().reset_parent_pointers_of_children();
+
+ if (dir_ns->exceeds_capacity())
+ {
+ split_node(dir_ns);
+ return;
+ }
+
+ if (dir_ns->count == 1)
+ dir_ns->extent = ns_box;
+ else
+ detail::rtree::enlarge_extent_to_fit<extent_type>(dir_ns->extent, ns_box);
+
+ extent_type bb = dir_ns->extent; // grab the parent bounding box.
+
+ // Propagate the bounding box update up the tree all the way to the root.
+ for (dir_ns = dir_ns->parent; dir_ns; dir_ns = dir_ns->parent)
+ {
+ assert(dir_ns->count > 0);
+ detail::rtree::enlarge_extent_to_fit<extent_type>(dir_ns->extent, bb);
+ }
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+typename rtree<KeyT, ValueT, Traits>::const_search_results rtree<KeyT, ValueT, Traits>::search(
+ const point_type& pt, search_type st) const
+{
+ search_condition_type dir_cond, value_cond;
+
+ switch (st)
+ {
+ case search_type::overlap:
+ {
+ dir_cond = [&pt](const node_store& ns) -> bool { return ns.extent.contains(pt); };
+
+ value_cond = dir_cond;
+ break;
+ }
+ case search_type::match:
+ {
+ dir_cond = [&pt](const node_store& ns) -> bool { return ns.extent.contains(pt); };
+
+ value_cond = [&pt](const node_store& ns) -> bool { return ns.extent.start == pt && ns.extent.end == pt; };
+
+ break;
+ }
+ default:
+ throw std::runtime_error("Unhandled search type.");
+ }
+
+ const_search_results ret;
+ search_descend(0, dir_cond, value_cond, m_root, ret);
+ return ret;
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+typename rtree<KeyT, ValueT, Traits>::search_results rtree<KeyT, ValueT, Traits>::search(
+ const point_type& pt, search_type st)
+{
+ search_condition_type dir_cond, value_cond;
+
+ switch (st)
+ {
+ case search_type::overlap:
+ {
+ dir_cond = [&pt](const node_store& ns) -> bool { return ns.extent.contains(pt); };
+
+ value_cond = dir_cond;
+ break;
+ }
+ case search_type::match:
+ {
+ dir_cond = [&pt](const node_store& ns) -> bool { return ns.extent.contains(pt); };
+
+ value_cond = [&pt](const node_store& ns) -> bool { return ns.extent.start == pt && ns.extent.end == pt; };
+
+ break;
+ }
+ default:
+ throw std::runtime_error("Unhandled search type.");
+ }
+
+ search_results ret;
+ search_descend(0, dir_cond, value_cond, m_root, ret);
+ return ret;
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+typename rtree<KeyT, ValueT, Traits>::const_search_results rtree<KeyT, ValueT, Traits>::search(
+ const extent_type& extent, search_type st) const
+{
+ search_condition_type dir_cond, value_cond;
+
+ switch (st)
+ {
+ case search_type::overlap:
+ {
+ dir_cond = [&extent](const node_store& ns) -> bool { return ns.extent.intersects(extent); };
+
+ value_cond = dir_cond;
+ break;
+ }
+ case search_type::match:
+ {
+ dir_cond = [&extent](const node_store& ns) -> bool { return ns.extent.contains(extent); };
+
+ value_cond = [&extent](const node_store& ns) -> bool { return ns.extent == extent; };
+
+ break;
+ }
+ default:
+ throw std::runtime_error("Unhandled search type.");
+ }
+
+ const_search_results ret;
+ search_descend(0, dir_cond, value_cond, m_root, ret);
+ return ret;
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+typename rtree<KeyT, ValueT, Traits>::search_results rtree<KeyT, ValueT, Traits>::search(
+ const extent_type& extent, search_type st)
+{
+ search_condition_type dir_cond, value_cond;
+
+ switch (st)
+ {
+ case search_type::overlap:
+ {
+ dir_cond = [&extent](const node_store& ns) -> bool { return ns.extent.intersects(extent); };
+
+ value_cond = dir_cond;
+ break;
+ }
+ case search_type::match:
+ {
+ dir_cond = [&extent](const node_store& ns) -> bool { return ns.extent.contains(extent); };
+
+ value_cond = [&extent](const node_store& ns) -> bool { return ns.extent == extent; };
+
+ break;
+ }
+ default:
+ throw std::runtime_error("Unhandled search type.");
+ }
+
+ search_results ret;
+ search_descend(0, dir_cond, value_cond, m_root, ret);
+ return ret;
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+void rtree<KeyT, ValueT, Traits>::erase(const const_iterator& pos)
+{
+ const node_store* ns = pos.m_pos->ns;
+ size_t depth = pos.m_pos->depth;
+ erase_impl(ns, depth);
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+void rtree<KeyT, ValueT, Traits>::erase(const iterator& pos)
+{
+ const node_store* ns = pos.m_pos->ns;
+ size_t depth = pos.m_pos->depth;
+ erase_impl(ns, depth);
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+void rtree<KeyT, ValueT, Traits>::erase_impl(const node_store* ns, size_t depth)
+{
+ assert(ns->type == node_type::value);
+ assert(ns->parent);
+
+ extent_type bb_erased = ns->extent;
+
+ // Move up to the parent and find its stored location.
+ node_store* dir_ns = ns->parent;
+ --depth;
+ assert(dir_ns->type == node_type::directory_leaf);
+ bool erased = dir_ns->erase_child(ns);
+ assert(erased);
+ (void)erased; // to avoid getting a warning on "variable set but not used".
+
+ if (dir_ns->is_root())
+ {
+ shrink_tree_upward(dir_ns, bb_erased);
+ return;
+ }
+
+ if (dir_ns->count >= traits_type::min_node_size)
+ {
+ // The parent directory still contains enough nodes. No need to dissolve it.
+ shrink_tree_upward(dir_ns, bb_erased);
+ return;
+ }
+
+ // Dissolve the node the erased value node belongs to, and reinsert
+ // all its siblings.
+
+ assert(!dir_ns->is_root());
+ assert(dir_ns->count < traits_type::min_node_size);
+
+ dir_store_type orphan_value_nodes;
+ directory_node* dir = static_cast<directory_node*>(dir_ns->node_ptr);
+ dir->children.swap(orphan_value_nodes); // moves all the rest of the value node entries to the orphan store.
+
+ // Move up one level, and remove this directory node from its parent directory node.
+ node_store* child_ns = dir_ns;
+ dir_ns = dir_ns->parent;
+ --depth;
+ erased = dir_ns->erase_child(child_ns);
+ assert(erased);
+
+ dir_ns->reset_parent_pointers();
+ dir_ns->pack();
+
+ orphan_node_entries_type orphan_dir_nodes;
+
+ while (!dir_ns->is_root() && dir_ns->count < traits_type::min_node_size)
+ {
+ // This directory node is now underfilled. Move all its children out
+ // for re-insertion and dissolve this node.
+ dir = static_cast<directory_node*>(dir_ns->node_ptr);
+
+ while (!dir->children.empty())
+ {
+ orphan_dir_nodes.emplace_back();
+ orphan_dir_nodes.back().ns.swap(dir->children.back());
+ orphan_dir_nodes.back().depth = depth + 1; // depth of the children.
+ dir->children.pop_back();
+ }
+
+ // Find and remove this node from its parent store.
+ node_store* dir_ns_child = dir_ns;
+ dir_ns = dir_ns->parent;
+ --depth;
+ erased = dir_ns->erase_child(dir_ns_child);
+ assert(erased);
+ dir_ns->reset_parent_pointers_of_children();
+ dir_ns->pack();
+ }
+
+ while (!orphan_dir_nodes.empty())
+ {
+ orphan_node_entry& entry = orphan_dir_nodes.back();
+ insert_dir(std::move(entry.ns), entry.depth);
+ orphan_dir_nodes.pop_back();
+ }
+
+ while (!orphan_value_nodes.empty())
+ {
+ insert(std::move(orphan_value_nodes.back()), nullptr);
+ orphan_value_nodes.pop_back();
+ }
+
+ if (m_root.count == 1)
+ {
+ // If the root node only has one child, make that child the new root.
+ // Be careful not to leak memory here...
+
+ dir = static_cast<directory_node*>(m_root.node_ptr);
+ assert(dir->children.size() == 1);
+ node_store new_root(std::move(dir->children.back()));
+ dir->children.clear();
+
+ new_root.parent = nullptr;
+ m_root.swap(new_root);
+ m_root.reset_parent_pointers();
+ }
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+const typename rtree<KeyT, ValueT, Traits>::extent_type& rtree<KeyT, ValueT, Traits>::extent() const
+{
+ return m_root.extent;
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+bool rtree<KeyT, ValueT, Traits>::empty() const
+{
+ return !m_root.count;
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+size_t rtree<KeyT, ValueT, Traits>::size() const
+{
+ size_t n = 0;
+ descend_with_func([&n](const node_properties& np) {
+ if (np.type == node_type::value)
+ ++n;
+ });
+
+ return n;
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+void rtree<KeyT, ValueT, Traits>::swap(rtree& other)
+{
+ m_root.swap(other.m_root);
+ m_root.reset_parent_pointers();
+ other.m_root.reset_parent_pointers();
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+void rtree<KeyT, ValueT, Traits>::clear()
+{
+ node_store new_root = node_store::create_leaf_directory_node();
+ m_root.swap(new_root);
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+template<typename FuncT>
+void rtree<KeyT, ValueT, Traits>::walk(FuncT func) const
+{
+ descend_with_func(std::move(func));
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+void rtree<KeyT, ValueT, Traits>::check_integrity(const integrity_check_properties& props) const
+{
+ auto func_ptr_to_string = build_ptr_to_string_map();
+
+ switch (m_root.type)
+ {
+ case node_type::directory_leaf:
+ case node_type::directory_nonleaf:
+ // Good.
+ break;
+ default:
+ throw integrity_error("The root node must be a directory node.");
+ }
+
+ if (m_root.parent)
+ throw integrity_error("The root node should not have a non-null parent.");
+
+ std::vector<const node_store*> ns_stack;
+
+ std::function<bool(const node_store*, int)> func_descend = [&ns_stack, &func_descend, &func_ptr_to_string,
+ &props](const node_store* ns, int level) -> bool {
+ bool valid = true;
+
+ std::string indent;
+ for (int i = 0; i < level; ++i)
+ indent += " ";
+
+ const node_store* parent = nullptr;
+ extent_type parent_bb;
+ if (!ns_stack.empty())
+ {
+ parent = ns_stack.back();
+ parent_bb = parent->extent;
+ }
+
+ if (!props.throw_on_first_error)
+ {
+ std::cout << indent << "node: " << func_ptr_to_string(ns) << "; parent: " << func_ptr_to_string(ns->parent)
+ << "; type: " << to_string(ns->type) << "; extent: " << ns->extent.to_string() << std::endl;
+ }
+
+ if (parent)
+ {
+ if (ns->parent != parent)
+ {
+ std::ostringstream os;
+ os << "The parent node pointer does not point to the real parent. (expected: " << parent
+ << "; stored in node: " << ns->parent << ")";
+ if (props.throw_on_first_error)
+ throw integrity_error(os.str());
+ std::cout << indent << "* " << os.str() << std::endl;
+ valid = false;
+ }
+
+ if (!parent_bb.contains(ns->extent))
+ {
+ std::ostringstream os;
+ os << "The extent of the child " << ns->extent.to_string() << " is not within the extent of the parent "
+ << parent_bb.to_string() << ".";
+ if (props.throw_on_first_error)
+ throw integrity_error(os.str());
+ std::cout << indent << "* " << os.str() << std::endl;
+ valid = false;
+ }
+
+ switch (ns->type)
+ {
+ case node_type::directory_leaf:
+ {
+ if (parent->type != node_type::directory_nonleaf)
+ {
+ std::ostringstream os;
+ os << "Parent of a leaf directory node must be non-leaf.";
+ if (props.throw_on_first_error)
+ throw integrity_error(os.str());
+ std::cout << indent << "* " << os.str() << std::endl;
+ valid = false;
+ }
+ break;
+ }
+ case node_type::directory_nonleaf:
+ {
+ if (parent->type != node_type::directory_nonleaf)
+ {
+ std::ostringstream os;
+ os << "Parent of a non-leaf directory node must also be non-leaf.";
+ if (props.throw_on_first_error)
+ throw integrity_error(os.str());
+ std::cout << indent << "* " << os.str() << std::endl;
+ valid = false;
+ }
+ break;
+ }
+ case node_type::value:
+ {
+ if (parent->type != node_type::directory_leaf)
+ {
+ std::ostringstream os;
+ os << "Parent of a value node must be a leaf directory node.";
+ if (props.throw_on_first_error)
+ throw integrity_error(os.str());
+ std::cout << indent << "* " << os.str() << std::endl;
+ valid = false;
+ }
+ break;
+ }
+ default:
+ throw integrity_error("Unexpected node type!");
+ }
+ }
+
+ ns_stack.push_back(ns);
+
+ switch (ns->type)
+ {
+ case node_type::directory_leaf:
+ case node_type::directory_nonleaf:
+ {
+ const directory_node* dir = static_cast<const directory_node*>(ns->node_ptr);
+
+ if (ns->count != dir->children.size())
+ {
+ std::ostringstream os;
+ os << "Incorrect count of child nodes detected. (expected: " << dir->children.size()
+ << "; actual: " << ns->count << ")";
+
+ if (props.throw_on_first_error)
+ throw integrity_error(os.str());
+
+ std::cout << indent << "* " << os.str() << std::endl;
+ valid = false;
+ }
+
+ bool node_underfill_allowed = false;
+ if (ns->is_root() && ns->type == node_type::directory_leaf)
+ // If the root directory is a leaf, it's allowed to be underfilled.
+ node_underfill_allowed = true;
+
+ if (!node_underfill_allowed && ns->count < traits_type::min_node_size)
+ {
+ std::ostringstream os;
+ os << "The number of child nodes (" << ns->count << ") is less than the minimum allowed number of "
+ << traits_type::min_node_size;
+
+ if (props.error_on_min_node_size && props.throw_on_first_error)
+ throw integrity_error(os.str());
+
+ std::cout << indent << "* " << os.str() << std::endl;
+
+ if (props.error_on_min_node_size)
+ valid = false;
+ }
+
+ if (traits_type::max_node_size < ns->count)
+ {
+ std::ostringstream os;
+ os << "The number of child nodes (" << ns->count << ") exceeds the maximum allowed number of "
+ << traits_type::max_node_size;
+
+ if (props.throw_on_first_error)
+ throw integrity_error(os.str());
+
+ std::cout << indent << "* " << os.str() << std::endl;
+ valid = false;
+ }
+
+ // Check to make sure the bounding box of the current node is
+ // tightly packed.
+ extent_type bb_expected = dir->calc_extent();
+
+ if (bb_expected != ns->extent)
+ {
+ std::ostringstream os;
+ os << "The extent of the node " << ns->extent.to_string() << " does not equal truly tight extent "
+ << bb_expected.to_string();
+
+ if (props.throw_on_first_error)
+ throw integrity_error(os.str());
+
+ std::cout << indent << "* " << os.str() << std::endl;
+ valid = false;
+ }
+
+ for (const node_store& ns_child : dir->children)
+ {
+ bool valid_subtree = func_descend(&ns_child, level + 1);
+ if (!valid_subtree)
+ valid = false;
+ }
+
+ break;
+ }
+ case node_type::value:
+ // Do nothing.
+ break;
+ default:
+ throw integrity_error("Unexpected node type!");
+ }
+
+ ns_stack.pop_back();
+
+ return valid;
+ };
+
+ bool valid = func_descend(&m_root, 0);
+
+ if (!valid)
+ throw integrity_error("Tree contains one or more errors.");
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+std::string rtree<KeyT, ValueT, Traits>::export_tree(export_tree_type mode) const
+{
+ switch (mode)
+ {
+ case export_tree_type::formatted_node_properties:
+ return export_tree_formatted();
+ case export_tree_type::extent_as_obj:
+ return export_tree_extent_as_obj();
+ case export_tree_type::extent_as_svg:
+ return export_tree_extent_as_svg();
+ default:
+ throw std::runtime_error("unhandled export tree type.");
+ }
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+detail::rtree::ptr_to_string<const typename rtree<KeyT, ValueT, Traits>::node_store*> rtree<
+ KeyT, ValueT, Traits>::build_ptr_to_string_map() const
+{
+ detail::rtree::ptr_to_string<const node_store*> func;
+
+ std::function<void(const node_store*, int, int)> func_build_node_ptr =
+ [&func_build_node_ptr, &func](const node_store* ns, int level, int pos) {
+ std::ostringstream os;
+ os << "(" << level << ", " << pos << ")";
+ func.node_ptr_map.insert(std::make_pair(ns, os.str()));
+
+ switch (ns->type)
+ {
+ case node_type::directory_leaf:
+ case node_type::directory_nonleaf:
+ {
+ const directory_node* dir = static_cast<const directory_node*>(ns->node_ptr);
+
+ int child_pos = 0;
+ for (const node_store& ns_child : dir->children)
+ func_build_node_ptr(&ns_child, level + 1, child_pos++);
+
+ break;
+ }
+ case node_type::value:
+ // Do nothing.
+ break;
+ default:
+ throw integrity_error("Unexpected node type!");
+ }
+ };
+
+ func_build_node_ptr(&m_root, 0, 0);
+
+ return func;
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+std::string rtree<KeyT, ValueT, Traits>::export_tree_formatted() const
+{
+ auto func_ptr_to_string = build_ptr_to_string_map();
+
+ std::ostringstream os;
+
+ std::function<void(const node_store*, int)> func_descend = [&func_descend, &os,
+ &func_ptr_to_string](const node_store* ns, int level) {
+ std::string indent;
+ for (int i = 0; i < level; ++i)
+ indent += " ";
+
+ os << indent << "node: " << func_ptr_to_string(ns) << "; parent: " << func_ptr_to_string(ns->parent)
+ << "; type: " << to_string(ns->type) << "; extent: " << ns->extent.to_string() << std::endl;
+
+ switch (ns->type)
+ {
+ case node_type::directory_leaf:
+ case node_type::directory_nonleaf:
+ {
+ const directory_node* dir = static_cast<const directory_node*>(ns->node_ptr);
+
+ for (const node_store& ns_child : dir->children)
+ func_descend(&ns_child, level + 1);
+
+ break;
+ }
+ case node_type::value:
+ // Do nothing.
+ break;
+ default:
+ throw integrity_error("Unexpected node type!");
+ }
+ };
+
+ func_descend(&m_root, 0);
+
+ return os.str();
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+std::string rtree<KeyT, ValueT, Traits>::export_tree_extent_as_obj() const
+{
+ if (traits_type::dimensions != 2u)
+ throw size_error("Only 2-dimensional trees are supported.");
+
+ float unit_height =
+ ((m_root.extent.end.d[0] - m_root.extent.start.d[0]) + (m_root.extent.end.d[1] - m_root.extent.start.d[1])) /
+ 5.0f;
+
+ // Calculate the width to use for point data.
+ float pt_width = std::min<float>(
+ m_root.extent.end.d[0] - m_root.extent.start.d[0], m_root.extent.end.d[1] - m_root.extent.start.d[1]);
+ pt_width /= 400.0f;
+ pt_width = std::min<float>(pt_width, 1.0f);
+
+ std::ostringstream os;
+ size_t counter = 0;
+
+ std::function<void(const node_store*, int)> func_descend = [&](const node_store* ns, int level) {
+ size_t offset = counter * 4;
+ point_type s = ns->extent.start;
+ point_type e = ns->extent.end;
+ if (s == e)
+ {
+ s.d[0] -= pt_width;
+ s.d[1] -= pt_width;
+ e.d[0] += pt_width;
+ e.d[1] += pt_width;
+ }
+
+ os << "o extent " << counter << " (level " << level << ") " << s.to_string() << " - " << e.to_string()
+ << std::endl;
+ os << "v " << s.d[0] << ' ' << (level * unit_height) << ' ' << s.d[1] << std::endl;
+ os << "v " << s.d[0] << ' ' << (level * unit_height) << ' ' << e.d[1] << std::endl;
+ os << "v " << e.d[0] << ' ' << (level * unit_height) << ' ' << e.d[1] << std::endl;
+ os << "v " << e.d[0] << ' ' << (level * unit_height) << ' ' << s.d[1] << std::endl;
+ os << "f " << (offset + 1) << ' ' << (offset + 2) << ' ' << (offset + 3) << ' ' << (offset + 4) << std::endl;
+
+ ++counter;
+
+ switch (ns->type)
+ {
+ case node_type::directory_leaf:
+ case node_type::directory_nonleaf:
+ {
+ const directory_node* dir = static_cast<const directory_node*>(ns->node_ptr);
+
+ for (const node_store& ns_child : dir->children)
+ func_descend(&ns_child, level + 1);
+
+ break;
+ }
+ case node_type::value:
+ // Do nothing.
+ break;
+ default:
+ throw integrity_error("Unexpected node type!");
+ }
+ };
+
+ func_descend(&m_root, 0);
+
+ return os.str();
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+std::string rtree<KeyT, ValueT, Traits>::export_tree_extent_as_svg() const
+{
+ if (traits_type::dimensions != 2u)
+ throw size_error("Only 2-dimensional trees are supported.");
+
+ constexpr float min_avg_root_length = 800.0f;
+ constexpr float max_avg_root_length = 1000.0f;
+ float root_w = m_root.extent.end.d[0] - m_root.extent.start.d[0];
+ float root_h = m_root.extent.end.d[1] - m_root.extent.start.d[1];
+
+ // Adjust zooming for optimal output size. We don't want it to be too
+ // large or too small.
+ float zoom_ratio = 1.0;
+ float root_avg = (root_w + root_h) / 2.0f;
+ if (root_avg >= max_avg_root_length)
+ zoom_ratio = max_avg_root_length / root_avg;
+ if (root_avg <= min_avg_root_length)
+ zoom_ratio = min_avg_root_length / root_avg;
+
+ root_w *= zoom_ratio;
+ root_h *= zoom_ratio;
+ float root_x = m_root.extent.start.d[0] * zoom_ratio;
+ float root_y = m_root.extent.start.d[1] * zoom_ratio;
+ float r = root_avg / 100.0f * zoom_ratio;
+ float stroke_w = r / 10.0f; // stroke width
+
+ const std::string indent = " ";
+
+ // Uniform attributes to use for all drawing objects.
+
+ std::string attrs_dir;
+ {
+ std::ostringstream os;
+ os << " stroke=\"#999999\" stroke-width=\"" << stroke_w << "\" fill=\"green\" fill-opacity=\"0.05\"";
+ attrs_dir = os.str();
+ }
+
+ std::string attrs_value;
+ {
+ std::ostringstream os;
+ os << " stroke=\"brown\" stroke-width=\"" << stroke_w << "\" fill=\"brown\" fill-opacity=\"0.2\"";
+ attrs_value = os.str();
+ }
+
+ std::ostringstream os;
+ os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
+ os << "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n";
+
+ std::function<void(const node_store*, int)> func_descend = [&](const node_store* ns, int level) {
+ const extent_type& ext = ns->extent;
+
+ float w = ext.end.d[0] - ext.start.d[0];
+ float h = ext.end.d[1] - ext.start.d[1];
+ float x = ext.start.d[0];
+ float y = ext.start.d[1];
+ w *= zoom_ratio;
+ h *= zoom_ratio;
+ x *= zoom_ratio;
+ y *= zoom_ratio;
+ x -= root_x;
+ y -= root_y;
+
+ if (level > 0)
+ {
+ const char* attrs = (ns->type == node_type::value) ? attrs_value.data() : attrs_dir.data();
+
+ if (ext.is_point())
+ {
+ os << indent << "<circle cx=\"" << x << "\" cy=\"" << y << "\" r=\"" << r << "\"" << attrs << "/>\n";
+ }
+ else
+ {
+ os << indent << "<rect x=\"" << x << "\" y=\"" << y << "\" width=\"" << w << "\" height=\"" << h << "\""
+ << attrs << "/>\n";
+ }
+ }
+
+ switch (ns->type)
+ {
+ case node_type::directory_leaf:
+ case node_type::directory_nonleaf:
+ {
+ const directory_node* dir = static_cast<const directory_node*>(ns->node_ptr);
+
+ for (const node_store& ns_child : dir->children)
+ func_descend(&ns_child, level + 1);
+
+ break;
+ }
+ case node_type::value:
+ // Do nothing.
+ break;
+ default:
+ throw integrity_error("Unexpected node type!");
+ }
+ };
+
+ os << "<svg version=\"1.2\" width=\"" << root_w << "\" height=\"" << root_h << "\">\n";
+ func_descend(&m_root, 0);
+ os << "</svg>";
+
+ return os.str();
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+void rtree<KeyT, ValueT, Traits>::split_node(node_store* ns)
+{
+ directory_node* dir = ns->get_directory_node();
+
+ assert(dir);
+ assert(ns->count == traits_type::max_node_size + 1);
+
+ dir_store_type& children = dir->children;
+
+ sort_dir_store_by_split_dimension(children);
+
+ size_t dist = pick_optimal_distribution(children);
+ distribution dist_picked(dist, children);
+
+ // Move the child nodes in group 2 into a brand-new sibling node.
+ node_store node_g2 = node_store::create_leaf_directory_node();
+ node_g2.type = ns->type;
+ directory_node* dir_sibling = static_cast<directory_node*>(node_g2.node_ptr);
+
+ for (auto it = dist_picked.g2.begin; it != dist_picked.g2.end; ++it)
+ {
+ assert(!it->valid_pointer);
+ dir_sibling->children.push_back(std::move(*it));
+ }
+
+ node_g2.count = dir_sibling->children.size();
+ node_g2.pack();
+
+ // Remove the nodes in group 2 from the original node by shrinking the node store.
+ ns->count = dist_picked.g1.size;
+ assert(dist_picked.g1.size < dir->children.size());
+ dir->children.resize(dist_picked.g1.size);
+ ns->pack(); // Re-calculate the bounding box.
+
+ if (ns->is_root())
+ {
+ // Create a new root node and make it the parent of the original root
+ // and the new sibling nodes.
+ assert(ns == &m_root);
+ node_store node_g1 = node_store::create_nonleaf_directory_node();
+ m_root.swap(node_g1);
+ node_g1.parent = &m_root;
+ node_g2.parent = &m_root;
+ directory_node* dir_root = static_cast<directory_node*>(m_root.node_ptr);
+ dir_root->children.emplace_back(std::move(node_g1));
+ dir_root->children.emplace_back(std::move(node_g2));
+ m_root.count = 2;
+ m_root.pack();
+
+ for (node_store& ns_child : dir_root->children)
+ ns_child.reset_parent_pointers_of_children();
+ }
+ else
+ {
+ // Place the new sibling (node_g2) under the same parent as ns.
+ assert(ns->parent);
+ node_g2.parent = ns->parent;
+ node_store* ns_parent = ns->parent;
+ assert(ns_parent->type == node_type::directory_nonleaf);
+ directory_node* dir_parent = static_cast<directory_node*>(ns_parent->node_ptr);
+ dir_parent->children.emplace_back(std::move(node_g2));
+ ++ns_parent->count;
+ bool parent_size_changed = ns_parent->pack();
+
+ // Update the parent pointer of the children _after_ the group 2 node
+ // has been inserted into the buffer, as the pointer value of the node
+ // changes after the insertion.
+ ns->reset_parent_pointers();
+ dir_parent->children.back().reset_parent_pointers_of_children();
+
+ if (ns_parent->count > traits_type::max_node_size)
+ // The parent node is overfilled. Split it and keep working upward.
+ split_node(ns_parent);
+ else if (parent_size_changed)
+ // The extent of the parent node has changed. Propagate the change upward.
+ ns_parent->pack_upward();
+ }
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+void rtree<KeyT, ValueT, Traits>::perform_forced_reinsertion(
+ node_store* ns, std::unordered_set<size_t>& reinserted_depth)
+{
+ assert(ns->count == traits_type::max_node_size + 1);
+
+ // Compute the distance between the centers of the value extents and the
+ // center of the extent of the parent directory.
+
+ point_type center_of_dir = detail::rtree::get_center_point(ns->extent);
+
+ directory_node* dir = ns->get_directory_node();
+ assert(dir);
+
+ using buckets_type = std::vector<detail::rtree::reinsertion_bucket<key_type>>;
+ buckets_type buckets;
+ buckets.reserve(ns->count);
+
+ size_t pos = 0;
+ for (const node_store& ns_child : dir->children)
+ {
+ buckets.emplace_back();
+ buckets.back().src_pos = pos++;
+
+ point_type center_of_child = detail::rtree::get_center_point(ns_child.extent);
+ buckets.back().distance = detail::rtree::calc_square_distance(center_of_dir, center_of_child);
+ }
+
+ // Sort the value entries in decreasing order of their distances.
+
+ std::sort(
+ buckets.begin(), buckets.end(),
+ [](const typename buckets_type::value_type& left, const typename buckets_type::value_type& right) -> bool {
+ return left.distance < right.distance;
+ });
+
+ assert(traits_type::reinsertion_size < buckets.size());
+
+ // Remove the first set of entries from the parent directory.
+ std::deque<node_store> nodes_to_reinsert(traits_type::reinsertion_size);
+
+ for (size_t i = 0; i < traits_type::reinsertion_size; ++i)
+ {
+ size_t this_pos = buckets[i].src_pos;
+ dir->children[this_pos].swap(nodes_to_reinsert[i]);
+ }
+
+ // Erase the swapped out nodes from the directory.
+ auto it = std::remove_if(dir->children.begin(), dir->children.end(), [](const node_store& this_ns) -> bool {
+ return this_ns.type == node_type::unspecified;
+ });
+
+ dir->children.erase(it, dir->children.end());
+ ns->count -= nodes_to_reinsert.size();
+ assert(ns->count == dir->children.size());
+
+ // No need to invalidate pointers since they are all value nodes.
+
+ if (ns->pack())
+ ns->pack_upward();
+
+ // Re-insert the values from the closest to farthest.
+
+ while (!nodes_to_reinsert.empty())
+ {
+ node_store ns_to_reinsert(std::move(nodes_to_reinsert.front()));
+ nodes_to_reinsert.pop_front();
+
+ insert(std::move(ns_to_reinsert), &reinserted_depth);
+ }
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+void rtree<KeyT, ValueT, Traits>::sort_dir_store_by_split_dimension(dir_store_type& children)
+{
+ // Store the sum of margins for each dimension axis.
+ detail::rtree::min_value_pos<key_type> min_margin_dim;
+
+ for (size_t dim = 0; dim < traits_type::dimensions; ++dim)
+ {
+ // Sort the entries by the lower then by the upper value of their
+ // bounding boxes. This invalidates the pointers of the child nodes.
+ sort_dir_store_by_dimension(dim, children);
+
+ key_type sum_of_margins = key_type(); // it's actually the sum of half margins.
+
+ for (size_t dist = 1; dist <= max_dist_size; ++dist)
+ {
+ // The first group contains m-1+dist entries, while the second
+ // group contains the rest.
+
+ auto it = children.begin();
+ auto it_end = it;
+ std::advance(it_end, traits_type::min_node_size - 1 + dist);
+
+ extent_type bb1 = detail::rtree::calc_extent(it, it_end);
+ it = it_end;
+ it_end = children.end();
+ assert(it != it_end);
+ extent_type bb2 = detail::rtree::calc_extent(it, it_end);
+
+ // Compute the half margins of the first and second groups.
+ key_type margin1 = detail::rtree::calc_half_margin<extent_type>(bb1);
+ key_type margin2 = detail::rtree::calc_half_margin<extent_type>(bb2);
+ key_type margins = margin1 + margin2;
+
+ sum_of_margins += margins;
+ }
+
+ min_margin_dim.assign(sum_of_margins, dim);
+ }
+
+ // Pick the dimension axis with the lowest sum of margins.
+ size_t min_dim = min_margin_dim.pos;
+
+ sort_dir_store_by_dimension(min_dim, children);
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+void rtree<KeyT, ValueT, Traits>::sort_dir_store_by_dimension(size_t dim, dir_store_type& children)
+{
+ std::sort(children.begin(), children.end(), [dim](const node_store& a, const node_store& b) -> bool {
+ if (a.extent.start.d[dim] != b.extent.start.d[dim])
+ return a.extent.start.d[dim] < b.extent.start.d[dim];
+
+ return a.extent.end.d[dim] < b.extent.end.d[dim];
+ });
+
+ for (node_store& ns : children)
+ ns.valid_pointer = false;
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+size_t rtree<KeyT, ValueT, Traits>::pick_optimal_distribution(dir_store_type& children) const
+{
+ // Along the chosen dimension axis, pick the distribution with the minimum
+ // overlap value.
+ detail::rtree::min_value_pos<key_type> min_overlap_dist;
+
+ for (size_t dist = 1; dist <= max_dist_size; ++dist)
+ {
+ // The first group contains m-1+dist entries, while the second
+ // group contains the rest.
+ distribution dist_data(dist, children);
+ extent_type bb1 = detail::rtree::calc_extent(dist_data.g1.begin, dist_data.g1.end);
+ extent_type bb2 = detail::rtree::calc_extent(dist_data.g2.begin, dist_data.g2.end);
+
+ key_type overlap = detail::rtree::calc_intersection<extent_type>(bb1, bb2);
+ min_overlap_dist.assign(overlap, dist);
+ }
+
+ return min_overlap_dist.pos;
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+typename rtree<KeyT, ValueT, Traits>::insertion_point rtree<
+ KeyT, ValueT, Traits>::find_leaf_directory_node_for_insertion(const extent_type& bb)
+{
+ insertion_point ret;
+ ret.ns = &m_root;
+
+ for (size_t i = 0; i <= traits_type::max_tree_depth; ++i)
+ {
+ if (ret.ns->type == node_type::directory_leaf)
+ {
+ ret.depth = i;
+ return ret;
+ }
+
+ assert(ret.ns->type == node_type::directory_nonleaf);
+
+ directory_node* dir = static_cast<directory_node*>(ret.ns->node_ptr);
+
+ // If this non-leaf directory contains at least one leaf directory,
+ // pick the entry with minimum overlap increase. If all of its child
+ // nodes are non-leaf directories, then pick the entry with minimum
+ // area enlargement.
+
+ if (dir->has_leaf_directory())
+ ret.ns = dir->get_child_with_minimal_overlap(bb);
+ else
+ ret.ns = dir->get_child_with_minimal_area_enlargement(bb);
+ }
+
+ throw std::runtime_error("Maximum tree depth has been reached.");
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+typename rtree<KeyT, ValueT, Traits>::node_store* rtree<
+ KeyT, ValueT, Traits>::find_nonleaf_directory_node_for_insertion(const extent_type& bb, size_t max_depth)
+{
+ node_store* dst = &m_root;
+
+ for (size_t i = 0; i <= traits_type::max_tree_depth; ++i)
+ {
+ assert(dst->is_directory());
+
+ if (!dst->count)
+ // This node has no children.
+ return dst;
+
+ assert(dst->type == node_type::directory_nonleaf);
+
+ if (i == max_depth)
+ return dst;
+
+ directory_node* dir = static_cast<directory_node*>(dst->node_ptr);
+
+ if (dir->has_leaf_directory())
+ return dst;
+
+ assert(dst->type == node_type::directory_nonleaf);
+ dst = dir->get_child_with_minimal_area_enlargement(bb);
+ assert(dst);
+ }
+
+ throw std::runtime_error("Maximum tree depth has been reached.");
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+template<typename FuncT>
+void rtree<KeyT, ValueT, Traits>::descend_with_func(FuncT func) const
+{
+ std::function<void(const node_store*)> func_descend = [&](const node_store* ns) {
+ node_properties np;
+ np.type = ns->type;
+ np.extent = ns->extent;
+ func(np);
+
+ switch (ns->type)
+ {
+ case node_type::directory_leaf:
+ case node_type::directory_nonleaf:
+ {
+ const directory_node* dir = static_cast<const directory_node*>(ns->node_ptr);
+
+ for (const node_store& ns_child : dir->children)
+ func_descend(&ns_child);
+
+ break;
+ }
+ case node_type::value:
+ // Do nothing.
+ break;
+ default:
+ assert(!"The tree should not contain node of this type!");
+ }
+ };
+
+ func_descend(&m_root);
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+template<typename _ResT>
+void rtree<KeyT, ValueT, Traits>::search_descend(
+ size_t depth, const search_condition_type& dir_cond, const search_condition_type& value_cond,
+ typename _ResT::node_store_type& ns, _ResT& results) const
+{
+ switch (ns.type)
+ {
+ case node_type::directory_nonleaf:
+ case node_type::directory_leaf:
+ {
+ if (!dir_cond(ns))
+ return;
+
+ auto* dir_node = ns.get_directory_node();
+ for (auto& child : dir_node->children)
+ search_descend(depth + 1, dir_cond, value_cond, child, results);
+ break;
+ }
+ case node_type::value:
+ {
+ if (!value_cond(ns))
+ return;
+
+ results.add_node_store(&ns, depth);
+ break;
+ }
+ case node_type::unspecified:
+ throw std::runtime_error("unspecified node type.");
+ }
+}
+
+template<typename KeyT, typename ValueT, typename Traits>
+void rtree<KeyT, ValueT, Traits>::shrink_tree_upward(node_store* ns, const extent_type& bb_affected)
+{
+ if (!ns)
+ return;
+
+ // Check if the affected bounding box is at a corner.
+ if (!ns->extent.contains_at_boundary(bb_affected))
+ return;
+
+ extent_type original_bb = ns->extent; // Store the original bounding box before the packing.
+ bool updated = ns->pack();
+
+ if (!updated)
+ // The extent hasn't changed. There is no point going upward.
+ return;
+
+ shrink_tree_upward(ns->parent, original_bb);
+}
+
+} // namespace mdds
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/mdds/segment_tree.hpp b/include/mdds/segment_tree.hpp
new file mode 100644
index 0000000..8414b72
--- /dev/null
+++ b/include/mdds/segment_tree.hpp
@@ -0,0 +1,704 @@
+/*************************************************************************
+ *
+ * Copyright (c) 2010-2015 Kohei Yoshida
+ *
+ * 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, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ ************************************************************************/
+
+#ifndef INCLUDED_MDDS_SEGMENTTREE_HPP
+#define INCLUDED_MDDS_SEGMENTTREE_HPP
+
+#include "mdds/node.hpp"
+#include "mdds/global.hpp"
+
+#include <vector>
+#include <iostream>
+#include <map>
+#include <unordered_map>
+#include <memory>
+
+#ifdef MDDS_UNIT_TEST
+#include <sstream>
+#endif
+
+namespace mdds {
+
+template<typename _Key, typename _Value>
+class segment_tree
+{
+public:
+ typedef _Key key_type;
+ typedef _Value value_type;
+ typedef size_t size_type;
+ typedef std::vector<value_type> search_results_type;
+
+#ifdef MDDS_UNIT_TEST
+ struct segment_data
+ {
+ key_type begin_key;
+ key_type end_key;
+ value_type pdata;
+
+ segment_data(key_type _beg, key_type _end, value_type p) : begin_key(_beg), end_key(_end), pdata(p)
+ {}
+
+ bool operator==(const segment_data& r) const
+ {
+ return begin_key == r.begin_key && end_key == r.end_key && pdata == r.pdata;
+ }
+
+ bool operator!=(const segment_data& r) const
+ {
+ return !operator==(r);
+ }
+ };
+
+ struct segment_map_printer
+ {
+ void operator()(const ::std::pair<value_type, ::std::pair<key_type, key_type>>& r) const
+ {
+ using namespace std;
+ cout << r.second.first << "-" << r.second.second << ": " << r.first->name << endl;
+ }
+ };
+#endif
+
+public:
+ typedef ::std::vector<value_type> data_chain_type;
+ typedef std::unordered_map<value_type, ::std::pair<key_type, key_type>> segment_map_type;
+ typedef ::std::map<value_type, ::std::pair<key_type, key_type>> sorted_segment_map_type;
+
+ struct nonleaf_value_type
+ {
+ key_type low; /// low range value (inclusive)
+ key_type high; /// high range value (non-inclusive)
+ data_chain_type* data_chain;
+
+ bool operator==(const nonleaf_value_type& r) const
+ {
+ return low == r.low && high == r.high && data_chain == r.data_chain;
+ }
+ };
+
+ struct leaf_value_type
+ {
+ key_type key;
+ data_chain_type* data_chain;
+
+ bool operator==(const leaf_value_type& r) const
+ {
+ return key == r.key && data_chain == r.data_chain;
+ }
+ };
+
+ struct fill_nonleaf_value_handler;
+ struct init_handler;
+ struct dispose_handler;
+#ifdef MDDS_UNIT_TEST
+ struct to_string_handler;
+#endif
+
+ typedef __st::node<segment_tree> node;
+ typedef typename node::node_ptr node_ptr;
+
+ typedef typename __st::nonleaf_node<segment_tree> nonleaf_node;
+
+ struct fill_nonleaf_value_handler
+ {
+ void operator()(
+ __st::nonleaf_node<segment_tree>& _self, const __st::node_base* left_node,
+ const __st::node_base* right_node)
+ {
+ // Parent node should carry the range of all of its child nodes.
+ if (left_node)
+ {
+ _self.value_nonleaf.low = left_node->is_leaf
+ ? static_cast<const node*>(left_node)->value_leaf.key
+ : static_cast<const nonleaf_node*>(left_node)->value_nonleaf.low;
+ }
+ else
+ {
+ // Having a left node is prerequisite.
+ throw general_error("segment_tree::fill_nonleaf_value_handler: Having a left node is prerequisite.");
+ }
+
+ if (right_node)
+ {
+ if (right_node->is_leaf)
+ {
+ // When the child nodes are leaf nodes, the upper bound
+ // must be the value of the node that comes after the
+ // right leaf node (if such node exists).
+
+ const node* p = static_cast<const node*>(right_node);
+ if (p->next)
+ _self.value_nonleaf.high = p->next->value_leaf.key;
+ else
+ _self.value_nonleaf.high = p->value_leaf.key;
+ }
+ else
+ {
+ _self.value_nonleaf.high = static_cast<const nonleaf_node*>(right_node)->value_nonleaf.high;
+ }
+ }
+ else
+ {
+ _self.value_nonleaf.high = left_node->is_leaf
+ ? static_cast<const node*>(left_node)->value_leaf.key
+ : static_cast<const nonleaf_node*>(left_node)->value_nonleaf.high;
+ }
+ }
+ };
+
+#ifdef MDDS_UNIT_TEST
+ struct to_string_handler
+ {
+ std::string operator()(const node& _self) const
+ {
+ std::ostringstream os;
+ os << "[" << _self.value_leaf.key << "] ";
+ return os.str();
+ }
+
+ std::string operator()(const __st::nonleaf_node<segment_tree>& _self) const
+ {
+ std::ostringstream os;
+ os << "[" << _self.value_nonleaf.low << "-" << _self.value_nonleaf.high << ")";
+ if (_self.value_nonleaf.data_chain)
+ {
+ os << " { ";
+ typename data_chain_type::const_iterator itr, itr_beg = _self.value_nonleaf.data_chain->begin(),
+ itr_end = _self.value_nonleaf.data_chain->end();
+ for (itr = itr_beg; itr != itr_end; ++itr)
+ {
+ if (itr != itr_beg)
+ os << ", ";
+ os << (*itr)->name;
+ }
+ os << " }";
+ }
+ os << " ";
+ return os.str();
+ }
+ };
+#endif
+
+ struct init_handler
+ {
+ void operator()(node& _self)
+ {
+ _self.value_leaf.data_chain = nullptr;
+ }
+
+ void operator()(__st::nonleaf_node<segment_tree>& _self)
+ {
+ _self.value_nonleaf.data_chain = nullptr;
+ }
+ };
+
+ struct dispose_handler
+ {
+ void operator()(node& _self)
+ {
+ delete _self.value_leaf.data_chain;
+ }
+
+ void operator()(__st::nonleaf_node<segment_tree>& _self)
+ {
+ delete _self.value_nonleaf.data_chain;
+ }
+ };
+
+#ifdef MDDS_UNIT_TEST
+ struct node_printer
+ {
+ void operator()(const __st::node_base* p) const
+ {
+ if (p->is_leaf)
+ std::cout << static_cast<const node*>(p)->to_string() << " ";
+ else
+ std::cout << static_cast<const nonleaf_node*>(p)->to_string() << " ";
+ }
+ };
+#endif
+
+private:
+ /**
+ * This base class takes care of collecting data chain pointers during
+ * tree descend for search.
+ */
+ class search_results_base
+ {
+ public:
+ typedef std::vector<data_chain_type*> res_chains_type;
+ typedef std::shared_ptr<res_chains_type> res_chains_ptr;
+
+ public:
+ search_results_base() : mp_res_chains(static_cast<res_chains_type*>(nullptr))
+ {}
+
+ search_results_base(const search_results_base& r) : mp_res_chains(r.mp_res_chains)
+ {}
+
+ size_t size() const
+ {
+ size_t combined = 0;
+ if (!mp_res_chains)
+ return combined;
+
+ typename res_chains_type::const_iterator itr = mp_res_chains->begin(), itr_end = mp_res_chains->end();
+ for (; itr != itr_end; ++itr)
+ combined += (*itr)->size();
+ return combined;
+ }
+
+ void push_back_chain(data_chain_type* chain)
+ {
+ if (!chain || chain->empty())
+ return;
+
+ if (!mp_res_chains)
+ mp_res_chains.reset(new res_chains_type);
+ mp_res_chains->push_back(chain);
+ }
+
+ res_chains_ptr& get_res_chains()
+ {
+ return mp_res_chains;
+ }
+
+ private:
+ res_chains_ptr mp_res_chains;
+ };
+
+ class iterator_base
+ {
+ protected:
+ typedef typename search_results_base::res_chains_type res_chains_type;
+ typedef typename search_results_base::res_chains_ptr res_chains_ptr;
+
+ iterator_base(const res_chains_ptr& p) : mp_res_chains(p), m_end_pos(true)
+ {}
+
+ public:
+ typedef ::std::bidirectional_iterator_tag iterator_category;
+ typedef typename data_chain_type::value_type value_type;
+ typedef typename data_chain_type::pointer pointer;
+ typedef typename data_chain_type::reference reference;
+ typedef typename data_chain_type::difference_type difference_type;
+
+ iterator_base() : mp_res_chains(static_cast<res_chains_type*>(nullptr)), m_end_pos(true)
+ {}
+
+ iterator_base(const iterator_base& r)
+ : mp_res_chains(r.mp_res_chains), m_cur_chain(r.m_cur_chain), m_cur_pos_in_chain(r.m_cur_pos_in_chain),
+ m_end_pos(r.m_end_pos)
+ {}
+
+ iterator_base& operator=(const iterator_base& r)
+ {
+ mp_res_chains = r.mp_res_chains;
+ m_cur_chain = r.m_cur_chain;
+ m_cur_pos_in_chain = r.m_cur_pos_in_chain;
+ m_end_pos = r.m_end_pos;
+ return *this;
+ }
+
+ typename data_chain_type::value_type* operator++()
+ {
+ // We don't check for end position flag for performance reasons.
+ // The caller is responsible for making sure not to increment past
+ // end position.
+
+ // When reaching the end position, the internal iterators still
+ // need to be pointing at the last item before the end position.
+ // This is why we need to make copies of the iterators, and copy
+ // them back once done.
+
+ typename data_chain_type::iterator cur_pos_in_chain = m_cur_pos_in_chain;
+
+ if (++cur_pos_in_chain == (*m_cur_chain)->end())
+ {
+ // End of current chain. Inspect the next chain if exists.
+ typename res_chains_type::iterator cur_chain = m_cur_chain;
+ ++cur_chain;
+ if (cur_chain == mp_res_chains->end())
+ {
+ m_end_pos = true;
+ return nullptr;
+ }
+ m_cur_chain = cur_chain;
+ m_cur_pos_in_chain = (*m_cur_chain)->begin();
+ }
+ else
+ ++m_cur_pos_in_chain;
+
+ return operator->();
+ }
+
+ typename data_chain_type::value_type* operator--()
+ {
+ if (!mp_res_chains)
+ return nullptr;
+
+ if (m_end_pos)
+ {
+ m_end_pos = false;
+ return &(*m_cur_pos_in_chain);
+ }
+
+ if (m_cur_pos_in_chain == (*m_cur_chain)->begin())
+ {
+ if (m_cur_chain == mp_res_chains->begin())
+ {
+ // Already at the first data chain. Don't move the iterator position.
+ return nullptr;
+ }
+ --m_cur_chain;
+ m_cur_pos_in_chain = (*m_cur_chain)->end();
+ }
+ --m_cur_pos_in_chain;
+ return operator->();
+ }
+
+ bool operator==(const iterator_base& r) const
+ {
+ if (mp_res_chains.get())
+ {
+ // non-empty result set.
+ return mp_res_chains.get() == r.mp_res_chains.get() && m_cur_chain == r.m_cur_chain &&
+ m_cur_pos_in_chain == r.m_cur_pos_in_chain && m_end_pos == r.m_end_pos;
+ }
+
+ // empty result set.
+ if (r.mp_res_chains.get())
+ return false;
+ return m_end_pos == r.m_end_pos;
+ }
+
+ bool operator!=(const iterator_base& r) const
+ {
+ return !operator==(r);
+ }
+
+ typename data_chain_type::value_type& operator*()
+ {
+ return *m_cur_pos_in_chain;
+ }
+
+ typename data_chain_type::value_type* operator->()
+ {
+ return &(*m_cur_pos_in_chain);
+ }
+
+ protected:
+ void move_to_front()
+ {
+ if (!mp_res_chains)
+ {
+ // Empty data set.
+ m_end_pos = true;
+ return;
+ }
+
+ // We assume that there is at least one chain list, and no
+ // empty chain list exists. So, skip the check.
+ m_cur_chain = mp_res_chains->begin();
+ m_cur_pos_in_chain = (*m_cur_chain)->begin();
+ m_end_pos = false;
+ }
+
+ void move_to_end()
+ {
+ m_end_pos = true;
+ if (!mp_res_chains)
+ // Empty data set.
+ return;
+
+ m_cur_chain = mp_res_chains->end();
+ --m_cur_chain;
+ m_cur_pos_in_chain = (*m_cur_chain)->end();
+ --m_cur_pos_in_chain;
+ }
+
+ private:
+ res_chains_ptr mp_res_chains;
+ typename res_chains_type::iterator m_cur_chain;
+ typename data_chain_type::iterator m_cur_pos_in_chain;
+ bool m_end_pos : 1;
+ };
+
+public:
+ class search_results : public search_results_base
+ {
+ typedef typename search_results_base::res_chains_type res_chains_type;
+ typedef typename search_results_base::res_chains_ptr res_chains_ptr;
+
+ public:
+ class iterator : public iterator_base
+ {
+ friend class segment_tree<_Key, _Value>::search_results;
+
+ private:
+ iterator(const res_chains_ptr& p) : iterator_base(p)
+ {}
+
+ public:
+ iterator() : iterator_base()
+ {}
+ };
+
+ typename search_results::iterator begin()
+ {
+ typename search_results::iterator itr(search_results_base::get_res_chains());
+ itr.move_to_front();
+ return itr;
+ }
+
+ typename search_results::iterator end()
+ {
+ typename search_results::iterator itr(search_results_base::get_res_chains());
+ itr.move_to_end();
+ return itr;
+ }
+ };
+
+ class search_result_vector_inserter
+ {
+ public:
+ search_result_vector_inserter(search_results_type& result) : m_result(result)
+ {}
+ void operator()(data_chain_type* node_data)
+ {
+ if (!node_data)
+ return;
+
+ typename data_chain_type::const_iterator itr = node_data->begin(), itr_end = node_data->end();
+ for (; itr != itr_end; ++itr)
+ m_result.push_back(*itr);
+ }
+
+ private:
+ search_results_type& m_result;
+ };
+
+ class search_result_inserter
+ {
+ public:
+ search_result_inserter(search_results_base& result) : m_result(result)
+ {}
+ void operator()(data_chain_type* node_data)
+ {
+ if (!node_data)
+ return;
+
+ m_result.push_back_chain(node_data);
+ }
+
+ private:
+ search_results_base& m_result;
+ };
+
+ segment_tree();
+ segment_tree(const segment_tree& r);
+ ~segment_tree();
+
+ /**
+ * Equality between two segment_tree instances is evaluated by comparing
+ * the segments that they store. The trees are not compared.
+ */
+ bool operator==(const segment_tree& r) const;
+
+ bool operator!=(const segment_tree& r) const
+ {
+ return !operator==(r);
+ }
+
+ /**
+ * Check whether or not the internal tree is in a valid state. The tree
+ * must be valid in order to perform searches.
+ *
+ * @return true if the tree is valid, false otherwise.
+ */
+ bool is_tree_valid() const
+ {
+ return m_valid_tree;
+ }
+
+ /**
+ * Build or re-build tree based on the current set of segments.
+ */
+ void build_tree();
+
+ /**
+ * Insert a new segment.
+ *
+ * @param begin_key begin point of the segment. The value is inclusive.
+ * @param end_key end point of the segment. The value is non-inclusive.
+ * @param pdata pointer to the data instance associated with this segment.
+ * Note that <i>the caller must manage the life cycle of the
+ * data instance</i>.
+ */
+ bool insert(key_type begin_key, key_type end_key, value_type pdata);
+
+ /**
+ * Search the tree and collect all segments that include a specified
+ * point.
+ *
+ * @param point specified point value
+ * @param result doubly-linked list of data instances associated with
+ * the segments that include the specified point.
+ * <i>Note that the search result gets appended to the
+ * list; the list will not get emptied on each
+ * search.</i> It is caller's responsibility to empty
+ * the list before passing it to this method in case the
+ * caller so desires.
+ *
+ * @return true if the search is performed successfully, false if the
+ * search has ended prematurely due to error conditions.
+ */
+ bool search(key_type point, search_results_type& result) const;
+
+ /**
+ * Search the tree and collect all segments that include a specified
+ * point.
+ *
+ * @param point specified point value
+ *
+ * @return object containing the result of the search, which can be
+ * accessed via iterator.
+ */
+ search_results search(key_type point) const;
+
+ /**
+ * Remove a segment that matches by the value. This will <i>not</i>
+ * invalidate the tree; however, if you have removed lots of segments, you
+ * might want to re-build the tree to shrink its size.
+ *
+ * @param value value to remove a segment by.
+ */
+ void remove(value_type value);
+
+ /**
+ * Remove all segments data.
+ */
+ void clear();
+
+ /**
+ * Return the number of segments currently stored in this container.
+ */
+ size_t size() const;
+
+ /**
+ * Return whether or not the container stores any segments or none at all.
+ */
+ bool empty() const;
+
+ /**
+ * Return the number of leaf nodes.
+ *
+ * @return number of leaf nodes.
+ */
+ size_t leaf_size() const;
+
+#ifdef MDDS_UNIT_TEST
+ void dump_tree() const;
+ void dump_leaf_nodes() const;
+ void dump_segment_data() const;
+ bool verify_node_lists() const;
+
+ struct leaf_node_check
+ {
+ key_type key;
+ data_chain_type data_chain;
+ };
+
+ bool verify_leaf_nodes(const ::std::vector<leaf_node_check>& checks) const;
+
+ /**
+ * Verify the validity of the segment data array.
+ *
+ * @param checks null-terminated array of expected values. The last item
+ * must have a nullptr pdata value to terminate the array.
+ */
+ bool verify_segment_data(const segment_map_type& checks) const;
+#endif
+
+private:
+ /**
+ * To be called from rectangle_set.
+ */
+ void search(key_type point, search_results_base& result) const;
+
+ typedef std::vector<__st::node_base*> node_list_type;
+ typedef std::map<value_type, std::unique_ptr<node_list_type>> data_node_map_type;
+
+ static void create_leaf_node_instances(const ::std::vector<key_type>& keys, node_ptr& left, node_ptr& right);
+
+ /**
+ * Descend the tree from the root node, and mark appropriate nodes, both
+ * leaf and non-leaf, based on segment's end points. When marking nodes,
+ * record their positions as a list of node pointers.
+ */
+ void descend_tree_and_mark(
+ __st::node_base* pnode, value_type pdata, key_type begin_key, key_type end_key, node_list_type* plist);
+
+ void build_leaf_nodes();
+
+ /**
+ * Go through the list of nodes, and remove the specified data pointer
+ * value from the nodes.
+ */
+ void remove_data_from_nodes(node_list_type* plist, const value_type pdata);
+ void remove_data_from_chain(data_chain_type& chain, const value_type pdata);
+
+ void clear_all_nodes();
+
+#ifdef MDDS_UNIT_TEST
+ static bool has_data_pointer(const node_list_type& node_list, const value_type pdata);
+ static void print_leaf_value(const leaf_value_type& v);
+#endif
+
+private:
+ std::vector<nonleaf_node> m_nonleaf_node_pool;
+
+ segment_map_type m_segment_data;
+
+ /**
+ * For each data pointer, it keeps track of all nodes, leaf or non-leaf,
+ * that stores the data pointer label. This data is used when removing
+ * segments by the data pointer value.
+ */
+ data_node_map_type m_tagged_node_map;
+
+ nonleaf_node* m_root_node;
+ node_ptr m_left_leaf;
+ node_ptr m_right_leaf;
+ bool m_valid_tree : 1;
+};
+
+} // namespace mdds
+
+#include "segment_tree_def.inl"
+
+#endif
diff --git a/include/mdds/segment_tree_def.inl b/include/mdds/segment_tree_def.inl
new file mode 100644
index 0000000..011449c
--- /dev/null
+++ b/include/mdds/segment_tree_def.inl
@@ -0,0 +1,644 @@
+/*************************************************************************
+ *
+ * Copyright (c) 2015 Kohei Yoshida
+ *
+ * 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, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ ************************************************************************/
+
+#include <algorithm>
+
+namespace mdds {
+
+namespace __st {
+
+template<typename T, typename _Inserter>
+void descend_tree_for_search(typename T::key_type point, const __st::node_base* pnode, _Inserter& result)
+{
+ typedef typename T::node leaf_node;
+ typedef typename T::nonleaf_node nonleaf_node;
+
+ typedef typename T::nonleaf_value_type nonleaf_value_type;
+ typedef typename T::leaf_value_type leaf_value_type;
+
+ if (!pnode)
+ // This should never happen, but just in case.
+ return;
+
+ if (pnode->is_leaf)
+ {
+ result(static_cast<const leaf_node*>(pnode)->value_leaf.data_chain);
+ return;
+ }
+
+ const nonleaf_node* pnonleaf = static_cast<const nonleaf_node*>(pnode);
+ const nonleaf_value_type& v = pnonleaf->value_nonleaf;
+ if (point < v.low || v.high <= point)
+ // Query point is out-of-range.
+ return;
+
+ result(v.data_chain);
+
+ // Check the left child node first, then the right one.
+ __st::node_base* pchild = pnonleaf->left;
+ if (!pchild)
+ return;
+
+ assert(pnonleaf->right ? pchild->is_leaf == pnonleaf->right->is_leaf : true);
+
+ if (pchild->is_leaf)
+ {
+ // The child node are leaf nodes.
+ const leaf_value_type& vleft = static_cast<const leaf_node*>(pchild)->value_leaf;
+ if (point < vleft.key)
+ {
+ // Out-of-range. Nothing more to do.
+ return;
+ }
+
+ if (pnonleaf->right)
+ {
+ assert(pnonleaf->right->is_leaf);
+ const leaf_value_type& vright = static_cast<const leaf_node*>(pnonleaf->right)->value_leaf;
+ if (vright.key <= point)
+ // Follow the right node.
+ pchild = pnonleaf->right;
+ }
+ }
+ else
+ {
+ // This child nodes are non-leaf nodes.
+
+ const nonleaf_value_type& vleft = static_cast<const nonleaf_node*>(pchild)->value_nonleaf;
+
+ if (point < vleft.low)
+ {
+ // Out-of-range. Nothing more to do.
+ return;
+ }
+ if (vleft.high <= point && pnonleaf->right)
+ // Follow the right child.
+ pchild = pnonleaf->right;
+
+ assert(
+ static_cast<const nonleaf_node*>(pchild)->value_nonleaf.low <= point &&
+ point < static_cast<const nonleaf_node*>(pchild)->value_nonleaf.high);
+ }
+
+ descend_tree_for_search<T, _Inserter>(point, pchild, result);
+}
+
+} // namespace __st
+
+template<typename _Key, typename _Value>
+segment_tree<_Key, _Value>::segment_tree() : m_root_node(nullptr), m_valid_tree(false)
+{}
+
+template<typename _Key, typename _Value>
+segment_tree<_Key, _Value>::segment_tree(const segment_tree& r)
+ : m_segment_data(r.m_segment_data), m_root_node(nullptr), m_valid_tree(r.m_valid_tree)
+{
+ if (m_valid_tree)
+ build_tree();
+}
+
+template<typename _Key, typename _Value>
+segment_tree<_Key, _Value>::~segment_tree()
+{
+ clear_all_nodes();
+}
+
+template<typename _Key, typename _Value>
+bool segment_tree<_Key, _Value>::operator==(const segment_tree& r) const
+{
+ if (m_valid_tree != r.m_valid_tree)
+ return false;
+
+ // Sort the data by key values first.
+ sorted_segment_map_type seg1(m_segment_data.begin(), m_segment_data.end());
+ sorted_segment_map_type seg2(r.m_segment_data.begin(), r.m_segment_data.end());
+ typename sorted_segment_map_type::const_iterator itr1 = seg1.begin(), itr1_end = seg1.end();
+ typename sorted_segment_map_type::const_iterator itr2 = seg2.begin(), itr2_end = seg2.end();
+
+ for (; itr1 != itr1_end; ++itr1, ++itr2)
+ {
+ if (itr2 == itr2_end)
+ return false;
+
+ if (*itr1 != *itr2)
+ return false;
+ }
+ if (itr2 != itr2_end)
+ return false;
+
+ return true;
+}
+
+template<typename _Key, typename _Value>
+void segment_tree<_Key, _Value>::build_tree()
+{
+ build_leaf_nodes();
+ m_nonleaf_node_pool.clear();
+
+ // Count the number of leaf nodes.
+ size_t leaf_count = __st::count_leaf_nodes(m_left_leaf.get(), m_right_leaf.get());
+
+ // Determine the total number of non-leaf nodes needed to build the whole tree.
+ size_t nonleaf_count = __st::count_needed_nonleaf_nodes(leaf_count);
+
+ m_nonleaf_node_pool.resize(nonleaf_count);
+
+ mdds::__st::tree_builder<segment_tree> builder(m_nonleaf_node_pool);
+ m_root_node = builder.build(m_left_leaf);
+
+ // Start "inserting" all segments from the root.
+ typename segment_map_type::const_iterator itr, itr_beg = m_segment_data.begin(), itr_end = m_segment_data.end();
+
+ data_node_map_type tagged_node_map;
+ for (itr = itr_beg; itr != itr_end; ++itr)
+ {
+ value_type pdata = itr->first;
+ auto r =
+ tagged_node_map.insert(typename data_node_map_type::value_type(pdata, std::make_unique<node_list_type>()));
+
+ node_list_type* plist = r.first->second.get();
+ plist->reserve(10);
+
+ descend_tree_and_mark(m_root_node, pdata, itr->second.first, itr->second.second, plist);
+ }
+
+ m_tagged_node_map.swap(tagged_node_map);
+ m_valid_tree = true;
+}
+
+template<typename _Key, typename _Value>
+void segment_tree<_Key, _Value>::descend_tree_and_mark(
+ __st::node_base* pnode, value_type pdata, key_type begin_key, key_type end_key, node_list_type* plist)
+{
+ if (!pnode)
+ return;
+
+ if (pnode->is_leaf)
+ {
+ // This is a leaf node.
+ node* pleaf = static_cast<node*>(pnode);
+ if (begin_key <= pleaf->value_leaf.key && pleaf->value_leaf.key < end_key)
+ {
+ leaf_value_type& v = pleaf->value_leaf;
+ if (!v.data_chain)
+ v.data_chain = new data_chain_type;
+ v.data_chain->push_back(pdata);
+ plist->push_back(pnode);
+ }
+ return;
+ }
+
+ nonleaf_node* pnonleaf = static_cast<nonleaf_node*>(pnode);
+ if (end_key < pnonleaf->value_nonleaf.low || pnonleaf->value_nonleaf.high <= begin_key)
+ return;
+
+ nonleaf_value_type& v = pnonleaf->value_nonleaf;
+ if (begin_key <= v.low && v.high < end_key)
+ {
+ // mark this non-leaf node and stop.
+ if (!v.data_chain)
+ v.data_chain = new data_chain_type;
+ v.data_chain->push_back(pdata);
+ plist->push_back(pnode);
+ return;
+ }
+
+ descend_tree_and_mark(pnonleaf->left, pdata, begin_key, end_key, plist);
+ descend_tree_and_mark(pnonleaf->right, pdata, begin_key, end_key, plist);
+}
+
+template<typename _Key, typename _Value>
+void segment_tree<_Key, _Value>::build_leaf_nodes()
+{
+ using namespace std;
+
+ disconnect_leaf_nodes(m_left_leaf.get(), m_right_leaf.get());
+
+ // In 1st pass, collect unique end-point values and sort them.
+ vector<key_type> keys_uniq;
+ keys_uniq.reserve(m_segment_data.size() * 2);
+ typename segment_map_type::const_iterator itr, itr_beg = m_segment_data.begin(), itr_end = m_segment_data.end();
+ for (itr = itr_beg; itr != itr_end; ++itr)
+ {
+ keys_uniq.push_back(itr->second.first);
+ keys_uniq.push_back(itr->second.second);
+ }
+
+ // sort and remove duplicates.
+ sort(keys_uniq.begin(), keys_uniq.end());
+ keys_uniq.erase(unique(keys_uniq.begin(), keys_uniq.end()), keys_uniq.end());
+
+ create_leaf_node_instances(keys_uniq, m_left_leaf, m_right_leaf);
+}
+
+template<typename _Key, typename _Value>
+void segment_tree<_Key, _Value>::create_leaf_node_instances(
+ const ::std::vector<key_type>& keys, node_ptr& left, node_ptr& right)
+{
+ if (keys.empty() || keys.size() < 2)
+ // We need at least two keys in order to build tree.
+ return;
+
+ typename ::std::vector<key_type>::const_iterator itr = keys.begin(), itr_end = keys.end();
+
+ // left-most node
+ left.reset(new node);
+ left->value_leaf.key = *itr;
+
+ // move on to next.
+ left->next.reset(new node);
+ node_ptr prev_node = left;
+ node_ptr cur_node = left->next;
+ cur_node->prev = prev_node;
+
+ for (++itr; itr != itr_end; ++itr)
+ {
+ cur_node->value_leaf.key = *itr;
+
+ // move on to next
+ cur_node->next.reset(new node);
+ prev_node = cur_node;
+ cur_node = cur_node->next;
+ cur_node->prev = prev_node;
+ }
+
+ // Remove the excess node.
+ prev_node->next.reset();
+ right = prev_node;
+}
+
+template<typename _Key, typename _Value>
+bool segment_tree<_Key, _Value>::insert(key_type begin_key, key_type end_key, value_type pdata)
+{
+ if (begin_key >= end_key)
+ return false;
+
+ if (m_segment_data.find(pdata) != m_segment_data.end())
+ // Insertion of duplicate data is not allowed.
+ return false;
+
+ ::std::pair<key_type, key_type> range;
+ range.first = begin_key;
+ range.second = end_key;
+ m_segment_data.insert(typename segment_map_type::value_type(pdata, range));
+
+ m_valid_tree = false;
+ return true;
+}
+
+template<typename _Key, typename _Value>
+bool segment_tree<_Key, _Value>::search(key_type point, search_results_type& result) const
+{
+ if (!m_valid_tree)
+ // Tree is invalidated.
+ return false;
+
+ if (!m_root_node)
+ // Tree doesn't exist. Since the tree is flagged valid, this means no
+ // segments have been inserted.
+ return true;
+
+ search_result_vector_inserter result_inserter(result);
+ typedef segment_tree<_Key, _Value> tree_type;
+ __st::descend_tree_for_search<tree_type, search_result_vector_inserter>(point, m_root_node, result_inserter);
+ return true;
+}
+
+template<typename _Key, typename _Value>
+typename segment_tree<_Key, _Value>::search_results segment_tree<_Key, _Value>::search(key_type point) const
+{
+ search_results result;
+ if (!m_valid_tree || !m_root_node)
+ return result;
+
+ search_result_inserter result_inserter(result);
+ typedef segment_tree<_Key, _Value> tree_type;
+ __st::descend_tree_for_search<tree_type, search_result_inserter>(point, m_root_node, result_inserter);
+
+ return result;
+}
+
+template<typename _Key, typename _Value>
+void segment_tree<_Key, _Value>::search(key_type point, search_results_base& result) const
+{
+ if (!m_valid_tree || !m_root_node)
+ return;
+
+ search_result_inserter result_inserter(result);
+ typedef segment_tree<_Key, _Value> tree_type;
+ __st::descend_tree_for_search<tree_type>(point, m_root_node, result_inserter);
+}
+
+template<typename _Key, typename _Value>
+void segment_tree<_Key, _Value>::remove(value_type value)
+{
+ using namespace std;
+
+ typename data_node_map_type::iterator itr = m_tagged_node_map.find(value);
+ if (itr != m_tagged_node_map.end())
+ {
+ // Tagged node list found. Remove all the tags from the tree nodes.
+ node_list_type* plist = itr->second.get();
+ if (!plist)
+ return;
+
+ remove_data_from_nodes(plist, value);
+
+ // Remove the tags associated with this pointer from the data set.
+ m_tagged_node_map.erase(itr);
+ }
+
+ // Remove from the segment data array.
+ m_segment_data.erase(value);
+}
+
+template<typename _Key, typename _Value>
+void segment_tree<_Key, _Value>::clear()
+{
+ m_tagged_node_map.clear();
+ m_segment_data.clear();
+ clear_all_nodes();
+ m_valid_tree = false;
+}
+
+template<typename _Key, typename _Value>
+size_t segment_tree<_Key, _Value>::size() const
+{
+ return m_segment_data.size();
+}
+
+template<typename _Key, typename _Value>
+bool segment_tree<_Key, _Value>::empty() const
+{
+ return m_segment_data.empty();
+}
+
+template<typename _Key, typename _Value>
+size_t segment_tree<_Key, _Value>::leaf_size() const
+{
+ return __st::count_leaf_nodes(m_left_leaf.get(), m_right_leaf.get());
+}
+
+template<typename _Key, typename _Value>
+void segment_tree<_Key, _Value>::remove_data_from_nodes(node_list_type* plist, const value_type pdata)
+{
+ typename node_list_type::iterator itr = plist->begin(), itr_end = plist->end();
+ for (; itr != itr_end; ++itr)
+ {
+ data_chain_type* chain = nullptr;
+ __st::node_base* p = *itr;
+ if (p->is_leaf)
+ chain = static_cast<node*>(p)->value_leaf.data_chain;
+ else
+ chain = static_cast<nonleaf_node*>(p)->value_nonleaf.data_chain;
+
+ if (!chain)
+ continue;
+
+ remove_data_from_chain(*chain, pdata);
+ }
+}
+
+template<typename _Key, typename _Value>
+void segment_tree<_Key, _Value>::remove_data_from_chain(data_chain_type& chain, const value_type pdata)
+{
+ typename data_chain_type::iterator itr = ::std::find(chain.begin(), chain.end(), pdata);
+ if (itr != chain.end())
+ {
+ *itr = chain.back();
+ chain.pop_back();
+ }
+}
+
+template<typename _Key, typename _Value>
+void segment_tree<_Key, _Value>::clear_all_nodes()
+{
+ disconnect_leaf_nodes(m_left_leaf.get(), m_right_leaf.get());
+ m_nonleaf_node_pool.clear();
+ m_left_leaf.reset();
+ m_right_leaf.reset();
+ m_root_node = nullptr;
+}
+
+#ifdef MDDS_UNIT_TEST
+template<typename _Key, typename _Value>
+void segment_tree<_Key, _Value>::dump_tree() const
+{
+ using ::std::cout;
+ using ::std::endl;
+
+ if (!m_valid_tree)
+ assert(!"attempted to dump an invalid tree!");
+
+ cout << "dump tree ------------------------------------------------------" << endl;
+ size_t node_count = mdds::__st::tree_dumper<node, nonleaf_node>::dump(m_root_node);
+ size_t node_instance_count = node::get_instance_count();
+
+ cout << "tree node count = " << node_count << " node instance count = " << node_instance_count << endl;
+}
+
+template<typename _Key, typename _Value>
+void segment_tree<_Key, _Value>::dump_leaf_nodes() const
+{
+ using ::std::cout;
+ using ::std::endl;
+
+ cout << "dump leaf nodes ------------------------------------------------" << endl;
+
+ node* p = m_left_leaf.get();
+ while (p)
+ {
+ print_leaf_value(p->value_leaf);
+ p = p->next.get();
+ }
+ cout << " node instance count = " << node::get_instance_count() << endl;
+}
+
+template<typename _Key, typename _Value>
+void segment_tree<_Key, _Value>::dump_segment_data() const
+{
+ using namespace std;
+ cout << "dump segment data ----------------------------------------------" << endl;
+
+ segment_map_printer func;
+ for_each(m_segment_data.begin(), m_segment_data.end(), func);
+}
+
+template<typename _Key, typename _Value>
+bool segment_tree<_Key, _Value>::verify_node_lists() const
+{
+ using namespace std;
+
+ typename data_node_map_type::const_iterator itr = m_tagged_node_map.begin(), itr_end = m_tagged_node_map.end();
+ for (; itr != itr_end; ++itr)
+ {
+ // Print stored nodes.
+ cout << "node list " << itr->first->name << ": ";
+ const node_list_type* plist = itr->second.get();
+ assert(plist);
+ node_printer func;
+ for_each(plist->begin(), plist->end(), func);
+ cout << endl;
+
+ // Verify that all of these nodes have the data pointer.
+ if (!has_data_pointer(*plist, itr->first))
+ return false;
+ }
+ return true;
+}
+
+template<typename _Key, typename _Value>
+bool segment_tree<_Key, _Value>::verify_leaf_nodes(const ::std::vector<leaf_node_check>& checks) const
+{
+ using namespace std;
+
+ node* cur_node = m_left_leaf.get();
+ typename ::std::vector<leaf_node_check>::const_iterator itr = checks.begin(), itr_end = checks.end();
+ for (; itr != itr_end; ++itr)
+ {
+ if (!cur_node)
+ // Position past the right-mode node. Invalid.
+ return false;
+
+ if (cur_node->value_leaf.key != itr->key)
+ // Key values differ.
+ return false;
+
+ if (itr->data_chain.empty())
+ {
+ if (cur_node->value_leaf.data_chain)
+ // The data chain should be empty (i.e. the pointer should be nullptr).
+ return false;
+ }
+ else
+ {
+ if (!cur_node->value_leaf.data_chain)
+ // This node should have data pointers!
+ return false;
+
+ data_chain_type chain1 = itr->data_chain;
+ data_chain_type chain2 = *cur_node->value_leaf.data_chain;
+
+ if (chain1.size() != chain2.size())
+ return false;
+
+ ::std::vector<value_type> test1, test2;
+ test1.reserve(chain1.size());
+ test2.reserve(chain2.size());
+ copy(chain1.begin(), chain1.end(), back_inserter(test1));
+ copy(chain2.begin(), chain2.end(), back_inserter(test2));
+
+ // Sort both arrays before comparing them.
+ sort(test1.begin(), test1.end());
+ sort(test2.begin(), test2.end());
+
+ if (test1 != test2)
+ return false;
+ }
+
+ cur_node = cur_node->next.get();
+ }
+
+ if (cur_node)
+ // At this point, we expect the current node to be at the position
+ // past the right-most node, which is nullptr.
+ return false;
+
+ return true;
+}
+
+template<typename _Key, typename _Value>
+bool segment_tree<_Key, _Value>::verify_segment_data(const segment_map_type& checks) const
+{
+ // Sort the data by key values first.
+ sorted_segment_map_type seg1(checks.begin(), checks.end());
+ sorted_segment_map_type seg2(m_segment_data.begin(), m_segment_data.end());
+
+ typename sorted_segment_map_type::const_iterator itr1 = seg1.begin(), itr1_end = seg1.end();
+ typename sorted_segment_map_type::const_iterator itr2 = seg2.begin(), itr2_end = seg2.end();
+ for (; itr1 != itr1_end; ++itr1, ++itr2)
+ {
+ if (itr2 == itr2_end)
+ return false;
+
+ if (*itr1 != *itr2)
+ return false;
+ }
+ if (itr2 != itr2_end)
+ return false;
+
+ return true;
+}
+
+template<typename _Key, typename _Value>
+bool segment_tree<_Key, _Value>::has_data_pointer(const node_list_type& node_list, const value_type pdata)
+{
+ using namespace std;
+
+ typename node_list_type::const_iterator itr = node_list.begin(), itr_end = node_list.end();
+
+ for (; itr != itr_end; ++itr)
+ {
+ // Check each node, and make sure each node has the pdata pointer
+ // listed.
+ const __st::node_base* pnode = *itr;
+ const data_chain_type* chain = nullptr;
+ if (pnode->is_leaf)
+ chain = static_cast<const node*>(pnode)->value_leaf.data_chain;
+ else
+ chain = static_cast<const nonleaf_node*>(pnode)->value_nonleaf.data_chain;
+
+ if (!chain)
+ return false;
+
+ if (find(chain->begin(), chain->end(), pdata) == chain->end())
+ return false;
+ }
+ return true;
+}
+
+template<typename _Key, typename _Value>
+void segment_tree<_Key, _Value>::print_leaf_value(const leaf_value_type& v)
+{
+ using namespace std;
+ cout << v.key << ": { ";
+ if (v.data_chain)
+ {
+ const data_chain_type* pchain = v.data_chain;
+ typename data_chain_type::const_iterator itr, itr_beg = pchain->begin(), itr_end = pchain->end();
+ for (itr = itr_beg; itr != itr_end; ++itr)
+ {
+ if (itr != itr_beg)
+ cout << ", ";
+ cout << (*itr)->name;
+ }
+ }
+ cout << " }" << endl;
+}
+#endif
+
+} // namespace mdds
diff --git a/include/mdds/sorted_string_map.hpp b/include/mdds/sorted_string_map.hpp
new file mode 100644
index 0000000..c7c0685
--- /dev/null
+++ b/include/mdds/sorted_string_map.hpp
@@ -0,0 +1,125 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * Copyright (c) 2022 Kohei Yoshida
+ *
+ * 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, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ ************************************************************************/
+
+#pragma once
+
+#include <cstdlib>
+#include <string_view>
+
+namespace mdds {
+
+/**
+ * Single key-value entry. Caller must provide at compile time a static array
+ * of these entries.
+ *
+ * @param key memory address of the first character of the char buffer that
+ * stores the key.
+ * @param key_length length of the char buffer.
+ * @param value value associated with the key.
+ */
+template<typename ValueT, typename SizeT>
+struct chars_map_entry
+{
+ const char* key;
+ SizeT key_length;
+ ValueT value;
+};
+
+template<typename ValueT, typename SizeT>
+struct string_view_map_entry
+{
+ std::string_view key;
+ ValueT value;
+};
+
+/**
+ * sorted_string_map provides an efficient way to map string keys to
+ * arbitrary values, provided that the keys are known at compile time and
+ * are sorted in ascending order.
+ */
+template<typename ValueT, template<typename, typename> class EntryT = chars_map_entry>
+class sorted_string_map
+{
+public:
+ using value_type = ValueT;
+ using size_type = std::size_t;
+ using entry = EntryT<ValueT, size_type>;
+
+ /**
+ * Constructor.
+ *
+ * @param entries pointer to the array of key-value entries.
+ * @param entry_size size of the key-value entry array.
+ * @param null_value null value to return when the find method fails to
+ * find a matching entry.
+ */
+ sorted_string_map(const entry* entries, size_type entry_size, value_type null_value);
+
+ /**
+ * Find a value associated with a specified string key.
+ *
+ * @param input pointer to a C-style string whose value represents the key
+ * to match.
+ * @param len length of the matching string value.
+ *
+ * @return value associated with the key, or the null value in case the
+ * key is not found.
+ */
+ value_type find(const char* input, size_type len) const;
+
+ /**
+ * Find a value associated with a specified string key.
+ *
+ * @param input string key to match.
+ *
+ * @return value associated with the key, or the null value in case the
+ * key is not found.
+ */
+ value_type find(std::string_view input) const;
+
+ /**
+ * Return the number of entries in the map. Since the number of entries
+ * is statically defined at compile time, this method always returns the
+ * same value.
+ *
+ * @return the number of entries in the map.
+ */
+ size_type size() const;
+
+private:
+ const entry* m_entries;
+ value_type m_null_value;
+ size_type m_entry_size;
+ const entry* m_entry_end;
+};
+
+} // namespace mdds
+
+#include "sorted_string_map_def.inl"
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/mdds/sorted_string_map_def.inl b/include/mdds/sorted_string_map_def.inl
new file mode 100644
index 0000000..be0c081
--- /dev/null
+++ b/include/mdds/sorted_string_map_def.inl
@@ -0,0 +1,167 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * Copyright (c) 2022 Kohei Yoshida
+ *
+ * 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, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ ************************************************************************/
+
+#include "./global.hpp"
+
+#include <cstring>
+#include <algorithm>
+
+namespace mdds {
+
+namespace detail {
+
+template<typename ValueT, typename SizeT, template<typename, typename> class EntryT>
+struct entry_funcs;
+
+template<typename ValueT, typename SizeT>
+struct entry_funcs<ValueT, SizeT, chars_map_entry>
+{
+ using entry = chars_map_entry<ValueT, SizeT>;
+ using size_type = SizeT;
+
+ static bool compare(const entry& entry1, const entry& entry2)
+ {
+ if (entry1.key_length != entry2.key_length)
+ {
+ std::size_t key_length = std::min(entry1.key_length, entry2.key_length);
+ int ret = std::memcmp(entry1.key, entry2.key, key_length);
+ if (ret == 0)
+ return entry1.key_length < entry2.key_length;
+
+ return ret < 0;
+ }
+ else
+ {
+ return std::memcmp(entry1.key, entry2.key, entry1.key_length) < 0;
+ }
+ }
+
+ static entry to_entry(const char* input, std::size_t len)
+ {
+ return entry{input, len, ValueT{}};
+ }
+
+ static const char* get_key_ptr(const entry& e)
+ {
+ return e.key;
+ }
+
+ static size_type get_key_size(const entry& e)
+ {
+ return e.key_length;
+ }
+};
+
+template<typename ValueT, typename SizeT>
+struct entry_funcs<ValueT, SizeT, string_view_map_entry>
+{
+ using entry = string_view_map_entry<ValueT, SizeT>;
+ using size_type = SizeT;
+
+ static bool compare(const entry& entry1, const entry& entry2)
+ {
+ if (entry1.key.size() != entry2.key.size())
+ {
+ std::size_t key_length = std::min(entry1.key.size(), entry2.key.size());
+ int ret = std::memcmp(entry1.key.data(), entry2.key.data(), key_length);
+ if (ret == 0)
+ return entry1.key.size() < entry2.key.size();
+
+ return ret < 0;
+ }
+ else
+ {
+ return std::memcmp(entry1.key.data(), entry2.key.data(), entry1.key.size()) < 0;
+ }
+ }
+
+ static entry to_entry(const char* input, std::size_t len)
+ {
+ return entry{{input, len}, ValueT{}};
+ }
+
+ static const char* get_key_ptr(const entry& e)
+ {
+ return e.key.data();
+ }
+
+ static size_type get_key_size(const entry& e)
+ {
+ return e.key.size();
+ }
+};
+
+} // namespace detail
+
+template<typename ValueT, template<typename, typename> class EntryT>
+sorted_string_map<ValueT, EntryT>::sorted_string_map(const entry* entries, size_type entry_size, value_type null_value)
+ : m_entries(entries), m_null_value(null_value), m_entry_size(entry_size), m_entry_end(m_entries + m_entry_size)
+{
+#ifdef MDDS_SORTED_STRING_MAP_DEBUG
+ using entry_funcs = detail::entry_funcs<value_type, size_type, EntryT>;
+
+ if (!std::is_sorted(m_entries, m_entry_end, entry_funcs::compare))
+ throw invalid_arg_error("mapped entries are not sorted");
+#endif
+}
+
+template<typename ValueT, template<typename, typename> class EntryT>
+typename sorted_string_map<ValueT, EntryT>::value_type sorted_string_map<ValueT, EntryT>::find(
+ const char* input, size_type len) const
+{
+ if (m_entry_size == 0)
+ return m_null_value;
+
+ using entry_funcs = detail::entry_funcs<value_type, size_type, EntryT>;
+ entry ent = entry_funcs::to_entry(input, len);
+
+ const entry* val = std::lower_bound(m_entries, m_entry_end, ent, entry_funcs::compare);
+
+ if (val == m_entry_end || entry_funcs::get_key_size(*val) != len ||
+ std::memcmp(entry_funcs::get_key_ptr(*val), input, len))
+ return m_null_value;
+
+ return val->value;
+}
+
+template<typename ValueT, template<typename, typename> class EntryT>
+typename sorted_string_map<ValueT, EntryT>::value_type sorted_string_map<ValueT, EntryT>::find(
+ std::string_view input) const
+{
+ return find(input.data(), input.size());
+}
+
+template<typename ValueT, template<typename, typename> class EntryT>
+typename sorted_string_map<ValueT, EntryT>::size_type sorted_string_map<ValueT, EntryT>::size() const
+{
+ return m_entry_size;
+}
+
+} // namespace mdds
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/mdds/trie_map.hpp b/include/mdds/trie_map.hpp
new file mode 100644
index 0000000..c0fe6a0
--- /dev/null
+++ b/include/mdds/trie_map.hpp
@@ -0,0 +1,713 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * Copyright (c) 2015-2020 Kohei Yoshida
+ *
+ * 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, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ ************************************************************************/
+
+#ifndef INCLUDED_MDDS_TRIE_MAP_HPP
+#define INCLUDED_MDDS_TRIE_MAP_HPP
+
+#include "trie_map_itr.hpp"
+
+#include <vector>
+#include <string>
+#include <deque>
+#include <map>
+#include <memory>
+
+namespace mdds {
+
+namespace trie {
+
+/**
+ * Template for a key type implemented using a typical STL container type.
+ */
+template<typename ContainerT>
+struct std_container_traits
+{
+ /** type used to store a key value. */
+ using key_type = ContainerT;
+
+ /**
+ * type used to build an intermediate key value, from which a final key
+ * value is to be created. It is expected to be an array structure whose
+ * content is contiguous in memory. Its elements must be of
+ * key_unit_type.
+ */
+ using key_buffer_type = key_type;
+
+ /**
+ * type that represents a single character inside a key or a key buffer
+ * object. A key object is expected to store a series of elements of this
+ * type.
+ */
+ using key_unit_type = typename key_type::value_type;
+
+ /**
+ * Function called to create and initialize a buffer object from a given
+ * initial key value.
+ *
+ * @param str pointer to the first character of the key value.
+ * @param length length of the key value.
+ *
+ * @return buffer object containing the specified key value.
+ */
+ static key_buffer_type to_key_buffer(const key_unit_type* str, size_t length)
+ {
+ return key_buffer_type(str, length);
+ }
+
+ /**
+ * Function called to create and initialize a buffer object from a given
+ * initial key value.
+ *
+ * @param key key value
+ *
+ * @return buffer object containing the specified key value.
+ */
+ static key_buffer_type to_key_buffer(const key_type& key)
+ {
+ return key_buffer_type(key);
+ }
+
+ static const key_unit_type* buffer_data(const key_buffer_type& buf)
+ {
+ return buf.data();
+ }
+
+ static size_t buffer_size(const key_buffer_type& buf)
+ {
+ return buf.size();
+ }
+
+ /**
+ * Function called to append a single character to the end of a key
+ * buffer.
+ *
+ * @param buffer buffer object to append character to.
+ * @param c character to append to the buffer.
+ */
+ static void push_back(key_buffer_type& buffer, key_unit_type c)
+ {
+ buffer.push_back(c);
+ }
+
+ /**
+ * Function called to remove a single character from the tail of an
+ * existing key buffer.
+ *
+ * @param buffer buffer object to remove character from.
+ */
+ static void pop_back(key_buffer_type& buffer)
+ {
+ buffer.pop_back();
+ }
+
+ /**
+ * Function called to create a final string object from an existing
+ * buffer.
+ *
+ * @param buf buffer object to create a final string object from.
+ *
+ * @return string object whose content is created from the buffer object.
+ */
+ static key_type to_key(const key_buffer_type& buf)
+ {
+ return buf;
+ }
+};
+
+using std_string_traits = std_container_traits<std::string>;
+
+/** Serializer for numeric data types. */
+template<typename T>
+struct numeric_value_serializer
+{
+ static constexpr bool variable_size = false;
+
+ static constexpr size_t value_size = sizeof(T);
+
+ static void write(std::ostream& os, const T& v);
+
+ static void read(std::istream& is, size_t n, T& v);
+};
+
+/** Serializer for variable-size data types. */
+template<typename T>
+struct variable_value_serializer
+{
+ static constexpr bool variable_size = true;
+
+ static void write(std::ostream& os, const T& v);
+
+ static void read(std::istream& is, size_t n, T& v);
+};
+
+/**
+ * Serializer for standard sequence container whose value type is of
+ * numeric value type.
+ */
+template<typename T>
+struct numeric_sequence_value_serializer
+{
+ using element_serializer = numeric_value_serializer<typename T::value_type>;
+
+ static constexpr bool variable_size = true;
+
+ static void write(std::ostream& os, const T& v);
+
+ static void read(std::istream& is, size_t n, T& v);
+};
+
+/**
+ * Default value serializer for mdds::packed_trie_map's load_state and
+ * save_state methods. The primary template is used for numeric value
+ * types, and template specializations exist for std::string, as
+ * well as sequence containers, such as std::vector, whose elements are of
+ * numeric types.
+ */
+template<typename T, typename U = void>
+struct value_serializer : numeric_value_serializer<T>
+{
+};
+
+template<typename T>
+struct value_serializer<T, typename std::enable_if<mdds::detail::has_value_type<T>::value>::type>
+ : numeric_sequence_value_serializer<T>
+{
+};
+
+template<>
+struct value_serializer<std::string> : variable_value_serializer<std::string>
+{
+};
+
+} // namespace trie
+
+template<typename KeyTraits, typename ValueT>
+class packed_trie_map;
+
+/**
+ * Trie map provides storage for multiple key-value pairs where keys are
+ * either strings, or otherwise consist of arrays of characters. The keys
+ * are stored in an ordered tree structure known as trie, or sometimes
+ * referred to as prefix tree.
+ */
+template<typename KeyTraits, typename ValueT>
+class trie_map
+{
+ friend class packed_trie_map<KeyTraits, ValueT>;
+ friend class trie::detail::iterator_base<trie_map, true>;
+ friend class trie::detail::iterator_base<trie_map, false>;
+ friend class trie::detail::const_iterator<trie_map>;
+ friend class trie::detail::iterator<trie_map>;
+ friend class trie::detail::search_results<trie_map>;
+ friend trie::detail::get_node_stack_type<trie_map, std::true_type>;
+ friend trie::detail::get_node_stack_type<trie_map, std::false_type>;
+
+public:
+ typedef packed_trie_map<KeyTraits, ValueT> packed_type;
+ typedef KeyTraits key_traits_type;
+ typedef typename key_traits_type::key_type key_type;
+ typedef typename key_traits_type::key_buffer_type key_buffer_type;
+ typedef typename key_traits_type::key_unit_type key_unit_type;
+ typedef ValueT value_type;
+ typedef size_t size_type;
+ typedef std::pair<key_type, value_type> key_value_type;
+
+ using const_iterator = trie::detail::const_iterator<trie_map>;
+ using iterator = trie::detail::iterator<trie_map>;
+ typedef trie::detail::search_results<trie_map> search_results;
+
+private:
+ struct trie_node
+ {
+ typedef std::map<key_unit_type, trie_node> children_type;
+
+ children_type children;
+ value_type value;
+ bool has_value;
+
+ trie_node();
+ trie_node(const trie_node& other);
+ trie_node(trie_node&& other);
+
+ void swap(trie_node& other);
+ };
+
+ template<bool IsConst>
+ struct stack_item
+ {
+ using _is_const = std::bool_constant<IsConst>;
+
+ using child_pos_type =
+ typename mdds::detail::get_iterator_type<typename trie_node::children_type, _is_const>::type;
+
+ using trie_node_type = typename mdds::detail::const_or_not<trie_node, _is_const>::type;
+
+ trie_node_type* node;
+ child_pos_type child_pos;
+
+ stack_item(trie_node_type* _node, const child_pos_type& _child_pos) : node(_node), child_pos(_child_pos)
+ {}
+
+ bool operator==(const stack_item& r) const
+ {
+ return node == r.node && child_pos == r.child_pos;
+ }
+
+ bool operator!=(const stack_item& r) const
+ {
+ return !operator==(r);
+ }
+ };
+
+ using const_node_stack_type = std::vector<stack_item<true>>;
+ using node_stack_type = std::vector<stack_item<false>>;
+
+public:
+ /**
+ * Default constructor.
+ */
+ trie_map();
+
+ trie_map(const trie_map& other);
+
+ trie_map(trie_map&& other);
+
+ const_iterator begin() const;
+
+ iterator begin();
+
+ const_iterator end() const;
+
+ iterator end();
+
+ trie_map& operator=(trie_map other);
+
+ void swap(trie_map& other);
+
+ /**
+ * Insert a new key-value pair.
+ *
+ * @param key key value.
+ * @param value value to associate with the key.
+ */
+ void insert(const key_type& key, const value_type& value);
+
+ /**
+ * Insert a new key-value pair.
+ *
+ * @param key pointer to the first character of a character array that
+ * stores key value.
+ * @param len length of the character array storing the key.
+ * @param value value to associate with the key.
+ */
+ void insert(const key_unit_type* key, size_type len, const value_type& value);
+
+ /**
+ * Erase a key and the value associated with it.
+ *
+ * @param key pointer to the first character of a character array that
+ * stores key value.
+ * @param len length of the character array storing the key.
+ *
+ * @return true if a key is erased, false otherwise.
+ */
+ bool erase(const key_unit_type* key, size_type len);
+
+ /**
+ * Find a value associated with a specified key.
+ *
+ * @param key key to match.
+ *
+ * @return immutable iterator that references a value associated with the
+ * key, or the end position in case the key is not found.
+ */
+ const_iterator find(const key_type& key) const;
+
+ /**
+ * Find a value associated with a specified key.
+ *
+ * @param input pointer to an array whose value represents the key to
+ * match.
+ * @param len length of the matching key value.
+ *
+ * @return immutable iterator that references a value associated with the
+ * key, or the end position in case the key is not found.
+ */
+ const_iterator find(const key_unit_type* input, size_type len) const;
+
+ /**
+ * Find a value associated with a specified key.
+ *
+ * @param key key to match.
+ *
+ * @return mutable iterator that references a value associated with the
+ * key, or the end position in case the key is not found.
+ */
+ iterator find(const key_type& key);
+
+ /**
+ * Find a value associated with a specified key.
+ *
+ * @param input pointer to an array whose value represents the key to
+ * match.
+ * @param len length of the matching key value.
+ *
+ * @return mutable iterator that references a value associated with the
+ * key, or the end position in case the key is not found.
+ */
+ iterator find(const key_unit_type* input, size_type len);
+
+ /**
+ * Retrieve all key-value pairs whose keys start with specified prefix.
+ * You can also retrieve all key-value pairs by passing a null prefix and
+ * a length of zero.
+ *
+ * @param prefix prefix to match.
+ *
+ * @return results object that contains all matching key-value pairs. The
+ * results are sorted by the key in ascending order.
+ */
+ search_results prefix_search(const key_type& prefix) const;
+
+ /**
+ * Retrieve all key-value pairs whose keys start with specified prefix.
+ * You can also retrieve all key-value pairs by passing a null prefix and
+ * a length of zero.
+ *
+ * @param prefix pointer to an array whose value represents the prefix to
+ * match.
+ * @param len length of the prefix value to match.
+ *
+ * @return results object that contains all matching key-value pairs. The
+ * results are sorted by the key in ascending order.
+ */
+ search_results prefix_search(const key_unit_type* prefix, size_type len) const;
+
+ /**
+ * Return the number of entries in the map.
+ *
+ * @return the number of entries in the map.
+ */
+ size_type size() const;
+
+ bool empty() const noexcept;
+
+ /**
+ * Empty the container.
+ */
+ void clear();
+
+ /**
+ * Create a compressed and immutable version of the container which
+ * provides better search performance and requires much less memory
+ * footprint.
+ *
+ * @return an instance of mdds::packed_trie_map with the same content.
+ */
+ packed_type pack() const;
+
+private:
+ void insert_into_tree(
+ trie_node& node, const key_unit_type* key, const key_unit_type* key_end, const value_type& value);
+
+ const trie_node* find_prefix_node(
+ const trie_node& node, const key_unit_type* prefix, const key_unit_type* prefix_end) const;
+
+ template<bool IsConst>
+ void find_prefix_node_with_stack(
+ std::vector<stack_item<IsConst>>& node_stack, mdds::detail::const_t<trie_node, IsConst>& node,
+ const key_unit_type* prefix, const key_unit_type* prefix_end) const;
+
+ template<bool IsConst>
+ key_buffer_type build_key_buffer_from_node_stack(const std::vector<stack_item<IsConst>>& node_stack) const;
+
+ void count_values(size_type& n, const trie_node& node) const;
+
+private:
+ trie_node m_root;
+};
+
+/**
+ * An immutable trie container that packs its content into a contiguous
+ * array to achieve both space efficiency and lookup performance. The user
+ * of this data structure must provide a pre-constructed list of key-value
+ * entries that are sorted by the key in ascending order, or construct from
+ * an instance of mdds::trie_map.
+ *
+ * Note that, since this container is immutable, the content of the
+ * container cannot be modified once constructed.
+ */
+template<typename KeyTraits, typename ValueT>
+class packed_trie_map
+{
+ friend class trie::detail::packed_iterator_base<packed_trie_map>;
+ friend class trie::detail::packed_search_results<packed_trie_map>;
+
+public:
+ typedef KeyTraits key_traits_type;
+ typedef typename key_traits_type::key_type key_type;
+ typedef typename key_traits_type::key_buffer_type key_buffer_type;
+ typedef typename key_traits_type::key_unit_type key_unit_type;
+ typedef ValueT value_type;
+ typedef size_t size_type;
+ typedef std::pair<key_type, value_type> key_value_type;
+ typedef trie::detail::packed_iterator_base<packed_trie_map> const_iterator;
+ typedef trie::detail::packed_search_results<packed_trie_map> search_results;
+
+ /**
+ * Single key-value entry. Caller must provide at compile time a static
+ * array of these entries.
+ */
+ struct entry
+ {
+ const key_unit_type* key;
+ size_type keylen;
+ value_type value;
+
+ entry(const key_unit_type* _key, size_type _keylen, value_type _value)
+ : key(_key), keylen(_keylen), value(_value)
+ {}
+ };
+
+private:
+ struct trie_node
+ {
+ key_unit_type key;
+ const value_type* value;
+
+ std::deque<trie_node*> children;
+
+ trie_node(key_unit_type _key) : key(_key), value(nullptr)
+ {}
+ };
+
+ struct stack_item
+ {
+ const uintptr_t* node_pos;
+ const uintptr_t* child_pos;
+ const uintptr_t* child_end;
+
+ stack_item(const uintptr_t* _node_pos, const uintptr_t* _child_pos, const uintptr_t* _child_end)
+ : node_pos(_node_pos), child_pos(_child_pos), child_end(_child_end)
+ {}
+
+ bool operator==(const stack_item& other) const
+ {
+ return node_pos == other.node_pos && child_pos == other.child_pos;
+ }
+
+ bool operator!=(const stack_item& other) const
+ {
+ return !operator==(other);
+ }
+
+ bool has_value() const
+ {
+ const value_type* pv = reinterpret_cast<const value_type*>(*node_pos);
+ return pv;
+ }
+
+ const value_type* get_value() const
+ {
+ return reinterpret_cast<const value_type*>(*node_pos);
+ }
+ };
+
+ typedef std::vector<stack_item> node_stack_type;
+
+ typedef std::deque<trie_node> node_pool_type;
+ typedef std::vector<uintptr_t> packed_type;
+ typedef std::deque<value_type> value_store_type;
+ typedef std::vector<std::tuple<size_t, key_unit_type>> child_offsets_type;
+
+public:
+ packed_trie_map();
+
+ /**
+ * Constructor that initializes the content from a static list of
+ * key-value entries. The caller <em>must</em> ensure that the key-value
+ * entries are sorted in ascending order, else the behavior is undefined.
+ *
+ * @param entries pointer to the array of key-value entries.
+ * @param entry_size size of the key-value entry array.
+ */
+ packed_trie_map(const entry* entries, size_type entry_size);
+
+ /**
+ * Constructor to allow construction of an instance from the content of a
+ * mdds::trie_map instance.
+ *
+ * @param other mdds::trie_map instance to build content from.
+ */
+ packed_trie_map(const trie_map<key_traits_type, value_type>& other);
+
+ packed_trie_map(const packed_trie_map& other);
+
+ packed_trie_map(packed_trie_map&& other);
+
+ packed_trie_map& operator=(packed_trie_map other);
+
+ bool operator==(const packed_trie_map& other) const;
+
+ bool operator!=(const packed_trie_map& other) const;
+
+ const_iterator begin() const;
+
+ const_iterator end() const;
+
+ const_iterator cbegin() const;
+
+ const_iterator cend() const;
+
+ /**
+ * Find a value associated with a specified key.
+ *
+ * @param key key to match.
+ *
+ * @return iterator that references a value associated with the key, or
+ * the end position in case the key is not found.
+ */
+ const_iterator find(const key_type& key) const;
+
+ /**
+ * Find a value associated with a specified key.
+ *
+ * @param input pointer to an array whose value represents the key to
+ * match.
+ * @param len length of the matching key value.
+ *
+ * @return iterator that references a value associated with the key, or
+ * the end position in case the key is not found.
+ */
+ const_iterator find(const key_unit_type* input, size_type len) const;
+
+ /**
+ * Retrieve all key-value pairs whose keys start with specified prefix.
+ * You can also retrieve all key-value pairs by passing a null prefix and
+ * a length of zero.
+ *
+ * @param prefix prefix to match.
+ *
+ * @return results object containing all matching key-value pairs.
+ */
+ search_results prefix_search(const key_type& prefix) const;
+
+ /**
+ * Retrieve all key-value pairs whose keys start with specified prefix.
+ * You can also retrieve all key-value pairs by passing a null prefix and
+ * a length of zero.
+ *
+ * @param prefix pointer to an array whose value represents the prefix to
+ * match.
+ * @param len length of the prefix value to match.
+ *
+ * @return results object that contains all matching key-value pairs. The
+ * results are sorted by the key in ascending order.
+ */
+ search_results prefix_search(const key_unit_type* prefix, size_type len) const;
+
+ /**
+ * Return the number of entries in the map.
+ *
+ * @return the number of entries in the map.
+ */
+ size_type size() const noexcept;
+
+ bool empty() const noexcept;
+
+ void swap(packed_trie_map& other);
+
+ /**
+ * Save the state of the instance of this class to an external buffer.
+ *
+ * @param os output stream to write the state to.
+ */
+ template<typename FuncT = trie::value_serializer<value_type>>
+ void save_state(std::ostream& os) const;
+
+ /**
+ * Restore the state of the instance of this class from an external
+ * buffer.
+ *
+ * @param is input stream to load the state from.
+ */
+ template<typename FuncT = trie::value_serializer<value_type>>
+ void load_state(std::istream& is);
+
+ /**
+ * Dump the structure of the trie content for debugging. You must define
+ * <code>MDDS_TRIE_MAP_DEBUG</code> in order for this method to generate
+ * output.
+ */
+ void dump_structure() const;
+
+private:
+ node_stack_type get_root_stack() const;
+
+ void traverse_range(
+ trie_node& root, node_pool_type& node_pool, const entry* start, const entry* end, size_type pos);
+
+ size_type compact_node(const trie_node& node);
+ size_type compact_node(const typename trie_map<KeyTraits, ValueT>::trie_node& node);
+
+ void push_child_offsets(size_type offset, const child_offsets_type& child_offsets);
+
+ void compact(const trie_node& root);
+ void compact(const typename trie_map<KeyTraits, ValueT>::trie_node& root);
+
+ const uintptr_t* find_prefix_node(
+ const uintptr_t* p, const key_unit_type* prefix, const key_unit_type* prefix_end) const;
+
+ void find_prefix_node_with_stack(
+ node_stack_type& node_stack, const uintptr_t* p, const key_unit_type* prefix,
+ const key_unit_type* prefix_end) const;
+
+ template<typename _Handler>
+ void traverse_tree(_Handler hdl) const;
+
+ template<typename _Handler>
+ void traverse_buffer(_Handler hdl) const;
+
+#ifdef MDDS_TRIE_MAP_DEBUG
+ void dump_node(key_buffer_type& buffer, const trie_node& node) const;
+ void dump_trie(const trie_node& root) const;
+ void dump_packed_trie() const;
+#endif
+
+private:
+ value_store_type m_value_store;
+ packed_type m_packed;
+};
+
+} // namespace mdds
+
+#include "trie_map_def.inl"
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/mdds/trie_map_def.inl b/include/mdds/trie_map_def.inl
new file mode 100644
index 0000000..e716469
--- /dev/null
+++ b/include/mdds/trie_map_def.inl
@@ -0,0 +1,1735 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * Copyright (c) 2015 Kohei Yoshida
+ *
+ * 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, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ ************************************************************************/
+
+#include "mdds/global.hpp"
+
+#include <cassert>
+#include <algorithm>
+#include <memory>
+#include <sstream>
+#include <type_traits>
+#include <cstring>
+
+#ifdef MDDS_TRIE_MAP_DEBUG
+#include <iostream>
+#include <iomanip>
+using std::cout;
+using std::endl;
+#endif
+
+namespace mdds {
+
+namespace detail { namespace trie {
+
+inline const char* value_type_size_name(bool variable_size)
+{
+ return variable_size ? "varaible size" : "fixed size";
+}
+
+union bin_value
+{
+ char buffer[8];
+ uint8_t ui8;
+ uint16_t ui16;
+ uint32_t ui32;
+ uint64_t ui64;
+};
+
+using value_addrs_type = std::map<const void*, size_t>;
+
+template<typename FuncT, typename ValueT>
+struct write_variable_size_values_to_ostream
+{
+ value_addrs_type operator()(std::ostream& os, const std::deque<ValueT>& value_store) const
+ {
+ bin_value bv;
+
+ value_addrs_type value_addrs;
+
+ size_t pos = 0;
+ for (const ValueT& v : value_store)
+ {
+ auto sp_size = os.tellp(); // position to come back to to write the size.
+ bv.ui32 = 0;
+ os.write(bv.buffer, 4); // write 0 as a placeholder.
+
+ auto sp_start = os.tellp();
+ FuncT::write(os, v);
+ auto sp_end = os.tellp();
+
+ bv.ui32 = sp_end - sp_start; // bytes written
+
+ // go back and write the actual bytes written.
+ os.seekp(sp_size);
+ os.write(bv.buffer, 4);
+ os.seekp(sp_end);
+
+ value_addrs.insert({&v, pos++});
+ }
+
+ return value_addrs;
+ }
+};
+
+template<typename FuncT, typename ValueT>
+struct write_fixed_size_values_to_ostream
+{
+ value_addrs_type operator()(std::ostream& os, const std::deque<ValueT>& value_store) const
+ {
+ bin_value bv;
+ value_addrs_type value_addrs;
+
+ // Write the size of constant-size values.
+ bv.ui32 = FuncT::value_size;
+ os.write(bv.buffer, 4);
+
+ size_t pos = 0;
+ for (const ValueT& v : value_store)
+ {
+ auto sp_start = os.tellp();
+ FuncT::write(os, v);
+ auto sp_end = os.tellp();
+
+ size_t bytes_written = sp_end - sp_start;
+ if (bytes_written != FuncT::value_size)
+ {
+ std::ostringstream msg;
+ msg << "bytes written (" << bytes_written << ") does not equal the value size (" << FuncT::value_size
+ << ")";
+ throw size_error(msg.str());
+ }
+
+ value_addrs.insert({&v, pos++});
+ }
+
+ return value_addrs;
+ }
+};
+
+template<typename FuncT, typename ValueT, typename SizeTrait>
+struct write_values_to_ostream;
+
+template<typename FuncT, typename ValueT>
+struct write_values_to_ostream<FuncT, ValueT, std::true_type> : write_variable_size_values_to_ostream<FuncT, ValueT>
+{
+};
+
+template<typename FuncT, typename ValueT>
+struct write_values_to_ostream<FuncT, ValueT, std::false_type> : write_fixed_size_values_to_ostream<FuncT, ValueT>
+{
+};
+
+template<typename FuncT, typename ValueT>
+struct read_fixed_size_values_from_istream
+{
+ using value_store_type = std::deque<ValueT>;
+
+ value_store_type operator()(std::istream& is, uint32_t value_count) const
+ {
+ value_store_type value_store;
+ bin_value bv;
+
+ // read the size of the value.
+ is.read(bv.buffer, 4);
+ size_t size = bv.ui32;
+
+ if (size != FuncT::value_size)
+ {
+ std::ostringstream os;
+ os << "wrong size of fixed value type (expected: " << FuncT::value_size << "; actual: " << size << ")";
+ throw std::invalid_argument(os.str());
+ }
+
+ for (uint32_t i = 0; i < value_count; ++i)
+ {
+ value_store.emplace_back();
+ FuncT::read(is, size, value_store.back());
+ }
+
+ return value_store;
+ }
+};
+
+template<typename FuncT, typename ValueT>
+struct read_variable_size_values_from_istream
+{
+ using value_store_type = std::deque<ValueT>;
+
+ value_store_type operator()(std::istream& is, uint32_t value_count) const
+ {
+ value_store_type value_store;
+ bin_value bv;
+
+ for (uint32_t i = 0; i < value_count; ++i)
+ {
+ is.read(bv.buffer, 4);
+ size_t size = bv.ui32;
+
+ ValueT v;
+ FuncT::read(is, size, v);
+
+ value_store.push_back(std::move(v));
+ }
+
+ return value_store;
+ }
+};
+
+template<typename FuncT, typename ValueT, typename SizeTrait>
+struct read_values_from_istream;
+
+template<typename FuncT, typename ValueT>
+struct read_values_from_istream<FuncT, ValueT, std::true_type> : read_variable_size_values_from_istream<FuncT, ValueT>
+{
+};
+
+template<typename FuncT, typename ValueT>
+struct read_values_from_istream<FuncT, ValueT, std::false_type> : read_fixed_size_values_from_istream<FuncT, ValueT>
+{
+};
+
+}} // namespace detail::trie
+
+namespace trie {
+
+template<typename T>
+void numeric_value_serializer<T>::write(std::ostream& os, const T& v)
+{
+ static_assert(std::is_arithmetic<T>::value, "not a numeric type.");
+
+ constexpr size_t s = sizeof(T);
+ const char* p = reinterpret_cast<const char*>(&v);
+ os.write(p, s);
+}
+
+template<typename T>
+void numeric_value_serializer<T>::read(std::istream& is, size_t n, T& v)
+{
+ static_assert(std::is_arithmetic<T>::value, "not a numeric type.");
+
+ constexpr size_t s = sizeof(T);
+ assert(s == n);
+
+ union
+ {
+ char buffer[s];
+ T v;
+
+ } buf;
+
+ char* p = buf.buffer;
+
+ while (n)
+ {
+ is.read(p, s);
+ auto size_read = is.gcount();
+ n -= size_read;
+ p += size_read;
+ }
+
+ v = buf.v;
+}
+
+template<typename T>
+void numeric_sequence_value_serializer<T>::write(std::ostream& os, const T& v)
+{
+ static_assert(
+ std::is_arithmetic<typename T::value_type>::value, "value type of this vector is not a numeric type.");
+
+ for (const auto& elem : v)
+ element_serializer::write(os, elem);
+}
+
+template<typename T>
+void numeric_sequence_value_serializer<T>::read(std::istream& is, size_t n, T& v)
+{
+ using elem_type = typename T::value_type;
+ static_assert(
+ std::is_arithmetic<typename T::value_type>::value, "value type of this vector is not a numeric type.");
+
+ constexpr size_t elem_size = element_serializer::value_size;
+ assert(n % elem_size == 0);
+
+ size_t elem_count = n / elem_size;
+
+ for (size_t i = 0; i < elem_count; ++i)
+ {
+ elem_type elem;
+ element_serializer::read(is, elem_size, elem);
+ v.push_back(elem);
+ }
+}
+
+template<>
+inline void variable_value_serializer<std::string>::write(std::ostream& os, const std::string& v)
+{
+ os.write(v.data(), v.size());
+}
+
+template<>
+inline void variable_value_serializer<std::string>::read(std::istream& is, size_t n, std::string& v)
+{
+ v.resize(n);
+ char* p = const_cast<char*>(v.data());
+
+ while (n)
+ {
+ is.read(p, n);
+ auto size_read = is.gcount();
+ n -= size_read;
+ p += size_read;
+ }
+}
+
+} // namespace trie
+
+template<typename KeyTraits, typename ValueT>
+trie_map<KeyTraits, ValueT>::trie_map::trie_node::trie_node() : value(value_type()), has_value(false)
+{}
+
+template<typename KeyTraits, typename ValueT>
+trie_map<KeyTraits, ValueT>::trie_map::trie_node::trie_node(const trie_node& other)
+ : children(other.children), value(other.value), has_value(other.has_value)
+{}
+
+template<typename KeyTraits, typename ValueT>
+trie_map<KeyTraits, ValueT>::trie_map::trie_node::trie_node(trie_node&& other)
+ : children(std::move(other.children)), value(std::move(other.value)), has_value(std::move(other.has_value))
+{}
+
+template<typename KeyTraits, typename ValueT>
+void trie_map<KeyTraits, ValueT>::trie_map::trie_node::swap(trie_node& other)
+{
+ children.swap(other.children);
+ std::swap(value, other.value);
+ std::swap(has_value, other.has_value);
+}
+
+template<typename KeyTraits, typename ValueT>
+trie_map<KeyTraits, ValueT>::trie_map()
+{}
+
+template<typename KeyTraits, typename ValueT>
+trie_map<KeyTraits, ValueT>::trie_map(const trie_map& other) : m_root(other.m_root)
+{}
+
+template<typename KeyTraits, typename ValueT>
+trie_map<KeyTraits, ValueT>::trie_map(trie_map&& other) : m_root(std::move(other.m_root))
+{}
+
+template<typename KeyTraits, typename ValueT>
+typename trie_map<KeyTraits, ValueT>::const_iterator trie_map<KeyTraits, ValueT>::begin() const
+{
+ if (m_root.children.empty())
+ // empty container
+ return end();
+
+ // Push the root node.
+ key_buffer_type buf;
+ const_node_stack_type node_stack;
+ node_stack.emplace_back(&m_root, m_root.children.begin());
+
+ // Push root's first child node.
+ auto it = node_stack.back().child_pos;
+ const_iterator::push_child_node_to_stack(node_stack, buf, it);
+
+ // In theory there should always be at least one value node along the
+ // left-most branch.
+
+ while (!node_stack.back().node->has_value)
+ {
+ auto this_it = node_stack.back().child_pos;
+ const_iterator::push_child_node_to_stack(node_stack, buf, this_it);
+ }
+
+ return const_iterator(std::move(node_stack), std::move(buf), trie::detail::iterator_type::normal);
+}
+
+template<typename KeyTraits, typename ValueT>
+typename trie_map<KeyTraits, ValueT>::iterator trie_map<KeyTraits, ValueT>::begin()
+{
+ if (m_root.children.empty())
+ // empty container
+ return end();
+
+ // Push the root node.
+ key_buffer_type buf;
+ node_stack_type node_stack;
+ node_stack.emplace_back(&m_root, m_root.children.begin());
+
+ // Push root's first child node.
+ auto it = node_stack.back().child_pos;
+ iterator::push_child_node_to_stack(node_stack, buf, it);
+
+ // In theory there should always be at least one value node along the
+ // left-most branch.
+
+ while (!node_stack.back().node->has_value)
+ {
+ auto this_it = node_stack.back().child_pos;
+ iterator::push_child_node_to_stack(node_stack, buf, this_it);
+ }
+
+ return iterator(std::move(node_stack), std::move(buf), trie::detail::iterator_type::normal);
+}
+
+template<typename KeyTraits, typename ValueT>
+typename trie_map<KeyTraits, ValueT>::const_iterator trie_map<KeyTraits, ValueT>::end() const
+{
+ const_node_stack_type node_stack;
+ node_stack.emplace_back(&m_root, m_root.children.end());
+ return const_iterator(std::move(node_stack), key_buffer_type(), trie::detail::iterator_type::end);
+}
+
+template<typename KeyTraits, typename ValueT>
+typename trie_map<KeyTraits, ValueT>::iterator trie_map<KeyTraits, ValueT>::end()
+{
+ node_stack_type node_stack;
+ node_stack.emplace_back(&m_root, m_root.children.end());
+ return iterator(std::move(node_stack), key_buffer_type(), trie::detail::iterator_type::end);
+}
+
+template<typename KeyTraits, typename ValueT>
+trie_map<KeyTraits, ValueT>& trie_map<KeyTraits, ValueT>::operator=(trie_map other)
+{
+ trie_map tmp(std::move(other));
+ tmp.swap(*this);
+ return *this;
+}
+
+template<typename KeyTraits, typename ValueT>
+void trie_map<KeyTraits, ValueT>::swap(trie_map& other)
+{
+ m_root.swap(other.m_root);
+}
+
+template<typename KeyTraits, typename ValueT>
+void trie_map<KeyTraits, ValueT>::insert(const key_type& key, const value_type& value)
+{
+ using ktt = key_traits_type;
+
+ key_buffer_type buf = ktt::to_key_buffer(key);
+ const key_unit_type* p = ktt::buffer_data(buf);
+ size_t n = ktt::buffer_size(buf);
+ const key_unit_type* p_end = p + n;
+ insert_into_tree(m_root, p, p_end, value);
+}
+
+template<typename KeyTraits, typename ValueT>
+void trie_map<KeyTraits, ValueT>::insert(const key_unit_type* key, size_type len, const value_type& value)
+{
+ const key_unit_type* key_end = key + len;
+ insert_into_tree(m_root, key, key_end, value);
+}
+
+template<typename KeyTraits, typename ValueT>
+bool trie_map<KeyTraits, ValueT>::erase(const key_unit_type* key, size_type len)
+{
+ const key_unit_type* key_end = key + len;
+
+ const_node_stack_type node_stack;
+ find_prefix_node_with_stack(node_stack, m_root, key, key_end);
+
+ if (node_stack.empty() || !node_stack.back().node->has_value)
+ // Nothing is erased.
+ return false;
+
+ const trie_node* node = node_stack.back().node;
+ trie_node* node_mod = const_cast<trie_node*>(node);
+ node_mod->has_value = false;
+
+ // If this is a leaf node, remove it, and keep removing its parents until
+ // we reach a parent node that still has at least one child node.
+
+ while (!node->has_value && node->children.empty() && node_stack.size() > 1)
+ {
+ node_stack.pop_back();
+ auto& si = node_stack.back();
+
+ const_cast<trie_node*>(si.node)->children.erase(si.child_pos);
+ node = si.node;
+ }
+
+ return true;
+}
+
+template<typename KeyTraits, typename ValueT>
+void trie_map<KeyTraits, ValueT>::insert_into_tree(
+ trie_node& node, const key_unit_type* key, const key_unit_type* key_end, const value_type& value)
+{
+ if (key == key_end)
+ {
+ node.value = value;
+ node.has_value = true;
+ return;
+ }
+
+ key_unit_type c = *key;
+
+ auto it = node.children.lower_bound(c);
+ if (it == node.children.end() || node.children.key_comp()(c, it->first))
+ {
+ // Insert a new node.
+ it = node.children.insert(it, typename trie_node::children_type::value_type(c, trie_node()));
+ }
+
+ ++key;
+ insert_into_tree(it->second, key, key_end, value);
+}
+
+template<typename KeyTraits, typename ValueT>
+const typename trie_map<KeyTraits, ValueT>::trie_node* trie_map<KeyTraits, ValueT>::find_prefix_node(
+ const trie_node& node, const key_unit_type* prefix, const key_unit_type* prefix_end) const
+{
+ if (prefix == prefix_end)
+ // Right node is found.
+ return &node;
+
+ auto it = node.children.find(*prefix);
+ if (it == node.children.end())
+ return nullptr;
+
+ ++prefix;
+ return find_prefix_node(it->second, prefix, prefix_end);
+}
+
+template<typename KeyTraits, typename ValueT>
+template<bool IsConst>
+void trie_map<KeyTraits, ValueT>::find_prefix_node_with_stack(
+ std::vector<stack_item<IsConst>>& node_stack, mdds::detail::const_t<trie_node, IsConst>& node,
+ const key_unit_type* prefix, const key_unit_type* prefix_end) const
+{
+ if (prefix == prefix_end)
+ {
+ // Right node is found.
+ node_stack.emplace_back(&node, node.children.begin());
+ return;
+ }
+
+ auto it = node.children.find(*prefix);
+ if (it == node.children.end())
+ return;
+
+ node_stack.emplace_back(&node, it);
+
+ ++prefix;
+ find_prefix_node_with_stack(node_stack, it->second, prefix, prefix_end);
+}
+
+template<typename KeyTraits, typename ValueT>
+template<bool IsConst>
+typename trie_map<KeyTraits, ValueT>::key_buffer_type trie_map<KeyTraits, ValueT>::build_key_buffer_from_node_stack(
+ const std::vector<stack_item<IsConst>>& node_stack) const
+{
+ // Build the key value from the stack.
+ key_buffer_type buf;
+ auto end = node_stack.end();
+ --end; // Skip the node with value which doesn't store a key element.
+ std::for_each(node_stack.begin(), end, [&](const stack_item<IsConst>& si) {
+ using ktt = key_traits_type;
+ ktt::push_back(buf, si.child_pos->first);
+ });
+
+ return buf;
+}
+
+template<typename KeyTraits, typename ValueT>
+void trie_map<KeyTraits, ValueT>::count_values(size_type& n, const trie_node& node) const
+{
+ if (node.has_value)
+ ++n;
+
+ std::for_each(
+ node.children.begin(), node.children.end(),
+ [&](const typename trie_node::children_type::value_type& v) { count_values(n, v.second); });
+}
+
+template<typename KeyTraits, typename ValueT>
+typename trie_map<KeyTraits, ValueT>::const_iterator trie_map<KeyTraits, ValueT>::find(const key_type& key) const
+{
+ using ktt = key_traits_type;
+ key_buffer_type buf = ktt::to_key_buffer(key);
+ const key_unit_type* p = ktt::buffer_data(buf);
+ size_t n = ktt::buffer_size(buf);
+
+ return find(p, n);
+}
+
+template<typename KeyTraits, typename ValueT>
+typename trie_map<KeyTraits, ValueT>::const_iterator trie_map<KeyTraits, ValueT>::find(
+ const key_unit_type* input, size_type len) const
+{
+ const key_unit_type* input_end = input + len;
+ const_node_stack_type node_stack;
+ find_prefix_node_with_stack(node_stack, m_root, input, input_end);
+ if (node_stack.empty() || !node_stack.back().node->has_value)
+ // Specified key doesn't exist.
+ return end();
+
+ key_buffer_type buf = build_key_buffer_from_node_stack(node_stack);
+
+ return const_iterator(std::move(node_stack), std::move(buf), trie::detail::iterator_type::normal);
+}
+
+template<typename KeyTraits, typename ValueT>
+typename trie_map<KeyTraits, ValueT>::iterator trie_map<KeyTraits, ValueT>::find(const key_type& key)
+{
+ using ktt = key_traits_type;
+ key_buffer_type buf = ktt::to_key_buffer(key);
+ const key_unit_type* p = ktt::buffer_data(buf);
+ size_t n = ktt::buffer_size(buf);
+
+ return find(p, n);
+}
+
+template<typename KeyTraits, typename ValueT>
+typename trie_map<KeyTraits, ValueT>::iterator trie_map<KeyTraits, ValueT>::find(
+ const key_unit_type* input, size_type len)
+{
+ const key_unit_type* input_end = input + len;
+ node_stack_type node_stack;
+ find_prefix_node_with_stack(node_stack, m_root, input, input_end);
+ if (node_stack.empty() || !node_stack.back().node->has_value)
+ // Specified key doesn't exist.
+ return end();
+
+ key_buffer_type buf = build_key_buffer_from_node_stack(node_stack);
+
+ return iterator(std::move(node_stack), std::move(buf), trie::detail::iterator_type::normal);
+}
+
+template<typename KeyTraits, typename ValueT>
+typename trie_map<KeyTraits, ValueT>::search_results trie_map<KeyTraits, ValueT>::prefix_search(
+ const key_type& key) const
+{
+ using ktt = key_traits_type;
+ key_buffer_type buf = ktt::to_key_buffer(key);
+ const key_unit_type* p = ktt::buffer_data(buf);
+ size_t n = ktt::buffer_size(buf);
+
+ return prefix_search(p, n);
+}
+
+template<typename KeyTraits, typename ValueT>
+typename trie_map<KeyTraits, ValueT>::search_results trie_map<KeyTraits, ValueT>::prefix_search(
+ const key_unit_type* prefix, size_type len) const
+{
+ using ktt = key_traits_type;
+
+ const key_unit_type* prefix_end = prefix + len;
+ std::vector<key_value_type> matches;
+
+ const trie_node* node = find_prefix_node(m_root, prefix, prefix_end);
+ return search_results(node, ktt::to_key_buffer(prefix, len));
+}
+
+template<typename KeyTraits, typename ValueT>
+typename trie_map<KeyTraits, ValueT>::size_type trie_map<KeyTraits, ValueT>::size() const
+{
+ size_type n = 0;
+ count_values(n, m_root);
+ return n;
+}
+
+template<typename KeyTraits, typename ValueT>
+bool trie_map<KeyTraits, ValueT>::empty() const noexcept
+{
+ return m_root.children.empty() && !m_root.has_value;
+}
+
+template<typename KeyTraits, typename ValueT>
+void trie_map<KeyTraits, ValueT>::clear()
+{
+ m_root.children.clear();
+ m_root.has_value = false;
+}
+
+template<typename KeyTraits, typename ValueT>
+typename trie_map<KeyTraits, ValueT>::packed_type trie_map<KeyTraits, ValueT>::pack() const
+{
+ return packed_type(*this);
+}
+
+#ifdef MDDS_TRIE_MAP_DEBUG
+
+template<typename KeyTraits, typename ValueT>
+void packed_trie_map<KeyTraits, ValueT>::dump_node(key_buffer_type& buffer, const trie_node& node) const
+{
+ using namespace std;
+
+ using ktt = key_traits_type;
+
+ if (node.value)
+ {
+ // This node has value.
+ cout << buffer << ":" << *static_cast<const value_type*>(node.value) << endl;
+ }
+
+ std::for_each(node.children.begin(), node.children.end(), [&](const trie_node* p) {
+ const trie_node& this_node = *p;
+ ktt::push_back(buffer, this_node.key);
+ dump_node(buffer, this_node);
+ ktt::pop_back(buffer);
+ });
+}
+
+template<typename KeyTraits, typename ValueT>
+void packed_trie_map<KeyTraits, ValueT>::dump_trie(const trie_node& root) const
+{
+ key_buffer_type buffer;
+ dump_node(buffer, root);
+}
+
+template<typename KeyTraits, typename ValueT>
+void packed_trie_map<KeyTraits, ValueT>::dump_packed_trie() const
+{
+ cout << "packed size: " << m_packed.size() << endl;
+
+ struct _handler
+ {
+ size_t this_node_offset = 0;
+
+ /** first element in the buffer. */
+ void root_offset(size_t i, const uintptr_t& v) const
+ {
+ cout << std::setw(4) << i << ": root node offset: " << v << endl;
+ }
+
+ /** first element in each node section. */
+ void node_value(size_t i, const uintptr_t& v)
+ {
+ this_node_offset = i;
+ const value_type* value = reinterpret_cast<const value_type*>(v);
+ cout << std::setw(4) << i << ": node value pointer: " << value << endl;
+ }
+
+ /**
+ * second element in each node section that stores the size of
+ * the child data sub-section.
+ */
+ void node_index_size(size_t i, const uintptr_t& v) const
+ {
+ cout << std::setw(4) << i << ": index size: " << size_t(v) << endl;
+ }
+
+ /** element that stores the key value for child node. */
+ void node_child_key(size_t i, const uintptr_t& v) const
+ {
+ key_unit_type key = v;
+ cout << std::setw(4) << i << ": key: " << key << endl;
+ }
+
+ /** element that stores the relative offset of the child node. */
+ void node_child_offset(size_t i, const uintptr_t& v) const
+ {
+ size_t offset = v;
+ cout << std::setw(4) << i << ": offset: " << offset << " (abs: " << (this_node_offset - offset) << ")"
+ << endl;
+ }
+
+ } handler;
+
+ traverse_buffer(handler);
+}
+
+#endif
+
+template<typename KeyTraits, typename ValueT>
+void packed_trie_map<KeyTraits, ValueT>::traverse_range(
+ trie_node& root, node_pool_type& node_pool, const typename packed_trie_map<KeyTraits, ValueT>::entry* start,
+ const typename packed_trie_map<KeyTraits, ValueT>::entry* end, size_type pos)
+{
+ const entry* p = start;
+ const entry* range_start = start;
+ const entry* range_end = nullptr;
+ key_unit_type range_char = 0;
+ size_type range_count = 0;
+
+ for (; p != end; ++p)
+ {
+ if (pos > p->keylen)
+ continue;
+
+ if (pos == p->keylen)
+ {
+ root.value = &p->value;
+ continue;
+ }
+
+ ++range_count;
+ key_unit_type c = p->key[pos];
+
+ if (!range_char)
+ range_char = c;
+ else if (range_char != c)
+ {
+ // End of current character range.
+ range_end = p;
+
+ node_pool.emplace_back(range_char);
+ root.children.push_back(&node_pool.back());
+ traverse_range(*root.children.back(), node_pool, range_start, range_end, pos + 1);
+ range_start = range_end;
+ range_char = range_start->key[pos];
+ range_end = nullptr;
+ range_count = 1;
+ }
+ }
+
+ if (range_count)
+ {
+ assert(range_char);
+ node_pool.emplace_back(range_char);
+ root.children.push_back(&node_pool.back());
+ traverse_range(*root.children.back(), node_pool, range_start, end, pos + 1);
+ }
+}
+
+template<typename KeyTraits, typename ValueT>
+typename packed_trie_map<KeyTraits, ValueT>::size_type packed_trie_map<KeyTraits, ValueT>::compact_node(
+ const trie_node& node)
+{
+ std::vector<std::tuple<size_t, key_unit_type>> child_offsets;
+ child_offsets.reserve(node.children.size());
+
+ // Process child nodes first.
+ std::for_each(node.children.begin(), node.children.end(), [&](const trie_node* p) {
+ const trie_node& child_node = *p;
+ size_type child_offset = compact_node(child_node);
+ child_offsets.emplace_back(child_offset, child_node.key);
+ });
+
+ // Process this node.
+ size_type offset = m_packed.size();
+ if (node.value)
+ {
+ m_value_store.push_back(*node.value); // copy the value object.
+ m_packed.push_back(uintptr_t(&m_value_store.back()));
+ }
+ else
+ m_packed.push_back(uintptr_t(0));
+
+ push_child_offsets(offset, child_offsets);
+ return offset;
+}
+
+template<typename KeyTraits, typename ValueT>
+typename packed_trie_map<KeyTraits, ValueT>::size_type packed_trie_map<KeyTraits, ValueT>::compact_node(
+ const typename trie_map<KeyTraits, ValueT>::trie_node& node)
+{
+ using node_type = typename trie_map<KeyTraits, ValueT>::trie_node;
+
+ std::vector<std::tuple<size_t, key_unit_type>> child_offsets;
+ child_offsets.reserve(node.children.size());
+
+ // Process child nodes first.
+ std::for_each(
+ node.children.begin(), node.children.end(), [&](const typename node_type::children_type::value_type& v) {
+ key_unit_type key = v.first;
+ const node_type& child_node = v.second;
+ size_type child_offset = compact_node(child_node);
+ child_offsets.emplace_back(child_offset, key);
+ });
+
+ // Process this node.
+ size_type offset = m_packed.size();
+ if (node.has_value)
+ {
+ m_value_store.push_back(node.value); // copy the value object.
+ m_packed.push_back(uintptr_t(&m_value_store.back()));
+ }
+ else
+ m_packed.push_back(uintptr_t(0));
+
+ push_child_offsets(offset, child_offsets);
+ return offset;
+}
+
+template<typename KeyTraits, typename ValueT>
+void packed_trie_map<KeyTraits, ValueT>::push_child_offsets(size_type offset, const child_offsets_type& child_offsets)
+{
+ m_packed.push_back(uintptr_t(child_offsets.size() * 2));
+
+ std::for_each(child_offsets.begin(), child_offsets.end(), [&](const std::tuple<size_t, key_unit_type>& v) {
+ key_unit_type key = std::get<1>(v);
+ size_t child_offset = std::get<0>(v);
+ m_packed.push_back(key);
+ m_packed.push_back(offset - child_offset);
+ });
+}
+
+template<typename KeyTraits, typename ValueT>
+void packed_trie_map<KeyTraits, ValueT>::compact(const trie_node& root)
+{
+ packed_type init(size_t(1), uintptr_t(0));
+ m_packed.swap(init);
+ assert(m_packed.size() == 1);
+
+ size_t root_offset = compact_node(root);
+ m_packed[0] = root_offset;
+}
+
+template<typename KeyTraits, typename ValueT>
+void packed_trie_map<KeyTraits, ValueT>::compact(const typename trie_map<KeyTraits, ValueT>::trie_node& root)
+{
+ packed_type init(size_t(1), uintptr_t(0));
+ m_packed.swap(init);
+ assert(m_packed.size() == 1);
+
+ size_t root_offset = compact_node(root);
+ m_packed[0] = root_offset;
+}
+
+template<typename KeyTraits, typename ValueT>
+packed_trie_map<KeyTraits, ValueT>::packed_trie_map() : m_packed(3, 0u)
+{
+ // root offset (0), root value (1), and root child count (2).
+ m_packed[0] = 1;
+}
+
+template<typename KeyTraits, typename ValueT>
+packed_trie_map<KeyTraits, ValueT>::packed_trie_map(const entry* entries, size_type entry_size)
+{
+ const entry* p = entries;
+ const entry* p_end = p + entry_size;
+
+#if defined(MDDS_TRIE_MAP_DEBUG)
+ // Make sure the entries really are sorted.
+ auto func_compare = [](const entry& left, const entry& right) -> bool {
+ size_type n_key = std::min(left.keylen, right.keylen);
+ int ret = std::memcmp(left.key, right.key, n_key);
+ if (ret == 0)
+ return left.keylen < right.keylen;
+
+ return ret < 0;
+ };
+
+ if (!std::is_sorted(p, p_end, func_compare))
+ throw integrity_error("the list of entries is not sorted.");
+
+#endif
+
+ // Populate the normal tree first.
+ trie_node root(0);
+ node_pool_type node_pool;
+ traverse_range(root, node_pool, p, p_end, 0);
+#if defined(MDDS_TRIE_MAP_DEBUG) && defined(MDDS_TRIE_MAP_DEBUG_DUMP_TRIE)
+ dump_trie(root);
+#endif
+
+ // Compact the trie into a packed array.
+ compact(root);
+#if defined(MDDS_TRIE_MAP_DEBUG) && defined(MDDS_TRIE_MAP_DEBUG_DUMP_PACKED)
+ dump_packed_trie();
+#endif
+}
+
+template<typename KeyTraits, typename ValueT>
+packed_trie_map<KeyTraits, ValueT>::packed_trie_map(const trie_map<key_traits_type, value_type>& other)
+{
+ compact(other.m_root);
+}
+
+template<typename KeyTraits, typename ValueT>
+packed_trie_map<KeyTraits, ValueT>::packed_trie_map(const packed_trie_map& other) : m_packed(other.m_packed)
+{
+ struct _handler
+ {
+ packed_trie_map& m_parent;
+
+ void node(const uintptr_t* node_pos, key_unit_type /*c*/, size_t /*depth*/, size_t /*index_size*/)
+ {
+ uintptr_t value_ptr = *node_pos;
+
+ if (value_ptr)
+ {
+ auto p = reinterpret_cast<const value_type*>(value_ptr);
+ m_parent.m_value_store.push_back(*p); // copy the value object.
+ const uintptr_t* head = m_parent.m_packed.data();
+ size_t offset = std::distance(head, node_pos);
+ m_parent.m_packed[offset] = uintptr_t(&m_parent.m_value_store.back());
+ }
+ }
+
+ void move_up(const uintptr_t*, const uintptr_t*, const uintptr_t*)
+ {}
+
+ void next_child()
+ {}
+
+ void end()
+ {}
+
+ _handler(packed_trie_map& parent) : m_parent(parent)
+ {}
+
+ } handler(*this);
+
+ traverse_tree(handler);
+}
+
+template<typename KeyTraits, typename ValueT>
+packed_trie_map<KeyTraits, ValueT>::packed_trie_map(packed_trie_map&& other)
+ : m_value_store(std::move(other.m_value_store)), m_packed(std::move(other.m_packed))
+{
+ // Even the empty structure needs to have the root offset and the empty root record.
+ other.m_packed.resize(3, 0u); // root offset (0), root value (1), and root child count (2).
+ other.m_packed[0] = 1;
+}
+
+template<typename KeyTraits, typename ValueT>
+packed_trie_map<KeyTraits, ValueT>& packed_trie_map<KeyTraits, ValueT>::operator=(packed_trie_map other)
+{
+ packed_trie_map tmp(std::move(other));
+ tmp.swap(*this);
+ return *this;
+}
+
+template<typename KeyTraits, typename ValueT>
+bool packed_trie_map<KeyTraits, ValueT>::operator==(const packed_trie_map& other) const
+{
+ if (m_value_store.size() != other.m_value_store.size())
+ return false;
+
+ // Since the two containers are of the same size, the iterator ranges should
+ // be the same as well.
+ auto left = cbegin(), right = other.cbegin();
+ for (; left != cend(); ++left, ++right)
+ {
+ assert(right != other.cend());
+ if (*left != *right)
+ return false;
+ }
+
+ return true;
+}
+
+template<typename KeyTraits, typename ValueT>
+bool packed_trie_map<KeyTraits, ValueT>::operator!=(const packed_trie_map& other) const
+{
+ return !operator==(other);
+}
+
+template<typename KeyTraits, typename ValueT>
+typename packed_trie_map<KeyTraits, ValueT>::const_iterator packed_trie_map<KeyTraits, ValueT>::begin() const
+{
+ return cbegin();
+}
+
+template<typename KeyTraits, typename ValueT>
+typename packed_trie_map<KeyTraits, ValueT>::const_iterator packed_trie_map<KeyTraits, ValueT>::end() const
+{
+ return cend();
+}
+
+template<typename KeyTraits, typename ValueT>
+typename packed_trie_map<KeyTraits, ValueT>::const_iterator packed_trie_map<KeyTraits, ValueT>::cbegin() const
+{
+ using ktt = key_traits_type;
+
+ node_stack_type node_stack = get_root_stack();
+
+ const stack_item* si = &node_stack.back();
+ if (si->child_pos == si->child_end)
+ // empty container.
+ return const_iterator(std::move(node_stack), key_buffer_type());
+
+ const uintptr_t* node_pos = si->node_pos;
+ const uintptr_t* child_pos = si->child_pos;
+ const uintptr_t* child_end = si->child_end;
+ const uintptr_t* p = child_pos;
+
+ // Follow the root node's left-most child.
+ key_buffer_type buf;
+ key_unit_type c = *p;
+ ktt::push_back(buf, c);
+ ++p;
+ size_t offset = *p;
+
+ node_pos -= offset; // jump to the child node.
+ p = node_pos;
+ ++p;
+ size_t index_size = *p;
+ ++p;
+ child_pos = p;
+ child_end = child_pos + index_size;
+
+ // Push this child node onto the stack.
+ node_stack.emplace_back(node_pos, child_pos, child_end);
+
+ const value_type* pv = reinterpret_cast<const value_type*>(*node_pos);
+ while (!pv)
+ {
+ // Keep following the left child node until we reach a node with value.
+ const_iterator::push_child_node_to_stack(node_stack, buf, node_stack.back().child_pos);
+ pv = reinterpret_cast<const value_type*>(*node_stack.back().node_pos);
+ }
+
+ return const_iterator(std::move(node_stack), std::move(buf), *pv);
+}
+
+template<typename KeyTraits, typename ValueT>
+typename packed_trie_map<KeyTraits, ValueT>::const_iterator packed_trie_map<KeyTraits, ValueT>::cend() const
+{
+ node_stack_type node_stack = get_root_stack();
+ node_stack.back().child_pos = node_stack.back().child_end;
+ return const_iterator(std::move(node_stack), key_buffer_type());
+}
+
+template<typename KeyTraits, typename ValueT>
+typename packed_trie_map<KeyTraits, ValueT>::node_stack_type packed_trie_map<KeyTraits, ValueT>::get_root_stack() const
+{
+ assert(!m_packed.empty());
+ size_t root_offset = m_packed[0];
+ assert(root_offset < m_packed.size());
+ const uintptr_t* p = m_packed.data() + root_offset;
+ const uintptr_t* node_pos = p;
+ ++p;
+ size_t index_size = *p;
+ ++p;
+ const uintptr_t* child_pos = p;
+ const uintptr_t* child_end = child_pos + index_size;
+
+ node_stack_type node_stack;
+ node_stack.emplace_back(node_pos, child_pos, child_end);
+
+ return node_stack;
+}
+
+template<typename KeyTraits, typename ValueT>
+typename packed_trie_map<KeyTraits, ValueT>::const_iterator packed_trie_map<KeyTraits, ValueT>::find(
+ const key_type& key) const
+{
+ using ktt = key_traits_type;
+ key_buffer_type buf = ktt::to_key_buffer(key);
+ const key_unit_type* p = ktt::buffer_data(buf);
+ size_t n = ktt::buffer_size(buf);
+
+ return find(p, n);
+}
+
+template<typename KeyTraits, typename ValueT>
+typename packed_trie_map<KeyTraits, ValueT>::const_iterator packed_trie_map<KeyTraits, ValueT>::find(
+ const key_unit_type* input, size_type len) const
+{
+ if (m_value_store.empty())
+ return end();
+
+ const key_unit_type* key_end = input + len;
+ size_t root_offset = m_packed[0];
+ assert(root_offset < m_packed.size());
+ const uintptr_t* root = m_packed.data() + root_offset;
+
+ node_stack_type node_stack;
+ find_prefix_node_with_stack(node_stack, root, input, key_end);
+ if (node_stack.empty() || !node_stack.back().node_pos)
+ return end();
+
+ const stack_item& si = node_stack.back();
+ const value_type* pv = reinterpret_cast<const value_type*>(*si.node_pos);
+ if (!pv)
+ return end();
+
+ // Build the key value from the stack.
+ key_buffer_type buf;
+ auto end = node_stack.end();
+ --end; // Skip the node with value which doesn't store a key element.
+ std::for_each(node_stack.begin(), end, [&](const stack_item& this_si) {
+ using ktt = key_traits_type;
+ ktt::push_back(buf, *this_si.child_pos);
+ });
+
+ return const_iterator(std::move(node_stack), std::move(buf), *pv);
+}
+
+template<typename KeyTraits, typename ValueT>
+typename packed_trie_map<KeyTraits, ValueT>::search_results packed_trie_map<KeyTraits, ValueT>::prefix_search(
+ const key_type& key) const
+{
+ using ktt = key_traits_type;
+ key_buffer_type buf = ktt::to_key_buffer(key);
+ const key_unit_type* p = ktt::buffer_data(buf);
+ size_t n = ktt::buffer_size(buf);
+
+ return prefix_search(p, n);
+}
+
+template<typename KeyTraits, typename ValueT>
+typename packed_trie_map<KeyTraits, ValueT>::search_results packed_trie_map<KeyTraits, ValueT>::prefix_search(
+ const key_unit_type* prefix, size_type len) const
+{
+ using ktt = key_traits_type;
+
+ if (m_value_store.empty())
+ return search_results(nullptr, key_buffer_type());
+
+ const key_unit_type* prefix_end = prefix + len;
+
+ size_t root_offset = m_packed[0];
+ assert(root_offset < m_packed.size());
+ const uintptr_t* root = m_packed.data() + root_offset;
+
+ const uintptr_t* node = find_prefix_node(root, prefix, prefix_end);
+ key_buffer_type buf = ktt::to_key_buffer(prefix, len);
+ return search_results(node, std::move(buf));
+}
+
+template<typename KeyTraits, typename ValueT>
+typename packed_trie_map<KeyTraits, ValueT>::size_type packed_trie_map<KeyTraits, ValueT>::size() const noexcept
+{
+ return m_value_store.size();
+}
+
+template<typename KeyTraits, typename ValueT>
+bool packed_trie_map<KeyTraits, ValueT>::empty() const noexcept
+{
+ return m_value_store.empty();
+}
+
+template<typename KeyTraits, typename ValueT>
+void packed_trie_map<KeyTraits, ValueT>::swap(packed_trie_map& other)
+{
+ m_value_store.swap(other.m_value_store);
+ m_packed.swap(other.m_packed);
+}
+
+template<typename KeyTraits, typename ValueT>
+template<typename FuncT>
+void packed_trie_map<KeyTraits, ValueT>::save_state(std::ostream& os) const
+{
+ detail::trie::bin_value bv;
+
+ bv.ui16 = 0x0000; // write 2-byte flags
+ bv.ui16 |= (0x0001 & FuncT::variable_size);
+ os.write(bv.buffer, 2);
+
+ // Write the number of values (4-bytes).
+ bv.ui32 = m_value_store.size();
+ os.write(bv.buffer, 4);
+
+ using value_addrs_type = std::map<const void*, size_t>;
+ value_addrs_type value_addrs;
+
+ // Dump the stored values first.
+ using value_size_type = std::bool_constant<FuncT::variable_size>;
+
+ detail::trie::write_values_to_ostream<FuncT, value_type, value_size_type> func;
+ value_addrs = func(os, m_value_store);
+
+ // Write 0xFF to signify the end of the value section.
+ bv.ui8 = 0xFF;
+ os.write(bv.buffer, 1);
+
+ // Write the size of uintptr_t. It must be either 4 or 8.
+ bv.ui8 = sizeof(uintptr_t);
+ os.write(bv.buffer, 1);
+
+ // Write the size of the packed blob.
+ bv.ui64 = m_packed.size();
+ os.write(bv.buffer, 8);
+
+ struct _handler
+ {
+ size_t const m_elem_size = sizeof(uintptr_t);
+ std::ostream& m_os;
+ const value_addrs_type& m_value_addrs;
+ const packed_trie_map& m_parent;
+
+ inline void write(uintptr_t v) const
+ {
+ const char* p = reinterpret_cast<const char*>(&v);
+ m_os.write(p, m_elem_size);
+ }
+
+ /** first element in the buffer. */
+ void root_offset(size_t /*i*/, const uintptr_t& v) const
+ {
+ write(v);
+ }
+
+ /** first element in each node section. */
+ void node_value(size_t /*i*/, const uintptr_t& v) const
+ {
+ const value_type* p = reinterpret_cast<const value_type*>(v);
+ if (p)
+ {
+ // Replace the pointer of the value with its index into the value store.
+ auto it = m_value_addrs.find(p);
+ assert(it != m_value_addrs.cend());
+ uintptr_t index = it->second;
+
+ write(index);
+ }
+ else
+ {
+ // Use the numeric value with all bits set, to encode 0.
+ // Because the index of the value is 0-based, we can't just
+ // leave it as zero.
+ uintptr_t max_bits = 0;
+ max_bits = ~max_bits;
+ write(max_bits);
+ }
+ }
+
+ /**
+ * second element in each node section that stores the size of
+ * the child data sub-section.
+ */
+ void node_index_size(size_t /*i*/, const uintptr_t& v) const
+ {
+ write(v);
+ }
+
+ /** element that stores the key value for child node. */
+ void node_child_key(size_t /*i*/, const uintptr_t& v) const
+ {
+ write(v);
+ }
+
+ /** element that stores the relative offset of the child node. */
+ void node_child_offset(size_t /*i*/, const uintptr_t& v) const
+ {
+ write(v);
+ }
+
+ _handler(std::ostream& os, const value_addrs_type& value_addrs, const packed_trie_map& parent)
+ : m_os(os), m_value_addrs(value_addrs), m_parent(parent)
+ {}
+
+ } handler(os, value_addrs, *this);
+
+ traverse_buffer(handler);
+
+ // Write 0xFF to signify the end of the packed blob.
+ bv.ui8 = 0xFF;
+ os.write(bv.buffer, 1);
+}
+
+template<typename KeyTraits, typename ValueT>
+template<typename FuncT>
+void packed_trie_map<KeyTraits, ValueT>::load_state(std::istream& is)
+{
+ detail::trie::bin_value bv;
+ is.read(bv.buffer, 2);
+
+ uint16_t flags = bv.ui16;
+ bool variable_size = (flags & 0x0001) != 0;
+
+ if (variable_size != FuncT::variable_size)
+ {
+ std::ostringstream os;
+ os << "This stream is meant for a value type of " << detail::trie::value_type_size_name(variable_size)
+ << ", but the actual value type is of " << detail::trie::value_type_size_name(FuncT::variable_size) << ".";
+ throw std::invalid_argument(os.str());
+ }
+
+ // read the number of values
+ is.read(bv.buffer, 4);
+ uint32_t value_count = bv.ui32;
+
+ using value_size_type = std::bool_constant<FuncT::variable_size>;
+ detail::trie::read_values_from_istream<FuncT, value_type, value_size_type> func;
+ m_value_store = func(is, value_count);
+
+ // There should be a check byte of 0xFF.
+ is.read(bv.buffer, 1);
+ if (bv.ui8 != 0xFF)
+ throw std::invalid_argument("failed to find the check byte at the end of the value section.");
+
+ // Size of uintptr_t
+ is.read(bv.buffer, 1);
+ size_t ptr_size = bv.ui8;
+
+ if (ptr_size != sizeof(uintptr_t))
+ throw std::invalid_argument("size of uintptr_t is different.");
+
+ // Size of the packed blob.
+ is.read(bv.buffer, 8);
+
+ size_t n = bv.ui64;
+ packed_type packed;
+ packed.reserve(n);
+
+ for (size_t i = 0; i < n; ++i)
+ {
+ is.read(bv.buffer, sizeof(uintptr_t));
+ const uintptr_t* p = reinterpret_cast<const uintptr_t*>(bv.buffer);
+ packed.push_back(*p);
+ }
+
+ // the last byte must be 0xFF.
+ is.read(bv.buffer, 1);
+ if (bv.ui8 != 0xFF)
+ throw std::invalid_argument("failed to find the check byte at the end of the packed blob section.");
+
+ m_packed.swap(packed);
+
+ struct _handler
+ {
+ uintptr_t m_max_value;
+ packed_trie_map& m_parent;
+
+ void root_offset(size_t /*i*/, const uintptr_t& /*v*/) const
+ {}
+
+ void node_value(size_t i, const uintptr_t& v) const
+ {
+ if (v == m_max_value)
+ m_parent.m_packed[i] = 0;
+ else
+ {
+ // Replace the value index with its memory address.
+ const value_type& val = m_parent.m_value_store[v];
+ const uintptr_t addr = reinterpret_cast<const uintptr_t>(&val);
+ m_parent.m_packed[i] = addr;
+ }
+ }
+
+ void node_index_size(size_t /*i*/, const uintptr_t& /*v*/) const
+ {}
+ void node_child_key(size_t /*i*/, const uintptr_t& /*v*/) const
+ {}
+ void node_child_offset(size_t /*i*/, const uintptr_t& /*v*/) const
+ {}
+
+ _handler(packed_trie_map& parent) : m_max_value(0), m_parent(parent)
+ {
+ m_max_value = ~m_max_value;
+ }
+
+ } handler(*this);
+
+ traverse_buffer(handler);
+}
+
+template<typename KeyTraits, typename ValueT>
+void packed_trie_map<KeyTraits, ValueT>::dump_structure() const
+{
+#ifdef MDDS_TRIE_MAP_DEBUG
+ dump_packed_trie();
+
+ cout << "--" << endl;
+ cout << "entry size: " << m_value_store.size() << endl;
+ cout << "memory size: " << m_packed.size() << endl;
+
+ const uintptr_t* head = m_packed.data();
+
+ struct _handler
+ {
+ const uintptr_t* m_head;
+
+ _handler(const uintptr_t* head) : m_head(head)
+ {}
+
+ void node(const uintptr_t* node_pos, key_unit_type c, size_t depth, size_t index_size)
+ {
+ uintptr_t value_ptr = *node_pos;
+ size_t offset = std::distance(m_head, node_pos);
+
+ cout << " --" << endl;
+ cout << " offset: " << offset << endl;
+ cout << " key: " << c << endl;
+ cout << " depth: " << depth << endl;
+ cout << " child count: " << (index_size / 2) << endl;
+ cout << " value address: " << value_ptr;
+
+ if (value_ptr)
+ {
+ auto p = reinterpret_cast<const value_type*>(value_ptr);
+ cout << "; value: " << *p;
+ }
+
+ cout << endl;
+ }
+
+ void move_up(const uintptr_t* node_pos, const uintptr_t* child_pos, const uintptr_t* child_end)
+ {
+ size_t offset = std::distance(m_head, node_pos);
+ size_t child_size = std::distance(child_pos, child_end) / 2;
+ cout << " --" << endl;
+ cout << " move up: (offset: " << offset << "; child count: " << child_size << ")" << endl;
+ }
+
+ void next_child()
+ {
+ cout << " next child" << endl;
+ }
+
+ void end()
+ {}
+
+ } handler(head);
+
+ traverse_tree(handler);
+#endif
+}
+
+template<typename KeyTraits, typename ValueT>
+const uintptr_t* packed_trie_map<KeyTraits, ValueT>::find_prefix_node(
+ const uintptr_t* p, const key_unit_type* prefix, const key_unit_type* prefix_end) const
+{
+ if (prefix == prefix_end)
+ return p;
+
+ const uintptr_t* p0 = p; // store the head offset position of this node.
+
+ // Find the child node with a matching key character.
+
+ ++p;
+ size_t index_size = *p;
+ size_t n = index_size / 2;
+ ++p;
+
+ if (!n)
+ // This is a leaf node.
+ return nullptr;
+
+ for (size_type low = 0, high = n - 1; low <= high;)
+ {
+ size_type i = (low + high) / 2;
+
+ const uintptr_t* p_this = p + i * 2;
+ key_unit_type node_key = *p_this;
+ size_t offset = *(p_this + 1);
+
+ if (*prefix == node_key)
+ {
+ // Match found!
+ const uintptr_t* p_child = p0 - offset;
+ ++prefix;
+ return find_prefix_node(p_child, prefix, prefix_end);
+ }
+
+ if (low == high)
+ // No more child node key to test. Bail out.
+ break;
+
+ if (high - low == 1)
+ {
+ // Only two more child keys left.
+ if (i == low)
+ low = high;
+ else
+ {
+ assert(i == high);
+ high = low;
+ }
+ }
+ else if (*prefix < node_key)
+ // Move on to the lower sub-group.
+ high = i;
+ else
+ // Move on to the higher sub-group.
+ low = i;
+ }
+
+ return nullptr;
+}
+
+template<typename KeyTraits, typename ValueT>
+void packed_trie_map<KeyTraits, ValueT>::find_prefix_node_with_stack(
+ node_stack_type& node_stack, const uintptr_t* p, const key_unit_type* prefix, const key_unit_type* prefix_end) const
+{
+ if (prefix == prefix_end)
+ {
+ size_t index_size = *(p + 1);
+ const uintptr_t* child_pos = p + 2;
+ const uintptr_t* child_end = child_pos + index_size;
+ node_stack.emplace_back(p, child_pos, child_end);
+ return;
+ }
+
+ const uintptr_t* p0 = p; // store the head offset position of this node.
+
+ // Find the child node with a matching key character.
+
+ ++p;
+ size_t index_size = *p;
+ size_t n = index_size / 2;
+ ++p;
+
+ if (!n)
+ {
+ // This is a leaf node.
+ node_stack.emplace_back(nullptr, nullptr, nullptr);
+ return;
+ }
+
+ const uintptr_t* child_end = p + index_size;
+
+ for (size_type low = 0, high = n - 1; low <= high;)
+ {
+ size_type i = (low + high) / 2;
+
+ const uintptr_t* child_pos = p + i * 2;
+ key_unit_type node_key = *child_pos;
+ size_t offset = *(child_pos + 1);
+
+ if (*prefix == node_key)
+ {
+ // Match found!
+ node_stack.emplace_back(p0, child_pos, child_end);
+ const uintptr_t* p_child = p0 - offset;
+ ++prefix;
+ find_prefix_node_with_stack(node_stack, p_child, prefix, prefix_end);
+ return;
+ }
+
+ if (low == high)
+ // No more child node key to test. Bail out.
+ break;
+
+ if (high - low == 1)
+ {
+ // Only two more child keys left.
+ if (i == low)
+ low = high;
+ else
+ {
+ assert(i == high);
+ high = low;
+ }
+ }
+ else if (*prefix < node_key)
+ // Move on to the lower sub-group.
+ high = i;
+ else
+ // Move on to the higher sub-group.
+ low = i;
+ }
+
+ node_stack.emplace_back(nullptr, nullptr, nullptr);
+}
+
+template<typename KeyTraits, typename ValueT>
+template<typename _Handler>
+void packed_trie_map<KeyTraits, ValueT>::traverse_tree(_Handler hdl) const
+{
+ node_stack_type node_stack = get_root_stack();
+ stack_item* si = &node_stack.back();
+ if (si->child_pos == si->child_end)
+ // empty container
+ return;
+
+ const uintptr_t* node_pos = si->node_pos;
+ const uintptr_t* child_pos = si->child_pos;
+ const uintptr_t* child_end = si->child_end;
+ const uintptr_t* p = child_pos;
+
+ for (bool in_tree = true; in_tree;)
+ {
+ // Descend until the leaf node is reached by following the left-most child nodes.
+ while (true)
+ {
+ auto key = *p;
+ size_t depth = node_stack.size();
+ ++p;
+ size_t offset = *p;
+
+ // jump down to the child node.
+ node_pos -= offset;
+ p = node_pos;
+ ++p;
+ size_t index_size = *p; // size of the buffer that stores child nodes' keys and offsets.
+
+ hdl.node(node_pos, key, depth, index_size);
+
+ ++p;
+ child_pos = p;
+ child_end = child_pos + index_size;
+
+ // Push this child node onto the stack.
+ node_stack.emplace_back(node_pos, child_pos, child_end);
+
+ if (!index_size)
+ // no child nodes i.e. leaf node. Bail out of the loop.
+ break;
+ }
+
+ // Ascend up the tree until a node with an unvisited child node is
+ // found, then move sideways.
+ while (true)
+ {
+ // move up.
+ node_stack.pop_back();
+ si = &node_stack.back();
+ hdl.move_up(si->node_pos, si->child_pos, si->child_end);
+ std::advance(si->child_pos, 2); // move to the next child node slot.
+ if (si->child_pos != si->child_end)
+ {
+ // This is an unvisited child node. Bail out of the loop.
+ hdl.next_child();
+ node_pos = si->node_pos;
+ p = si->child_pos;
+ break;
+ }
+
+ if (node_stack.size() == 1)
+ {
+ // End of the tree has reached.
+ in_tree = false;
+ break;
+ }
+ }
+ }
+
+ hdl.end();
+}
+
+template<typename KeyTraits, typename ValueT>
+template<typename _Handler>
+void packed_trie_map<KeyTraits, ValueT>::traverse_buffer(_Handler hdl) const
+{
+ size_t n = m_packed.size();
+ size_t i = 0;
+ hdl.root_offset(i, m_packed[i]);
+ ++i;
+
+ while (i < n)
+ {
+ hdl.node_value(i, m_packed[i]);
+ ++i;
+
+ hdl.node_index_size(i, m_packed[i]);
+ size_t index_size = m_packed[i];
+ ++i;
+ index_size /= 2;
+
+ for (size_t j = 0; j < index_size; ++j)
+ {
+ hdl.node_child_key(i, m_packed[i]);
+ ++i;
+ hdl.node_child_offset(i, m_packed[i]);
+ ++i;
+ }
+ }
+}
+
+} // namespace mdds
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/mdds/trie_map_itr.hpp b/include/mdds/trie_map_itr.hpp
new file mode 100644
index 0000000..cc993fb
--- /dev/null
+++ b/include/mdds/trie_map_itr.hpp
@@ -0,0 +1,889 @@
+/*************************************************************************
+ *
+ * Copyright (c) 2016-2020 Kohei Yoshida
+ *
+ * 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, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ ************************************************************************/
+
+#ifndef INCLUDED_MDDS_TRIE_MAP_ITR_HPP
+#define INCLUDED_MDDS_TRIE_MAP_ITR_HPP
+
+#include <utility>
+#include <cassert>
+#include <iostream>
+#ifdef MDDS_TRIE_MAP_DEBUG
+#include <sstream>
+#endif
+
+#include "mdds/global.hpp"
+#include "mdds/ref_pair.hpp"
+
+namespace mdds { namespace trie { namespace detail {
+
+enum class iterator_type
+{
+ /**
+ * Normal iterator is expected to have at least one element on the node
+ * stack i.e. root.
+ */
+ normal,
+ /**
+ * End iterator is the same as a normal iterator except that it is
+ * positioned past the last node position. A normal iterator becomes an
+ * end iterator when incrementing past the last node position.
+ */
+ end,
+ /**
+ * Empty iterator doesn't reference any node in the tree but still is a
+ * valid iterator (therefore differs from a singular iterator). Its node
+ * stack is empty.
+ */
+ empty
+};
+
+enum empty_iterator_type
+{
+ empty_iterator
+};
+
+template<typename _TrieType, typename _C>
+struct get_node_stack_type;
+
+template<typename _TrieType>
+struct get_node_stack_type<_TrieType, std::true_type>
+{
+ using type = typename _TrieType::const_node_stack_type;
+};
+
+template<typename _TrieType>
+struct get_node_stack_type<_TrieType, std::false_type>
+{
+ using type = typename _TrieType::node_stack_type;
+};
+
+template<typename _TrieType>
+class search_results;
+
+template<typename _TrieType, bool IsConst>
+class iterator_base
+{
+protected:
+ using trie_type = _TrieType;
+
+ using _is_const = std::bool_constant<IsConst>;
+
+ friend trie_type;
+ friend search_results<trie_type>;
+
+ using node_stack_type = typename get_node_stack_type<trie_type, _is_const>::type;
+ using trie_node_type = mdds::detail::const_t<typename trie_type::trie_node, IsConst>;
+ using trie_node_child_pos_type = typename mdds::detail::get_iterator_type<
+ typename std::remove_const<trie_node_type>::type::children_type, _is_const>::type;
+
+ using key_traits_type = typename trie_type::key_traits_type;
+ using key_type = typename key_traits_type::key_type;
+ using key_buffer_type = typename key_traits_type::key_buffer_type;
+ using trie_value_type = typename mdds::detail::const_or_not<typename trie_type::value_type, _is_const>::type;
+
+public:
+ // iterator traits
+ using value_type = mdds::detail::ref_pair<typename std::add_const<key_type>::type, trie_value_type>;
+ using pointer = value_type*;
+ using reference = value_type&;
+ using difference_type = std::ptrdiff_t;
+ using iterator_category = std::bidirectional_iterator_tag;
+
+protected:
+ node_stack_type m_node_stack;
+ key_buffer_type m_buffer;
+ key_type m_current_key;
+ trie_value_type* m_current_value_ptr;
+ iterator_type m_type;
+
+ static trie_node_type* push_child_node_to_stack(
+ node_stack_type& node_stack, key_buffer_type& buf, trie_node_child_pos_type& child_pos)
+ {
+ using ktt = key_traits_type;
+
+ trie_node_type* node = &child_pos->second;
+ ktt::push_back(buf, child_pos->first);
+ node_stack.emplace_back(node, node->children.begin());
+
+ return node;
+ }
+
+ /**
+ * From the current node, move to its previous child node and descend all
+ * the way to the leaf node.
+ */
+ static trie_node_type* descend_to_previus_leaf_node(node_stack_type& node_stack, key_buffer_type& buf)
+ {
+ using ktt = key_traits_type;
+
+ trie_node_type* cur_node = nullptr;
+
+ do
+ {
+ // Keep moving down on the right-most child nodes until the
+ // leaf node is reached.
+
+ auto& si = node_stack.back();
+
+ --si.child_pos;
+ ktt::push_back(buf, si.child_pos->first);
+ cur_node = &si.child_pos->second;
+ node_stack.emplace_back(cur_node, cur_node->children.end());
+ } while (!cur_node->children.empty());
+
+ return cur_node;
+ }
+
+ iterator_base(empty_iterator_type) : m_current_value_ptr(nullptr), m_type(iterator_type::empty)
+ {}
+
+public:
+ iterator_base() : m_current_value_ptr(nullptr), m_type(iterator_type::normal)
+ {}
+
+ iterator_base(node_stack_type&& node_stack, key_buffer_type&& buf, iterator_type type)
+ : m_node_stack(std::move(node_stack)), m_buffer(std::move(buf)),
+ m_current_key(key_traits_type::to_key(m_buffer)), m_current_value_ptr(&m_node_stack.back().node->value),
+ m_type(type)
+ {}
+
+ bool operator==(const iterator_base& other) const
+ {
+ if (m_type != other.m_type)
+ return false;
+
+ if (m_type == iterator_type::empty)
+ return true;
+
+ return m_node_stack.back() == other.m_node_stack.back();
+ }
+
+ bool operator!=(const iterator_base& other) const
+ {
+ return !operator==(other);
+ }
+
+ value_type operator*()
+ {
+ return value_type(m_current_key, *m_current_value_ptr);
+ }
+
+ value_type operator->()
+ {
+ return value_type(m_current_key, *m_current_value_ptr);
+ }
+
+ iterator_base& operator++()
+ {
+ using ktt = key_traits_type;
+
+ trie_node_type* cur_node = m_node_stack.back().node;
+
+ do
+ {
+ if (cur_node->children.empty())
+ {
+ // Current node is a leaf node. Keep moving up the stack until we
+ // reach a parent node with unvisited children.
+
+ while (true)
+ {
+ if (m_node_stack.size() == 1)
+ {
+#ifdef MDDS_TRIE_MAP_DEBUG
+ if (m_type == iterator_type::end)
+ {
+ std::ostringstream os;
+ os << "iterator_base::operator++#" << __LINE__ << ": moving past the end position!";
+ throw general_error(os.str());
+ }
+#endif
+ // We've reached the end position. Bail out.
+ m_type = iterator_type::end;
+ return *this;
+ }
+
+ // Move up one parent and see if it has an unvisited child node.
+ ktt::pop_back(m_buffer);
+ m_node_stack.pop_back();
+ auto& si = m_node_stack.back();
+ ++si.child_pos;
+
+ if (si.child_pos != si.node->children.end())
+ {
+ // Move down to this unvisited child node.
+ cur_node = push_child_node_to_stack(m_node_stack, m_buffer, si.child_pos);
+ break;
+ }
+ }
+ }
+ else
+ {
+ // Current node has child nodes. Follow the first child node.
+ auto child_pos = cur_node->children.begin();
+ cur_node = push_child_node_to_stack(m_node_stack, m_buffer, child_pos);
+ }
+ } while (!cur_node->has_value);
+
+ m_current_key = ktt::to_key(m_buffer);
+ m_current_value_ptr = &cur_node->value;
+ return *this;
+ }
+
+ iterator_base operator++(int)
+ {
+ iterator_base tmp(*this);
+ operator++();
+ return tmp;
+ }
+
+ iterator_base& operator--()
+ {
+ using ktt = key_traits_type;
+ trie_node_type* cur_node = m_node_stack.back().node;
+
+ if (m_type == iterator_type::end && cur_node->has_value)
+ {
+ assert(m_node_stack.size() == 1);
+ m_type = iterator_type::normal;
+ }
+ else if (m_node_stack.size() == 1)
+ {
+ // This is the end position aka root node. Move down to the
+ // right-most leaf node.
+ auto& si = m_node_stack.back();
+ assert(si.child_pos == cur_node->children.end());
+ cur_node = descend_to_previus_leaf_node(m_node_stack, m_buffer);
+ m_type = iterator_type::normal;
+ }
+ else if (cur_node->children.empty())
+ {
+ // This is a leaf node. Keep going up until it finds a parent
+ // node with unvisited child nodes on the left side, then descend
+ // on that path all the way to its leaf.
+
+ do
+ {
+ // Go up one node.
+
+ ktt::pop_back(m_buffer);
+ m_node_stack.pop_back();
+ auto& si = m_node_stack.back();
+ cur_node = si.node;
+
+ if (si.child_pos != cur_node->children.begin())
+ {
+ // Left and down.
+ cur_node = descend_to_previus_leaf_node(m_node_stack, m_buffer);
+ assert(cur_node->has_value);
+ }
+ } while (!cur_node->has_value);
+ }
+ else
+ {
+ // Non-leaf node with value. Keep going up until either the root
+ // node or another node with value is reached.
+
+ assert(cur_node->has_value);
+ assert(m_node_stack.back().child_pos == cur_node->children.begin());
+
+ do
+ {
+ // Go up.
+ ktt::pop_back(m_buffer);
+ m_node_stack.pop_back();
+ auto& si = m_node_stack.back();
+ cur_node = si.node;
+
+ if (m_node_stack.size() == 1)
+ {
+ // Root node reached. Left and down.
+ cur_node = descend_to_previus_leaf_node(m_node_stack, m_buffer);
+ assert(cur_node->has_value);
+ }
+ } while (!cur_node->has_value);
+ }
+
+ assert(cur_node->has_value);
+ m_current_key = ktt::to_key(m_buffer);
+ m_current_value_ptr = &cur_node->value;
+ return *this;
+ }
+
+ iterator_base operator--(int)
+ {
+ iterator_base tmp(*this);
+ operator--();
+ return tmp;
+ }
+};
+
+template<typename _TrieType>
+class const_iterator;
+
+template<typename _TrieType>
+class iterator : public iterator_base<_TrieType, false>
+{
+ using trie_type = _TrieType;
+
+ friend trie_type;
+ friend search_results<trie_type>;
+ friend const_iterator<trie_type>;
+
+ using base_type = iterator_base<trie_type, false>;
+ using node_stack_type = typename base_type::node_stack_type;
+ using key_buffer_type = typename base_type::key_buffer_type;
+
+ iterator(empty_iterator_type t) : base_type(t)
+ {}
+
+public:
+ iterator() : base_type()
+ {}
+
+ iterator(node_stack_type&& node_stack, key_buffer_type&& buf, iterator_type type)
+ : base_type(std::move(node_stack), std::move(buf), type)
+ {}
+};
+
+template<typename _TrieType>
+class const_iterator : public iterator_base<_TrieType, true>
+{
+ using trie_type = _TrieType;
+
+ friend trie_type;
+ friend search_results<trie_type>;
+
+ using base_type = iterator_base<trie_type, true>;
+ using node_stack_type = typename base_type::node_stack_type;
+ using key_buffer_type = typename base_type::key_buffer_type;
+
+ using base_type::m_buffer;
+ using base_type::m_current_key;
+ using base_type::m_current_value_ptr;
+ using base_type::m_node_stack;
+ using base_type::m_type;
+
+ const_iterator(empty_iterator_type t) : base_type(t)
+ {}
+
+public:
+ const_iterator() : base_type()
+ {}
+
+ const_iterator(node_stack_type&& node_stack, key_buffer_type&& buf, iterator_type type)
+ : base_type(std::move(node_stack), std::move(buf), type)
+ {}
+
+ const_iterator(const iterator<_TrieType>& it) : base_type()
+ {
+ m_buffer = it.m_buffer;
+ m_current_key = it.m_current_key;
+ m_current_value_ptr = it.m_current_value_ptr;
+ m_type = it.m_type;
+
+ for (const auto& e : it.m_node_stack)
+ m_node_stack.emplace_back(e.node, e.child_pos);
+ }
+};
+
+template<typename _TrieType>
+bool operator==(const iterator<_TrieType>& left, const const_iterator<_TrieType>& right)
+{
+ return const_iterator<_TrieType>(left) == right;
+}
+
+template<typename _TrieType>
+bool operator!=(const iterator<_TrieType>& left, const const_iterator<_TrieType>& right)
+{
+ return const_iterator<_TrieType>(left) != right;
+}
+
+template<typename _TrieType>
+bool operator==(const const_iterator<_TrieType>& left, const iterator<_TrieType>& right)
+{
+ return left == const_iterator<_TrieType>(right);
+}
+
+template<typename _TrieType>
+bool operator!=(const const_iterator<_TrieType>& left, const iterator<_TrieType>& right)
+{
+ return left != const_iterator<_TrieType>(right);
+}
+
+template<typename _TrieType>
+class search_results
+{
+ using trie_type = _TrieType;
+ friend trie_type;
+ using node_stack_type = typename trie_type::const_node_stack_type;
+
+ using trie_node = typename trie_type::trie_node;
+ using key_traits_type = typename trie_type::key_traits_type;
+ using key_type = typename key_traits_type::key_type;
+ using key_buffer_type = typename key_traits_type::key_buffer_type;
+ using key_unit_type = typename key_traits_type::key_unit_type;
+
+ const trie_node* m_node;
+ key_buffer_type m_buffer;
+ node_stack_type m_node_stack;
+
+ search_results(const trie_node* node, key_buffer_type&& buf) : m_node(node), m_buffer(buf)
+ {}
+
+public:
+ using const_iterator = typename trie_type::const_iterator;
+
+ const_iterator begin() const
+ {
+ if (!m_node)
+ // empty results.
+ return const_iterator(empty_iterator);
+
+ // Push the root node.
+ key_buffer_type buf(m_buffer);
+ node_stack_type node_stack;
+ node_stack.emplace_back(m_node, m_node->children.begin());
+
+ while (!node_stack.back().node->has_value)
+ {
+ // There should always be at least one value node along the
+ // left-most branch.
+
+ auto it = node_stack.back().child_pos;
+ const_iterator::push_child_node_to_stack(node_stack, buf, it);
+ }
+
+ return const_iterator(std::move(node_stack), std::move(buf), iterator_type::normal);
+ }
+
+ const_iterator end() const
+ {
+ if (!m_node)
+ // empty results.
+ return const_iterator(empty_iterator);
+
+ node_stack_type node_stack;
+ node_stack.emplace_back(m_node, m_node->children.end());
+ return const_iterator(std::move(node_stack), key_buffer_type(m_buffer), iterator_type::end);
+ }
+};
+
+template<typename _TrieType>
+class packed_search_results;
+
+template<typename _TrieType>
+class packed_iterator_base
+{
+ using trie_type = _TrieType;
+ friend trie_type;
+ friend packed_search_results<trie_type>;
+
+ using stack_item = typename trie_type::stack_item;
+ using node_stack_type = typename trie_type::node_stack_type;
+
+ using key_traits_type = typename trie_type::key_traits_type;
+ using key_type = typename key_traits_type::key_type;
+ using key_buffer_type = typename key_traits_type::key_buffer_type;
+ using key_unit_type = typename key_traits_type::key_unit_type;
+
+public:
+ // iterator traits
+ using value_type = typename trie_type::key_value_type;
+ using pointer = value_type*;
+ using reference = value_type&;
+ using difference_type = std::ptrdiff_t;
+ using iterator_category = std::bidirectional_iterator_tag;
+
+private:
+ node_stack_type m_node_stack;
+ key_buffer_type m_buffer;
+ value_type m_current_value;
+ iterator_type m_type;
+
+ /**
+ * Given a child offset position (child_pos), jump to the actual child
+ * node position and push that onto the stack as stack_item.
+ */
+ static void push_child_node_to_stack(node_stack_type& node_stack, key_buffer_type& buf, const uintptr_t* child_pos)
+ {
+ using ktt = key_traits_type;
+
+ const uintptr_t* node_pos = node_stack.back().node_pos;
+
+ key_unit_type c = static_cast<key_unit_type>(*child_pos);
+ ktt::push_back(buf, c);
+ ++child_pos;
+ size_t offset = static_cast<size_t>(*child_pos);
+ node_pos -= offset; // Jump to the head of the child node.
+ const uintptr_t* p = node_pos;
+ ++p;
+ size_t index_size = *p;
+ ++p;
+ child_pos = p;
+ const uintptr_t* child_end = child_pos + index_size;
+
+ // Push it onto the stack.
+ node_stack.emplace_back(node_pos, child_pos, child_end);
+ }
+
+ static const void descend_to_previus_leaf_node(node_stack_type& node_stack, key_buffer_type& buf)
+ {
+ using ktt = key_traits_type;
+
+ const uintptr_t* node_pos = nullptr;
+ size_t index_size = 0;
+
+ do
+ {
+ // Keep moving down on the right-most child nodes until the
+ // leaf node is reached.
+
+ stack_item* si = &node_stack.back();
+ node_pos = si->node_pos;
+ --si->child_pos;
+ size_t offset = *si->child_pos;
+ --si->child_pos;
+ key_unit_type c = *si->child_pos;
+ node_pos -= offset; // Jump to the head of the child node.
+ ktt::push_back(buf, c);
+
+ const uintptr_t* p = node_pos;
+ ++p;
+ index_size = *p;
+ ++p;
+ const uintptr_t* child_pos = p;
+ const uintptr_t* child_end = child_pos + index_size;
+ node_stack.emplace_back(node_pos, child_end, child_end);
+ } while (index_size);
+ }
+
+ packed_iterator_base(empty_iterator_type) : m_type(iterator_type::empty)
+ {}
+
+public:
+ packed_iterator_base() : m_type(iterator_type::normal)
+ {}
+
+ packed_iterator_base(node_stack_type&& node_stack, key_buffer_type&& buf, const typename trie_type::value_type& v)
+ : m_node_stack(std::move(node_stack)), m_buffer(std::move(buf)),
+ m_current_value(key_traits_type::to_key(m_buffer), v), m_type(iterator_type::normal)
+ {}
+
+ packed_iterator_base(node_stack_type&& node_stack, key_buffer_type&& buf)
+ : m_node_stack(std::move(node_stack)), m_buffer(std::move(buf)), m_type(iterator_type::end)
+ {}
+
+ bool operator==(const packed_iterator_base& other) const
+ {
+ if (m_type != other.m_type)
+ return false;
+
+ if (m_type == iterator_type::empty)
+ return true;
+
+ return m_node_stack.back() == other.m_node_stack.back();
+ }
+
+ bool operator!=(const packed_iterator_base& other) const
+ {
+ return !operator==(other);
+ }
+
+ const value_type& operator*()
+ {
+ return m_current_value;
+ }
+
+ const value_type* operator->()
+ {
+ return &m_current_value;
+ }
+
+ packed_iterator_base& operator++()
+ {
+ using ktt = key_traits_type;
+
+ stack_item* si = &m_node_stack.back();
+ const typename trie_type::value_type* pv = nullptr;
+ size_t index_size = *(si->node_pos + 1);
+
+ do
+ {
+ if (!index_size)
+ {
+ // Current node is a leaf node. Keep moving up the stack until we
+ // reach a parent node with unvisited children.
+
+ while (true)
+ {
+ if (m_node_stack.size() == 1)
+ {
+#ifdef MDDS_TRIE_MAP_DEBUG
+ if (m_type == iterator_type::end)
+ {
+ std::ostringstream os;
+ os << "packed_iterator_base::operator++#" << __LINE__ << ": moving past the end position!";
+ throw general_error(os.str());
+ }
+#endif
+ // We've reached the end position. Bail out.
+ m_type = iterator_type::end;
+ return *this;
+ }
+
+ // Move up one parent and see if it has an unvisited child node.
+ ktt::pop_back(m_buffer);
+ m_node_stack.pop_back();
+ si = &m_node_stack.back();
+ std::advance(si->child_pos, 2);
+
+ if (si->child_pos != si->child_end)
+ {
+ // Move down to this unvisited child node.
+ push_child_node_to_stack(m_node_stack, m_buffer, si->child_pos);
+ break;
+ }
+ }
+ }
+ else
+ {
+ // Current node has child nodes. Follow the first child node.
+ push_child_node_to_stack(m_node_stack, m_buffer, si->child_pos);
+ }
+
+ si = &m_node_stack.back();
+ pv = reinterpret_cast<const typename trie_type::value_type*>(*si->node_pos);
+ index_size = *(si->node_pos + 1);
+ } while (!pv);
+
+ assert(pv);
+ m_current_value = value_type(ktt::to_key(m_buffer), *pv);
+
+ return *this;
+ }
+
+ packed_iterator_base operator++(int)
+ {
+ packed_iterator_base tmp(*this);
+ operator++();
+ return tmp;
+ }
+
+ packed_iterator_base& operator--()
+ {
+ using ktt = key_traits_type;
+
+ stack_item* si = &m_node_stack.back();
+ const typename trie_type::value_type* pv =
+ reinterpret_cast<const typename trie_type::value_type*>(*si->node_pos);
+ size_t index_size = *(si->node_pos + 1); // index size for child nodes.
+
+ if (m_type == iterator_type::end && pv)
+ {
+ assert(m_node_stack.size() == 1);
+ m_type = iterator_type::normal;
+ }
+ else if (m_node_stack.size() == 1)
+ {
+ // This is the end position aka root node. Move down to the
+ // right-most leaf node.
+ assert(si->child_pos == si->child_end);
+ descend_to_previus_leaf_node(m_node_stack, m_buffer);
+ si = &m_node_stack.back();
+ pv = reinterpret_cast<const typename trie_type::value_type*>(*si->node_pos);
+ m_type = iterator_type::normal;
+ }
+ else if (!index_size)
+ {
+ // This is a leaf node. Keep going up until it finds a parent
+ // node with unvisited child nodes on the left side, then descend
+ // on that path all the way to its leaf.
+
+ do
+ {
+ // Go up one node.
+
+ ktt::pop_back(m_buffer);
+ m_node_stack.pop_back();
+ si = &m_node_stack.back();
+ const uintptr_t* p = si->node_pos;
+ pv = reinterpret_cast<const typename trie_type::value_type*>(*p);
+ ++p;
+ index_size = *p;
+ ++p;
+ const uintptr_t* first_child = p;
+
+ if (si->child_pos != first_child)
+ {
+ // Left and down.
+ descend_to_previus_leaf_node(m_node_stack, m_buffer);
+ si = &m_node_stack.back();
+ p = si->node_pos;
+ pv = reinterpret_cast<const typename trie_type::value_type*>(*p);
+ assert(pv);
+ }
+ } while (!pv);
+ }
+ else
+ {
+ // Non-leaf node with value. Keep going up until either the root
+ // node or another node with value is reached.
+
+ assert(*si->node_pos); // this node should have a value.
+ assert(si->child_pos == (si->node_pos + 2));
+
+ do
+ {
+ // Go up.
+ ktt::pop_back(m_buffer);
+ m_node_stack.pop_back();
+ si = &m_node_stack.back();
+ pv = reinterpret_cast<const typename trie_type::value_type*>(*si->node_pos);
+
+ if (m_node_stack.size() == 1)
+ {
+ // Root node reached.
+ descend_to_previus_leaf_node(m_node_stack, m_buffer);
+ si = &m_node_stack.back();
+ pv = reinterpret_cast<const typename trie_type::value_type*>(*si->node_pos);
+ assert(pv);
+ }
+ } while (!pv);
+ }
+
+ assert(pv);
+ m_current_value = value_type(ktt::to_key(m_buffer), *pv);
+
+ return *this;
+ }
+
+ packed_iterator_base operator--(int)
+ {
+ packed_iterator_base tmp(*this);
+ operator--();
+ return tmp;
+ }
+};
+
+template<typename _TrieType>
+class packed_search_results
+{
+ using trie_type = _TrieType;
+ friend trie_type;
+ using node_stack_type = typename trie_type::node_stack_type;
+
+ using key_traits_type = typename trie_type::key_traits_type;
+ using key_type = typename key_traits_type::key_type;
+ using key_buffer_type = typename key_traits_type::key_buffer_type;
+ using key_unit_type = typename key_traits_type::key_unit_type;
+
+ const uintptr_t* m_node;
+ key_buffer_type m_buffer;
+
+ packed_search_results(const uintptr_t* node, key_buffer_type&& buf) : m_node(node), m_buffer(std::move(buf))
+ {}
+
+ node_stack_type get_root_node() const
+ {
+ const uintptr_t* p = m_node;
+ ++p;
+ size_t index_size = *p;
+ ++p;
+ const uintptr_t* child_pos = p;
+ const uintptr_t* child_end = child_pos + index_size;
+
+ // Push this child node onto the stack.
+ node_stack_type node_stack;
+ node_stack.emplace_back(m_node, child_pos, child_end);
+ return node_stack;
+ }
+
+ void swap(packed_search_results& other)
+ {
+ std::swap(m_node, other.m_node);
+ std::swap(m_buffer, other.m_buffer);
+ }
+
+public:
+ using const_iterator = packed_iterator_base<trie_type>;
+
+ packed_search_results() : m_node(nullptr)
+ {}
+
+ packed_search_results(const packed_search_results& other) : m_node(other.m_node), m_buffer(other.m_buffer)
+ {}
+
+ packed_search_results(packed_search_results&& other) : m_node(other.m_node), m_buffer(std::move(other.m_buffer))
+ {
+ other.m_node = nullptr;
+ }
+
+ packed_search_results& operator=(packed_search_results other)
+ {
+ packed_search_results tmp(std::move(other));
+ swap(tmp);
+ return *this;
+ }
+
+ const_iterator begin() const
+ {
+ if (!m_node)
+ // empty results.
+ return const_iterator(empty_iterator);
+
+ // Push the root node.
+ key_buffer_type buf(m_buffer);
+ node_stack_type node_stack = get_root_node();
+
+ while (!node_stack.back().has_value())
+ {
+ // There should always be at least one value node along the
+ // left-most branch.
+
+ const_iterator::push_child_node_to_stack(node_stack, buf, node_stack.back().child_pos);
+ }
+
+ return const_iterator(std::move(node_stack), std::move(buf), *node_stack.back().get_value());
+ }
+
+ const_iterator end() const
+ {
+ if (!m_node)
+ // empty results.
+ return const_iterator(empty_iterator);
+
+ node_stack_type node_stack = get_root_node();
+ auto& si = node_stack.back();
+ si.child_pos = si.child_end;
+ return const_iterator(std::move(node_stack), key_buffer_type(m_buffer));
+ }
+};
+
+}}} // namespace mdds::trie::detail
+
+#endif