Import Upstream version 2.0.3

This commit is contained in:
zhouganqing 2022-11-07 15:52:08 +08:00
commit 58c1ba07ea
268 changed files with 107597 additions and 0 deletions

15
AUTHORS Normal file
View File

@ -0,0 +1,15 @@
Kohei Yoshida <kohei.yoshida@gmail.com> (Maintainer)
David Tardon <dtardon@redhat.com>
Markus Mohrhard <markus.mohrhard@googlemail.com>
Caolán McNamara <caolanm@redhat.com>
Takeshi Abe <tabe@fixedpoint.jp>
Stephan Bergmann <sbergman@redhat.com>
William BONNET <william@wbonnet.net>
Fridrich Štrba <fridrich.strba@bluewin.ch>
Petr Mladek <pmladek@suse.cz>
Philipp Thomas <pth@suse.de>
Chris Mayo <aklhfex@gmail.com>
Dennis Francis <dennis.francis@collabora.co.uk>
Heiko Becker <heirecka@exherbo.org>
Luboš Luňák <l.lunak@centrum.cz>
Tomáš Chvátal <scarabeus@gentoo.org>

855
CHANGELOG Normal file
View File

@ -0,0 +1,855 @@
mdds 2.0.3
* general
* defined clang-format rules, and globally applied them to all active
source files.
* multi_type_vector
* revised the block position lookup implementation to avoid using the
internal STL iterators. The new implementation should be able to handle
invalid position hints more gracefully without potential process
termination.
mdds 2.0.2
* multi_type_vector
* added optional trace function that gets called on every called public
method.
mdds 2.0.1
* general
* addressed various coverity issues.
* multi_type_vector
* fixed random compiler warnings.
* fixed event handling in copy construction. In aos, the event object was
not copied when the parent container was copied. In soa, the
element_block_acquired() callback was not called for the cloned element
blocks.
* added move constructors and move assignment operators to both aos and soa
variants.
mdds 2.0.0
* general
* set the baseline C++ version to C++17.
* multi_type_vector
* implemented structure-of-arrays (SoA) storage as its default storage
layout for better CPU cache efficiency.
* added multiple block position adjustment implementations with various
loop-unrolling factors combined with SSE2 and AVX2 features.
* added a tool called runtime-env to benchmark different block position
adjustment implementations to determine the optimal loop-unrolling factor.
* rectangle_set
* permanently removed.
* rtree
* fixed a bug where the memory positions of invalidated child nodes were not
properly updated after tree mutation. The problem manifested itself when
using libc++ as stdlib with clang.
mdds 1.7.0
* trie_map
* added copy and move constructors.
* added a variant of find() method that returns a mutable iterator object.
The user can now update the value associated with a key directly via the
iterator object.
* packed_trie_map
* added copy and move constructors.
* added load_state() and save_state() methods to allow loading state from
and saving state to binary files.
mdds 1.6.0
* multi_type_vector
* switched to using binary search on block position lookup, which
significantly improves element access performance in general, at the
expense of slight performance degradation on block shifting.
* added support for lcov, to visualize test coverage.
mdds 1.5.0
* documentation
* moved the documentation hosting to readthedocs.io, and adjusted the build
steps.
* moved the API incompatibility notes from README to the rst doc.
* added the overview section for flat_segment_tree.
* multi_type_vector
* fixed the static get(const const_position_type& pos) method for the
boolean_element_block, by adding specialization for it to work around the
issue with std::vector<bool> not having the at() method.
* fixed an issue with the const position() method not returning a valid end
position the same way the non-const variant does.
* added steps to traverse blocks backward from the postiion specified in the
position hint. This may result in improved performance in some
situations.
* the standard integer blocks now use fixed size integer types i.e.
* (u)int8_t
* (u)int16_t
* (u)int32_t
* (u)int64_t
* The numeric_element_block has been renamed to double_element_block.
* added new block type to store float element values.
* general
* added gdb pretty printers that prints the contents of the data structures.
mdds 1.4.3
* documentation
* added details on how to use two type of iterators with
flat_segment_tree.
* added new section to describe how to use mtv::collection to iterate
through multiple multi_type_vector instances as a single collection
in the direction orthogonal to the direction of the individual
vectors.
* added new page for R-tree.
* flat_segment_tree
* fixed invalid memory access issue related to the swap() method which
previously did not swap the non-leaf node pool store. The invalid
memory access may occur after the contents of two instances get
swapped, one instance get destroyed then the caller calls
search_tree() on the other instance still alive.
mdds 1.4.2
* all
* fixed CXXFLAGS incorrectly being overwritten.
* addressed a number of Coverity issues.
mdds 1.4.1
* all
* fixed all warnings on shadowed variables.
* multi_type_matrix
* all of its walk() methods now return either a copied or moved
instance of the function object passed in as an input argument.
Previously these methods had no return values.
mdds 1.4.0
* rtree (new)
* new data structure designed for optimal storage and query
performance on multi-dimensional spatial data. The structure allows
storage of both point and extent-based boundaries as keys associated
with values.
* multi_type_vector
* mtv::elemnt_block now has the following methods: data(), cbegin(),
cend(), crbegin() and crend().
* multi_type_vector now has cbegin(), cend(), crbegin(), and crend()
methods.
* some unnecessary user-provided special members have been removed to
avoid warnings with -Wdeprecated-copy with GCC 9.
* multi_type_matrix
* all of its walk() methods now allow in-line lambdas to be used, by
not taking a reference of the function object parameters.
mdds 1.3.1
* flat_segment_tree
* fixed a bug that caused an assertion error when inserting a
out-of-bound segment whose start value equals the max key value.
mdds 1.3.0
* multi_type_vector
* changed the primary block array storage to remove additional
indirection, for improved memory locality.
mdds 1.2.3
* all
* changed the configure script to use --docdir unmodified.
* flat_segment_tree
* added a segment iterator whose node value consists of the start
and end keys and the value associated with each segment. its
start and end positions can be retrieved via begin_segment() and
end_segment() methods.
mdds 1.2.2
* flat_segment_tree
* fixed a bug that would cause segmentation faults with the insert()
method with out-of-bound segment value pair.
mdds 1.2.1
* multi_type_vector
* added size() method to the element block type, which returns the
actual size of the element block, instead of the cached size value
stored in the parent structure that stores the element block.
* fixed a double-deletion bug in the swap() method which would
triggered when used with a managed element block.
* mtv::collection
* fixed collection iterator's get() method to properly return values
from the boolean element block.
mdds 1.2.0
* packed_trie_map
* added begin() and end() methods that return read-only iterators.
* find() method now returns a const_iterator instance.
* prefix_search() method now returns a search_results instance that
can be iterated.
* null value no longer needs to be passed to the constructor.
* find() and prefix_search() now have a variant that can take a key
value that is of key_type directly.
* trie_map
* added begin() and end() methods that return read-only iterators.
* find() method now returns a const_iterator instance.
* prefix_search() method now returns a search_results instance that
can be iterated.
* null value no longer needs to be passed to the constructor.
* find(), insert, and prefix_search() now have a variant that can
take a key value that is of key_type directly.
* sorted_string_map
* fix build failure with _GLIBCXX_DEBUG defined.
* multi_type_vector
* remove compiler warning about shadowed variable.
* added a supplemental class mdds::mtv::collection which allows
multiple multi_type_vector instances of the same length to be
grouped together in order to iterate through their elements
sideways.
* a variant of advance_position() static method that takes
const_position_type has been added.
* const_position_type advance_position(const const_position_type& pos, int steps)
* multi_type_matrix
* matrix_position() is now a const method.
* the sub-matrix variant of walk() method now throws size_error
exception when invalid start and end positions are passed.
* slight performance improvement with the sub-matrix variant of
walk() method that involves multiple column traversal.
* added 2 new variants of walk() methods that allow parallel walking
with another matrix instance.
* template<typename _Func>
void walk(_Func& func, const multi_type_matrix& right) const
* template<typename _Func>
void walk(_Func& func, const multi_type_matrix& right, const size_pair_type& start, const size_pair_type& end) const
* improved performance of copy() and resize() methods.
* added a variant of copy() that takes an array of values.
* template<typename _T>
void copy(size_type rows, size_type cols, const _T& it_begin, const _T& it_end)
* integer type has been added to the list of types the matrix can
store. In conjunction with this change, what was formerly known
as the string trait structure is now known as the matrix trait,
which specifies the actual integer type the matrix stores.
* point_quad_tree
* search_result has been renamed to search_results.
mdds 1.1.0
* all
* switched our build system to using automake.
* packed_trie_map (new)
* new data structure that implements a trie also known as a prefix
tree. This implementation requires all key values be known at
construction time, after which its content is considered
immutable. Internally it packs all its nodes in a single
contiguous array for space and lookup efficiencies.
* trie_map (new)
* new data structure that implements a trie. It works similar to
packed_trie_map except that this version is mutable.
* multi_type_matrix
* added a variant of walk() that takes the upper-left and
lower-right corners to allow walking through a subset of the
original matrix.
* multi_type_vector
* fixed incorrect return values of the increment and decrement
operators of in-block iterators. They would previously return a
value_type pointer which did not conform to the behaviors of STL
iterators.
* added support for custom event handlers for element block
acquisitions and releases.
* flat_segment_tree
* fixed incorrect return values of the increment and decrement
operators of its leaf-node iterators as in multi_type_vector's
fix.
* sorted_string_map
* significantly improved the performance of its find() method by
switching from using linear search to using binary search. The
improvement is especially visible with a large number of elements.
mdds 1.0.0
* all
* introduced API versioning to ease parallel installation of API
incompatible versions. Version 1.0.0 will have an API versoin of
1.0.
* C++11 is now a hard requirement.
* added API documentation via Doxygen, Sphinx and Breathe.
* mixed_type_matrix
* officially removed for good in favor of multi_type_matrix.
* multi_type_vector
* added memory usage reduction by conditionally shrinking the
capacity of the underlying vector containers.
* added slight performance gain by revising block adjustment policy
during splitting of blocks.
* sorted_string_map
* fixed a bug where a non-matching key was incorrectly returned as a
matching key.
mdds 0.12.1
* flat_segment_tree
* removed construction-from-int requirement from value_type to allow
non-numeric types to be stored.
* multi_type_vector
* added static method advance_position() to allow incrementing or
decrementing the logical position of a position_type object:
* position_type advance_position(const position_type& pos, int steps)
mdds 0.12.0
* segment_tree
* removed pointer requirement from value_type to allow non-pointer
type to be stored.
* multi_type_vector
* fixed a bug in the equality operator method.
mdds 0.11.2
* multi_type_vector
* fixed various memory leaks associated with the set() method when a
value overwrites an existing element in a managed block.
mdds 0.11.1
* all
* fixed a large number of outstanding defects reported by Coverity
Scan.
* multi_type_vector
* fixed 2 cases of double-free bug in the variant of swap() that
allows segmented swapping.
mdds 0.11.0
* sorted_string_map (new)
* new data structure to support efficient mapping of textural keys
to numeric values when the key values are known at compile time.
* multi_type_vector
* fixed a bug in transfer() where two adjacent blocks of identical
type would fail to be merged in some circumstances.
* added shrink_to_fit() to allow trimming of any excess capacity
from all non-empty blocks.
* fixed a double-free bug in the variant of swap() that allows
segmented swapping.
* improved the exception message when the block position lookup
fails to find valid block position, to make it easier to debug.
mdds 0.10.3
* multi_type_vector
* added 2 variants of release_range() that take start and end positions,
to allow releasing of elements in specified interval. One of the
variants takes iterator as a block position hint.
* iterator release_range(size_type start_pos, size_type end_pos)
* iterator release_range(const iterator& pos_hint, size_type start_pos, size_type end_pos)
* added push_back() and push_back_empty(), to allow efficient way to
append new values to the end of the container.
* template<typename _T>
iterator push_back(const _T& value)
* iterator push_back_empty()
mdds 0.10.2
* multi_type_vector
* fixed a bug in transfer() that would trigger an assertion and
eventually lead to a crash. The problem occurred when a range of
data to be transferred spanned over 2 blocks and consisted of the
lower part of an upper block and the upper part of a lower block.
mdds 0.10.1
* multi_type_matrix
* added a variant of set_empty() that takes an additional length
parameter.
* void set_empty(size_type row, size_type col, size_type length)
mdds 0.10.0
* flat_segment_tree
* significant performance improvement on build_tree() and
search_tree(), by optimizing the non-leaf node object generation
and storage to achieve better locality of reference.
* segment_tree
* slight performance improvement on build_tree(), as a result of the
optimization done for flat_segment_tree since these two structures
share the same tree generation code.
* multi_type_vector
* improved debug message on mis-matched block types (only when
MDDS_MULTI_TYPE_VECTOR_DEBUG is defined).
mdds 0.9.1
* multi_type_vector
* added several convenience methods for position objects.
* performance improvement on setting array values.
* added new constructor that takes an array of values as initial
element values.
* multi_type_matrix
* setter methods that take a position object to also return a
position object.
* added several convenience methods for position objects.
* added new constructor that takes an array of values as initial
element values.
mdds 0.9.0
* multi_type_vector
* added another block function template to make it easier to declare
container with 3 custom element types.
* added two variants of release():
* template<typename _T> iterator
release(size_type pos, _T& value)
* template<typename _T> iterator
release(const iterator& pos_hint, size_type pos, _T& value)
* added a variant of release() that takes no arguments. This one
releases all elements and makes the container empty afterward.
* added a new variant of position() that takes const_iterator as
position hint.
* std::pair<const_iterator, size_type>
position(const const_iterator& pos_hint, size_type pos) const
* fixed a memory leak in
* set(size_type pos, const _T& it_begin, const _T& it_end).
* added compile-time macro MDDS_MULTI_TYPE_VECTOR_USE_DEQUE to allow
users to specify std::deque as the underlying data array. By
default, multi_type_vector uses std::vector as the underlying data
array container.
* added a new variant of swap() that allows partial swapping of
content with another container.
* added static block type identifier so that the numeric block type
ID can be deduced from the block type directly.
* value_type (which is a type of object returned when dereferencing
an iterator) now stores 'position' which is the logical position
of the first element of a block.
* added position_type and const_position_type which are typedefs to
the return types of position() methods.
* multi_type_matrix:
* get_numeric(), get_boolean(), and get_string() are made more
efficient.
* added position() method that returns a reference object to an
element (position object).
* added variants of get_numeric(), get_boolean() and get_string()
that retrieves elements from position objects.
* added variants of set() that sets new element values via position
objects.
mdds 0.8.1
* multi_type_vector
* fixed a bug in the erase() method where adjacent blocks of the
same type would fail to merge after the erase() call.
* add a variant of the position() method that takes an iterator as
positional hint. Note that there is no variant of position() that
takes const_iterator.
mdds 0.8.0
* all
* added .pc file for pkg-config.
* flat_segment_tree
* changed the return type of search_tree from bool to
std::pair<const_iterator,bool>, to make it consistent with the
search() method. Note that this is an API-incompatible change.
* multi_type_vector
* added char and unsigned char types to the standard types supported
by default.
* added position() member method that takes a logical element
position and returns a pair of block iterator where the element
resides and its offset within that block.
* added at() static member method to the data block, which calls the
at() method of the underlying std::vector container.
* added release() member method to allow caller to release an object
stored inside a managed block.
* added two templates to ease creation of custom element block
functions when using one or two custom element types.
* added transfer() member method to allow elements in a specified
range to be transferred from one container to another. When
transferring elements stored in a managed element block, the
ownership of those elements is also transferred.
mdds 0.7.1
* multi_type_vector
* fixed a bug in set_empty() where emptying a whole or partial block
would fail to merge its adjacent block(s) even when they are also
empty.
mdds 0.7.0
* multi_type_vector
* add variants of set() methods (both single- and multi-value)
insert(), set_empty() and insert_empty() methods that take an
iterator as an additional position hint parameter for block lookup
speed optimization.
* add support for non-const iterators which allow the client code to
modify values directly from the iterators.
* set() methods (both single- and multi-parameter variants),
set_empty(), insert() and insert_empty() methods now return
iterator that references the block to which the values are set or
inserted.
* fixed bugs in set() method (single-parameter variant) which would
insert a new block at incorrect position.
* fixed bugs in set() method (multi-parameter variant) which would
fail to merge neighboring blocks of identical type under certain
conditions.
mdds 0.6.1
* all
* use property files in the Visual Studio project files, to share
some of the common custom build variables across all projects.
* various build fixes and compiler warning eliminations.
* fixed link error with boost 1.50.
* fixed make installer script which previously would not install
mdds/compat headers.
* flat_segment_tree
* fixed a bug in its iterator implementation, which previously would
always treat the last valid position before the end position as
the end position. This fix affects both in const_iterator and
const_reverse_iterator.
mdds 0.6.0
* all
* added MSVS Solution file, to make it easier to build unit test
programs on Windows.
* mixed_type_matrix
* improved performance of size() method by caching it.
* multi_type_vector (new)
* new data structure to support efficient storage of data of different
types.
* multi_type_matrix (new)
* new data structure to eventually replace mixed_type_matrix. It uses
multi_type_vector as its backend storage.
mdds 0.5.4
* segment_tree
* fixed build breakage, to allow it to be buildable when UNIT_TEST
is not defined.
* fixed a crasher with MSVC when comparing iterators of empty
search_result instances.
* point_quad_tree
* fixed a bug where de-referencing copied search_result iterators
would return an uninitialized node data.
mdds 0.5.3
* mixed_type_matrix
* re-implemented the filled storage for better performance, with two
separate implementations for zero and emtpy matrix types. The
newer implementation should improve object creation time
considerably.
mdds 0.5.2
* flat_segment_tree
* fixed a crash on assignment by properly implementing assignment
operator().
* fixed several bugs in shift_right():
* shifting of all existing nodes was not handled properly.
* leaf nodes were not properly linked under certain conditions.
* shifting with skip node option was not properly skipping the
node at insertion position when the insertion position was at
the leftmost node.
* implemented min_key(), max_key(), default_value(), clear() and
swap().
* fixed a bug in operator==() where two different containers were
incorrectly evaluated to be equal.
* added quickcheck test code.
mdds 0.5.1
* fixed build issues on Windows, using MSVC compilers.
mdds 0.5.0
* flat_segment_tree's search methods now return a std::pair of
const_iterator and bool, instead of just returning bool.
* fixed a weird enum value mis-handling with mixed_type_matrix when
compiled with MSVC++.
* added new insert() method to flat_segment_tree that takes a
positional hint in order to speed up insertion speed. Also, all
three insert() methods now return the start position of the
segment that an inserted segment belongs to.
* slight performance improvement on the insert methods of
flat_segment_tree.
* slight performance improvement on the iterators of
flat_segment_tree.
* re-organized the structure of flat_segment_tree to split it into
multiple headers.
* properly support prefix, docdir, includedir configure options.
* support DESTDIR environment variable for make install.
mdds 0.4.0
* implemented mixed_type_matrix.
mdds 0.3.1
* added support for boost::unordered_map (boost) and std::hash_map
(stlport) in addition to C++0x's std::unordered_map.
mdds 0.3.0
* implemented point_quad_tree.
mdds 0.2.1
* added example files on how to use these data structures.
* fixed a bug in segment_tree::search_result object, to make it work
with empty result set.
* fixed segment_tree to make it really usable outside of unit test
code.
mdds 0.2.0
* other general performance improvements.
* lots of code cleanups.
* support for search result iterator in segment_tree and
rectangle_set, for better search performance.
* implemented rectnagle_set.
* fixed lots of bugs in the segment_tree implementation.
mdds 0.1.2
* implemented segment_tree.
* node_base class is now without virtual methods to avoid vtable
generation.

22
LICENSE Normal file
View File

@ -0,0 +1,22 @@
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.

391
Makefile.am Normal file
View File

@ -0,0 +1,391 @@
SUBDIRS = include example test tools
pkgconfdir = $(datarootdir)/pkgconfig
AM_CPPFLAGS = \
-I$(top_srcdir)/include \
-I$(top_srcdir)/test/include \
$(CXXFLAGS_UNITTESTS)
dist_doc_DATA = AUTHORS README.md
nodist_pkgconf_DATA = misc/mdds-@API_VERSION@.pc
DISTCLEANFILES = \
misc/mdds-@API_VERSION@.pc \
rtree-test-*.obj \
rtree-test-*.svg
EXTRA_DIST = \
autogen.sh \
CHANGELOG \
LICENSE \
doc/conf.py \
doc/doxygen.conf \
doc/flat_segment_tree.rst \
doc/index.rst \
doc/multi_type_matrix.rst \
doc/multi_type_vector.rst \
doc/point_quad_tree.rst \
doc/segment_tree.rst \
doc/sorted_string_map.rst \
doc/trie_map.rst \
doc/_static/images/mtv_block_structure.png \
example/flat_segment_tree.cpp \
example/multi_type_matrix.cpp \
example/multi_type_vector.cpp \
example/multi_type_vector_element_block1.cpp \
example/multi_type_vector_event1.cpp \
example/multi_type_vector_pos_hint.cpp \
example/packed_trie_map.cpp \
example/point_quad_tree.cpp \
example/segment_tree.cpp \
example/trie_map.cpp \
misc/gdb/mdds/__init__.py \
misc/matrix_perf.cpp \
misc/mdds.pc.in \
misc/sorted_string_map_perf.cpp \
quickcheck/flat_segment_tree.cpp \
test/mem-wrapper.sh \
test/test-wrapper.sh \
test/test.mem.in
check_PROGRAMS = \
flat_segment_tree_test \
multi_type_matrix_test \
multi_type_matrix_test_walk \
multi_type_vector_test_event_aos \
multi_type_vector_test_event_soa \
multi_type_vector_test_custom_aos \
multi_type_vector_test_custom_soa \
multi_type_vector_test_default_aos \
multi_type_vector_test_default_soa \
multi_type_vector_test_perf \
multi_type_vector_test_collection_aos \
multi_type_vector_test_collection_soa \
point_quad_tree_test \
segment_tree_test \
sorted_string_map_test \
stlperf_test \
template_test \
trie_map_test \
rtree_test \
rtree_test_bulkload \
ref_pair_test
flat_segment_tree_test_SOURCES = \
test/flat_segment_tree_test.cpp \
test/include/test_global.hpp \
test/test_global.cpp
multi_type_matrix_test_SOURCES = \
test/multi_type_matrix_test.cpp \
test/include/test_global.hpp \
test/test_global.cpp
multi_type_matrix_test_walk_SOURCES = \
test/multi_type_matrix_test_walk.cpp \
test/include/test_global.hpp \
test/test_global.cpp
multi_type_vector_test_event_aos_SOURCES = \
test/multi_type_vector/event/aos/test_main.hpp \
test/multi_type_vector/event/aos/test_main.cpp \
test/multi_type_vector/event/aos/test_block_counter.cpp \
test/multi_type_vector/event/aos/test_block_init.cpp \
test/multi_type_vector/event/aos/test_swap.cpp \
test/multi_type_vector/event/tc/block_counter.inl \
test/multi_type_vector/event/tc/block_init.inl \
test/multi_type_vector/event/tc/swap.inl \
test/include/test_global.hpp \
test/test_global.cpp
multi_type_vector_test_event_aos_CPPFLAGS = \
-I$(top_srcdir)/test/multi_type_vector/event \
-I$(top_srcdir)/test/multi_type_vector/event/tc \
$(AM_CPPFLAGS)
multi_type_vector_test_event_soa_SOURCES = \
test/multi_type_vector/event/soa/test_main.hpp \
test/multi_type_vector/event/soa/test_main.cpp \
test/multi_type_vector/event/soa/test_block_counter.cpp \
test/multi_type_vector/event/soa/test_block_init.cpp \
test/multi_type_vector/event/aos/test_swap.cpp \
test/include/test_global.hpp \
test/test_global.cpp
multi_type_vector_test_event_soa_CPPFLAGS = \
-I$(top_srcdir)/test/multi_type_vector/event \
-I$(top_srcdir)/test/multi_type_vector/event/tc \
$(AM_CPPFLAGS)
multi_type_vector_test_custom_aos_SOURCES = \
test/multi_type_vector/custom/aos/test_main.hpp \
test/multi_type_vector/custom/aos/test_main.cpp \
test/multi_type_vector/custom/aos/test_basic.cpp \
test/multi_type_vector/custom/aos/test_managed_block.cpp \
test/multi_type_vector/custom/aos/test_misc.cpp \
test/multi_type_vector/custom/aos/test_swap.cpp \
test/multi_type_vector/custom/aos/test_transfer.cpp \
test/multi_type_vector/custom/tc/basic.inl \
test/multi_type_vector/custom/tc/managed_block.inl \
test/multi_type_vector/custom/tc/misc.inl \
test/multi_type_vector/custom/tc/swap.inl \
test/multi_type_vector/custom/tc/transfer.inl \
test/multi_type_vector/custom/common_types.hpp \
test/include/test_global.hpp \
test/test_global.cpp
multi_type_vector_test_custom_aos_CPPFLAGS = \
-I$(top_srcdir)/test/multi_type_vector/custom \
-I$(top_srcdir)/test/multi_type_vector/custom/tc \
$(AM_CPPFLAGS)
multi_type_vector_test_custom_soa_SOURCES = \
test/multi_type_vector/custom/soa/test_main.hpp \
test/multi_type_vector/custom/soa/test_main.cpp \
test/multi_type_vector/custom/soa/test_basic.cpp \
test/multi_type_vector/custom/soa/test_managed_block.cpp \
test/multi_type_vector/custom/soa/test_misc.cpp \
test/multi_type_vector/custom/soa/test_swap.cpp \
test/multi_type_vector/custom/soa/test_transfer.cpp \
test/multi_type_vector/custom/common_types.hpp \
test/include/test_global.hpp \
test/test_global.cpp
multi_type_vector_test_custom_soa_CPPFLAGS = \
-I$(top_srcdir)/test/multi_type_vector/custom \
-I$(top_srcdir)/test/multi_type_vector/custom/tc \
$(AM_CPPFLAGS)
multi_type_vector_test_default_aos_SOURCES = \
test/multi_type_vector/default/aos/test_construction.cpp \
test/multi_type_vector/default/aos/test_basic.cpp \
test/multi_type_vector/default/aos/test_empty_cells.cpp \
test/multi_type_vector/default/aos/test_erase.cpp \
test/multi_type_vector/default/aos/test_hints.cpp \
test/multi_type_vector/default/aos/test_insert.cpp \
test/multi_type_vector/default/aos/test_iterators.cpp \
test/multi_type_vector/default/aos/test_iterators_insert.cpp \
test/multi_type_vector/default/aos/test_iterators_set.cpp \
test/multi_type_vector/default/aos/test_iterators_set_empty.cpp \
test/multi_type_vector/default/aos/test_misc.cpp \
test/multi_type_vector/default/aos/test_position.cpp \
test/multi_type_vector/default/aos/test_set.cpp \
test/multi_type_vector/default/aos/test_swap_range.cpp \
test/multi_type_vector/default/aos/test_transfer.cpp \
test/multi_type_vector/default/aos/test_main.hpp \
test/multi_type_vector/default/aos/test_main.cpp \
test/multi_type_vector/default/tc/construction.inl \
test/multi_type_vector/default/tc/basic.inl \
test/multi_type_vector/default/tc/empty_cells.inl \
test/multi_type_vector/default/tc/erase.inl \
test/multi_type_vector/default/tc/hints.inl \
test/multi_type_vector/default/tc/insert.inl \
test/multi_type_vector/default/tc/iterators.inl \
test/multi_type_vector/default/tc/iterators_insert.inl \
test/multi_type_vector/default/tc/iterators_set.inl \
test/multi_type_vector/default/tc/iterators_set_empty.inl \
test/multi_type_vector/default/tc/misc.inl \
test/multi_type_vector/default/tc/position.inl \
test/multi_type_vector/default/tc/set.inl \
test/multi_type_vector/default/tc/swap_range.inl \
test/multi_type_vector/default/tc/transfer.inl \
test/include/test_global.hpp \
test/test_global.cpp
multi_type_vector_test_default_aos_CPPFLAGS = \
-I$(top_srcdir)/test/multi_type_vector/default/tc \
$(AM_CPPFLAGS)
multi_type_vector_test_default_soa_SOURCES = \
test/multi_type_vector/default/soa/test_main.hpp \
test/multi_type_vector/default/soa/test_main.cpp \
test/multi_type_vector/default/soa/test_construction.cpp \
test/multi_type_vector/default/soa/test_basic.cpp \
test/multi_type_vector/default/soa/test_empty_cells.cpp \
test/multi_type_vector/default/soa/test_erase.cpp \
test/multi_type_vector/default/soa/test_hints.cpp \
test/multi_type_vector/default/soa/test_insert.cpp \
test/multi_type_vector/default/soa/test_iterators.cpp \
test/multi_type_vector/default/soa/test_iterators_insert.cpp \
test/multi_type_vector/default/soa/test_iterators_set.cpp \
test/multi_type_vector/default/soa/test_iterators_set_empty.cpp \
test/multi_type_vector/default/soa/test_misc.cpp \
test/multi_type_vector/default/soa/test_position.cpp \
test/multi_type_vector/default/soa/test_set.cpp \
test/multi_type_vector/default/soa/test_swap_range.cpp \
test/multi_type_vector/default/soa/test_transfer.cpp \
test/include/test_global.hpp \
test/test_global.cpp
multi_type_vector_test_default_soa_CPPFLAGS = \
-I$(top_srcdir)/test/multi_type_vector/default/tc \
$(AM_CPPFLAGS)
multi_type_vector_test_perf_SOURCES = \
test/multi_type_vector/perf/test_main.cpp \
test/include/test_global.hpp \
test/test_global.cpp
multi_type_vector_test_collection_aos_SOURCES = \
test/multi_type_vector/collection/aos/test_main.cpp \
test/multi_type_vector/collection/tc/all.inl \
test/include/test_global.hpp \
test/test_global.cpp
multi_type_vector_test_collection_aos_CPPFLAGS = \
-I$(top_srcdir)/test/multi_type_vector/collection/tc \
$(AM_CPPFLAGS)
multi_type_vector_test_collection_soa_SOURCES = \
test/multi_type_vector/collection/soa/test_main.cpp \
test/multi_type_vector/collection/tc/all.inl \
test/include/test_global.hpp \
test/test_global.cpp
multi_type_vector_test_collection_soa_CPPFLAGS = \
-I$(top_srcdir)/test/multi_type_vector/collection/tc \
$(AM_CPPFLAGS)
point_quad_tree_test_SOURCES = \
test/point_quad_tree_test.cpp \
test/include/test_global.hpp \
test/test_global.cpp
segment_tree_test_SOURCES = \
test/segment_tree_test.cpp \
test/include/test_global.hpp \
test/test_global.cpp
stlperf_test_SOURCES = test/stlperf_test.cpp
sorted_string_map_test_SOURCES = \
test/sorted_string_map_test.cpp \
test/include/test_global.hpp \
test/test_global.cpp
template_test_SOURCES = test/template_test.cpp
trie_map_test_SOURCES = \
test/trie_map_test.cpp \
test/include/test_global.hpp \
test/test_global.cpp
rtree_test_SOURCES = \
test/rtree/test_main.cpp \
test/rtree/test_basic.cpp \
test/rtree/test_copy.cpp \
test/rtree/test_erase_directories.cpp \
test/rtree/test_node_split.cpp \
test/rtree/test_directory_node_split.cpp \
test/rtree/test_intersection.cpp \
test/rtree/test_move.cpp \
test/rtree/test_square_distance.cpp \
test/rtree/test_geometry.cpp \
test/rtree/test_exact_search_by_extent.cpp \
test/rtree/test_exact_search_by_point.cpp \
test/rtree/test_forced_reinsertion.cpp \
test/rtree/test_point_objects.cpp \
test/rtree/test_global_rtree.hpp \
test/include/test_global.hpp \
test/test_global.cpp
rtree_test_bulkload_SOURCES = \
test/rtree/test_bulkload_main.cpp \
test/rtree/test_global_rtree.hpp \
test/include/test_global.hpp \
test/test_global.cpp
ref_pair_test_SOURCES = test/ref_pair_test.cpp test/test_global.cpp
test.fst.perf: flat_segment_tree_test
./flat_segment_tree_test perf
test.st.perf: segment_tree_test
./segment_tree_test perf
test.mtv.perf: multi_type_vector_test_perf
./multi_type_vector_test_perf
test.mtmatrix.perf: multi_type_matrix_test
./multi_type_matrix_test perf
test.stl: stlperf_test
./stlperf_test
TEST_EXTENSIONS = .mem
LOG_COMPILER = $(top_srcdir)/test/test-wrapper.sh
MEM_LOG_COMPILER = $(top_srcdir)/test/mem-wrapper.sh
AM_TESTS_ENVIRONMENT = \
VALGRIND="$(VALGRIND)"; export VALGRIND; \
VALGRINDFLAGS="$(VALGRINDFLAGS)"; export VALGRINDFLAGS; \
EXEEXT="$(EXEEXT)"; export EXEEXT;
TESTS = \
flat_segment_tree_test \
multi_type_matrix_test \
multi_type_matrix_test_walk \
multi_type_vector_test_event_aos \
multi_type_vector_test_event_soa \
multi_type_vector_test_custom_aos \
multi_type_vector_test_custom_soa \
multi_type_vector_test_default_aos \
multi_type_vector_test_default_soa \
multi_type_vector_test_collection_aos \
multi_type_vector_test_collection_soa \
point_quad_tree_test \
segment_tree_test \
sorted_string_map_test \
trie_map_test \
rtree_test \
rtree_test_bulkload \
ref_pair_test
# NOTE: AFAIK automake does not support running the same test executable
# in different ways. I workaround this limitation by creating dummy
# test files and redirecting to the real executable inside the wrapper.
if RUN_MEMORY_TESTS
TESTS += \
flat_segment_tree_test_mem.mem \
multi_type_matrix_test_mem.mem \
multi_type_matrix_test_walk_mem.mem \
multi_type_vector_test_event_aos_mem.mem \
multi_type_vector_test_event_soa_mem.mem \
multi_type_vector_test_custom_aos_mem.mem \
multi_type_vector_test_custom_soa_mem.mem \
multi_type_vector_test_default_aos_mem.mem \
multi_type_vector_test_default_soa_mem.mem \
multi_type_vector_test_collection_aos_mem.mem \
multi_type_vector_test_collection_soa_mem.mem \
point_quad_tree_test_mem.mem \
segment_tree_test_mem.mem \
sorted_string_map_test_mem.mem \
trie_map_test_mem.mem \
rtree_test_mem.mem \
rtree_test_bulkload_mem.mem
endif
install-data-local:
$(MKDIR_P) $(DESTDIR)$(docdir)
$(INSTALL_DATA) $(top_srcdir)/LICENSE $(DESTDIR)$(docdir)/COPYING
$(INSTALL_DATA) $(top_srcdir)/CHANGELOG $(DESTDIR)$(docdir)/NEWS
uninstall-local:
rm -f $(DESTDIR)$(docdir)/COPYING $(DESTDIR)$(docdir)/NEWS
if BUILD_DOCS
doc-doxygen:
@echo "Building documentation by doxygen..."
@cd doc && $(DOXYGEN) doxygen.conf
doc-sphinx:
@echo "Building documentation by sphinx..."
@$(SPHINX) -b html ./doc/ ./doc/_build
doc: doc-doxygen doc-sphinx
endif
check-gdb:
make -C test/gdb check

3638
Makefile.in Normal file

File diff suppressed because it is too large Load Diff

126
README.md Normal file
View File

@ -0,0 +1,126 @@
Multi-Dimensional Data Structure (mdds)
=======================================
A collection of multi-dimensional data structure and indexing
algorithm.
Overview of data structures included in mdds
--------------------------------------------
This library implements the following data structures:
* segment_tree
* flat_segment_tree
* point_quad_tree
* multi_type_vector
* multi_type_matrix
* sorted_string_map
* trie_map
* packed_trie_map
* rtree
### Segment Tree
Segment tree is a balanced-binary-tree based data structure efficient
for detecting all intervals (or segments) that contain a given point.
The segments may overlap with each other. The end points of stored
segments are not inclusive, that is, when an interval spans from 2 to
6, an arbitrary point x within that interval can take a value of 2 <=
x < 6.
### Flat Segment Tree
Flat segment tree is a variant of segment tree that is designed to
store a collection of non-overlapping segments. This structure is
efficient when you need to store values associated with 1 dimensional
segments that never overlap with each other. Like segment tree,
stored segments' end points are non-inclusive.
### Point Quad Tree
Point quad tree stores 2-dimensional points and provides an efficient
way to query all points within specified rectangular region.
### Multi Type Vector
Multi-type vector allows storage of unspecified number of types in a single
logical array such that contiguous elements of identical type are stored in
contiguous segment in memory space.
### Multi Type Matrix
Multi-type matrix is a matrix structure that allows storage of four different
element types: numeric, string, boolean and empty. It uses multi-type vector as
its underlying storage.
### Sorted String Map
Sorted string map is a simple data structure that takes a pre-sorted list of
key-value pairs that are known at compile time, and allows efficient lookup.
It does not allocate memory to duplicate its content, as it directly uses the
pre-sorted list provided by the caller.
### Trie Map
Trie map is an associative container that stores multiple key-value pairs
where keys are stored in a trie structure to optimize for prefix searches.
### Packed Trie Map
Packed trie map is nearly identical to the trie map counterpart except that
this one is immutable. It packs all its content in a contiguous array for
optimum storage and lookup efficiency. This implementation is based on the
paper titled [Tightly Packed Tries: How to Fit Large Models into Memory, and Make them Load Fast, Too](https://www.aclweb.org/anthology/W09-1505/)
by Ulrich Germann, Eric Joanis, and Samuel Larkin.
### R-tree
[R-tree](https://en.wikipedia.org/wiki/R-tree) is a tree-based data structure
designed to store multi-dimensional geometric data with bounding boxes and
provide optimal performance on region- or point-based queries. The one
implemented in this library is a variant of R-tree known as
[R*-tree](https://en.wikipedia.org/wiki/R*_tree).
Documentation
=============
[Official API documentation](https://mdds.readthedocs.io/en/latest/) for general
users of the library.
Packages
========
Please see the [Releases](https://gitlab.com/mdds/mdds/-/releases) page for
source package downloads.
If you need old packages, please find them [here](OLD-DOWNLOADS.md).
Installation
============
Please refer to the [CONTRIBUTING.md](CONTRIBUTING.md) file for build and
installation instructions.
License
=======
mdds is free software. You may copy, distribute, and modify it under
the terms of the License contained in the file COPYING distributed
with this package. This license is the same as the MIT/X Consortium
license.
Who uses mdds?
==============
These are the projects that are known to use mdds.
* [LibreOffice](http://www.libreoffice.org)
* [Calligra](https://www.calligra.org/)
* [libetonyek](https://wiki.documentfoundation.org/DLP/Libraries/libetonyek)
* [ixion](https://gitlab.com/ixion/ixion)
* [orcus](https://gitlab.com/orcus/orcus)
If you use mdds and would like your project to be included in the above list,
please let us know.

1188
aclocal.m4 vendored Normal file

File diff suppressed because it is too large Load Diff

45
autogen.sh Executable file
View File

@ -0,0 +1,45 @@
#!/bin/sh
srcdir=`dirname $0`
test -z "$srcdir" && srcdir=.
olddir=`pwd`
cd $srcdir
aclocal --version > /dev/null 2> /dev/null || {
echo "error: aclocal not found"
exit 1
}
automake --version > /dev/null 2> /dev/null || {
echo "error: automake not found"
exit 1
}
amcheck=`automake --version | grep 'automake (GNU automake) 1.5'`
if test "x$amcheck" = "xautomake (GNU automake) 1.5"; then
echo "warning: you appear to be using automake 1.5"
echo " this version has a bug - GNUmakefile.am dependencies are not generated"
fi
rm -rf autom4te*.cache
libtoolize --force --copy || {
echo "error: libtoolize failed"
exit 1
}
aclocal $ACLOCAL_FLAGS || {
echo "error: aclocal $ACLOCAL_FLAGS failed"
exit 1
}
automake -a -c --foreign || {
echo "warning: automake failed"
}
autoconf || {
echo "error: autoconf failed"
exit 1
}
if test x$NOCONFIGURE = x; then
./configure $@
fi

348
compile Executable file
View File

@ -0,0 +1,348 @@
#! /bin/sh
# Wrapper for compilers which do not understand '-c -o'.
scriptversion=2018-03-07.03; # UTC
# Copyright (C) 1999-2021 Free Software Foundation, Inc.
# Written by Tom Tromey <tromey@cygnus.com>.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
# This file is maintained in Automake, please report
# bugs to <bug-automake@gnu.org> or send patches to
# <automake-patches@gnu.org>.
nl='
'
# We need space, tab and new line, in precisely that order. Quoting is
# there to prevent tools from complaining about whitespace usage.
IFS=" "" $nl"
file_conv=
# func_file_conv build_file lazy
# Convert a $build file to $host form and store it in $file
# Currently only supports Windows hosts. If the determined conversion
# type is listed in (the comma separated) LAZY, no conversion will
# take place.
func_file_conv ()
{
file=$1
case $file in
/ | /[!/]*) # absolute file, and not a UNC file
if test -z "$file_conv"; then
# lazily determine how to convert abs files
case `uname -s` in
MINGW*)
file_conv=mingw
;;
CYGWIN* | MSYS*)
file_conv=cygwin
;;
*)
file_conv=wine
;;
esac
fi
case $file_conv/,$2, in
*,$file_conv,*)
;;
mingw/*)
file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'`
;;
cygwin/* | msys/*)
file=`cygpath -m "$file" || echo "$file"`
;;
wine/*)
file=`winepath -w "$file" || echo "$file"`
;;
esac
;;
esac
}
# func_cl_dashL linkdir
# Make cl look for libraries in LINKDIR
func_cl_dashL ()
{
func_file_conv "$1"
if test -z "$lib_path"; then
lib_path=$file
else
lib_path="$lib_path;$file"
fi
linker_opts="$linker_opts -LIBPATH:$file"
}
# func_cl_dashl library
# Do a library search-path lookup for cl
func_cl_dashl ()
{
lib=$1
found=no
save_IFS=$IFS
IFS=';'
for dir in $lib_path $LIB
do
IFS=$save_IFS
if $shared && test -f "$dir/$lib.dll.lib"; then
found=yes
lib=$dir/$lib.dll.lib
break
fi
if test -f "$dir/$lib.lib"; then
found=yes
lib=$dir/$lib.lib
break
fi
if test -f "$dir/lib$lib.a"; then
found=yes
lib=$dir/lib$lib.a
break
fi
done
IFS=$save_IFS
if test "$found" != yes; then
lib=$lib.lib
fi
}
# func_cl_wrapper cl arg...
# Adjust compile command to suit cl
func_cl_wrapper ()
{
# Assume a capable shell
lib_path=
shared=:
linker_opts=
for arg
do
if test -n "$eat"; then
eat=
else
case $1 in
-o)
# configure might choose to run compile as 'compile cc -o foo foo.c'.
eat=1
case $2 in
*.o | *.[oO][bB][jJ])
func_file_conv "$2"
set x "$@" -Fo"$file"
shift
;;
*)
func_file_conv "$2"
set x "$@" -Fe"$file"
shift
;;
esac
;;
-I)
eat=1
func_file_conv "$2" mingw
set x "$@" -I"$file"
shift
;;
-I*)
func_file_conv "${1#-I}" mingw
set x "$@" -I"$file"
shift
;;
-l)
eat=1
func_cl_dashl "$2"
set x "$@" "$lib"
shift
;;
-l*)
func_cl_dashl "${1#-l}"
set x "$@" "$lib"
shift
;;
-L)
eat=1
func_cl_dashL "$2"
;;
-L*)
func_cl_dashL "${1#-L}"
;;
-static)
shared=false
;;
-Wl,*)
arg=${1#-Wl,}
save_ifs="$IFS"; IFS=','
for flag in $arg; do
IFS="$save_ifs"
linker_opts="$linker_opts $flag"
done
IFS="$save_ifs"
;;
-Xlinker)
eat=1
linker_opts="$linker_opts $2"
;;
-*)
set x "$@" "$1"
shift
;;
*.cc | *.CC | *.cxx | *.CXX | *.[cC]++)
func_file_conv "$1"
set x "$@" -Tp"$file"
shift
;;
*.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO])
func_file_conv "$1" mingw
set x "$@" "$file"
shift
;;
*)
set x "$@" "$1"
shift
;;
esac
fi
shift
done
if test -n "$linker_opts"; then
linker_opts="-link$linker_opts"
fi
exec "$@" $linker_opts
exit 1
}
eat=
case $1 in
'')
echo "$0: No command. Try '$0 --help' for more information." 1>&2
exit 1;
;;
-h | --h*)
cat <<\EOF
Usage: compile [--help] [--version] PROGRAM [ARGS]
Wrapper for compilers which do not understand '-c -o'.
Remove '-o dest.o' from ARGS, run PROGRAM with the remaining
arguments, and rename the output as expected.
If you are trying to build a whole package this is not the
right script to run: please start by reading the file 'INSTALL'.
Report bugs to <bug-automake@gnu.org>.
EOF
exit $?
;;
-v | --v*)
echo "compile $scriptversion"
exit $?
;;
cl | *[/\\]cl | cl.exe | *[/\\]cl.exe | \
icl | *[/\\]icl | icl.exe | *[/\\]icl.exe )
func_cl_wrapper "$@" # Doesn't return...
;;
esac
ofile=
cfile=
for arg
do
if test -n "$eat"; then
eat=
else
case $1 in
-o)
# configure might choose to run compile as 'compile cc -o foo foo.c'.
# So we strip '-o arg' only if arg is an object.
eat=1
case $2 in
*.o | *.obj)
ofile=$2
;;
*)
set x "$@" -o "$2"
shift
;;
esac
;;
*.c)
cfile=$1
set x "$@" "$1"
shift
;;
*)
set x "$@" "$1"
shift
;;
esac
fi
shift
done
if test -z "$ofile" || test -z "$cfile"; then
# If no '-o' option was seen then we might have been invoked from a
# pattern rule where we don't need one. That is ok -- this is a
# normal compilation that the losing compiler can handle. If no
# '.c' file was seen then we are probably linking. That is also
# ok.
exec "$@"
fi
# Name of file we expect compiler to create.
cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'`
# Create the lock directory.
# Note: use '[/\\:.-]' here to ensure that we don't use the same name
# that we are using for the .o file. Also, base the name on the expected
# object file name, since that is what matters with a parallel build.
lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d
while true; do
if mkdir "$lockdir" >/dev/null 2>&1; then
break
fi
sleep 1
done
# FIXME: race condition here if user kills between mkdir and trap.
trap "rmdir '$lockdir'; exit 1" 1 2 15
# Run the compile.
"$@"
ret=$?
if test -f "$cofile"; then
test "$cofile" = "$ofile" || mv "$cofile" "$ofile"
elif test -f "${cofile}bj"; then
test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile"
fi
rmdir "$lockdir"
exit $ret
# Local Variables:
# mode: shell-script
# sh-indentation: 2
# eval: (add-hook 'before-save-hook 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC0"
# time-stamp-end: "; # UTC"
# End:

8546
configure vendored Executable file

File diff suppressed because it is too large Load Diff

186
configure.ac Normal file
View File

@ -0,0 +1,186 @@
AC_INIT(mdds, 2.0.3, kohei.yoshida@gmail.com)
AM_INIT_AUTOMAKE([foreign dist-bzip2 dist-xz subdir-objects])
AM_MAINTAINER_MODE
VERSION=AC_PACKAGE_VERSION
API_VERSION=2.0
AC_SUBST(VERSION)
AC_SUBST(API_VERSION)
AC_CONFIG_MACRO_DIRS([m4])
PACKAGE_TARNAME=AC_PACKAGE_TARNAME
AC_SUBST(PACKAGE_TARNAME)
OBJDIR=obj
INCDIR=include
MISCDIR=misc
QUICKCHECKDIR=quickcheck
AC_SUBST(OBJDIR)
AC_SUBST(INCDIR)
AC_SUBST(MISCDIR)
AC_SUBST(QUICKCHECKDIR)
AX_CXX_COMPILE_STDCXX_17([noext], [mandatory])
CPPFLAGS="$CPPFLAGS -Wall -Wshadow -g -pedantic-errors"
CPPFLAGS="$CPPFLAGS -I/usr/include -I/usr/local/include"
AC_CHECK_SIZEOF([void *])
CXXFLAGS="$CXXFLAGS -DSIZEOF_VOID_P=$ac_cv_sizeof_void_p"
AC_ARG_ENABLE(debug-stdcxx,
[
--enable-debug-stdcxx Enable libstdc++ debug mode during make check.
]
,debug_stdcxx=yes)
AS_IF([test x"$debug_stdcxx" == "xyes"], [
CXXFLAGS="$CXXFLAGS -D_GLIBCXX_DEBUG"
])
AC_ARG_ENABLE(release-tests,
[
--enable-release-tests Enable release builds for unit tests.
]
,release_tests=yes)
AS_IF([test x"$release_tests" == "xyes"], [
CXXFLAGS="$CXXFLAGS -DNDEBUG -O2"
])
AC_ARG_ENABLE(gcov,
AS_HELP_STRING([--enable-gcov], [Enable generation of gcov information.]),
[enable_gcov="$enableval"],[enable_gcov=no]
)
AC_ARG_ENABLE(loop-unrolling,
AS_HELP_STRING([--disable-loop-unrolling], [Disable use of loop unrolling.]),
[enable_loop_unrolling="$enableval"],[enable_loop_unrolling=yes]
)
AS_IF([test x"$enable_loop_unrolling" == "xno"], [
CXXFLAGS="$CXXFLAGS -DMDDS_LOOP_UNROLLING=0"
])
AS_IF([test x"$enable_gcov" == "xyes"], [
CXXFLAGS="$CXXFLAGS --coverage -O0"
])
AC_ARG_ENABLE(openmp,
AS_HELP_STRING([--enable-openmp], [Enable use of OpenMP.]),
[enable_openmp="$enableval"],[enable_openmp=no]
)
AS_IF([test x"$enable_openmp" == "xyes"], [
CXXFLAGS="$CXXFLAGS -fopenmp -DMDDS_USE_OPENMP=1"
LDFLAGS="$LDFLAGS -fopenmp"
])
AC_ARG_ENABLE(sanitizer-coverage,
AS_HELP_STRING([--enable-sanitizer-coverage], [Enable generation of sanitizer coverage information.]),
[enable_sanitizer_coverage="$enableval"],[enable_sanitizer_coverage=no]
)
AS_IF([test x"$enable_sanitizer_coverage" == "xyes"], [
CXXFLAGS="$CXXFLAGS -fprofile-instr-generate -fcoverage-mapping -O0"
])
AC_SUBST(CPPFLAGS)
AC_SUBST(CPPFLAGS_NODEBUG)
AC_PROG_CXX
AC_PATH_PROG([GDB], [gdb])
AC_PATH_PROG([EXPECT], [expect])
AC_PATH_PROG([RUNTEST_BIN], [runtest])
AC_ARG_ENABLE([memory-tests],
[AS_HELP_STRING([--enable-memory-tests], [Run memory tests.])],
[enable_memory_tests=$enableval],
[enable_memory_tests=no]
)
AS_IF([test "$enable_memory_tests" = yes],
[AC_PATH_PROG([VALGRIND], [valgrind])]
AS_IF([test -z "$VALGRIND"], [AC_MSG_ERROR([valgrind not found])])
)
AM_CONDITIONAL([RUN_MEMORY_TESTS], [test "$enable_memory_tests" = yes])
AC_ARG_ENABLE([docs],
[AS_HELP_STRING([--enable-docs], [Generate docs during build.])],
[enable_docs=$enableval],
[enable_docs=no]
)
AS_IF([test "$enable_docs" = yes],
[
AC_PATH_PROG([DOXYGEN], [doxygen])
AS_IF([test -z "$DOXYGEN"], [AC_MSG_ERROR([doxygen not found])])
AC_PATH_PROG([SPHINX], [sphinx-build])
AS_IF([test -z "$SPHINX"], [AC_MSG_ERROR([sphinx-build not found])])
]
)
AM_CONDITIONAL([BUILD_DOCS], [test "$enable_docs" = yes])
AC_ARG_ENABLE([werror],
[AS_HELP_STRING([--enable-werror], [Treat all warnings as errors, useful for development])],
[enable_werror="$enableval"],
[enable_werror=no]
)
AS_IF([test x"$enable_werror" == "xyes"], [
CXXFLAGS="$CXXFLAGS -Werror"
])
CXXFLAGS_UNITTESTS="-DMDDS_DEBUG_NODE_BASE -DMDDS_UNIT_TEST"
AC_SUBST(CXXFLAGS_UNITTESTS)
AC_CONFIG_FILES([
Makefile
include/Makefile
include/mdds/Makefile
include/mdds/multi_type_vector/Makefile
include/mdds/multi_type_vector/aos/Makefile
include/mdds/multi_type_vector/soa/Makefile
example/Makefile
test/Makefile
test/gdb/Makefile
test/gdb/src/Makefile
test/multi_type_vector/Makefile
test/multi_type_vector/custom-trait/Makefile
test/multi_type_vector/custom-trait/aos/Makefile
test/multi_type_vector/custom-trait/soa/Makefile
test/multi_type_vector/debug-util/Makefile
test/multi_type_vector/debug-util/soa/Makefile
tools/Makefile
tools/runtime-env/Makefile
flat_segment_tree_test_mem.mem:test/test.mem.in
misc/mdds-$API_VERSION.pc:misc/mdds.pc.in
multi_type_matrix_test_mem.mem:test/test.mem.in
multi_type_matrix_test_walk_mem.mem:test/test.mem.in
multi_type_vector_test_event_aos_mem.mem:test/test.mem.in
multi_type_vector_test_event_soa_mem.mem:test/test.mem.in
multi_type_vector_test_custom_aos_mem.mem:test/test.mem.in
multi_type_vector_test_custom_soa_mem.mem:test/test.mem.in
multi_type_vector_test_default_aos_mem.mem:test/test.mem.in
multi_type_vector_test_default_soa_mem.mem:test/test.mem.in
multi_type_vector_test_collection_aos_mem.mem:test/test.mem.in
multi_type_vector_test_collection_soa_mem.mem:test/test.mem.in
point_quad_tree_test_mem.mem:test/test.mem.in
rectangle_set_test_mem.mem:test/test.mem.in
segment_tree_test_mem.mem:test/test.mem.in
sorted_string_map_test_mem.mem:test/test.mem.in
trie_map_test_mem.mem:test/test.mem.in
rtree_test_mem.mem:test/test.mem.in
rtree_test_bulkload_mem.mem:test/test.mem.in
])
AC_OUTPUT
AC_MSG_NOTICE([
==============================================================================
Build configuration:
gcov $enable_gcov
openmp $enable_openmp
sanitizer-coverage $enable_sanitizer_coverage
loop-unrolling $enable_loop_unrolling
CXXFLAGS $CXXFLAGS
==============================================================================
])

791
depcomp Executable file
View File

@ -0,0 +1,791 @@
#! /bin/sh
# depcomp - compile a program generating dependencies as side-effects
scriptversion=2018-03-07.03; # UTC
# Copyright (C) 1999-2021 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
# Originally written by Alexandre Oliva <oliva@dcc.unicamp.br>.
case $1 in
'')
echo "$0: No command. Try '$0 --help' for more information." 1>&2
exit 1;
;;
-h | --h*)
cat <<\EOF
Usage: depcomp [--help] [--version] PROGRAM [ARGS]
Run PROGRAMS ARGS to compile a file, generating dependencies
as side-effects.
Environment variables:
depmode Dependency tracking mode.
source Source file read by 'PROGRAMS ARGS'.
object Object file output by 'PROGRAMS ARGS'.
DEPDIR directory where to store dependencies.
depfile Dependency file to output.
tmpdepfile Temporary file to use when outputting dependencies.
libtool Whether libtool is used (yes/no).
Report bugs to <bug-automake@gnu.org>.
EOF
exit $?
;;
-v | --v*)
echo "depcomp $scriptversion"
exit $?
;;
esac
# Get the directory component of the given path, and save it in the
# global variables '$dir'. Note that this directory component will
# be either empty or ending with a '/' character. This is deliberate.
set_dir_from ()
{
case $1 in
*/*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;;
*) dir=;;
esac
}
# Get the suffix-stripped basename of the given path, and save it the
# global variable '$base'.
set_base_from ()
{
base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'`
}
# If no dependency file was actually created by the compiler invocation,
# we still have to create a dummy depfile, to avoid errors with the
# Makefile "include basename.Plo" scheme.
make_dummy_depfile ()
{
echo "#dummy" > "$depfile"
}
# Factor out some common post-processing of the generated depfile.
# Requires the auxiliary global variable '$tmpdepfile' to be set.
aix_post_process_depfile ()
{
# If the compiler actually managed to produce a dependency file,
# post-process it.
if test -f "$tmpdepfile"; then
# Each line is of the form 'foo.o: dependency.h'.
# Do two passes, one to just change these to
# $object: dependency.h
# and one to simply output
# dependency.h:
# which is needed to avoid the deleted-header problem.
{ sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile"
sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile"
} > "$depfile"
rm -f "$tmpdepfile"
else
make_dummy_depfile
fi
}
# A tabulation character.
tab=' '
# A newline character.
nl='
'
# Character ranges might be problematic outside the C locale.
# These definitions help.
upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ
lower=abcdefghijklmnopqrstuvwxyz
digits=0123456789
alpha=${upper}${lower}
if test -z "$depmode" || test -z "$source" || test -z "$object"; then
echo "depcomp: Variables source, object and depmode must be set" 1>&2
exit 1
fi
# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po.
depfile=${depfile-`echo "$object" |
sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`}
tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
rm -f "$tmpdepfile"
# Avoid interferences from the environment.
gccflag= dashmflag=
# Some modes work just like other modes, but use different flags. We
# parameterize here, but still list the modes in the big case below,
# to make depend.m4 easier to write. Note that we *cannot* use a case
# here, because this file can only contain one case statement.
if test "$depmode" = hp; then
# HP compiler uses -M and no extra arg.
gccflag=-M
depmode=gcc
fi
if test "$depmode" = dashXmstdout; then
# This is just like dashmstdout with a different argument.
dashmflag=-xM
depmode=dashmstdout
fi
cygpath_u="cygpath -u -f -"
if test "$depmode" = msvcmsys; then
# This is just like msvisualcpp but w/o cygpath translation.
# Just convert the backslash-escaped backslashes to single forward
# slashes to satisfy depend.m4
cygpath_u='sed s,\\\\,/,g'
depmode=msvisualcpp
fi
if test "$depmode" = msvc7msys; then
# This is just like msvc7 but w/o cygpath translation.
# Just convert the backslash-escaped backslashes to single forward
# slashes to satisfy depend.m4
cygpath_u='sed s,\\\\,/,g'
depmode=msvc7
fi
if test "$depmode" = xlc; then
# IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information.
gccflag=-qmakedep=gcc,-MF
depmode=gcc
fi
case "$depmode" in
gcc3)
## gcc 3 implements dependency tracking that does exactly what
## we want. Yay! Note: for some reason libtool 1.4 doesn't like
## it if -MD -MP comes after the -MF stuff. Hmm.
## Unfortunately, FreeBSD c89 acceptance of flags depends upon
## the command line argument order; so add the flags where they
## appear in depend2.am. Note that the slowdown incurred here
## affects only configure: in makefiles, %FASTDEP% shortcuts this.
for arg
do
case $arg in
-c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;;
*) set fnord "$@" "$arg" ;;
esac
shift # fnord
shift # $arg
done
"$@"
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile"
exit $stat
fi
mv "$tmpdepfile" "$depfile"
;;
gcc)
## Note that this doesn't just cater to obsosete pre-3.x GCC compilers.
## but also to in-use compilers like IMB xlc/xlC and the HP C compiler.
## (see the conditional assignment to $gccflag above).
## There are various ways to get dependency output from gcc. Here's
## why we pick this rather obscure method:
## - Don't want to use -MD because we'd like the dependencies to end
## up in a subdir. Having to rename by hand is ugly.
## (We might end up doing this anyway to support other compilers.)
## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
## -MM, not -M (despite what the docs say). Also, it might not be
## supported by the other compilers which use the 'gcc' depmode.
## - Using -M directly means running the compiler twice (even worse
## than renaming).
if test -z "$gccflag"; then
gccflag=-MD,
fi
"$@" -Wp,"$gccflag$tmpdepfile"
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile"
exit $stat
fi
rm -f "$depfile"
echo "$object : \\" > "$depfile"
# The second -e expression handles DOS-style file names with drive
# letters.
sed -e 's/^[^:]*: / /' \
-e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
## This next piece of magic avoids the "deleted header file" problem.
## The problem is that when a header file which appears in a .P file
## is deleted, the dependency causes make to die (because there is
## typically no way to rebuild the header). We avoid this by adding
## dummy dependencies for each header file. Too bad gcc doesn't do
## this for us directly.
## Some versions of gcc put a space before the ':'. On the theory
## that the space means something, we add a space to the output as
## well. hp depmode also adds that space, but also prefixes the VPATH
## to the object. Take care to not repeat it in the output.
## Some versions of the HPUX 10.20 sed can't process this invocation
## correctly. Breaking it into two sed invocations is a workaround.
tr ' ' "$nl" < "$tmpdepfile" \
| sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \
| sed -e 's/$/ :/' >> "$depfile"
rm -f "$tmpdepfile"
;;
hp)
# This case exists only to let depend.m4 do its work. It works by
# looking at the text of this script. This case will never be run,
# since it is checked for above.
exit 1
;;
sgi)
if test "$libtool" = yes; then
"$@" "-Wp,-MDupdate,$tmpdepfile"
else
"$@" -MDupdate "$tmpdepfile"
fi
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile"
exit $stat
fi
rm -f "$depfile"
if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files
echo "$object : \\" > "$depfile"
# Clip off the initial element (the dependent). Don't try to be
# clever and replace this with sed code, as IRIX sed won't handle
# lines with more than a fixed number of characters (4096 in
# IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines;
# the IRIX cc adds comments like '#:fec' to the end of the
# dependency line.
tr ' ' "$nl" < "$tmpdepfile" \
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \
| tr "$nl" ' ' >> "$depfile"
echo >> "$depfile"
# The second pass generates a dummy entry for each header file.
tr ' ' "$nl" < "$tmpdepfile" \
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
>> "$depfile"
else
make_dummy_depfile
fi
rm -f "$tmpdepfile"
;;
xlc)
# This case exists only to let depend.m4 do its work. It works by
# looking at the text of this script. This case will never be run,
# since it is checked for above.
exit 1
;;
aix)
# The C for AIX Compiler uses -M and outputs the dependencies
# in a .u file. In older versions, this file always lives in the
# current directory. Also, the AIX compiler puts '$object:' at the
# start of each line; $object doesn't have directory information.
# Version 6 uses the directory in both cases.
set_dir_from "$object"
set_base_from "$object"
if test "$libtool" = yes; then
tmpdepfile1=$dir$base.u
tmpdepfile2=$base.u
tmpdepfile3=$dir.libs/$base.u
"$@" -Wc,-M
else
tmpdepfile1=$dir$base.u
tmpdepfile2=$dir$base.u
tmpdepfile3=$dir$base.u
"$@" -M
fi
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
exit $stat
fi
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
do
test -f "$tmpdepfile" && break
done
aix_post_process_depfile
;;
tcc)
# tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26
# FIXME: That version still under development at the moment of writing.
# Make that this statement remains true also for stable, released
# versions.
# It will wrap lines (doesn't matter whether long or short) with a
# trailing '\', as in:
#
# foo.o : \
# foo.c \
# foo.h \
#
# It will put a trailing '\' even on the last line, and will use leading
# spaces rather than leading tabs (at least since its commit 0394caf7
# "Emit spaces for -MD").
"$@" -MD -MF "$tmpdepfile"
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile"
exit $stat
fi
rm -f "$depfile"
# Each non-empty line is of the form 'foo.o : \' or ' dep.h \'.
# We have to change lines of the first kind to '$object: \'.
sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile"
# And for each line of the second kind, we have to emit a 'dep.h:'
# dummy dependency, to avoid the deleted-header problem.
sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile"
rm -f "$tmpdepfile"
;;
## The order of this option in the case statement is important, since the
## shell code in configure will try each of these formats in the order
## listed in this file. A plain '-MD' option would be understood by many
## compilers, so we must ensure this comes after the gcc and icc options.
pgcc)
# Portland's C compiler understands '-MD'.
# Will always output deps to 'file.d' where file is the root name of the
# source file under compilation, even if file resides in a subdirectory.
# The object file name does not affect the name of the '.d' file.
# pgcc 10.2 will output
# foo.o: sub/foo.c sub/foo.h
# and will wrap long lines using '\' :
# foo.o: sub/foo.c ... \
# sub/foo.h ... \
# ...
set_dir_from "$object"
# Use the source, not the object, to determine the base name, since
# that's sadly what pgcc will do too.
set_base_from "$source"
tmpdepfile=$base.d
# For projects that build the same source file twice into different object
# files, the pgcc approach of using the *source* file root name can cause
# problems in parallel builds. Use a locking strategy to avoid stomping on
# the same $tmpdepfile.
lockdir=$base.d-lock
trap "
echo '$0: caught signal, cleaning up...' >&2
rmdir '$lockdir'
exit 1
" 1 2 13 15
numtries=100
i=$numtries
while test $i -gt 0; do
# mkdir is a portable test-and-set.
if mkdir "$lockdir" 2>/dev/null; then
# This process acquired the lock.
"$@" -MD
stat=$?
# Release the lock.
rmdir "$lockdir"
break
else
# If the lock is being held by a different process, wait
# until the winning process is done or we timeout.
while test -d "$lockdir" && test $i -gt 0; do
sleep 1
i=`expr $i - 1`
done
fi
i=`expr $i - 1`
done
trap - 1 2 13 15
if test $i -le 0; then
echo "$0: failed to acquire lock after $numtries attempts" >&2
echo "$0: check lockdir '$lockdir'" >&2
exit 1
fi
if test $stat -ne 0; then
rm -f "$tmpdepfile"
exit $stat
fi
rm -f "$depfile"
# Each line is of the form `foo.o: dependent.h',
# or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'.
# Do two passes, one to just change these to
# `$object: dependent.h' and one to simply `dependent.h:'.
sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile"
# Some versions of the HPUX 10.20 sed can't process this invocation
# correctly. Breaking it into two sed invocations is a workaround.
sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \
| sed -e 's/$/ :/' >> "$depfile"
rm -f "$tmpdepfile"
;;
hp2)
# The "hp" stanza above does not work with aCC (C++) and HP's ia64
# compilers, which have integrated preprocessors. The correct option
# to use with these is +Maked; it writes dependencies to a file named
# 'foo.d', which lands next to the object file, wherever that
# happens to be.
# Much of this is similar to the tru64 case; see comments there.
set_dir_from "$object"
set_base_from "$object"
if test "$libtool" = yes; then
tmpdepfile1=$dir$base.d
tmpdepfile2=$dir.libs/$base.d
"$@" -Wc,+Maked
else
tmpdepfile1=$dir$base.d
tmpdepfile2=$dir$base.d
"$@" +Maked
fi
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile1" "$tmpdepfile2"
exit $stat
fi
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2"
do
test -f "$tmpdepfile" && break
done
if test -f "$tmpdepfile"; then
sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile"
# Add 'dependent.h:' lines.
sed -ne '2,${
s/^ *//
s/ \\*$//
s/$/:/
p
}' "$tmpdepfile" >> "$depfile"
else
make_dummy_depfile
fi
rm -f "$tmpdepfile" "$tmpdepfile2"
;;
tru64)
# The Tru64 compiler uses -MD to generate dependencies as a side
# effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'.
# At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
# dependencies in 'foo.d' instead, so we check for that too.
# Subdirectories are respected.
set_dir_from "$object"
set_base_from "$object"
if test "$libtool" = yes; then
# Libtool generates 2 separate objects for the 2 libraries. These
# two compilations output dependencies in $dir.libs/$base.o.d and
# in $dir$base.o.d. We have to check for both files, because
# one of the two compilations can be disabled. We should prefer
# $dir$base.o.d over $dir.libs/$base.o.d because the latter is
# automatically cleaned when .libs/ is deleted, while ignoring
# the former would cause a distcleancheck panic.
tmpdepfile1=$dir$base.o.d # libtool 1.5
tmpdepfile2=$dir.libs/$base.o.d # Likewise.
tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504
"$@" -Wc,-MD
else
tmpdepfile1=$dir$base.d
tmpdepfile2=$dir$base.d
tmpdepfile3=$dir$base.d
"$@" -MD
fi
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
exit $stat
fi
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
do
test -f "$tmpdepfile" && break
done
# Same post-processing that is required for AIX mode.
aix_post_process_depfile
;;
msvc7)
if test "$libtool" = yes; then
showIncludes=-Wc,-showIncludes
else
showIncludes=-showIncludes
fi
"$@" $showIncludes > "$tmpdepfile"
stat=$?
grep -v '^Note: including file: ' "$tmpdepfile"
if test $stat -ne 0; then
rm -f "$tmpdepfile"
exit $stat
fi
rm -f "$depfile"
echo "$object : \\" > "$depfile"
# The first sed program below extracts the file names and escapes
# backslashes for cygpath. The second sed program outputs the file
# name when reading, but also accumulates all include files in the
# hold buffer in order to output them again at the end. This only
# works with sed implementations that can handle large buffers.
sed < "$tmpdepfile" -n '
/^Note: including file: *\(.*\)/ {
s//\1/
s/\\/\\\\/g
p
}' | $cygpath_u | sort -u | sed -n '
s/ /\\ /g
s/\(.*\)/'"$tab"'\1 \\/p
s/.\(.*\) \\/\1:/
H
$ {
s/.*/'"$tab"'/
G
p
}' >> "$depfile"
echo >> "$depfile" # make sure the fragment doesn't end with a backslash
rm -f "$tmpdepfile"
;;
msvc7msys)
# This case exists only to let depend.m4 do its work. It works by
# looking at the text of this script. This case will never be run,
# since it is checked for above.
exit 1
;;
#nosideeffect)
# This comment above is used by automake to tell side-effect
# dependency tracking mechanisms from slower ones.
dashmstdout)
# Important note: in order to support this mode, a compiler *must*
# always write the preprocessed file to stdout, regardless of -o.
"$@" || exit $?
# Remove the call to Libtool.
if test "$libtool" = yes; then
while test "X$1" != 'X--mode=compile'; do
shift
done
shift
fi
# Remove '-o $object'.
IFS=" "
for arg
do
case $arg in
-o)
shift
;;
$object)
shift
;;
*)
set fnord "$@" "$arg"
shift # fnord
shift # $arg
;;
esac
done
test -z "$dashmflag" && dashmflag=-M
# Require at least two characters before searching for ':'
# in the target name. This is to cope with DOS-style filenames:
# a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise.
"$@" $dashmflag |
sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile"
rm -f "$depfile"
cat < "$tmpdepfile" > "$depfile"
# Some versions of the HPUX 10.20 sed can't process this sed invocation
# correctly. Breaking it into two sed invocations is a workaround.
tr ' ' "$nl" < "$tmpdepfile" \
| sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
| sed -e 's/$/ :/' >> "$depfile"
rm -f "$tmpdepfile"
;;
dashXmstdout)
# This case only exists to satisfy depend.m4. It is never actually
# run, as this mode is specially recognized in the preamble.
exit 1
;;
makedepend)
"$@" || exit $?
# Remove any Libtool call
if test "$libtool" = yes; then
while test "X$1" != 'X--mode=compile'; do
shift
done
shift
fi
# X makedepend
shift
cleared=no eat=no
for arg
do
case $cleared in
no)
set ""; shift
cleared=yes ;;
esac
if test $eat = yes; then
eat=no
continue
fi
case "$arg" in
-D*|-I*)
set fnord "$@" "$arg"; shift ;;
# Strip any option that makedepend may not understand. Remove
# the object too, otherwise makedepend will parse it as a source file.
-arch)
eat=yes ;;
-*|$object)
;;
*)
set fnord "$@" "$arg"; shift ;;
esac
done
obj_suffix=`echo "$object" | sed 's/^.*\././'`
touch "$tmpdepfile"
${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
rm -f "$depfile"
# makedepend may prepend the VPATH from the source file name to the object.
# No need to regex-escape $object, excess matching of '.' is harmless.
sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile"
# Some versions of the HPUX 10.20 sed can't process the last invocation
# correctly. Breaking it into two sed invocations is a workaround.
sed '1,2d' "$tmpdepfile" \
| tr ' ' "$nl" \
| sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
| sed -e 's/$/ :/' >> "$depfile"
rm -f "$tmpdepfile" "$tmpdepfile".bak
;;
cpp)
# Important note: in order to support this mode, a compiler *must*
# always write the preprocessed file to stdout.
"$@" || exit $?
# Remove the call to Libtool.
if test "$libtool" = yes; then
while test "X$1" != 'X--mode=compile'; do
shift
done
shift
fi
# Remove '-o $object'.
IFS=" "
for arg
do
case $arg in
-o)
shift
;;
$object)
shift
;;
*)
set fnord "$@" "$arg"
shift # fnord
shift # $arg
;;
esac
done
"$@" -E \
| sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
-e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
| sed '$ s: \\$::' > "$tmpdepfile"
rm -f "$depfile"
echo "$object : \\" > "$depfile"
cat < "$tmpdepfile" >> "$depfile"
sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile"
rm -f "$tmpdepfile"
;;
msvisualcpp)
# Important note: in order to support this mode, a compiler *must*
# always write the preprocessed file to stdout.
"$@" || exit $?
# Remove the call to Libtool.
if test "$libtool" = yes; then
while test "X$1" != 'X--mode=compile'; do
shift
done
shift
fi
IFS=" "
for arg
do
case "$arg" in
-o)
shift
;;
$object)
shift
;;
"-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
set fnord "$@"
shift
shift
;;
*)
set fnord "$@" "$arg"
shift
shift
;;
esac
done
"$@" -E 2>/dev/null |
sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile"
rm -f "$depfile"
echo "$object : \\" > "$depfile"
sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile"
echo "$tab" >> "$depfile"
sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile"
rm -f "$tmpdepfile"
;;
msvcmsys)
# This case exists only to let depend.m4 do its work. It works by
# looking at the text of this script. This case will never be run,
# since it is checked for above.
exit 1
;;
none)
exec "$@"
;;
*)
echo "Unknown depmode $depmode" 1>&2
exit 1
;;
esac
exit 0
# Local Variables:
# mode: shell-script
# sh-indentation: 2
# eval: (add-hook 'before-save-hook 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC0"
# time-stamp-end: "; # UTC"
# End:

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

271
doc/conf.py Normal file
View File

@ -0,0 +1,271 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# mdds documentation build configuration file, created by
# sphinx-quickstart on Tue Sep 22 20:54:14 2015.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys
import os
import subprocess
rtd_build = os.environ.get('READTHEDOCS', None) == 'True'
if rtd_build:
subprocess.call("doxygen --version; doxygen doxygen.conf", shell=True)
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = ['breathe']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = 'mdds'
copyright = '2022, Kohei Yoshida'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '2.0'
# The full version, including alpha/beta/rc tags.
release = '2.0.3'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
# The reST default role (used for this markup: `text`) to use for all
# documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# If true, keep warnings as "system message" paragraphs in the built documents.
#keep_warnings = False
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'sphinx_rtd_theme'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
#html_extra_path = []
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'mddsdoc'
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
('index', 'mdds.tex', 'mdds Documentation',
'Kohei Yoshida', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# If true, show page references after internal links.
#latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'mdds', 'mdds Documentation',
['Kohei Yoshida'], 1)
]
# If true, show URL addresses after external links.
#man_show_urls = False
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'mdds', 'mdds Documentation',
'Kohei Yoshida', 'mdds', 'One line description of project.',
'Miscellaneous'),
]
# Documents to append as an appendix to all manuals.
#texinfo_appendices = []
# If false, no module index is generated.
#texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'
# If true, do not generate a @detailmenu in the "Top" node's menu.
#texinfo_no_detailmenu = False
breathe_projects = {"mdds": "./_doxygen/xml"}
breathe_default_project = "mdds"
breathe_default_members = ('members', 'undoc-members')

2276
doc/doxygen.conf Normal file

File diff suppressed because it is too large Load Diff

232
doc/flat_segment_tree.rst Normal file
View File

@ -0,0 +1,232 @@
.. highlight:: cpp
Flat Segment Tree
=================
Overview
--------
Flat segment tree is a derivative of `segment tree
<https://en.wikipedia.org/wiki/Segment_tree>`_, and is designed to store
non-overlapping 1-dimensional range values such that *the values of the
neighboring ranges are guaranteed to be different.* An insertion of a range
value into this structure will always overwrite one or more existing ranges
that overlap with the new range. If an insertion of a new range would cause
any adjacent ranges to have the equal value, those ranges will be merged into
one range.
An instance of this structure is initialized with fixed lower and upper
bounaries, which will not change throughout the life time of the instance.
The flat segment tree structure consists of two parts: the leaf-node part
which also forms a doubly-linked list, and the non-leaf-node part which forms
a balanced-binary tree and is used only when performing tree-based queries.
The range values are stored in the leaf-nodes, while the non-leaf nodes are
used only for queries.
Quick start
-----------
The following code demonstrates a simple use case of storing non-overlapping
ranged values and performing queries using :cpp:class:`~mdds::flat_segment_tree`:
.. literalinclude:: ../example/flat_segment_tree.cpp
:language: C++
:lines: 29-61
Let's walk through this code step-by-step. The first step is to declare the
instance::
// Define the begin and end points of the whole segment, and the default
// value.
fst_type db(0, 500, 0);
Here, the first and second arguments specify the lower and upper boundaries of
the whole segment. The third argument specifies the value for the empty
segments. What this line does is to create a new instance and initializes it
with one initial segment ranging from 0 to 500 with a value of 0.
.. figure:: _static/images/fst_example1_initial.png
:align: center
Internally, this initial range is represented by two leaf nodes, with the
first one storing the start key and the value for the segment both of which
happen to be 0 in this example, and the second one storing the end key of 500.
The following lines insert two new segments into this structure::
db.insert_front(10, 20, 10);
db.insert_back(50, 70, 15);
The first line inserts a segment ranging from 10 to 20 with a value of 10, and
the second line from 50 to 70 with a value of 15.
.. figure:: _static/images/fst_example1_insert1.png
:align: center
You can insert a new segment either via :cpp:func:`~mdds::flat_segment_tree::insert_front`
or :cpp:func:`~mdds::flat_segment_tree::insert_back`. The end result will be
the same regardless of which method you use; the difference is that
:cpp:func:`~mdds::flat_segment_tree::insert_front` begins its search for
the insertion point from the first node associated with the minimum key value,
whereas :cpp:func:`~mdds::flat_segment_tree::insert_back` starts its search
from the last node associated with the maximum key value.
At this point, the tree contains six leaf nodes in total to represent all
stored segments. Note that one leaf node represents both the end of a segment
and the start of the adjacent segment that comes after it, unless it's either
the first or the last node.
The next line inserts another segment ranging from 60 to 65 having a value of
5::
db.insert_back(60, 65, 5);
As this new segment overlaps with the existing segment of 50 to 70, it will
cut into a middle part of that segment to make room for itself. At this point,
the tree contains eight leaf nodes representing seven segments in total.
.. figure:: _static/images/fst_example1_insert2.png
:align: center
The next part queries the value associated with a key value of 15 via
:cpp:func:`~mdds::flat_segment_tree::search`::
int value = -1;
long beg = -1, end = -1;
// Perform linear search. This doesn't require the tree to be built
// beforehand. Note that the begin and end point parameters are optional.
db.search(15, value, &beg, &end);
cout << "The value at 15 is " << value << ", and this segment spans from " << beg << " to " << end << endl;;
When executing this code, you will see the following output:
.. code-block:: none
The value at 15 is 10, and this segment spans from 10 to 20
One thing to note is that the :cpp:func:`~mdds::flat_segment_tree::search`
method performs a linear search which involves traversing only through
the leaf nodes of the structure in order to find the target segment. As such,
the worst-case lookup performance is directly proportional to the number of
linear nodes.
There is another way to perform the query with better worse-case performance,
that is through :cpp:func:`~mdds::flat_segment_tree::search_tree` as seen in
the following code::
// Don't forget to build tree before calling search_tree().
db.build_tree();
// Perform tree search. Tree search is generally a lot faster than linear
// search, but requires the tree to be built beforehand.
db.search_tree(62, value, &beg, &end);
cout << "The value at 62 is " << value << ", and this segment spans from " << beg << " to " << end << endl;;
The signature of the :cpp:func:`~mdds::flat_segment_tree::search_tree` method
is identical to that of the :cpp:func:`~mdds::flat_segment_tree::search` method
except for the name. This code generates the following output:
.. code-block:: none
The value at 62 is 5, and this segment spans from 60 to 65
Query via :cpp:func:`~mdds::flat_segment_tree::search_tree` generally performs
better since it traverses through the search tree to find the target segment.
But it does require the search tree to be built ahead of time by calling
:cpp:func:`~mdds::flat_segment_tree::build_tree`.
Iterate through stored segments
-------------------------------
:cpp:class:`~mdds::flat_segment_tree` supports two types of iterators to allow
you to iterate through the segments stored in your tree. The first way is to
iterate through the individual leaf nodes one at a time by using
:cpp:func:`~mdds::flat_segment_tree::begin` and :cpp:func:`~mdds::flat_segment_tree::end`::
for (auto it = db.begin(); it != db.end(); ++it)
{
cout << "key: " << it->first << "; value: " << it->second << endl;
}
Each iterator value contains a pair of two values named ``first`` and ``second``,
with the first one being the key of the segment that the node initiates, and the
second one being the value associated with that segment. When executing this
code with the tree from the example code above, you'll get the following output:
.. code-block:: none
key: 0; value: 0
key: 10; value: 10
key: 20; value: 0
key: 50; value: 15
key: 60; value: 5
key: 65; value: 15
key: 70; value: 0
key: 500; value: 0
Each node stores the start key and the value of the segment it initiates, and
the key stored in each node is also the end key of the segment that the
previous node initiates except for the first node.
except
for the last node, which stores the end key of the segment the previous note
initiates. Note that the value stored in the last node is not associated with
any of the segments stored in the tree; in fact it is the default value for
empty segments.
One thing to keep in mind is that :cpp:class:`~mdds::flat_segment_tree` does
not support mutable iterators that let you modify the stored keys or values.
.. note::
:cpp:class:`~mdds::flat_segment_tree` does not support mutable iterators;
you can only traverse the values in a read-only fashion.
You can also use range-based for loop to iterate through the leaf nodes in a
similar fashion::
for (const auto& node : db)
{
cout << "key: " << node.first << "; value: " << node.second << endl;
}
The output from this code is identical to that from the previous one.
Now, one major inconvenience of navigating through the individual leaf nodes
one node at a time is that you need to keep track of the start and end points
of each segment if you need to operate on the segments rather than the nodes
that comprise the segments. The good news is that :cpp:class:`~mdds::flat_segment_tree`
does provide a way to iterate through the segments directly as the following
code demonstrates::
for (auto it = db.begin_segment(); it != db.end_segment(); ++it)
{
cout << "start: " << it->start << "; end: " << it->end << "; value: " << it->value << endl;
}
This code uses :cpp:func:`~mdds::flat_segment_tree::begin_segment` and
:cpp:func:`~mdds::flat_segment_tree::end_segment` to iterate through one
segment at a time with each iterator value containing ``start``, ``end`` and
``value`` members that correspond with the start key, end key and the value of
the segment, respectively. Running this code produces the following output:
.. code-block:: none
start: 0; end: 10; value: 0
start: 10; end: 20; value: 10
start: 20; end: 50; value: 0
start: 50; end: 60; value: 15
start: 60; end: 65; value: 5
start: 65; end: 70; value: 15
start: 70; end: 500; value: 0
API Reference
-------------
.. doxygenclass:: mdds::flat_segment_tree
:members:

35
doc/index.rst Normal file
View File

@ -0,0 +1,35 @@
.. mdds documentation master file, created by
sphinx-quickstart on Tue Sep 22 20:54:14 2015.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
mdds documentation
==================
Multi-dimensional data structure, or mdds for short, is a collection of data
structures and indexing algorithms that are useful for storing and indexing
multi-dimensional data for C++ projects.
Contents:
.. toctree::
:maxdepth: 1
global.rst
flat_segment_tree.rst
segment_tree.rst
point_quad_tree.rst
multi_type_vector.rst
multi_type_matrix.rst
sorted_string_map.rst
trie_map.rst
rtree.rst
api.rst
Indices and tables
==================
* :ref:`genindex`
* :ref:`search`

15
doc/multi_type_matrix.rst Normal file
View File

@ -0,0 +1,15 @@
.. highlight:: cpp
Multi Type Matrix
=================
API Reference
-------------
.. doxygenclass:: mdds::multi_type_matrix
:members:
.. doxygenstruct:: mdds::mtm::std_string_trait
:members:
.. doxygenenum:: mdds::mtm::element_t

742
doc/multi_type_vector.rst Normal file
View File

@ -0,0 +1,742 @@
.. highlight:: cpp
Multi Type Vector
=================
Quick start
-----------
The following code demonstrates a simple use case of storing values of double
and :cpp:class:`std::string` types in a single container using :cpp:type:`~mdds::multi_type_vector`.
.. literalinclude:: ../example/multi_type_vector.cpp
:language: C++
:start-after: //!code-start
:end-before: //!code-end
You'll see the following console output when you compile and execute this code:
.. code-block:: none
numeric block of size 8
* 1.1
* 1.2
* 1.3
* 10.1
* 10.2
* 10.3
* 10.4
* 10.5
empty block of size 2
- no data -
string block of size 3
* Andy
* Bruce
* Charlie
empty block of size 7
- no data -
.. figure:: _static/images/mtv_block_structure.png
:align: center
Logical structure between the primary array, blocks, and element blocks.
Each multi_type_vector instance maintains a logical storage structure of one
primary array containing one or more blocks each of which consists of ``type``,
``position``, ``size`` and ``data`` members:
* ``type`` - numeric value representing the block type.
* ``position`` - numeridc value representing the logical position of the first
element of the block.
* ``size`` - number of elements present in the block a.k.a its logical size.
* ``data`` - pointer to the secondary storage (element block) storing the element
values.
In this example code, the ``type`` member is referenced to determine its block
type and its logical size is determined from the ``size`` member. For the
numeric and string blocks, their ``data`` members, which should point to the
memory addresses of their respective element blocks, are dereferenced in order
to print out their element values to stdout inside the ``print_block`` function.
Use custom event handlers
-------------------------
It is also possible to define custom event handlers that get called when
certain events take place. To define custom event handlers, you need to
define either a class or a struct that has the following methods:
* **void element_block_acquired(mdds::mtv::base_element_block* block)**
* **void element_block_released(mdds::mtv::base_element_block* block)**
as its public methods, specify it as type named ``event_func`` in a trait struct,
and pass it as the second template argument when instantiating your
:cpp:type:`~mdds::multi_type_vector` type. Refer to :cpp:type:`mdds::mtv::empty_event_func`
for the detail on when each event handler method gets triggered.
The following code example demonstrates how this all works:
.. literalinclude:: ../example/multi_type_vector_event1.cpp
:language: C++
:start-after: //!code-start
:end-before: //!code-end
You'll see the following console output when you compile and execute this code:
.. code-block:: none
inserting string 'foo'...
* element block acquired
inserting string 'bah'...
inserting int 100...
* element block acquired
emptying the container...
* element block released
* element block released
exiting program...
In this example, the **element_block_acquired** handler gets triggered each
time the container creates (thus acquires) a new element block to store a value.
It does *not* get called when a new value is appended to a pre-existing element
block. Similarly, the **element_block_releasd** handler gets triggered each
time an existing element block storing non-empty values gets deleted. One
thing to keep in mind is that since these two handlers respond to events related
to element blocks which are owned by non-empty blocks in the primary array,
and empty blocks don't store any element block instances, creations or deletions
of empty blocks don't trigger these event handlers.
The trait also allows you to configure other behaviors of :cpp:type:`~mdds::multi_type_vector`.
Refer to :cpp:type:`mdds::mtv::default_trait` for all available parameters.
Get raw pointer to element block array
--------------------------------------
Sometimes you need to expose a pointer to an element block array
especially when you need to pass such an array pointer to C API that
requires one. You can do this by calling the ``data`` method of the
element_block template class . This works since the element block
internally just wraps :cpp:class:`std::vector` (or
:cpp:class:`std::deque` in case the ``MDDS_MULTI_TYPE_VECTOR_USE_DEQUE``
preprocessing macro is defined), and its ``data`` method simply exposes
vector's own ``data`` method which returns the memory location of its
internal array storage.
The following code demonstrates this by exposing raw array pointers to the
internal arrays of numeric and string element blocks, and printing their
element values directly from these array pointers.
.. literalinclude:: ../example/multi_type_vector_element_block1.cpp
:language: C++
:start-after: //!code-start
:end-before: //!code-end
Compiling and execute this code produces the following output:
.. code-block:: none
block size: 2
--
1.1
1.2
1.3
1.4
1.5
--
A
B
C
D
E
Traverse multiple multi_type_vector instances "sideways"
--------------------------------------------------------
In this section we will demonstrate a way to traverse multiple instances of
:cpp:type:`~mdds::multi_type_vector` "sideways" using the
:cpp:class:`mdds::mtv::collection` class. What this class does is to wrap
multiple instances of :cpp:type:`~mdds::multi_type_vector` and generate
iterators that let you iterate the individual element values collectively in
the direction orthogonal to the direction of the individual vector instances.
The best way to explain this feature is to use a spreadsheet analogy. Let's
say we are implementing a data store to store a 2-dimensional tabular data
where each cell in the data set is associated with row and column indices.
Each cell may store a value of string type, integer type, numeric type, etc.
And let's say that the data looks like the following spreadsheet data:
.. figure:: _static/images/mtv_collection_sheet.png
:align: center
It consists of five columns, with each column storing 21 rows of data. The
first row is a header row, followed by 20 rows of values. In this example, We
will be using one :cpp:type:`~mdds::multi_type_vector` instance for each
column thus creating five instances in total, and store them in a
``std::vector`` container.
The declaration of the data store will look like this:
.. literalinclude:: ../example/mtv_collection.cpp
:language: C++
:start-after: //!code-start: declare
:end-before: //!code-end: declare
:dedent: 4
The first two lines specify the concrete :cpp:type:`~mdds::multi_type_vector`
type used for each individual column and the collection type that wraps the
columns. The third line instantiates the ``std::vector`` instance to store
the columns, and we are setting its size to five to accommodate for five
columns. We will make use of the collection_type later in this example after
the columns have been populated.
Now, we need to populate the columns with values. First, we are setting the
header row:
.. literalinclude:: ../example/mtv_collection.cpp
:language: C++
:start-after: //!code-start: header-row
:end-before: //!code-end: header-row
:dedent: 4
We are then filling each column individually from column 1 through column 5.
First up is column 1:
.. literalinclude:: ../example/mtv_collection.cpp
:language: C++
:start-after: //!code-start: column-1
:end-before: //!code-end: column-1
:dedent: 4
Hopefully this code is straight-forward. It initializes an array of values
and push them to the column one at a time via
:cpp:func:`~mdds::mtv::soa::multi_type_vector::push_back`. Next up is column 2:
.. literalinclude:: ../example/mtv_collection.cpp
:language: C++
:start-after: //!code-start: column-2
:end-before: //!code-end: column-2
:dedent: 4
This is similar to the code for column 1, except that because we are using an
array of string literals which implicitly becomes an initializer list of type
``const char*``, we need to explicitly specify the type for the
:cpp:func:`~mdds::mtv::soa::multi_type_vector::push_back` call to be ``std::string``.
The code for column 3 is very similar to this:
.. literalinclude:: ../example/mtv_collection.cpp
:language: C++
:start-after: //!code-start: column-3
:end-before: //!code-end: column-3
:dedent: 4
Populating column 4 needs slight pre-processing. We are inserting a string
value of "unknown" in lieu of an integer value of -1. Therefore the following
code will do:
.. literalinclude:: ../example/mtv_collection.cpp
:language: C++
:start-after: //!code-start: column-4
:end-before: //!code-end: column-4
:dedent: 4
Finally, the last column to fill, which uses the same logic as for columns 2
and 3:
.. literalinclude:: ../example/mtv_collection.cpp
:language: C++
:start-after: //!code-start: column-5
:end-before: //!code-end: column-5
:dedent: 4
At this point, the content we've put into the ``columns`` variable roughly
reflects the tabular data shown at the beginning of this section. Now we can
use the collection type we've declared earlier to wrap the columns:
.. literalinclude:: ../example/mtv_collection.cpp
:language: C++
:start-after: //!code-start: wrap
:end-before: //!code-end: wrap
:dedent: 4
We are naming this variable ``rows`` since what we are doing with this wrapper
is to traverse the content of the tabular data in row-wise direction. For
this reason, calling it ``rows`` is quite fitting.
The :cpp:class:`~mdds::mtv::collection` class offers some flexibility as to
how the instances that you are trying to traverse orthogonally are stored.
That being said, you must meet the following prerequisites when passing the
collection of vector instances to the constructor of the
:cpp:class:`~mdds::mtv::collection` class:
1. All :cpp:type:`~mdds::multi_type_vector` instances that comprise the
collection must be of the same logical length i.e. their
:cpp:func:`~mdds::mtv::soa::multi_type_vector::size` methods must all return the same
value.
2. The instances in the collection must be stored in the source container
either as
* concrete instances (as in this example),
* as pointers, or
* as heap instances wrapped within smart pointer class such as
``std::shared_ptr`` or ``std::unique_ptr``.
Although we are storing the vector instances in a ``std::vector`` container in
this example, you have the flexibility to pick a different type of container
to store the individual vector instances as long as it provides STL-compatible
standard iterator functionality.
Additionally, when using the :cpp:class:`~mdds::mtv::collection` class, you
must ensure that the content of the vector instances that it references will
not change for the duration of its use.
Finally, here is the code that does the traversing:
.. literalinclude:: ../example/mtv_collection.cpp
:language: C++
:start-after: //!code-start: traverse-row
:end-before: //!code-end: traverse-row
:dedent: 4
It's a simple for-loop, and in each iteration you get a single cell node that
contains metadata about that cell including its value. The node contains the
following members:
* ``type`` - an integer value representing the type of the value.
* ``index`` - a 0-based index of the :cpp:type:`~mdds::multi_type_vector`
instance within the collection. You can think of this as column index in
this example.
* ``position`` - a 0-based logical element position within each
:cpp:type:`~mdds::multi_type_vector` instance. You can think of this as
row index in this example.
In the current example we are only making use of the ``type`` and ``index``
members, but the ``position`` member will be there if you need it.
The node also provides a convenient ``get()`` method to fetch the value of the
cell. This method is a template method, and you need to explicitly specify
the element block type in order to access the value.
When executing this code, you will see the following outout:
.. code-block:: none
ID | Make | Model | Year | Color
1 | Nissan | Frontier | 1998 | Turquoise
2 | Mercedes-Benz | W201 | 1986 | Fuscia
3 | Nissan | Frontier | 2009 | Teal
4 | Suzuki | Equator | unknown | Fuscia
5 | Saab | 9-5 | unknown | Green
6 | Subaru | Tribeca | 2008 | Khaki
7 | GMC | Yukon XL 2500 | 2009 | Pink
8 | Mercedes-Benz | E-Class | 2008 | Goldenrod
9 | Toyota | Camry Hybrid | 2010 | Turquoise
10 | Nissan | Frontier | 2001 | Yellow
11 | Mazda | MX-5 | 2008 | Orange
12 | Dodge | Ram Van 1500 | 2000 | Goldenrod
13 | Ford | Edge | unknown | Fuscia
14 | Bentley | Azure | 2009 | Goldenrod
15 | GMC | Sonoma Club Coupe | 1998 | Mauv
16 | Audi | S4 | 2013 | Crimson
17 | GMC | 3500 Club Coupe | 1994 | Turquoise
18 | Mercury | Villager | 2000 | Teal
19 | Pontiac | Sunbird | 1990 | Indigo
20 | BMW | 3 Series | 1993 | LKhaki
which clearly shows that the code has traversed the content of the tabular
data horizontally across columns as intended.
Now, one feature that may come in handy is the ability to limit the iteration
range within the collection. You can do that by calling either
:cpp:func:`~mdds::mtv::collection::set_collection_range` to limit the column
range or :cpp:func:`~mdds::mtv::collection::set_element_range` to limit the
row range, or perhaps both.
Let's see how this works in the current example. Here, we are going to limit
the iteration range to only columns 2 and 3, and rows 2 through 11. The following
code will set this limit:
.. literalinclude:: ../example/mtv_collection.cpp
:language: C++
:start-after: //!code-start: limit-range
:end-before: //!code-end: limit-range
:dedent: 4
Then iterate through the collection once again:
.. literalinclude:: ../example/mtv_collection.cpp
:language: C++
:start-after: //!code-start: traverse-row-range
:end-before: //!code-end: traverse-row-range
:dedent: 4
This code is nearly identical to the previous one except for the index values
used to control when to insert column separators and line breaks at the top
and bottom of each iteration. When executing this code, you'll see the
following output:
.. code-block:: none
Nissan | Frontier
Mercedes-Benz | W201
Nissan | Frontier
Suzuki | Equator
Saab | 9-5
Subaru | Tribeca
GMC | Yukon XL 2500
Mercedes-Benz | E-Class
Toyota | Camry Hybrid
Nissan | Frontier
which clearly shows that your iteration range did indeed shrink as expected.
Performance Considerations
--------------------------
Select SoA or AoS storage types
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If you instantiate a multi_type_vector instance via
:cpp:type:`mdds::multi_type_vector`, which is an alias type for
:cpp:class:`mdds::mtv::soa::multi_type_vector`, you will be using the
structure-of-arrays (SoA) variant of its implementation which is new in 2.0.
Prior to 2.0, multi_type_vector used the array-of-structures (AoS) layout which
is still available post 2.0 via :cpp:class:`mdds::mtv::aos::multi_type_vector`
in case you need it.
Note, however, that the SoA variant generally yields better overall performance
since it can make more efficient use of CPU caches. It is therefore highly
recommended that you stick with the SoA variant unless you have a specific
reason not to.
Also note that both variants are API compatibile with each other.
Use of position hints to avoid the cost of block position lookup
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Consider the following example code:
.. literalinclude:: ../example/multi_type_vector_pos_hint.cpp
:language: C++
:start-after: //!code-start: no-pos-hint
:end-before: //!code-end: no-pos-hint
:dedent: 4
which, when executed, may take quite sometime to complete especially when you
are using an older version of mdds. This particular example exposes one
weakness that multi_type_vector has; because it needs to first look up the
position of the block to operate with, and that lookup *always* starts from the
first block, the time it takes to find the correct block increases as the number
of blocks goes up. This example demonstrates the worst case scenario of such
lookup complexity since it always inserts the next value at the last block
position.
Fortunately, there is a simple solution to this which the following code
demonstrates:
.. literalinclude:: ../example/multi_type_vector_pos_hint.cpp
:language: C++
:start-after: //!code-start: pos-hint
:end-before: //!code-end: pos-hint
:dedent: 4
Compiling and executing this code should take only a fraction of a second.
The only difference between the second example and the first one is that the
second one uses an interator as a position hint to keep track of the position of
the last modified block. Each
:cpp:func:`~mdds::mtv::soa::multi_type_vector::set` method call returns an
iterator which can then be passed to the next
:cpp:func:`~mdds::mtv::soa::multi_type_vector::set` call as the position hint.
Because an iterator object internally stores the location of the block the value
was inserted to, this lets the method to start the block position lookup process
from the last modified block, which in this example is always one block behind
the one the new value needs to go. Using the big-O notation, the use of the
position hint essentially turns the complexity of O(n^2) in the first example
into O(1) in the second one if you are using an older version of mdds where the
block position lookup had a linear complexity.
This strategy should work with any methods in :cpp:type:`~mdds::multi_type_vector`
that take a position hint as the first argument.
Note that, if you are using a more recent version of mdds (1.6.0 or newer), the
cost of block position lookup is significantly lessoned thanks to the switch to
binary search in performing the lookup.
.. note::
If you are using mdds 1.6.0 or newer, the cost of block position lookup is
much less significant even without the use of position hints. But the benefit
of using position hints may still be there. It's always a good idea to profile
your specific use case and decide whether the use of position hints is worth
it.
One important thing to note is that, as a user, you must ensure that the position
hint you pass stays valid between the calls. A position hint becomes invalid when
the content of the container changes. A good strategy to maintain a valid position
hint is to always receive the iterator returned from the mutator method you called
to which you passed the previous position hint, which is what the code above does.
Passing an invalid position hint to a method that takes one may result in invalid
memory access or otherwise in some sort of undefined behavior.
.. warning::
You must ensure that the position hint you pass stays valid. Passing an invalid
position hint to a method that takes one may result in invalid memory access
or otherwise in some sort of undefined behavior.
Block shifting performance and loop-unrolling factor
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The introduction of binary search in the block position lookup implementation
in version 1.6 has significantly improved its lookup performance, but has
also resulted in slight performance hit when shifting blocks during value
insertion. This is because when shifting the logical positions of the blocks
below the insertion point, their head positions need to be re-calculated to
account for their new positions.
The good news is that the switch to the structure-of-arrays (SoA) storage
layout in 2.0 alone may bring subtle but measurable improvement in the
block position adjustment performance due to the logical block positions now
being stored in a separate array thereby improving its cache efficiency. In
reality, however, this was somewhat dependent on the CPU types since some CPU's
didn't show any noticeable improvements or even showed worse performance, while
other CPU types showed consistent improvements with SoA over AoS.
Another factor that may play a role is `loop unrolling <https://en.wikipedia.org/wiki/Loop_unrolling>`_
factor which can be configured via the :cpp:var:`~mdds::mtv::default_trait::loop_unrolling`
variable in your custom trait type if you use version 2.0 or newer. This variable
is an enum class of type :cpp:type:`mdds::mtv::lu_factor_t` which enumerates
several pre-defined loop-unrolling factors as well as some SIMD features.
The hardest part is to figure out which loop unrolling factor is the best option
in your runtime environment, since it is highly dependent on the environment.
Luckily mdds comes with a tool called `runtime-env <https://gitlab.com/mdds/mdds/-/tree/master/tools/runtime-env>`_
which, when run, will perform some benchmarks and give you the best loop-unrolling
factor in your runtime environment. Be sure to build this tool with the same
compiler and compiler flags as your target program in order for this tool to give
you a representative answer.
Debugging
---------
Tracing of public methods
^^^^^^^^^^^^^^^^^^^^^^^^^
When using :cpp:class:`~mdds::mtv::soa::multi_type_vector` to handle a series
of data reads and writes in an non-trivial code base, sometimes you may find
yourself needing to track which methods are getting called when following a
certain code path during a debugging session. In such a situation, you can enable
an optional trace method which gets called whenever a public method of :cpp:class:`~mdds::mtv::soa::multi_type_vector`
is called.
First, you need to define a preprocessor macro named
``MDDS_MULTI_TYPE_VECTOR_DEBUG`` before including the header for
:cpp:class:`~mdds::mtv::soa::multi_type_vector`:
.. literalinclude:: ../example/multi_type_vector_debug_trace.cpp
:language: C++
:start-after: //!code-start: header
:end-before: //!code-end: header
to enable additional debug code. In this example the value of the macro is
set to 1, but it doesn't matter what the value of the macro is, as long as it
is defined. You can also define one as a compiler option as well.
Once defined, the next step is to add a ``trace`` method as a static function to
the trait type you pass as a template argument of multi_type_vector:
.. literalinclude:: ../example/multi_type_vector_debug_trace.cpp
:language: C++
:start-after: //!code-start: types
:end-before: //!code-end: types
Here, we are simply inheriting our trait type from the
:cpp:class:`~mdds::mtv::default_trait` type and simply adding a static ``trace``
function to it, and passing this trait type to the mtv_type definition below.
This trace function must take one argument of type
:cpp:class:`mdds::mtv::trace_method_properties_t` which includes various
properties of the traced call. In this example, we are simply printing the
properties named
:cpp:member:`~mdds::mtv::trace_method_properties_t::function_name` and
:cpp:member:`~mdds::mtv::trace_method_properties_t::function_args` each time a
traced method is called. Both of these properties are printable string types.
Note that this ``trace`` function is entirely optional; the code will compile
fine even when it's not defined. Also, it must be declared as static for it to
be called.
Let's instantiate an object of ``mtv_type``, call some of its methods and see
what happens. When executing the following code:
.. literalinclude:: ../example/multi_type_vector_debug_trace.cpp
:language: C++
:start-after: //!code-start: main
:end-before: //!code-end: main
:dedent: 4
You will see the following output:
.. code-block:: text
function:
name: multi_type_vector
args: init_size=10
function:
name: set
args: pos=0; value=? (type=5)
function:
name: set
args: pos=2; value=? (type=1)
function:
name: set
args: pos=4; value=? (type=3)
function:
name: ~multi_type_vector
args:
The :cpp:member:`~mdds::mtv::trace_method_properties_t::function_name`
property is hopefully self-explanatory. The
:cpp:member:`~mdds::mtv::trace_method_properties_t::function_args` property is
a single string value containing the information about the function's
arguments and optionally their values if their values are known to be
printable. If the value of an argument cannot be printed, ``?`` is placed
instead. For some argument types, an additional information is displayed e.g.
``(type=5)`` in the above output which indicates that the type of the value
being passed to the function is :cpp:var:`~mdds::mtv::element_type_int32`.
If you want to limit your tracing to a specific function type or types, you
can make use of the :cpp:member:`~mdds::mtv::trace_method_properties_t::type`
property which specifies the type of the traced method. Likewise, if you want
to only trace methods of a certain instance, use
:cpp:member:`~mdds::mtv::trace_method_properties_t::instance` to filter the
incoming trace calls based on the memory addresses of the instances whose
methods are being traced.
Note that this feature is available for version 2.0.2 and newer, and currently
only available for the SoA variant of :cpp:class:`~mdds::mtv::soa::multi_type_vector`.
.. note::
This feature is only available for version 2.0.2 and newer, and only for the
SoA variant.
API Reference
-------------
Core
^^^^
mdds::multi_type_vector
~~~~~~~~~~~~~~~~~~~~~~~
.. doxygentypedef:: mdds::multi_type_vector
mdds::mtv::soa::multi_type_vector
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. doxygenclass:: mdds::mtv::soa::multi_type_vector
:members:
mdds::mtv::aos::multi_type_vector
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. doxygenclass:: mdds::mtv::aos::multi_type_vector
:members:
mdds::mtv::empty_event_func
~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. doxygenstruct:: mdds::mtv::empty_event_func
:members:
mdds::mtv::default_trait
~~~~~~~~~~~~~~~~~~~~~~~~
.. doxygenstruct:: mdds::mtv::default_trait
:members:
Element Blocks
^^^^^^^^^^^^^^
.. doxygenclass:: mdds::mtv::base_element_block
:members:
.. doxygenclass:: mdds::mtv::element_block
:members:
.. doxygenstruct:: mdds::mtv::default_element_block
:members:
.. doxygenclass:: mdds::mtv::copyable_element_block
:members:
.. doxygenclass:: mdds::mtv::noncopyable_element_block
:members:
.. doxygenstruct:: mdds::mtv::managed_element_block
:members:
.. doxygenstruct:: mdds::mtv::noncopyable_managed_element_block
:members:
.. doxygenstruct:: mdds::mtv::element_block_func
:members:
Types
^^^^^
mdds::mtv::element_t
~~~~~~~~~~~~~~~~~~~~
.. doxygentypedef:: mdds::mtv::element_t
.. doxygenvariable:: mdds::mtv::element_type_empty
.. doxygenvariable:: mdds::mtv::element_type_boolean
.. doxygenvariable:: mdds::mtv::element_type_int8
.. doxygenvariable:: mdds::mtv::element_type_uint8
.. doxygenvariable:: mdds::mtv::element_type_int16
.. doxygenvariable:: mdds::mtv::element_type_uint16
.. doxygenvariable:: mdds::mtv::element_type_int32
.. doxygenvariable:: mdds::mtv::element_type_uint32
.. doxygenvariable:: mdds::mtv::element_type_int64
.. doxygenvariable:: mdds::mtv::element_type_uint64
.. doxygenvariable:: mdds::mtv::element_type_float
.. doxygenvariable:: mdds::mtv::element_type_double
.. doxygenvariable:: mdds::mtv::element_type_string
.. doxygenvariable:: mdds::mtv::element_type_user_start
mdds::mtv::lu_factor_t
~~~~~~~~~~~~~~~~~~~~~~
.. doxygenenum:: mdds::mtv::lu_factor_t
mdds::mtv::trace_method_t
~~~~~~~~~~~~~~~~~~~~~~~~~
.. doxygenenum:: mdds::mtv::trace_method_t
mdds::mtv::trace_method_properties_t
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. doxygenstruct:: mdds::mtv::trace_method_properties_t
Exceptions
^^^^^^^^^^
.. doxygenclass:: mdds::mtv::element_block_error
Collection
^^^^^^^^^^
.. doxygenclass:: mdds::mtv::collection
:members:

10
doc/point_quad_tree.rst Normal file
View File

@ -0,0 +1,10 @@
.. highlight:: cpp
Point Quad Tree
===============
API Reference
-------------
.. doxygenclass:: mdds::point_quad_tree
:members:

11
doc/segment_tree.rst Normal file
View File

@ -0,0 +1,11 @@
.. highlight:: cpp
Segment Tree
============
API Reference
-------------
.. doxygenclass:: mdds::segment_tree
:members:

11
doc/sorted_string_map.rst Normal file
View File

@ -0,0 +1,11 @@
.. highlight:: cpp
Sorted String Map
=================
API Reference
-------------
.. doxygenclass:: mdds::sorted_string_map
:members:

767
doc/trie_map.rst Normal file
View File

@ -0,0 +1,767 @@
.. highlight:: cpp
Trie Maps
=========
Examples
--------
Populating Trie Map
^^^^^^^^^^^^^^^^^^^
This section illustrates how to use :cpp:class:`~mdds::trie_map` to build a
database of city populations and perform prefix searches. In this example,
we will use the 2013 populations of cities in North Carolina, and use the city
names as keys.
Let's define the type first::
using trie_map_type = mdds::trie_map<mdds::trie::std_string_trait, int>;
The first template argument specifies the trait of the key. In this example,
we are using a pre-defined trait for std::string, which is defined in
:cpp:type:`~mdds::trie::std_string_trait`. The second template argument
specifies the value type, which in this example is simply an ``int``.
Once the type is defined, the next step is instantiation::
trie_map_type nc_cities;
It's pretty simple as you don't need to pass any arguments to the constructor.
Now, let's populate this data structure with some population data::
// Insert key-value pairs.
nc_cities.insert("Charlotte", 792862);
nc_cities.insert("Raleigh", 431746);
nc_cities.insert("Greensboro", 279639);
nc_cities.insert("Durham", 245475);
nc_cities.insert("Winston-Salem", 236441);
nc_cities.insert("Fayetteville", 204408);
nc_cities.insert("Cary", 151088);
nc_cities.insert("Wilmington", 112067);
nc_cities.insert("High Point", 107741);
nc_cities.insert("Greenville", 89130);
nc_cities.insert("Asheville", 87236);
nc_cities.insert("Concord", 83506);
nc_cities.insert("Gastonia", 73209);
nc_cities.insert("Jacksonville", 69079);
nc_cities.insert("Chapel Hill", 59635);
nc_cities.insert("Rocky Mount", 56954);
nc_cities.insert("Burlington", 51510);
nc_cities.insert("Huntersville", 50458);
nc_cities.insert("Wilson", 49628);
nc_cities.insert("Kannapolis", 44359);
nc_cities.insert("Apex", 42214);
nc_cities.insert("Hickory", 40361);
nc_cities.insert("Goldsboro", 36306);
It's pretty straight-forward. Each :cpp:func:`~mdds::trie_map::insert` call
expects a pair of string key and an integer value. You can insert your data
in any order regardless of key's sort order.
Now that the data is in, let's perform prefix search to query all cities whose
name begins with "Cha"::
cout << "Cities that start with 'Cha' and their populations:" << endl;
auto results = nc_cities.prefix_search("Cha");
for (const auto& kv : results)
{
cout << " " << kv.first << ": " << kv.second << endl;
}
You can perform prefix search via :cpp:func:`~mdds::trie_map::prefix_search`
method, which returns a results object that can be iterated over using a range-based
for loop. Running this code will produce the following output:
.. code-block:: none
Cities that start with 'Cha' and their populations:
Chapel Hill: 59635
Charlotte: 792862
Let's perform another prefix search, this time with a prefix of "W"::
cout << "Cities that start with 'W' and their populations:" << endl;
results = nc_cities.prefix_search("W");
for (const auto& kv : results)
{
cout << " " << kv.first << ": " << kv.second << endl;
}
You'll see the following output when running this code:
.. code-block:: none
Cities that start with 'W' and their populations:
Wilmington: 112067
Wilson: 49628
Winston-Salem: 236441
Note that the results are sorted in key's ascending order.
.. note::
Results from the prefix search are sorted in key's ascending order.
Creating Packed Trie Map from Trie Map
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
There is also another variant of trie called :cpp:class:`~mdds::packed_trie_map`
which is designed to store all its data in contiguous memory region. Unlike
:cpp:class:`~mdds::trie_map` which is mutable, :cpp:class:`~mdds::packed_trie_map`
is immutable; once populated, you can only perform queries and it is no longer
possible to add new entries into the container.
One way to create an instance of :cpp:class:`~mdds::packed_trie_map` is from
:cpp:class:`~mdds::trie_map` by calling its :cpp:func:`~mdds::trie_map::pack`
method::
auto packed = nc_cities.pack();
The query methods of :cpp:class:`~mdds::packed_trie_map` are identical to those
of :cpp:class:`~mdds::trie_map`. For instance, performing prefix search to find
all entries whose key begins with "C" can be done as follows::
cout << "Cities that start with 'C' and their populations:" << endl;
auto packed_results = packed.prefix_search("C");
for (const auto& kv : packed_results)
{
cout << " " << kv.first << ": " << kv.second << endl;
}
Running this code will generate the following output:
.. code-block:: none
Cities that start with 'C' and their populations:
Cary: 151088
Chapel Hill: 59635
Charlotte: 792862
Concord: 83506
You can also perform an exact-match query via :cpp:func:`~mdds::packed_trie_map::find`
method which returns an iterator associated with the key-value pair entry::
// Individual search.
auto it = packed.find("Wilmington");
cout << "Population of Wilmington: " << it->second << endl;
You'll see the following output with this code:
.. code-block:: none
Population of Wilmington: 112067
What if you performed an exact-match query with a key that doesn't exist in the
container? You will basically get the end iterator position as its return value.
Thus, running this code::
// You get an end position iterator when the container doesn't have the
// specified key.
it = packed.find("Asheboro");
cout << "Population of Asheboro: ";
if (it == packed.end())
cout << "not found";
else
cout << it->second;
cout << endl;
will generate the following output:
.. code-block:: none
Population of Asheboro: not found
The complete source code for the examples in these two sections is available
`here <https://gitlab.com/mdds/mdds/-/blob/master/example/trie_map.cpp>`__.
Using Packed Trie Map directly
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
In the previous example, we showed a way to create an instance of :cpp:class:`~mdds::packed_trie_map`
from a populated instance of :cpp:class:`~mdds::trie_map`. There is also a way
to instantiate and populate an instance of :cpp:class:`~mdds::packed_trie_map`
directly, and that is what we will cover in this section.
First, declare the type::
using trie_map_type = mdds::packed_trie_map<mdds::trie::std_string_trait, int>;
Once again, we are using the pre-defined trait for std::string as its key, and int
as its value type. The next step is to prepare its entries ahead of time::
trie_map_type::entry entries[] =
{
{ MDDS_ASCII("Apex"), 42214 },
{ MDDS_ASCII("Asheville"), 87236 },
{ MDDS_ASCII("Burlington"), 51510 },
{ MDDS_ASCII("Cary"), 151088 },
{ MDDS_ASCII("Chapel Hill"), 59635 },
{ MDDS_ASCII("Charlotte"), 792862 },
{ MDDS_ASCII("Concord"), 83506 },
{ MDDS_ASCII("Durham"), 245475 },
{ MDDS_ASCII("Fayetteville"), 204408 },
{ MDDS_ASCII("Gastonia"), 73209 },
{ MDDS_ASCII("Goldsboro"), 36306 },
{ MDDS_ASCII("Greensboro"), 279639 },
{ MDDS_ASCII("Greenville"), 89130 },
{ MDDS_ASCII("Hickory"), 40361 },
{ MDDS_ASCII("High Point"), 107741 },
{ MDDS_ASCII("Huntersville"), 50458 },
{ MDDS_ASCII("Jacksonville"), 69079 },
{ MDDS_ASCII("Kannapolis"), 44359 },
{ MDDS_ASCII("Raleigh"), 431746 },
{ MDDS_ASCII("Rocky Mount"), 56954 },
{ MDDS_ASCII("Wilmington"), 112067 },
{ MDDS_ASCII("Wilson"), 49628 },
{ MDDS_ASCII("Winston-Salem"), 236441 },
};
We need to do this since :cpp:class:`~mdds::packed_trie_map` is immutable, and
the only time we can populate its content is at instantiation time. Here, we
are using the :c:macro:`MDDS_ASCII` macro to expand a string literal to its
pointer value and size. Note that you need to ensure that the entries are sorted
by the key in ascending order.
.. warning::
When instantiating :cpp:class:`~mdds::packed_trie_map` directly with a static
set of entries, the entries must be sorted by the key in ascending order.
You can then pass this list of entries to construct the instance::
trie_map_type nc_cities(entries, MDDS_N_ELEMENTS(entries));
The :c:macro:`MDDS_N_ELEMENTS` macro will infer the size of a fixed-size array
from its static definition. Once it's instantiated, the rest of the example
for performing searches will be the same as in the previous section, which we
will not repeat here.
The complete source code for the example in this section is available
`here <https://gitlab.com/mdds/mdds/-/blob/master/example/packed_trie_map.cpp>`__.
Saving and loading Packed Trie Map instances
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
There are times when you need to save the state of a :cpp:class:`~mdds::packed_trie_map`
instance to a file, or an in-memory buffer, and load it back later. Doing that
is now possible by using the :cpp:func:`~mdds::packed_trie_map::save_state` and
:cpp:func:`~mdds::packed_trie_map::load_state` member methods of the
:cpp:class:`~mdds::packed_trie_map` class.
First, let's define the type of use::
using map_type = mdds::packed_trie_map<mdds::trie::std_string_trait, int>;
As with the previous examples, we will use ``std::string`` as the key type and
``int`` as the value type. In this example, we are going to use `the world's
largest cities and their 2018 populations
<https://en.wikipedia.org/wiki/List_of_largest_cities>`__ as the data to store
in the container.
The following code defines the entries::
std::vector<map_type::entry> entries =
{
{ MDDS_ASCII("Ahmedabad"), 7681000 },
{ MDDS_ASCII("Alexandria"), 5086000 },
{ MDDS_ASCII("Atlanta"), 5572000 },
{ MDDS_ASCII("Baghdad"), 6812000 },
{ MDDS_ASCII("Bangalore"), 11440000 },
{ MDDS_ASCII("Bangkok"), 10156000 },
{ MDDS_ASCII("Barcelona"), 5494000 },
{ MDDS_ASCII("Beijing"), 19618000 },
{ MDDS_ASCII("Belo Horizonte"), 5972000 },
{ MDDS_ASCII("Bogota"), 10574000 },
{ MDDS_ASCII("Buenos Aires"), 14967000 },
{ MDDS_ASCII("Cairo"), 20076000 },
{ MDDS_ASCII("Chengdu"), 8813000 },
{ MDDS_ASCII("Chennai"), 10456000 },
{ MDDS_ASCII("Chicago"), 8864000 },
{ MDDS_ASCII("Chongqing"), 14838000 },
{ MDDS_ASCII("Dalian"), 5300000 },
{ MDDS_ASCII("Dallas"), 6099000 },
{ MDDS_ASCII("Dar es Salaam"), 6048000 },
{ MDDS_ASCII("Delhi"), 28514000 },
{ MDDS_ASCII("Dhaka"), 19578000 },
{ MDDS_ASCII("Dongguan"), 7360000 },
{ MDDS_ASCII("Foshan"), 7236000 },
{ MDDS_ASCII("Fukuoka"), 5551000 },
{ MDDS_ASCII("Guadalajara"), 5023000 },
{ MDDS_ASCII("Guangzhou"), 12638000 },
{ MDDS_ASCII("Hangzhou"), 7236000 },
{ MDDS_ASCII("Harbin"), 6115000 },
{ MDDS_ASCII("Ho Chi Minh City"), 8145000 },
{ MDDS_ASCII("Hong Kong"), 7429000 },
{ MDDS_ASCII("Houston"), 6115000 },
{ MDDS_ASCII("Hyderabad"), 9482000 },
{ MDDS_ASCII("Istanbul"), 14751000 },
{ MDDS_ASCII("Jakarta"), 10517000 },
{ MDDS_ASCII("Jinan"), 5052000 },
{ MDDS_ASCII("Johannesburg"), 5486000 },
{ MDDS_ASCII("Karachi"), 15400000 },
{ MDDS_ASCII("Khartoum"), 5534000 },
{ MDDS_ASCII("Kinshasa"), 13171000 },
{ MDDS_ASCII("Kolkata"), 14681000 },
{ MDDS_ASCII("Kuala Lumpur"), 7564000 },
{ MDDS_ASCII("Lagos"), 13463000 },
{ MDDS_ASCII("Lahore"), 11738000 },
{ MDDS_ASCII("Lima"), 10391000 },
{ MDDS_ASCII("London"), 9046000 },
{ MDDS_ASCII("Los Angeles"), 12458000 },
{ MDDS_ASCII("Luanda"), 7774000 },
{ MDDS_ASCII("Madrid"), 6497000 },
{ MDDS_ASCII("Manila"), 13482000 },
{ MDDS_ASCII("Mexico City"), 21581000 },
{ MDDS_ASCII("Miami"), 6036000 },
{ MDDS_ASCII("Moscow"), 12410000 },
{ MDDS_ASCII("Mumbai"), 19980000 },
{ MDDS_ASCII("Nagoya"), 9507000 },
{ MDDS_ASCII("Nanjing"), 8245000 },
{ MDDS_ASCII("New York City"), 18819000 },
{ MDDS_ASCII("Osaka"), 19281000 },
{ MDDS_ASCII("Paris"), 10901000 },
{ MDDS_ASCII("Philadelphia"), 5695000 },
{ MDDS_ASCII("Pune"), 6276000 },
{ MDDS_ASCII("Qingdao"), 5381000 },
{ MDDS_ASCII("Rio de Janeiro"), 13293000 },
{ MDDS_ASCII("Riyadh"), 6907000 },
{ MDDS_ASCII("Saint Petersburg"), 5383000 },
{ MDDS_ASCII("Santiago"), 6680000 },
{ MDDS_ASCII("Sao Paulo"), 21650000 },
{ MDDS_ASCII("Seoul"), 9963000 },
{ MDDS_ASCII("Shanghai"), 25582000 },
{ MDDS_ASCII("Shenyang"), 6921000 },
{ MDDS_ASCII("Shenzhen"), 11908000 },
{ MDDS_ASCII("Singapore"), 5792000 },
{ MDDS_ASCII("Surat"), 6564000 },
{ MDDS_ASCII("Suzhou"), 6339000 },
{ MDDS_ASCII("Tehran"), 8896000 },
{ MDDS_ASCII("Tianjin"), 13215000 },
{ MDDS_ASCII("Tokyo"), 37400068 },
{ MDDS_ASCII("Toronto"), 6082000 },
{ MDDS_ASCII("Washington, D.C."), 5207000 },
{ MDDS_ASCII("Wuhan"), 8176000 },
{ MDDS_ASCII("Xi'an"), 7444000 },
{ MDDS_ASCII("Yangon"), 5157000 },
};
It's a bit long as it contains entries for 81 cities. We are then going to
create an instance of the :cpp:class:`~mdds::packed_trie_map` class directly::
map_type cities(entries.data(), entries.size());
Let's print the size of the container to make sure the container has been
successfully populated::
cout << "Number of cities: " << cities.size() << endl;
You will see the following output:
.. code-block:: none
Number of cities: 81
if the container has been successfully populated. Now, let's run a prefix
search on names beginning with an 'S'::
cout << "Cities that begin with 'S':" << endl;
auto results = cities.prefix_search("S");
for (const auto& city : results)
cout << " * " << city.first << ": " << city.second << endl;
to make sure you get the following ten cities and their populations as the
output:
.. code-block:: none
Cities that begin with 'S':
* Saint Petersburg: 5383000
* Santiago: 6680000
* Sao Paulo: 21650000
* Seoul: 9963000
* Shanghai: 25582000
* Shenyang: 6921000
* Shenzhen: 11908000
* Singapore: 5792000
* Surat: 6564000
* Suzhou: 6339000
So far so good. Next, we will use the :cpp:func:`~mdds::packed_trie_map::save_state`
method to dump the internal state of this container to a file named **cities.bin**::
std::ofstream outfile("cities.bin", std::ios::binary);
cities.save_state(outfile);
This will create a file named **cities.bin** which contains a binary blob
representing the content of this container in the current working directory.
Run the ``ls -l cities.bin`` command to make sure the file has been created:
.. code-block:: none
-rw-r--r-- 1 kohei kohei 17713 Jun 20 12:49 cities.bin
Now that the state of the container has been fully serialized to a file, let's
work on restoring its content in another, brand-new instance of
:cpp:class:`~mdds::packed_trie_map`.
::
map_type cities_loaded;
std::ifstream infile("cities.bin", std::ios::binary);
cities_loaded.load_state(infile);
Here, we used the :cpp:func:`~mdds::packed_trie_map::load_state` method to
restore the state from the file we have previously created. Let's make sure
that this new instance has content equivalent to that of the original::
cout << "Equal to the original? " << std::boolalpha << (cities == cities_loaded) << endl;
If you see the following output:
.. code-block:: none
Equal to the original? true
then this new instance has equivalent contant as the original one. Let's also
make sure that it contains the same number of entries as the original::
cout << "Number of cities: " << cities_loaded.size() << endl;
Hopefully you will see the following output:
.. code-block:: none
Number of cities: 81
Lastly, let's run on this new instance the same prefix search we did on the
original instance, to make sure we still get the same results::
cout << "Cities that begin with 'S':" << endl;
auto results = cities_loaded.prefix_search("S");
for (const auto& city : results)
cout << " * " << city.first << ": " << city.second << endl;
You should see the following output:
.. code-block:: none
Cities that begin with 'S':
* Saint Petersburg: 5383000
* Santiago: 6680000
* Sao Paulo: 21650000
* Seoul: 9963000
* Shanghai: 25582000
* Shenyang: 6921000
* Shenzhen: 11908000
* Singapore: 5792000
* Surat: 6564000
* Suzhou: 6339000
which is the same output we saw in the first prefix search.
The complete source code for this example is found
`here <https://gitlab.com/mdds/mdds/-/blob/master/example/packed_trie_state_int.cpp>`__.
Saving Packed Trie Map with custom value type
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
In the previos example, you didn't have to explicitly specify the serializer type
to the :cpp:func:`~mdds::packed_trie_map::save_state` and
:cpp:func:`~mdds::packed_trie_map::load_state` methods, even though these two
methods require the serializer type as their template arguments. That's because
the library provides default serializer types for
* numeric value types i.e. integers, float and double,
* ``std::string``, and
* the standard sequence types, such as ``std::vector``, whose elements are of
numeric value types,
and the previous example used ``int`` as the value type.
In this section, we are going to illustrate how you can write your own custom
serializer to allow serialization of your own custom value type. In this example,
we are going to use `the list of presidents of the United States
<https://en.wikipedia.org/wiki/List_of_presidents_of_the_United_States>`__,
with the names of the presidents as the keys, and their years of inauguration
and political affiliations as the values.
We will use the following structure to store the values::
enum affiliated_party_t : uint8_t
{
unaffiliated = 0,
federalist,
democratic_republican,
democratic,
whig,
republican,
national_union,
republican_national_union,
};
struct us_president
{
uint16_t year;
affiliated_party_t party;
};
Each entry stores the year as a 16-bit integer and the affiliated party as an enum
value of 8-bit width.
Next, let's define the container type::
using map_type = mdds::packed_trie_map<mdds::trie::std_string_trait, us_president>;
As with the previous example, the first step is to define the entries that are
sorted by the keys, which in this case are the president's names::
std::vector<map_type::entry> entries =
{
{ MDDS_ASCII("Abraham Lincoln"), { 1861, republican_national_union } },
{ MDDS_ASCII("Andrew Jackson"), { 1829, democratic } },
{ MDDS_ASCII("Andrew Johnson"), { 1865, national_union } },
{ MDDS_ASCII("Barack Obama"), { 2009, democratic } },
{ MDDS_ASCII("Benjamin Harrison"), { 1889, republican } },
{ MDDS_ASCII("Bill Clinton"), { 1993, democratic } },
{ MDDS_ASCII("Calvin Coolidge"), { 1923, republican } },
{ MDDS_ASCII("Chester A. Arthur"), { 1881, republican } },
{ MDDS_ASCII("Donald Trump"), { 2017, republican } },
{ MDDS_ASCII("Dwight D. Eisenhower"), { 1953, republican } },
{ MDDS_ASCII("Franklin D. Roosevelt"), { 1933, democratic } },
{ MDDS_ASCII("Franklin Pierce"), { 1853, democratic } },
{ MDDS_ASCII("George H. W. Bush"), { 1989, republican } },
{ MDDS_ASCII("George W. Bush"), { 2001, republican } },
{ MDDS_ASCII("George Washington"), { 1789, unaffiliated } },
{ MDDS_ASCII("Gerald Ford"), { 1974, republican } },
{ MDDS_ASCII("Grover Cleveland 1"), { 1885, democratic } },
{ MDDS_ASCII("Grover Cleveland 2"), { 1893, democratic } },
{ MDDS_ASCII("Harry S. Truman"), { 1945, democratic } },
{ MDDS_ASCII("Herbert Hoover"), { 1929, republican } },
{ MDDS_ASCII("James A. Garfield"), { 1881, republican } },
{ MDDS_ASCII("James Buchanan"), { 1857, democratic } },
{ MDDS_ASCII("James K. Polk"), { 1845, democratic } },
{ MDDS_ASCII("James Madison"), { 1809, democratic_republican } },
{ MDDS_ASCII("James Monroe"), { 1817, democratic_republican } },
{ MDDS_ASCII("Jimmy Carter"), { 1977, democratic } },
{ MDDS_ASCII("John Adams"), { 1797, federalist } },
{ MDDS_ASCII("John F. Kennedy"), { 1961, democratic } },
{ MDDS_ASCII("John Quincy Adams"), { 1825, democratic_republican } },
{ MDDS_ASCII("John Tyler"), { 1841, whig } },
{ MDDS_ASCII("Lyndon B. Johnson"), { 1963, democratic } },
{ MDDS_ASCII("Martin Van Buren"), { 1837, democratic } },
{ MDDS_ASCII("Millard Fillmore"), { 1850, whig } },
{ MDDS_ASCII("Richard Nixon"), { 1969, republican } },
{ MDDS_ASCII("Ronald Reagan"), { 1981, republican } },
{ MDDS_ASCII("Rutherford B. Hayes"), { 1877, republican } },
{ MDDS_ASCII("Theodore Roosevelt"), { 1901, republican } },
{ MDDS_ASCII("Thomas Jefferson"), { 1801, democratic_republican } },
{ MDDS_ASCII("Ulysses S. Grant"), { 1869, republican } },
{ MDDS_ASCII("Warren G. Harding"), { 1921, republican } },
{ MDDS_ASCII("William Henry Harrison"), { 1841, whig } },
{ MDDS_ASCII("William Howard Taft"), { 1909, republican } },
{ MDDS_ASCII("William McKinley"), { 1897, republican } },
{ MDDS_ASCII("Woodrow Wilson"), { 1913, democratic } },
{ MDDS_ASCII("Zachary Taylor"), { 1849, whig } },
};
Note that we need to add numeric suffixes to the entries for Grover Cleveland,
who became president twice in two separate periods, in order to make the keys
for his entries unique.
Now, proceed to create an instance of :cpp:class:`~mdds::packed_trie_map`::
map_type us_presidents(entries.data(), entries.size());
and inspect its size to make sure it is instantiated properly::
cout << "Number of entries: " << us_presidents.size() << endl;
You should see the following output:
.. code-block:: none
Number of entries: 45
Before we proceed to save the state of this instance, let's define the custom
serializer type first::
struct us_president_serializer
{
union bin_buffer
{
char buffer[2];
uint16_t i16;
affiliated_party_t party;
};
static constexpr bool variable_size = false;
static constexpr size_t value_size = 3;
static void write(std::ostream& os, const us_president& v)
{
bin_buffer buf;
// Write the year value first.
buf.i16 = v.year;
os.write(buf.buffer, 2);
// Write the affiliated party value.
buf.party = v.party;
os.write(buf.buffer, 1);
}
static void read(std::istream& is, size_t n, us_president& v)
{
// For a fixed-size value type, this should equal the defined value size.
assert(n == 3);
bin_buffer buf;
// Read the year value.
is.read(buf.buffer, 2);
v.year = buf.i16;
// Read the affiliated party value.
is.read(buf.buffer, 1);
v.party = buf.party;
}
};
A custom value type can be either variable-size or fixed-size. For a variable-size
value type, each value segment is preceded by the byte length of that segment.
For a fixed-size value type, the byte length of all of the value segments
is written only once up-front, followed by one or more value segments of equal
byte length.
Since the value type in this example is fixed-size, we set the value of the
``variable_size`` static constant to false, and define the size of the value to 3 (bytes)
as the ``value_size`` static constant. Keep in mind that you need to define
the ``value_size`` constant *only* for fixed-size value types; if your value
type is variable-size, you can leave it out.
Additionally, you need to define two static methods - one for writing to the
output stream, and one for reading from the input stream. The write method
must have the following signature::
static void write(std::ostream& os, const T& v);
where the ``T`` is the value type. In the body of this method you write to the
output stream the bytes that represent the value. The length of the bytes you
write must match the size specified by the ``value_size`` constant.
The read method must have the following signature::
static void read(std::istream& is, size_t n, T& v);
where the ``T`` is the value type, and the ``n`` specifies the length of the
bytes you need to read for the value. For a fixed-size value type, the value
of ``n`` should equal the ``value_size`` constant. Your job is to read the
bytes off of the input stream for the length specified by the ``n``, and
populate the value instance passed to the method as the third argument.
Now that we have defined the custom serializer type, let's proceed to save the
state to a file::
std::ofstream outfile("us-presidents.bin", std::ios::binary);
us_presidents.save_state<us_president_serializer>(outfile);
This time around, we are specifying the serializer type explicitly as the template
argument to the :cpp:func:`~mdds::packed_trie_map::save_state` method. Otherwise
it is no different than what we did in the previous example.
Let's create another instance of :cpp:class:`~mdds::packed_trie_map` and restore
the state back from the file we just created::
map_type us_presidents_loaded;
std::ifstream infile("us-presidents.bin", std::ios::binary);
us_presidents_loaded.load_state<us_president_serializer>(infile);
Once again, aside from explicitly specifying the serializer type as the template
argument to the :cpp:func:`~mdds::packed_trie_map::load_state` method, it is
identical to the way we did in the previous example.
Let's compare the new instance with the old one to see if the two are equal::
cout << "Equal to the original? " << std::boolalpha << (us_presidents == us_presidents_loaded) << endl;
The output says:
.. code-block:: none
Equal to the original? true
They are. While we are at it, let's run a simple prefix search to find out
all the US presidents whose first name is 'John'::
cout << "Presidents whose first name is 'John':" << endl;
auto results = us_presidents_loaded.prefix_search("John");
for (const auto& entry : results)
cout << " * " << entry.first << " (" << entry.second.year << "; " << entry.second.party << ")" << endl;
Here is the output:
.. code-block:: none
Presidents whose first name is 'John':
* John Adams (1797; Federalist)
* John F. Kennedy (1961; Democratic)
* John Quincy Adams (1825; Democratic Republican)
* John Tyler (1841; Whig)
This looks like the correct results!
You can find the complete source code for this example `here
<https://gitlab.com/mdds/mdds/-/blob/master/example/packed_trie_state_custom.cpp>`__.
API Reference
-------------
Trie Map
^^^^^^^^
.. doxygenclass:: mdds::trie_map
:members:
Packed Trie Map
^^^^^^^^^^^^^^^
.. doxygenclass:: mdds::packed_trie_map
:members:
Traits
^^^^^^
.. doxygenstruct:: mdds::trie::std_container_trait
:members:
.. doxygentypedef:: mdds::trie::std_string_trait
Value Serializers
^^^^^^^^^^^^^^^^^
.. doxygenstruct:: mdds::trie::value_serializer
:members:
.. doxygenstruct:: mdds::trie::numeric_value_serializer
:members:
.. doxygenstruct:: mdds::trie::variable_value_serializer
:members:
.. doxygenstruct:: mdds::trie::numeric_sequence_value_serializer
:members:

73
example/Makefile.am Normal file
View File

@ -0,0 +1,73 @@
AM_CPPFLAGS = -I$(top_srcdir)/include -DNDEBUG
EXTRA_PROGRAMS = \
flat-segment-tree \
flat-segment-tree-itrs \
mtv-collection \
multi-type-matrix \
multi-type-vector \
multi-type-vector-debug-trace \
multi-type-vector-element-block1 \
multi-type-vector-event1 \
multi-type-vector-pos-hint \
packed-trie-map \
packed-trie-state-custom \
packed-trie-state-int \
point-quad-tree \
segment-tree \
trie-map \
rtree-simple \
rtree-erase \
rtree-medium \
rtree-medium-bulkload
flat_segment_tree_SOURCES = flat_segment_tree.cpp
flat_segment_tree_itrs_SOURCES = flat_segment_tree_itrs.cpp
mtv_collection_SOURCES = mtv_collection.cpp
multi_type_matrix_SOURCES = multi_type_matrix.cpp
multi_type_vector_SOURCES = multi_type_vector.cpp
multi_type_vector_debug_trace_SOURCES = multi_type_vector_debug_trace.cpp
multi_type_vector_element_block1_SOURCES = multi_type_vector_element_block1.cpp
multi_type_vector_event1_SOURCES = multi_type_vector_event1.cpp
multi_type_vector_pos_hint_SOURCES = multi_type_vector_pos_hint.cpp
packed_trie_map_SOURCES = packed_trie_map.cpp
packed_trie_state_custom_SOURCES = packed_trie_state_custom.cpp
packed_trie_state_int_SOURCES = packed_trie_state_int.cpp
point_quad_tree_SOURCES = point_quad_tree.cpp
segment_tree_SOURCES = segment_tree.cpp
trie_map_SOURCES = trie_map.cpp
rtree_simple_SOURCES = rtree_simple.cpp
rtree_erase_SOURCES = rtree_erase.cpp
rtree_medium_SOURCES = rtree_medium.cpp
rtree_medium_bulkload_SOURCES = rtree_medium_bulkload.cpp
TESTS = \
flat-segment-tree \
flat-segment-tree-itrs \
mtv-collection \
multi-type-matrix \
multi-type-vector \
multi-type-vector-debug-trace \
multi-type-vector-element-block1 \
multi-type-vector-event1 \
multi-type-vector-pos-hint \
packed-trie-map \
packed-trie-state-custom \
packed-trie-state-int \
point-quad-tree \
segment-tree \
trie-map \
rtree-simple \
rtree-erase \
rtree-medium \
rtree-medium-bulkload
TEST_ARTIFACTS = \
*.svg \
us-presidents.bin \
cities.bin
distclean-local:
rm -f $(TESTS) $(TEST_ARTIFACTS)

1318
example/Makefile.in Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,67 @@
/* -*- 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.
*
************************************************************************/
#include <mdds/flat_segment_tree.hpp>
#include <iostream>
using std::cout;
using std::endl;
using fst_type = mdds::flat_segment_tree<long, int>;
int main() try
{
// Define the begin and end points of the whole segment, and the default
// value.
fst_type db(0, 500, 0);
db.insert_front(10, 20, 10);
db.insert_back(50, 70, 15);
db.insert_back(60, 65, 5);
int value = -1;
long beg = -1, end = -1;
// Perform linear search. This doesn't require the tree to be built
// beforehand. Note that the begin and end point parameters are optional.
db.search(15, value, &beg, &end);
cout << "The value at 15 is " << value << ", and this segment spans from " << beg << " to " << end << endl;;
// Don't forget to build tree before calling search_tree().
db.build_tree();
// Perform tree search. Tree search is generally a lot faster than linear
// search, but requires the tree to be built beforehand.
db.search_tree(62, value, &beg, &end);
cout << "The value at 62 is " << value << ", and this segment spans from " << beg << " to " << end << endl;;
}
catch (...)
{
return EXIT_FAILURE;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

View File

@ -0,0 +1,73 @@
/* -*- 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.
*
************************************************************************/
#include <mdds/flat_segment_tree.hpp>
#include <iostream>
using std::cout;
using std::endl;
using fst_type = mdds::flat_segment_tree<long, int>;
void iterate_nodes(const fst_type& db)
{
for (auto it = db.begin(); it != db.end(); ++it)
{
cout << "key: " << it->first << "; value: " << it->second << endl;
}
}
void loop_nodes(const fst_type& db)
{
for (const auto& node : db)
{
cout << "key: " << node.first << "; value: " << node.second << endl;
}
}
void iterate_segments(const fst_type& db)
{
for (auto it = db.begin_segment(); it != db.end_segment(); ++it)
{
cout << "start: " << it->start << "; end: " << it->end << "; value: " << it->value << endl;
}
}
int main()
{
fst_type db(0, 500, 0);
db.insert_front(10, 20, 10);
db.insert_back(50, 70, 15);
db.insert_back(60, 65, 5);
iterate_nodes(db);
loop_nodes(db);
iterate_segments(db);
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

192
example/mtv_collection.cpp Normal file
View File

@ -0,0 +1,192 @@
/* -*- 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.
*
************************************************************************/
#include <mdds/multi_type_vector.hpp>
#include <mdds/multi_type_vector/collection.hpp>
#include <mdds/multi_type_vector/trait.hpp>
#include <vector>
#include <iostream>
void example1()
{
//!code-start: declare
using mtv_type = mdds::multi_type_vector<mdds::mtv::element_block_func>;
using collection_type = mdds::mtv::collection<mtv_type>;
std::vector<mtv_type> columns(5);
//!code-end: declare
//!code-start: header-row
// Populate the header row.
const char* headers[] = { "ID", "Make", "Model", "Year", "Color" };
size_t i = 0;
for (const char* v : headers)
columns[i++].push_back<std::string>(v);
//!code-end: header-row
//!code-start: column-1
// Fill column 1.
int c1_values[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 };
for (int v : c1_values)
columns[0].push_back(v);
//!code-end: column-1
//!code-start: column-2
// Fill column 2.
const char* c2_values[] =
{
"Nissan", "Mercedes-Benz", "Nissan", "Suzuki", "Saab", "Subaru", "GMC", "Mercedes-Benz", "Toyota", "Nissan",
"Mazda", "Dodge", "Ford", "Bentley", "GMC", "Audi", "GMC", "Mercury", "Pontiac", "BMW",
};
for (const char* v : c2_values)
columns[1].push_back<std::string>(v);
//!code-end: column-2
//!code-start: column-3
// Fill column 3.
const char* c3_values[] =
{
"Frontier", "W201", "Frontier", "Equator", "9-5", "Tribeca", "Yukon XL 2500", "E-Class", "Camry Hybrid", "Frontier",
"MX-5", "Ram Van 1500", "Edge", "Azure", "Sonoma Club Coupe", "S4", "3500 Club Coupe", "Villager", "Sunbird", "3 Series",
};
for (const char* v : c3_values)
columns[2].push_back<std::string>(v);
//!code-end: column-3
//!code-start: column-4
// Fill column 4. Replace -1 with "unknown".
int32_t c4_values[] =
{
1998, 1986, 2009, -1, -1, 2008, 2009, 2008, 2010, 2001,
2008, 2000, -1, 2009, 1998, 2013, 1994, 2000, 1990, 1993,
};
for (int32_t v : c4_values)
{
if (v < 0)
// Insert a string value "unknown".
columns[3].push_back<std::string>("unknown");
else
columns[3].push_back(v);
}
//!code-end: column-4
//!code-start: column-5
// Fill column 5
const char* c5_values[] =
{
"Turquoise", "Fuscia", "Teal", "Fuscia", "Green", "Khaki", "Pink", "Goldenrod", "Turquoise", "Yellow",
"Orange", "Goldenrod", "Fuscia", "Goldenrod", "Mauv", "Crimson", "Turquoise", "Teal", "Indigo", "LKhaki",
};
for (const char* v : c5_values)
columns[4].push_back<std::string>(v);
//!code-end: column-5
//!code-start: wrap
// Wrap the columns with the 'collection'...
collection_type rows(columns.begin(), columns.end());
//!code-end: wrap
//!code-start: traverse-row
// Traverse the tabular data in row-wise direction.
for (const auto& cell : rows)
{
if (cell.index > 0)
// Insert a column separator before each cell except for the ones in the first column.
std::cout << " | ";
switch (cell.type)
{
// In this example, we use two element types only.
case mdds::mtv::element_type_int32:
std::cout << cell.get<mdds::mtv::int32_element_block>();
break;
case mdds::mtv::element_type_string:
std::cout << cell.get<mdds::mtv::string_element_block>();
break;
default:
std::cout << "???"; // The default case should not hit in this example.
}
if (cell.index == 4)
// We are in the last column. Insert a line break.
std::cout << std::endl;
}
//!code-end: traverse-row
//!code-start: limit-range
rows.set_collection_range(1, 2); // only columns 2 and 3.
rows.set_element_range(1, 10); // only rows 2 through 11.
//!code-end: limit-range
std::cout << "--" << std::endl;
//!code-start: traverse-row-range
for (const auto& cell : rows)
{
if (cell.index > 1)
// Insert a column separator before each cell except for the ones in the first column.
std::cout << " | ";
switch (cell.type)
{
// In this example, we use two element types only.
case mdds::mtv::element_type_int32:
std::cout << cell.get<mdds::mtv::int32_element_block>();
break;
case mdds::mtv::element_type_string:
std::cout << cell.get<mdds::mtv::string_element_block>();
break;
default:
std::cout << "???"; // The default case should not hit in this example.
}
if (cell.index == 2)
// We are in the last column. Insert a line break.
std::cout << std::endl;
}
//!code-end: traverse-row-range
}
int main() try
{
example1();
return EXIT_SUCCESS;
}
catch (...)
{
return EXIT_FAILURE;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

View File

@ -0,0 +1,59 @@
/* -*- 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.
*
************************************************************************/
#include <mdds/multi_type_matrix.hpp>
#include <vector>
typedef mdds::multi_type_matrix<mdds::mtm::std_string_trait> mtm_type;
int main() try
{
// Create a 3x3 matrix with empty elements.
mtm_type mx(3, 3);
// Store values of four different types.
mx.set(0, 0, 1.1); // numeric value (double)
mx.set(1, 0, std::string("text")); // string value
mx.set(0, 1, true); // boolean value
mx.set(1, 1, int(12)); // integer value
std::vector<double> values = {
1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9
};
// Set values for all elements in one step.
mx.set(0, 0, values.begin(), values.end());
return EXIT_SUCCESS;
}
catch (...)
{
return EXIT_FAILURE;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

View File

@ -0,0 +1,109 @@
/* -*- 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.
*
************************************************************************/
//!code-start
#include <mdds/multi_type_vector.hpp>
#include <mdds/multi_type_vector/trait.hpp>
#include <iostream>
#include <vector>
#include <string>
using std::cout;
using std::endl;
using mtv_type = mdds::multi_type_vector<mdds::mtv::element_block_func>;
template<typename _Blk>
void print_block(const mtv_type::value_type& v)
{
// Each element block has static begin() and end() methods that return
// begin and end iterators, respectively, from the passed element block
// instance.
auto it = _Blk::begin(*v.data);
auto it_end = _Blk::end(*v.data);
std::for_each(it, it_end,
[](const typename _Blk::value_type& elem)
{
cout << " * " << elem << endl;
}
);
}
int main() try
{
mtv_type con(20); // Initialized with 20 empty elements.
// Set values individually.
con.set(0, 1.1);
con.set(1, 1.2);
con.set(2, 1.3);
// Set a sequence of values in one step.
std::vector<double> vals = { 10.1, 10.2, 10.3, 10.4, 10.5 };
con.set(3, vals.begin(), vals.end());
// Set string values.
con.set(10, std::string("Andy"));
con.set(11, std::string("Bruce"));
con.set(12, std::string("Charlie"));
// Iterate through all blocks and print all elements.
for (const mtv_type::value_type& v : con)
{
switch (v.type)
{
case mdds::mtv::element_type_double:
{
cout << "numeric block of size " << v.size << endl;
print_block<mdds::mtv::double_element_block>(v);
break;
}
case mdds::mtv::element_type_string:
{
cout << "string block of size " << v.size << endl;
print_block<mdds::mtv::string_element_block>(v);
break;
}
case mdds::mtv::element_type_empty:
cout << "empty block of size " << v.size << endl;
cout << " - no data - " << endl;
default:
;
}
}
return EXIT_SUCCESS;
}
catch (...)
{
return EXIT_FAILURE;
}
//!code-end
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

View File

@ -0,0 +1,70 @@
/* -*- 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.
*
************************************************************************/
//!code-start: header
#define MDDS_MULTI_TYPE_VECTOR_DEBUG 1
#include <mdds/multi_type_vector/soa/main.hpp>
#include <mdds/multi_type_vector/trait.hpp>
#include <iostream>
//!code-end: header
//!code-start: types
namespace mtv = mdds::mtv;
struct mtv_trait : public mtv::default_trait
{
static void trace(const mtv::trace_method_properties_t& props)
{
std::cout << "function:" << std::endl
<< " name: " << props.function_name << std::endl
<< " args: " << props.function_args << std::endl;
}
};
using mtv_type = mtv::soa::multi_type_vector<mtv::element_block_func, mtv_trait>;
//!code-end: types
int main() try
{
//!code-start: main
mtv_type db(10);
db.set<int32_t>(0, 12);
db.set<int8_t>(2, 34);
db.set<int16_t>(4, 56);
//!code-end: main
return EXIT_SUCCESS;
}
catch (...)
{
return EXIT_FAILURE;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

View File

@ -0,0 +1,91 @@
/* -*- 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.
*
************************************************************************/
//!code-start
#include <mdds/multi_type_vector.hpp>
#include <mdds/multi_type_vector/trait.hpp>
#include <iostream>
using std::cout;
using std::endl;
using mdds::mtv::double_element_block;
using mdds::mtv::string_element_block;
using mtv_type = mdds::multi_type_vector<mdds::mtv::element_block_func>;
int main() try
{
mtv_type db; // starts with an empty container.
db.push_back(1.1);
db.push_back(1.2);
db.push_back(1.3);
db.push_back(1.4);
db.push_back(1.5);
db.push_back(std::string("A"));
db.push_back(std::string("B"));
db.push_back(std::string("C"));
db.push_back(std::string("D"));
db.push_back(std::string("E"));
// At this point, you have 2 blocks in the container.
cout << "block size: " << db.block_size() << endl;
cout << "--" << endl;
// Get an iterator that points to the first block in the primary array.
mtv_type::const_iterator it = db.begin();
// Get a pointer to the raw array of the numeric element block using the
// 'data' method.
const double* p = double_element_block::data(*it->data);
// Print the elements from this raw array pointer.
for (const double* p_end = p + it->size; p != p_end; ++p)
cout << *p << endl;
cout << "--" << endl;
++it; // move to the next block, which is a string block.
// Get a pointer to the raw array of the string element block.
const std::string* pz = string_element_block::data(*it->data);
// Print out the string elements.
for (const std::string* pz_end = pz + it->size; pz != pz_end; ++pz)
cout << *pz << endl;
return EXIT_SUCCESS;
}
catch (...)
{
return EXIT_FAILURE;
}
//!code-end
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

View File

@ -0,0 +1,88 @@
/* -*- 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.
*
************************************************************************/
//!code-start
#include <mdds/multi_type_vector.hpp>
#include <mdds/multi_type_vector/trait.hpp>
#include <iostream>
using std::cout;
using std::endl;
class event_hdl
{
public:
void element_block_acquired(mdds::mtv::base_element_block* block)
{
(void)block;
cout << " * element block acquired" << endl;
}
void element_block_released(mdds::mtv::base_element_block* block)
{
(void)block;
cout << " * element block released" << endl;
}
};
struct trait
{
using event_func = event_hdl;
constexpr static mdds::mtv::lu_factor_t loop_unrolling = mdds::mtv::lu_factor_t::none;
};
using mtv_type = mdds::multi_type_vector<mdds::mtv::element_block_func, trait>;
int main() try
{
mtv_type db; // starts with an empty container.
cout << "inserting string 'foo'..." << endl;
db.push_back(std::string("foo")); // creates a new string element block.
cout << "inserting string 'bah'..." << endl;
db.push_back(std::string("bah")); // appends to an existing string block.
cout << "inserting int 100..." << endl;
db.push_back(int(100)); // creates a new int element block.
cout << "emptying the container..." << endl;
db.clear(); // releases both the string and int element blocks.
cout << "exiting program..." << endl;
return EXIT_SUCCESS;
}
catch (...)
{
return EXIT_FAILURE;
}
//!code-end
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

View File

@ -0,0 +1,133 @@
/* -*- 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.
*
************************************************************************/
#include <mdds/multi_type_vector.hpp>
#include <mdds/multi_type_vector/trait.hpp>
#include <iostream>
#include <string>
#include <chrono>
namespace {
class stack_printer
{
public:
explicit stack_printer(const char* msg) :
m_msg(msg)
{
std::cout << m_msg << ": --begin" << std::endl;
m_start_time = get_time();
}
~stack_printer()
{
double end_time = get_time();
std::cout << m_msg << ": --end (duration: " << (end_time-m_start_time) << " sec)" << std::endl;
}
void print_time(int line) const
{
double end_time = get_time();
std::cout << m_msg << ": --(" << line << ") (duration: " << (end_time-m_start_time) << " sec)" << std::endl;
}
private:
double get_time() const
{
unsigned long usec_since_epoch =
std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::system_clock::now().time_since_epoch()).count();
return usec_since_epoch / 1000000.0;
}
std::string m_msg;
double m_start_time;
};
}
void run_no_position_hint()
{
stack_printer __stack_printer__("::run_no_position_hint");
//!code-start: no-pos-hint
using mtv_type = mdds::multi_type_vector<mdds::mtv::element_block_func>;
size_t size = 50000;
// Initialize the container with one empty block of size 50000.
mtv_type db(size);
// Set non-empty value at every other logical position from top down.
for (size_t i = 0; i < size; ++i)
{
if (i % 2)
db.set<double>(i, 1.0);
}
//!code-end: no-pos-hint
}
void run_with_position_hint()
{
stack_printer __stack_printer__("::run_with_position_hint");
//!code-start: pos-hint
using mtv_type = mdds::multi_type_vector<mdds::mtv::element_block_func>;
size_t size = 50000;
// Initialize the container with one empty block of size 50000.
mtv_type db(size);
mtv_type::iterator pos = db.begin();
// Set non-empty value at every other logical position from top down.
for (size_t i = 0; i < size; ++i)
{
if (i % 2)
// Pass the position hint as the first argument, and receive a new
// one returned from the method for the next call.
pos = db.set<double>(pos, i, 1.0);
}
//!code-end: pos-hint
}
int main() try
{
run_no_position_hint();
run_with_position_hint();
return EXIT_SUCCESS;
}
catch (...)
{
return EXIT_FAILURE;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

113
example/packed_trie_map.cpp Normal file
View File

@ -0,0 +1,113 @@
/* -*- 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.
*
************************************************************************/
#include <mdds/global.hpp> // for MDDS_ASCII and MDDS_N_ELEMENTS
#include <mdds/trie_map.hpp>
#include <iostream>
using std::cout;
using std::endl;
int main()
{
using trie_map_type = mdds::packed_trie_map<mdds::trie::std_string_trait, int>;
// Entries must be known prior to creating the instance, and they must be
// sorted by the key in ascending order.
trie_map_type::entry entries[] =
{
{ MDDS_ASCII("Apex"), 42214 },
{ MDDS_ASCII("Asheville"), 87236 },
{ MDDS_ASCII("Burlington"), 51510 },
{ MDDS_ASCII("Cary"), 151088 },
{ MDDS_ASCII("Chapel Hill"), 59635 },
{ MDDS_ASCII("Charlotte"), 792862 },
{ MDDS_ASCII("Concord"), 83506 },
{ MDDS_ASCII("Durham"), 245475 },
{ MDDS_ASCII("Fayetteville"), 204408 },
{ MDDS_ASCII("Gastonia"), 73209 },
{ MDDS_ASCII("Goldsboro"), 36306 },
{ MDDS_ASCII("Greensboro"), 279639 },
{ MDDS_ASCII("Greenville"), 89130 },
{ MDDS_ASCII("Hickory"), 40361 },
{ MDDS_ASCII("High Point"), 107741 },
{ MDDS_ASCII("Huntersville"), 50458 },
{ MDDS_ASCII("Jacksonville"), 69079 },
{ MDDS_ASCII("Kannapolis"), 44359 },
{ MDDS_ASCII("Raleigh"), 431746 },
{ MDDS_ASCII("Rocky Mount"), 56954 },
{ MDDS_ASCII("Wilmington"), 112067 },
{ MDDS_ASCII("Wilson"), 49628 },
{ MDDS_ASCII("Winston-Salem"), 236441 },
};
// Cities in North Carolina and their populations in 2013.
trie_map_type nc_cities(entries, MDDS_N_ELEMENTS(entries));
cout << "Cities that start with 'Cha' and their populations:" << endl;
auto results = nc_cities.prefix_search("Cha");
for (const auto& kv : results)
{
cout << " " << kv.first << ": " << kv.second << endl;
}
cout << "Cities that start with 'W' and their populations:" << endl;
results = nc_cities.prefix_search("W");
for (const auto& kv : results)
{
cout << " " << kv.first << ": " << kv.second << endl;
}
cout << "Cities that start with 'C' and their populations:" << endl;
results = nc_cities.prefix_search("C");
for (const auto& kv : results)
{
cout << " " << kv.first << ": " << kv.second << endl;
}
// Individual search.
auto it = nc_cities.find("Wilmington");
cout << "Population of Wilmington: " << it->second << endl;
// You get an end position iterator when the container doesn't have the
// specified key.
it = nc_cities.find("Asheboro");
cout << "Population of Asheboro: ";
if (it == nc_cities.end())
cout << "not found";
else
cout << it->second;
cout << endl;
return EXIT_SUCCESS;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

View File

@ -0,0 +1,212 @@
/* -*- 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.
*
************************************************************************/
#include <iostream>
#include <fstream>
#include <mdds/trie_map.hpp>
using std::cout;
using std::endl;
enum affiliated_party_t : uint8_t
{
unaffiliated = 0,
federalist,
democratic_republican,
democratic,
whig,
republican,
national_union,
republican_national_union,
};
struct us_president
{
uint16_t year;
affiliated_party_t party;
};
std::ostream& operator<< (std::ostream& os, affiliated_party_t v)
{
static const char* names[] = {
"Unaffiliated",
"Federalist",
"Democratic Republican",
"Democratic",
"Whig",
"Republican",
"National Union",
"Republican / National Union",
};
os << names[v];
return os;
}
bool operator== (const us_president& left, const us_president& right)
{
return left.year == right.year && left.party == right.party;
}
struct us_president_serializer
{
union bin_buffer
{
char buffer[2];
uint16_t i16;
affiliated_party_t party;
};
static constexpr bool variable_size = false;
static constexpr size_t value_size = 3;
static void write(std::ostream& os, const us_president& v)
{
bin_buffer buf;
// Write the year value first.
buf.i16 = v.year;
os.write(buf.buffer, 2);
// Write the affiliated party value.
buf.party = v.party;
os.write(buf.buffer, 1);
}
static void read(std::istream& is, size_t /*n*/, us_president& v)
{
// For a fixed-size value type, this should equal the defined value size.
assert(n == 3);
bin_buffer buf;
// Read the year value.
is.read(buf.buffer, 2);
v.year = buf.i16;
// Read the affiliated party value.
is.read(buf.buffer, 1);
v.party = buf.party;
}
};
int main() try
{
using map_type = mdds::packed_trie_map<mdds::trie::std_string_trait, us_president>;
// source: https://en.wikipedia.org/wiki/List_of_presidents_of_the_United_States
//
// The entries must be sorted by the keys.
std::vector<map_type::entry> entries =
{
{ MDDS_ASCII("Abraham Lincoln"), { 1861, republican_national_union } },
{ MDDS_ASCII("Andrew Jackson"), { 1829, democratic } },
{ MDDS_ASCII("Andrew Johnson"), { 1865, national_union } },
{ MDDS_ASCII("Barack Obama"), { 2009, democratic } },
{ MDDS_ASCII("Benjamin Harrison"), { 1889, republican } },
{ MDDS_ASCII("Bill Clinton"), { 1993, democratic } },
{ MDDS_ASCII("Calvin Coolidge"), { 1923, republican } },
{ MDDS_ASCII("Chester A. Arthur"), { 1881, republican } },
{ MDDS_ASCII("Donald Trump"), { 2017, republican } },
{ MDDS_ASCII("Dwight D. Eisenhower"), { 1953, republican } },
{ MDDS_ASCII("Franklin D. Roosevelt"), { 1933, democratic } },
{ MDDS_ASCII("Franklin Pierce"), { 1853, democratic } },
{ MDDS_ASCII("George H. W. Bush"), { 1989, republican } },
{ MDDS_ASCII("George W. Bush"), { 2001, republican } },
{ MDDS_ASCII("George Washington"), { 1789, unaffiliated } },
{ MDDS_ASCII("Gerald Ford"), { 1974, republican } },
{ MDDS_ASCII("Grover Cleveland 1"), { 1885, democratic } },
{ MDDS_ASCII("Grover Cleveland 2"), { 1893, democratic } },
{ MDDS_ASCII("Harry S. Truman"), { 1945, democratic } },
{ MDDS_ASCII("Herbert Hoover"), { 1929, republican } },
{ MDDS_ASCII("James A. Garfield"), { 1881, republican } },
{ MDDS_ASCII("James Buchanan"), { 1857, democratic } },
{ MDDS_ASCII("James K. Polk"), { 1845, democratic } },
{ MDDS_ASCII("James Madison"), { 1809, democratic_republican } },
{ MDDS_ASCII("James Monroe"), { 1817, democratic_republican } },
{ MDDS_ASCII("Jimmy Carter"), { 1977, democratic } },
{ MDDS_ASCII("John Adams"), { 1797, federalist } },
{ MDDS_ASCII("John F. Kennedy"), { 1961, democratic } },
{ MDDS_ASCII("John Quincy Adams"), { 1825, democratic_republican } },
{ MDDS_ASCII("John Tyler"), { 1841, whig } },
{ MDDS_ASCII("Lyndon B. Johnson"), { 1963, democratic } },
{ MDDS_ASCII("Martin Van Buren"), { 1837, democratic } },
{ MDDS_ASCII("Millard Fillmore"), { 1850, whig } },
{ MDDS_ASCII("Richard Nixon"), { 1969, republican } },
{ MDDS_ASCII("Ronald Reagan"), { 1981, republican } },
{ MDDS_ASCII("Rutherford B. Hayes"), { 1877, republican } },
{ MDDS_ASCII("Theodore Roosevelt"), { 1901, republican } },
{ MDDS_ASCII("Thomas Jefferson"), { 1801, democratic_republican } },
{ MDDS_ASCII("Ulysses S. Grant"), { 1869, republican } },
{ MDDS_ASCII("Warren G. Harding"), { 1921, republican } },
{ MDDS_ASCII("William Henry Harrison"), { 1841, whig } },
{ MDDS_ASCII("William Howard Taft"), { 1909, republican } },
{ MDDS_ASCII("William McKinley"), { 1897, republican } },
{ MDDS_ASCII("Woodrow Wilson"), { 1913, democratic } },
{ MDDS_ASCII("Zachary Taylor"), { 1849, whig } },
};
map_type us_presidents(entries.data(), entries.size());
cout << "Number of entries: " << us_presidents.size() << endl;
cout << endl;
{
std::ofstream outfile("us-presidents.bin", std::ios::binary);
us_presidents.save_state<us_president_serializer>(outfile);
}
map_type us_presidents_loaded;
{
std::ifstream infile("us-presidents.bin", std::ios::binary);
us_presidents_loaded.load_state<us_president_serializer>(infile);
}
std::ios_base::fmtflags origflags = cout.flags();
cout << "Equal to the original? " << std::boolalpha << (us_presidents == us_presidents_loaded) << endl;
cout.setf(origflags);
cout << endl;
cout << "Presidents whose first name is 'John':" << endl;
auto results = us_presidents_loaded.prefix_search("John");
for (const auto& entry : results)
cout << " * " << entry.first << " (" << entry.second.year << "; " << entry.second.party << ")" << endl;
return EXIT_SUCCESS;
}
catch (...)
{
return EXIT_FAILURE;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

View File

@ -0,0 +1,179 @@
/* -*- 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.
*
************************************************************************/
#include <iostream>
#include <vector>
#include <fstream>
#include <mdds/trie_map.hpp>
using std::cout;
using std::endl;
int main() try
{
using map_type = mdds::packed_trie_map<mdds::trie::std_string_trait, int>;
// List of world's largest cities and their populations. The entries must
// be sorted by the keys.
//
// c.f. https://en.wikipedia.org/wiki/List_of_largest_cities#cities
std::vector<map_type::entry> entries =
{
{ MDDS_ASCII("Ahmedabad"), 7681000 },
{ MDDS_ASCII("Alexandria"), 5086000 },
{ MDDS_ASCII("Atlanta"), 5572000 },
{ MDDS_ASCII("Baghdad"), 6812000 },
{ MDDS_ASCII("Bangalore"), 11440000 },
{ MDDS_ASCII("Bangkok"), 10156000 },
{ MDDS_ASCII("Barcelona"), 5494000 },
{ MDDS_ASCII("Beijing"), 19618000 },
{ MDDS_ASCII("Belo Horizonte"), 5972000 },
{ MDDS_ASCII("Bogota"), 10574000 },
{ MDDS_ASCII("Buenos Aires"), 14967000 },
{ MDDS_ASCII("Cairo"), 20076000 },
{ MDDS_ASCII("Chengdu"), 8813000 },
{ MDDS_ASCII("Chennai"), 10456000 },
{ MDDS_ASCII("Chicago"), 8864000 },
{ MDDS_ASCII("Chongqing"), 14838000 },
{ MDDS_ASCII("Dalian"), 5300000 },
{ MDDS_ASCII("Dallas"), 6099000 },
{ MDDS_ASCII("Dar es Salaam"), 6048000 },
{ MDDS_ASCII("Delhi"), 28514000 },
{ MDDS_ASCII("Dhaka"), 19578000 },
{ MDDS_ASCII("Dongguan"), 7360000 },
{ MDDS_ASCII("Foshan"), 7236000 },
{ MDDS_ASCII("Fukuoka"), 5551000 },
{ MDDS_ASCII("Guadalajara"), 5023000 },
{ MDDS_ASCII("Guangzhou"), 12638000 },
{ MDDS_ASCII("Hangzhou"), 7236000 },
{ MDDS_ASCII("Harbin"), 6115000 },
{ MDDS_ASCII("Ho Chi Minh City"), 8145000 },
{ MDDS_ASCII("Hong Kong"), 7429000 },
{ MDDS_ASCII("Houston"), 6115000 },
{ MDDS_ASCII("Hyderabad"), 9482000 },
{ MDDS_ASCII("Istanbul"), 14751000 },
{ MDDS_ASCII("Jakarta"), 10517000 },
{ MDDS_ASCII("Jinan"), 5052000 },
{ MDDS_ASCII("Johannesburg"), 5486000 },
{ MDDS_ASCII("Karachi"), 15400000 },
{ MDDS_ASCII("Khartoum"), 5534000 },
{ MDDS_ASCII("Kinshasa"), 13171000 },
{ MDDS_ASCII("Kolkata"), 14681000 },
{ MDDS_ASCII("Kuala Lumpur"), 7564000 },
{ MDDS_ASCII("Lagos"), 13463000 },
{ MDDS_ASCII("Lahore"), 11738000 },
{ MDDS_ASCII("Lima"), 10391000 },
{ MDDS_ASCII("London"), 9046000 },
{ MDDS_ASCII("Los Angeles"), 12458000 },
{ MDDS_ASCII("Luanda"), 7774000 },
{ MDDS_ASCII("Madrid"), 6497000 },
{ MDDS_ASCII("Manila"), 13482000 },
{ MDDS_ASCII("Mexico City"), 21581000 },
{ MDDS_ASCII("Miami"), 6036000 },
{ MDDS_ASCII("Moscow"), 12410000 },
{ MDDS_ASCII("Mumbai"), 19980000 },
{ MDDS_ASCII("Nagoya"), 9507000 },
{ MDDS_ASCII("Nanjing"), 8245000 },
{ MDDS_ASCII("New York City"), 18819000 },
{ MDDS_ASCII("Osaka"), 19281000 },
{ MDDS_ASCII("Paris"), 10901000 },
{ MDDS_ASCII("Philadelphia"), 5695000 },
{ MDDS_ASCII("Pune"), 6276000 },
{ MDDS_ASCII("Qingdao"), 5381000 },
{ MDDS_ASCII("Rio de Janeiro"), 13293000 },
{ MDDS_ASCII("Riyadh"), 6907000 },
{ MDDS_ASCII("Saint Petersburg"), 5383000 },
{ MDDS_ASCII("Santiago"), 6680000 },
{ MDDS_ASCII("Sao Paulo"), 21650000 },
{ MDDS_ASCII("Seoul"), 9963000 },
{ MDDS_ASCII("Shanghai"), 25582000 },
{ MDDS_ASCII("Shenyang"), 6921000 },
{ MDDS_ASCII("Shenzhen"), 11908000 },
{ MDDS_ASCII("Singapore"), 5792000 },
{ MDDS_ASCII("Surat"), 6564000 },
{ MDDS_ASCII("Suzhou"), 6339000 },
{ MDDS_ASCII("Tehran"), 8896000 },
{ MDDS_ASCII("Tianjin"), 13215000 },
{ MDDS_ASCII("Tokyo"), 37400068 },
{ MDDS_ASCII("Toronto"), 6082000 },
{ MDDS_ASCII("Washington, D.C."), 5207000 },
{ MDDS_ASCII("Wuhan"), 8176000 },
{ MDDS_ASCII("Xi'an"), 7444000 },
{ MDDS_ASCII("Yangon"), 5157000 },
};
map_type cities(entries.data(), entries.size());
cout << "Number of cities: " << cities.size() << endl;
cout << endl;
{
cout << "Cities that begin with 'S':" << endl;
auto results = cities.prefix_search("S");
for (const auto& city : results)
cout << " * " << city.first << ": " << city.second << endl;
}
cout << endl;
{
std::ofstream outfile("cities.bin", std::ios::binary);
cities.save_state(outfile);
}
map_type cities_loaded;
{
std::ifstream infile("cities.bin", std::ios::binary);
cities_loaded.load_state(infile);
}
std::ios_base::fmtflags origflags = cout.flags();
cout << "Equal to the original? " << std::boolalpha << (cities == cities_loaded) << endl;
cout.setf(origflags);
cout << endl;
cout << "Number of cities: " << cities_loaded.size() << endl;
cout << endl;
cout << "Cities that begin with 'S':" << endl;
auto results = cities_loaded.prefix_search("S");
for (const auto& city : results)
cout << " * " << city.first << ": " << city.second << endl;
return EXIT_SUCCESS;
}
catch (...)
{
return EXIT_FAILURE;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

View File

@ -0,0 +1,77 @@
/* -*- 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.
*
************************************************************************/
#include <mdds/point_quad_tree.hpp>
#include <string>
#include <iostream>
using std::cout;
using std::endl;
int main() try
{
typedef ::mdds::point_quad_tree<double, std::string> db_type;
db_type db;
std::string chicago("Chicago");
std::string hamburg("Hamburg");
std::string helsinki("Helsinki");
std::string london("London");
std::string paris("Paris");
std::string prague("Prague");
std::string shanghai("Shanghai");
std::string tokyo("Tokyo");
// insert cities by longitude and latitude.
db.insert(-87.755280, 41.784168, chicago);
db.insert(10.000000, 53.633331, hamburg);
db.insert(25.049999, 60.250000, helsinki);
db.insert(-0.450000, 51.483334, london);
db.insert(2.450000, 48.966667, paris);
db.insert(14.250000, 50.099998, prague);
db.insert(121.433334, 31.166668, shanghai);
db.insert(139.850006, 35.633331, tokyo);
// Perform region search.
db_type::search_results results = db.search_region(100, 30, 140, 40);
// Print out the result of the search.
cout << "Cities located between longitudes 100 and 140 east and latitudes 30 and 40 north are:" << endl;
for (const auto& res : results)
{
cout << " " << res.second
<< ": (latitude=" << res.first.x
<< ", longitude=" << res.first.y << ")" << endl;
}
}
catch (...)
{
return EXIT_FAILURE;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

78
example/rtree_erase.cpp Normal file
View File

@ -0,0 +1,78 @@
/* -*- 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.
*
************************************************************************/
#include <mdds/rtree.hpp>
#include <string>
#include <iostream>
int main() try
{
using rt_type = mdds::rtree<int, std::string>;
rt_type tree;
// Insert multiple values at the same point.
tree.insert({1, 1}, "A");
tree.insert({1, 1}, "B");
tree.insert({1, 1}, "C");
tree.insert({1, 1}, "D");
tree.insert({1, 1}, "E");
// This should return all five values.
auto results = tree.search({1, 1}, rt_type::search_type::match);
for (const std::string& v : results)
std::cout << v << std::endl;
// Erase "C".
for (auto it = results.begin(); it != results.end(); ++it)
{
if (*it == "C")
{
tree.erase(it);
break; // This invalidates the iterator. Bail out.
}
}
std::cout << "'C' has been erased." << std::endl;
// Now this should only return A, B, D and E.
results = tree.search({1, 1}, rt_type::search_type::match);
for (const std::string& v : results)
std::cout << v << std::endl;
return EXIT_SUCCESS;
}
catch (...)
{
return EXIT_FAILURE;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

113
example/rtree_medium.cpp Normal file
View File

@ -0,0 +1,113 @@
/* -*- 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.
*
************************************************************************/
#include <mdds/rtree.hpp>
#include <iostream>
#include <fstream>
// Make the node capacity intentionally small.
struct tiny_trait_2d
{
constexpr static size_t dimensions = 2;
constexpr static size_t min_node_size = 2;
constexpr static size_t max_node_size = 5;
constexpr static size_t max_tree_depth = 100;
constexpr static bool enable_forced_reinsertion = true;
constexpr static size_t reinsertion_size = 2;
};
using rt_type = mdds::rtree<int, int, tiny_trait_2d>;
int main() try
{
// 2D rectangle with the top-left position (x, y), width and height.
struct rect
{
int x;
int y;
int w;
int h;
};
std::vector<rect> rects =
{
{ 3731, 2433, 1356, 937 },
{ 6003, 3172, 1066, 743 },
{ 4119, 6403, 825, 1949 },
{ 10305, 2315, 776, 548 },
{ 13930, 5468, 1742, 626 },
{ 8614, 4107, 2709, 1793 },
{ 14606, 1887, 5368, 1326 },
{ 17990, 5196, 1163, 1911 },
{ 6728, 7881, 3676, 1210 },
{ 14704, 9789, 5271, 1092 },
{ 4071, 10723, 4739, 898 },
{ 11755, 9010, 1357, 2806 },
{ 13978, 4068, 776, 509 },
{ 17507, 3717, 777, 471 },
{ 20358, 6092, 824, 1093 },
{ 6390, 4535, 1066, 1715 },
{ 13978, 7182, 2516, 1365 },
{ 17942, 11580, 2854, 665 },
{ 9919, 10450, 873, 1716 },
{ 5568, 13215, 7446, 509 },
{ 7357, 15277, 3145, 3234 },
{ 3539, 12592, 631, 509 },
{ 4747, 14498, 825, 626 },
{ 4554, 16913, 969, 1443 },
{ 12771, 14693, 2323, 548 },
{ 18714, 8193, 2372, 586 },
{ 22292, 2743, 487, 1638 },
{ 20987, 17535, 1163, 1249 },
{ 19536, 18859, 632, 431 },
{ 19778, 15394, 1356, 626 },
{ 22969, 15394, 631, 2066 },
};
rt_type tree;
// Insert the rectangle objects into the tree.
int value = 0;
for (const auto& rect : rects)
tree.insert({{rect.x, rect.y}, {rect.x + rect.w, rect.y + rect.h}}, value++);
// Export the tree structure as a SVG for visualization.
std::string tree_svg = tree.export_tree(rt_type::export_tree_type::extent_as_svg);
std::ofstream fout("bounds.svg");
fout << tree_svg;
return EXIT_SUCCESS;
}
catch (...)
{
return EXIT_FAILURE;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

View File

@ -0,0 +1,157 @@
/* -*- 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.
*
************************************************************************/
#include <mdds/rtree.hpp>
#include <iostream>
#include <fstream>
// Make the node capacity intentionally small.
struct tiny_trait_2d
{
constexpr static size_t dimensions = 2;
constexpr static size_t min_node_size = 2;
constexpr static size_t max_node_size = 5;
constexpr static size_t max_tree_depth = 100;
constexpr static bool enable_forced_reinsertion = true;
constexpr static size_t reinsertion_size = 2;
};
using rt_type = mdds::rtree<int, int, tiny_trait_2d>;
// 2D rectangle with the top-left position (x, y), width and height.
struct rect
{
int x;
int y;
int w;
int h;
};
std::vector<rect> rects =
{
{ 3538, 9126, 1908, 1908 },
{ 34272, 52053, 2416, 2543 },
{ 32113, 9761, 2416, 638 },
{ 16493, 16747, 7369, 2289 },
{ 29192, 23732, 3432, 2035 },
{ 35797, 17000, 1781, 892 },
{ 15857, 29319, 2162, 1654 },
{ 5825, 24239, 3559, 8512 },
{ 9127, 46846, 2543, 1019 },
{ 7094, 54338, 5210, 892 },
{ 18779, 39734, 3813, 10417 },
{ 32749, 35923, 2289, 2924 },
{ 26018, 31098, 257, 2797 },
{ 6713, 37066, 2924, 1146 },
{ 19541, 3157, 3305, 1146 },
{ 21953, 10904, 4448, 892 },
{ 15984, 24240, 5210, 1273 },
{ 8237, 15350, 2670, 2797 },
{ 17001, 13826, 4067, 1273 },
{ 30970, 13826, 3940, 765 },
{ 9634, 6587, 1654, 1781 },
{ 38464, 47099, 511, 1400 },
{ 20556, 54085, 1400, 1527 },
{ 37575, 24113, 1019, 765 },
{ 20429, 21064, 1146, 1400 },
{ 31733, 4427, 2543, 638 },
{ 2142, 27161, 1273, 7369 },
{ 3920, 43289, 8131, 1146 },
{ 14714, 34272, 1400, 4956 },
{ 38464, 41258, 1273, 1273 },
{ 35542, 45703, 892, 1273 },
{ 25891, 50783, 1273, 5083 },
{ 35415, 28431, 2924, 1781 },
{ 15476, 7349, 1908, 765 },
{ 12555, 11159, 1654, 2035 },
{ 11158, 21445, 1908, 2416 },
{ 23350, 28049, 3432, 892 },
{ 28684, 15985, 2416, 4321 },
{ 24620, 21953, 1654, 638 },
{ 30208, 30716, 2670, 2162 },
{ 26907, 44179, 2797, 4067 },
{ 21191, 35416, 2162, 1019 },
{ 27668, 38717, 638, 3178 },
{ 3666, 50528, 2035, 1400 },
{ 15349, 48750, 2670, 1654 },
{ 28430, 7221, 2162, 892 },
{ 4808, 3158, 2416, 1273 },
{ 38464, 3666, 1527, 1781 },
{ 2777, 20937, 2289, 1146 },
{ 38209, 9254, 1908, 1781 },
{ 2269, 56497, 2289, 892 },
};
void load_tree()
{
rt_type tree;
// Insert the rectangle objects into the tree.
int value = 0;
for (const auto& rect : rects)
tree.insert({{rect.x, rect.y}, {rect.x + rect.w, rect.y + rect.h}}, value++);
// Export the tree structure as a SVG for visualization.
std::string tree_svg = tree.export_tree(rt_type::export_tree_type::extent_as_svg);
std::ofstream fout("bounds2.svg");
fout << tree_svg;
}
void bulkload_tree()
{
rt_type::bulk_loader loader;
// Insert the rectangle objects into the tree.
int value = 0;
for (const auto& rect : rects)
loader.insert({{rect.x, rect.y}, {rect.x + rect.w, rect.y + rect.h}}, value++);
// Start bulk-loading the tree.
rt_type tree = loader.pack();
// Export the tree structure as a SVG for visualization.
std::string tree_svg = tree.export_tree(rt_type::export_tree_type::extent_as_svg);
std::ofstream fout("bounds2-bulkload.svg");
fout << tree_svg;
}
int main() try
{
load_tree();
bulkload_tree();
return EXIT_SUCCESS;
}
catch (...)
{
return EXIT_FAILURE;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

92
example/rtree_simple.cpp Normal file
View File

@ -0,0 +1,92 @@
/* -*- 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.
*
************************************************************************/
#include <mdds/rtree.hpp>
#include <string>
#include <iostream>
int main() try
{
// key values are of type double, and we are storing std::string as a
// value for each spatial object. By default, tree becomes 2-dimensional
// object store unless otherwise specified.
using rt_type = mdds::rtree<double, std::string>;
rt_type tree;
tree.insert({{0.0, 0.0}, {15.0, 20.0}}, "first rectangle data");
rt_type::extent_type bounds({-2.0, -1.0}, {1.0, 2.0});
std::cout << "inserting value for " << bounds.to_string() << std::endl;
tree.insert(bounds, "second rectangle data");
bounds.start.d[0] = -1.0; // Change the first dimension value of the start rectangle point.
bounds.end.d[1] += 1.0; // Increment the second dimension value of the end rectangle point.
std::cout << "inserting value for " << bounds.to_string() << std::endl;
tree.insert(bounds, "third rectangle data");
tree.insert({5.0, 6.0}, "first point data");
{
// Search for all objects that overlap with a (4, 4) - (7, 7) rectangle.
auto results = tree.search({{4.0, 4.0}, {7.0, 7.0}}, rt_type::search_type::overlap);
for (const std::string& v : results)
std::cout << "value: " << v << std::endl;
}
{
// Search for all objects whose bounding rectangles are exactly (4, 4) - (7, 7).
auto results = tree.search({{4.0, 4.0}, {7.0, 7.0}}, rt_type::search_type::match);
std::cout << "number of results: " << std::distance(results.begin(), results.end()) << std::endl;
}
{
// Search for all objects whose bounding rectangles are exactly (0, 0) - (15, 20).
auto results = tree.search({{0.0, 0.0}, {15.0, 20.0}}, rt_type::search_type::match);
std::cout << "number of results: " << std::distance(results.begin(), results.end()) << std::endl;
std::cout << "value: " << *results.begin() << std::endl;
std::cout << "--" << std::endl;
auto it = results.begin();
std::cout << "value: " << *it << std::endl;
std::cout << "extent: " << it.extent().to_string() << std::endl;
std::cout << "depth: " << it.depth() << std::endl;
}
return EXIT_SUCCESS;
}
catch (...)
{
return EXIT_FAILURE;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

73
example/segment_tree.cpp Normal file
View File

@ -0,0 +1,73 @@
/* -*- 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.
*
************************************************************************/
#include <mdds/segment_tree.hpp>
#include <string>
#include <iostream>
using std::cout;
using std::endl;
typedef ::mdds::segment_tree<long, std::string> db_type;
struct string_printer
{
void operator() (const std::string& s) const
{
cout << "search hit: " << s << endl;
}
};
int main() try
{
db_type db;
std::string A("A");
std::string B("B");
std::string C("C");
// Insert data into the tree.
db.insert(0, 10, A);
db.insert(2, 20, B);
db.insert(10, 15, C);
// Don't forget to build it before calling search().
db.build_tree();
// Run search and get the result.
db_type::search_results result = db.search(5);
// Print the result.
cout << "result size: " << result.size() << endl;
std::for_each(result.begin(), result.end(), string_printer());
}
catch (...)
{
return EXIT_FAILURE;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

111
example/trie_map.cpp Normal file
View File

@ -0,0 +1,111 @@
/* -*- 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.
*
************************************************************************/
#include <mdds/trie_map.hpp>
#include <iostream>
using std::cout;
using std::endl;
int main()
{
using trie_map_type = mdds::trie_map<mdds::trie::std_string_trait, int>;
// Cities in North Carolina and their populations in 2013.
trie_map_type nc_cities;
// Insert key-value pairs.
nc_cities.insert("Charlotte", 792862);
nc_cities.insert("Raleigh", 431746);
nc_cities.insert("Greensboro", 279639);
nc_cities.insert("Durham", 245475);
nc_cities.insert("Winston-Salem", 236441);
nc_cities.insert("Fayetteville", 204408);
nc_cities.insert("Cary", 151088);
nc_cities.insert("Wilmington", 112067);
nc_cities.insert("High Point", 107741);
nc_cities.insert("Greenville", 89130);
nc_cities.insert("Asheville", 87236);
nc_cities.insert("Concord", 83506);
nc_cities.insert("Gastonia", 73209);
nc_cities.insert("Jacksonville", 69079);
nc_cities.insert("Chapel Hill", 59635);
nc_cities.insert("Rocky Mount", 56954);
nc_cities.insert("Burlington", 51510);
nc_cities.insert("Huntersville", 50458);
nc_cities.insert("Wilson", 49628);
nc_cities.insert("Kannapolis", 44359);
nc_cities.insert("Apex", 42214);
nc_cities.insert("Hickory", 40361);
nc_cities.insert("Goldsboro", 36306);
cout << "Cities that start with 'Cha' and their populations:" << endl;
auto results = nc_cities.prefix_search("Cha");
for (const auto& kv : results)
{
cout << " " << kv.first << ": " << kv.second << endl;
}
cout << "Cities that start with 'W' and their populations:" << endl;
results = nc_cities.prefix_search("W");
for (const auto& kv : results)
{
cout << " " << kv.first << ": " << kv.second << endl;
}
// Create a compressed version of the container. It works nearly identically.
auto packed = nc_cities.pack();
cout << "Cities that start with 'C' and their populations:" << endl;
auto packed_results = packed.prefix_search("C");
for (const auto& kv : packed_results)
{
cout << " " << kv.first << ": " << kv.second << endl;
}
// Individual search.
auto it = packed.find("Wilmington");
cout << "Population of Wilmington: " << it->second << endl;
// You get an end position iterator when the container doesn't have the
// specified key.
it = packed.find("Asheboro");
cout << "Population of Asheboro: ";
if (it == packed.end())
cout << "not found";
else
cout << it->second;
cout << endl;
return EXIT_SUCCESS;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

2
include/Makefile.am Normal file
View File

@ -0,0 +1,2 @@
SUBDIRS = mdds

596
include/Makefile.in Normal file
View File

@ -0,0 +1,596 @@
# 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)/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__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@
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@
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@
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"
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."
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 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:

32
include/mdds/Makefile.am Normal file
View File

@ -0,0 +1,32 @@
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_custom_func1.hpp \
multi_type_vector_custom_func2.hpp \
multi_type_vector_custom_func3.hpp \
multi_type_vector.hpp \
multi_type_vector_itr.hpp \
multi_type_vector_macro.hpp \
multi_type_vector_trait.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

682
include/mdds/Makefile.in Normal file
View File

@ -0,0 +1,682 @@
# 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)/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__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@
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@
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@
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_custom_func1.hpp \
multi_type_vector_custom_func2.hpp \
multi_type_vector_custom_func3.hpp \
multi_type_vector.hpp \
multi_type_vector_itr.hpp \
multi_type_vector_macro.hpp \
multi_type_vector_trait.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"
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."
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 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:

View File

@ -0,0 +1,788 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
*
* Copyright (c) 2008-2017 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(0), high(0)
{}
};
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(0), 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::itr_forward_handler<flat_segment_tree>;
friend struct ::mdds::__fst::itr_reverse_handler<flat_segment_tree>;
public:
class const_iterator : public ::mdds::__fst::const_iterator_base<
flat_segment_tree, ::mdds::__fst::itr_forward_handler<flat_segment_tree>>
{
typedef ::mdds::__fst::const_iterator_base<
flat_segment_tree, ::mdds::__fst::itr_forward_handler<flat_segment_tree>>
base_type;
friend class flat_segment_tree;
public:
const_iterator() : base_type(nullptr, false)
{}
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::const_iterator_base<
flat_segment_tree, ::mdds::__fst::itr_reverse_handler<flat_segment_tree>>
{
typedef ::mdds::__fst::const_iterator_base<
flat_segment_tree, ::mdds::__fst::itr_reverse_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)
{}
};
using const_segment_iterator = mdds::__fst::const_segment_iterator<flat_segment_tree>;
/**
* 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;
/**
* 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<key_type, value_type>& r);
~flat_segment_tree();
/**
* Assignment only copies the leaf nodes.
*/
flat_segment_tree<key_type, value_type>& operator=(const flat_segment_tree<key_type, value_type>& 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<key_type, value_type>& 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(start_key, end_key, 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(start_key, end_key, 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 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;
/**
* 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<key_type, value_type>& r) const;
bool operator!=(const flat_segment_tree<key_type, value_type>& r) const
{
return !operator==(r);
}
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
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:
flat_segment_tree(); // default constructor is not allowed.
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(key_type key, const node* start_pos) const;
const node* get_insertion_pos_leaf(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;
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: */

View File

@ -0,0 +1,775 @@
/*************************************************************************
*
* Copyright (c) 2010-2017 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>::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>
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(init_val), m_valid_tree(false)
{
// we need to create two end nodes during initialization.
m_left_leaf->value_leaf.key = min_val;
m_left_leaf->value_leaf.value = init_val;
m_left_leaf->next = m_right_leaf;
m_right_leaf->value_leaf.key = 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 = init_val;
}
template<typename Key, typename Value>
flat_segment_tree<Key, Value>::flat_segment_tree(const flat_segment_tree<Key, Value>& r)
: m_root_node(nullptr), m_left_leaf(new node(static_cast<const node&>(*r.m_left_leaf))),
m_right_leaf(static_cast<node*>(nullptr)), 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()
{
destroy();
}
template<typename Key, typename Value>
flat_segment_tree<Key, Value>& flat_segment_tree<Key, Value>::operator=(const flat_segment_tree<Key, Value>& other)
{
flat_segment_tree<Key, Value> copy(other);
swap(copy);
return *this;
}
template<typename Key, typename Value>
void flat_segment_tree<Key, Value>::swap(flat_segment_tree<Key, Value>& 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, start_key, end_key, 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 = 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 = 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>
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
{
typedef std::pair<const_iterator, bool> ret_type;
if (!m_root_node || !m_valid_tree)
{
// either tree has not been built, or is in an invalid state.
return ret_type(const_iterator(this, true), false);
}
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);
}
// 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 ret_type(const_iterator(this, true), false);
}
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 ret_type(const_iterator(this, true), false);
}
// 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 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>
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<key_type, value_type>& r) const
{
const node* n1 = m_left_leaf.get();
const node* n2 = r.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(
key_type key, const node* start_pos) const
{
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->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(
key_type key, const node* start_pos) const
{
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 (start_key >= end_key)
// Invalid order of segment range.
return false;
if (end_key < m_left_leaf->value_leaf.key || start_key >= m_right_leaf->value_leaf.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 (end_key > m_right_leaf->value_leaf.key)
// The end value should not be larger than the current maximum.
end_key = m_right_leaf->value_leaf.key;
return true;
}
} // namespace mdds

View File

@ -0,0 +1,327 @@
/*************************************************************************
*
* Copyright (c) 2010-2017 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 {
/**
* Handler for forward iterator
*/
template<typename _FstType>
struct itr_forward_handler
{
typedef _FstType fst_type;
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 itr_reverse_handler
{
typedef _FstType fst_type;
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;
}
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()
{}
};
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
#endif

187
include/mdds/global.hpp Normal file
View File

@ -0,0 +1,187 @@
/*************************************************************************
*
* 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)
{}
};
template<bool B>
using bool_constant = std::integral_constant<bool, B>;
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, bool_constant<Const>>::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;
}
} // namespace mdds
#endif

View File

@ -0,0 +1,840 @@
/*************************************************************************
*
* 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"
#include "multi_type_vector_trait.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_trait
{
typedef mdds::mtv::int32_element_block integer_element_block;
typedef mdds::mtv::string_element_block string_element_block;
typedef mdds::mtv::element_block_func element_block_func;
};
} // 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_trait.
*
* Internally it uses mdds::multi_type_vector as its value store. The
* element values are linearly stored in column-major order.
*/
template<typename _MtxTrait>
class multi_type_matrix
{
typedef _MtxTrait matrix_trait;
public:
typedef typename matrix_trait::string_element_block string_block_type;
typedef typename matrix_trait::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:
typedef mdds::multi_type_vector<typename matrix_trait::element_block_func> store_type;
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 _Func>
struct walk_func
{
_Func& m_func;
walk_func(_Func& 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 _Func>
_Func walk(_Func 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 _Func>
_Func walk(_Func 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 _Func>
_Func walk(_Func 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 _Func>
_Func walk(
_Func 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

View File

@ -0,0 +1,820 @@
/*************************************************************************
*
* 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 _MtxTrait>
multi_type_matrix<_MtxTrait>::element_block_node_type::element_block_node_type()
: type(mtm::element_empty), offset(0), size(0), data(nullptr)
{}
template<typename _MtxTrait>
multi_type_matrix<_MtxTrait>::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 _MtxTrait>
void multi_type_matrix<_MtxTrait>::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 _MtxTrait>
template<typename _Blk>
typename _Blk::const_iterator multi_type_matrix<_MtxTrait>::element_block_node_type::begin() const
{
typename _Blk::const_iterator it = _Blk::begin(*data);
std::advance(it, offset);
return it;
}
template<typename _MtxTrait>
template<typename _Blk>
typename _Blk::const_iterator multi_type_matrix<_MtxTrait>::element_block_node_type::end() const
{
typename _Blk::const_iterator it = _Blk::begin(*data);
std::advance(it, offset + size);
return it;
}
template<typename _MtxTrait>
typename multi_type_matrix<_MtxTrait>::position_type multi_type_matrix<_MtxTrait>::next_position(
const position_type& pos)
{
return store_type::next_position(pos);
}
template<typename _MtxTrait>
typename multi_type_matrix<_MtxTrait>::const_position_type multi_type_matrix<_MtxTrait>::next_position(
const const_position_type& pos)
{
return store_type::next_position(pos);
}
template<typename _MtxTrait>
multi_type_matrix<_MtxTrait>::multi_type_matrix() : m_size(0, 0)
{}
template<typename _MtxTrait>
multi_type_matrix<_MtxTrait>::multi_type_matrix(size_type rows, size_type cols)
: m_store(rows * cols), m_size(rows, cols)
{}
template<typename _MtxTrait>
template<typename _T>
multi_type_matrix<_MtxTrait>::multi_type_matrix(size_type rows, size_type cols, const _T& value)
: m_store(rows * cols, value), m_size(rows, cols)
{}
template<typename _MtxTrait>
template<typename _T>
multi_type_matrix<_MtxTrait>::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 _MtxTrait>
multi_type_matrix<_MtxTrait>::multi_type_matrix(const multi_type_matrix& r) : m_store(r.m_store), m_size(r.m_size)
{}
template<typename _MtxTrait>
multi_type_matrix<_MtxTrait>::~multi_type_matrix()
{}
template<typename _MtxTrait>
bool multi_type_matrix<_MtxTrait>::operator==(const multi_type_matrix& r) const
{
return m_size == r.m_size && m_store == r.m_store;
}
template<typename _MtxTrait>
bool multi_type_matrix<_MtxTrait>::operator!=(const multi_type_matrix& r) const
{
return !operator==(r);
}
template<typename _MtxTrait>
multi_type_matrix<_MtxTrait>& multi_type_matrix<_MtxTrait>::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 _MtxTrait>
typename multi_type_matrix<_MtxTrait>::position_type multi_type_matrix<_MtxTrait>::position(
size_type row, size_type col)
{
return m_store.position(get_pos(row, col));
}
template<typename _MtxTrait>
typename multi_type_matrix<_MtxTrait>::position_type multi_type_matrix<_MtxTrait>::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 _MtxTrait>
typename multi_type_matrix<_MtxTrait>::const_position_type multi_type_matrix<_MtxTrait>::position(
size_type row, size_type col) const
{
return m_store.position(get_pos(row, col));
}
template<typename _MtxTrait>
typename multi_type_matrix<_MtxTrait>::const_position_type multi_type_matrix<_MtxTrait>::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 _MtxTrait>
typename multi_type_matrix<_MtxTrait>::size_pair_type multi_type_matrix<_MtxTrait>::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 _MtxTrait>
typename multi_type_matrix<_MtxTrait>::position_type multi_type_matrix<_MtxTrait>::end_position()
{
return position_type(m_store.end(), 0);
}
template<typename _MtxTrait>
typename multi_type_matrix<_MtxTrait>::const_position_type multi_type_matrix<_MtxTrait>::end_position() const
{
return const_position_type(m_store.end(), 0);
}
template<typename _MtxTrait>
mtm::element_t multi_type_matrix<_MtxTrait>::get_type(const const_position_type& pos) const
{
return to_mtm_type(pos.first->type);
}
template<typename _MtxTrait>
mtm::element_t multi_type_matrix<_MtxTrait>::get_type(size_type row, size_type col) const
{
return to_mtm_type(m_store.get_type(get_pos(row, col)));
}
template<typename _MtxTrait>
double multi_type_matrix<_MtxTrait>::get_numeric(size_type row, size_type col) const
{
return get_numeric(m_store.position(get_pos(row, col)));
}
template<typename _MtxTrait>
double multi_type_matrix<_MtxTrait>::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 _MtxTrait>
typename multi_type_matrix<_MtxTrait>::integer_type multi_type_matrix<_MtxTrait>::get_integer(
size_type row, size_type col) const
{
return get_integer(m_store.position(get_pos(row, col)));
}
template<typename _MtxTrait>
typename multi_type_matrix<_MtxTrait>::integer_type multi_type_matrix<_MtxTrait>::get_integer(
const const_position_type& pos) const
{
return static_cast<integer_type>(get_numeric(pos));
}
template<typename _MtxTrait>
bool multi_type_matrix<_MtxTrait>::get_boolean(size_type row, size_type col) const
{
return static_cast<bool>(get_numeric(row, col));
}
template<typename _MtxTrait>
bool multi_type_matrix<_MtxTrait>::get_boolean(const const_position_type& pos) const
{
return static_cast<bool>(get_numeric(pos));
}
template<typename _MtxTrait>
const typename multi_type_matrix<_MtxTrait>::string_type& multi_type_matrix<_MtxTrait>::get_string(
size_type row, size_type col) const
{
return get_string(m_store.position(get_pos(row, col)));
}
template<typename _MtxTrait>
const typename multi_type_matrix<_MtxTrait>::string_type& multi_type_matrix<_MtxTrait>::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 _MtxTrait>
template<typename _T>
_T multi_type_matrix<_MtxTrait>::get(size_type row, size_type col) const
{
_T val;
m_store.get(get_pos(row, col), val);
return val;
}
template<typename _MtxTrait>
void multi_type_matrix<_MtxTrait>::set_empty(size_type row, size_type col)
{
m_store.set_empty(get_pos(row, col), get_pos(row, col));
}
template<typename _MtxTrait>
void multi_type_matrix<_MtxTrait>::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 _MtxTrait>
typename multi_type_matrix<_MtxTrait>::position_type multi_type_matrix<_MtxTrait>::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 _MtxTrait>
void multi_type_matrix<_MtxTrait>::set_column_empty(size_type col)
{
m_store.set_empty(get_pos(0, col), get_pos(m_size.row - 1, col));
}
template<typename _MtxTrait>
void multi_type_matrix<_MtxTrait>::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 _MtxTrait>
void multi_type_matrix<_MtxTrait>::set(size_type row, size_type col, double val)
{
m_store.set(get_pos(row, col), val);
}
template<typename _MtxTrait>
typename multi_type_matrix<_MtxTrait>::position_type multi_type_matrix<_MtxTrait>::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 _MtxTrait>
void multi_type_matrix<_MtxTrait>::set(size_type row, size_type col, bool val)
{
m_store.set(get_pos(row, col), val);
}
template<typename _MtxTrait>
typename multi_type_matrix<_MtxTrait>::position_type multi_type_matrix<_MtxTrait>::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 _MtxTrait>
void multi_type_matrix<_MtxTrait>::set(size_type row, size_type col, const string_type& str)
{
m_store.set(get_pos(row, col), str);
}
template<typename _MtxTrait>
typename multi_type_matrix<_MtxTrait>::position_type multi_type_matrix<_MtxTrait>::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 _MtxTrait>
void multi_type_matrix<_MtxTrait>::set(size_type row, size_type col, integer_type val)
{
m_store.set(get_pos(row, col), val);
}
template<typename _MtxTrait>
typename multi_type_matrix<_MtxTrait>::position_type multi_type_matrix<_MtxTrait>::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 _MtxTrait>
template<typename _T>
void multi_type_matrix<_MtxTrait>::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 _MtxTrait>
template<typename _T>
typename multi_type_matrix<_MtxTrait>::position_type multi_type_matrix<_MtxTrait>::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 _MtxTrait>
template<typename _T>
void multi_type_matrix<_MtxTrait>::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 _MtxTrait>
typename multi_type_matrix<_MtxTrait>::size_pair_type multi_type_matrix<_MtxTrait>::size() const
{
return m_size;
}
template<typename _MtxTrait>
multi_type_matrix<_MtxTrait>& multi_type_matrix<_MtxTrait>::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 _MtxTrait>
void multi_type_matrix<_MtxTrait>::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 _MtxTrait>
template<typename _T>
void multi_type_matrix<_MtxTrait>::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 _MtxTrait>
void multi_type_matrix<_MtxTrait>::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 _MtxTrait>
template<typename _T>
void multi_type_matrix<_MtxTrait>::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 _MtxTrait>
void multi_type_matrix<_MtxTrait>::clear()
{
m_store.clear();
m_size.row = 0;
m_size.column = 0;
}
template<typename _MtxTrait>
bool multi_type_matrix<_MtxTrait>::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 _MtxTrait>
bool multi_type_matrix<_MtxTrait>::empty() const
{
return m_store.empty();
}
template<typename _MtxTrait>
void multi_type_matrix<_MtxTrait>::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 _MtxTrait>
template<typename _Func>
_Func multi_type_matrix<_MtxTrait>::walk(_Func func) const
{
walk_func<_Func> wf(func);
std::for_each(m_store.begin(), m_store.end(), wf);
return func;
}
template<typename _MtxTrait>
template<typename _Func>
_Func multi_type_matrix<_MtxTrait>::walk(_Func 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 _MtxTrait>
template<typename _Func>
_Func multi_type_matrix<_MtxTrait>::walk(_Func 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 _MtxTrait>
template<typename _Func>
_Func multi_type_matrix<_MtxTrait>::walk(
_Func 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

View File

@ -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 ElemBlockFunc, typename Trait = mtv::default_trait>
using multi_type_vector = mtv::soa::multi_type_vector<ElemBlockFunc, Trait>;
} // namespace mdds
#endif

View File

@ -0,0 +1,16 @@
SUBDIRS = aos soa
headersdir = $(includedir)/mdds-@API_VERSION@/mdds/multi_type_vector
headers_HEADERS = \
collection_def.inl \
collection.hpp \
custom_func1.hpp \
custom_func2.hpp \
custom_func3.hpp \
iterator_node.hpp \
macro.hpp \
trait.hpp \
types.hpp \
util.hpp

View File

@ -0,0 +1,665 @@
# 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)/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__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@
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@
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@
SUBDIRS = aos soa
headersdir = $(includedir)/mdds-@API_VERSION@/mdds/multi_type_vector
headers_HEADERS = \
collection_def.inl \
collection.hpp \
custom_func1.hpp \
custom_func2.hpp \
custom_func3.hpp \
iterator_node.hpp \
macro.hpp \
trait.hpp \
types.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"
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."
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 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:

View File

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

View File

@ -0,0 +1,543 @@
# 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)/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__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@
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@
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@
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)
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."
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 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:

View File

@ -0,0 +1,240 @@
/* -*- 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(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: */

View File

@ -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 Trait>
class iterator_common_base
{
protected:
typedef typename Trait::parent parent_type;
typedef typename Trait::blocks blocks_type;
typedef typename Trait::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 Trait, typename NodeUpdateFunc>
class iterator_base : public iterator_common_base<Trait>
{
using parent_type = typename Trait::parent;
typedef NodeUpdateFunc node_update_func;
typedef iterator_common_base<Trait> common_base;
typedef typename Trait::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 Trait, typename NodeUpdateFunc, typename NonConstItrBase>
class const_iterator_base : public iterator_common_base<Trait>
{
using parent_type = typename Trait::parent;
typedef NodeUpdateFunc node_update_func;
typedef iterator_common_base<Trait> common_base;
typedef typename Trait::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<Trait>::operator==(other);
}
bool operator!=(const const_iterator_base& other) const
{
return iterator_common_base<Trait>::operator!=(other);
}
};
}}}} // namespace mdds::mtv::aos::detail
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@ -0,0 +1,248 @@
/* -*- 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_CUSTOM_FUNC1_HPP
#define INCLUDED_MDDS_MULTI_TYPE_VECTOR_DIR_CUSTOM_FUNC1_HPP
#include "types.hpp"
#include "trait.hpp"
namespace mdds { namespace mtv {
/**
* Block function template for multi_type_vector with 1 user-defined block.
*/
template<typename _Block>
struct custom_block_func1
{
static base_element_block* create_new_block(element_t type, size_t init_size)
{
switch (type)
{
case _Block::block_type:
return _Block::create_block(init_size);
default:;
}
return element_block_func::create_new_block(type, init_size);
}
static base_element_block* clone_block(const base_element_block& block)
{
switch (get_block_type(block))
{
case _Block::block_type:
return _Block::clone_block(block);
default:;
}
return element_block_func::clone_block(block);
}
static void delete_block(const base_element_block* p)
{
if (!p)
return;
switch (get_block_type(*p))
{
case _Block::block_type:
_Block::delete_block(p);
break;
default:
element_block_func::delete_block(p);
}
}
static void resize_block(base_element_block& block, size_t new_size)
{
switch (get_block_type(block))
{
case _Block::block_type:
_Block::resize_block(block, new_size);
break;
default:
element_block_func::resize_block(block, new_size);
}
}
static void print_block(const base_element_block& block)
{
switch (get_block_type(block))
{
case _Block::block_type:
_Block::print_block(block);
break;
default:
element_block_func::print_block(block);
}
}
static void erase(base_element_block& block, size_t pos)
{
switch (get_block_type(block))
{
case _Block::block_type:
_Block::erase_block(block, pos);
break;
default:
element_block_func::erase(block, pos);
}
}
static void erase(base_element_block& block, size_t pos, size_t size)
{
switch (get_block_type(block))
{
case _Block::block_type:
_Block::erase_block(block, pos, size);
break;
default:
element_block_func_base::erase(block, pos, size);
}
}
static void append_values_from_block(base_element_block& dest, const base_element_block& src)
{
switch (get_block_type(dest))
{
case _Block::block_type:
_Block::append_values_from_block(dest, src);
break;
default:
element_block_func_base::append_values_from_block(dest, src);
}
}
static void append_values_from_block(
base_element_block& dest, const base_element_block& src, size_t begin_pos, size_t len)
{
switch (get_block_type(dest))
{
case _Block::block_type:
_Block::append_values_from_block(dest, src, begin_pos, len);
break;
default:
element_block_func_base::append_values_from_block(dest, src, begin_pos, len);
}
}
static void assign_values_from_block(
base_element_block& dest, const base_element_block& src, size_t begin_pos, size_t len)
{
switch (get_block_type(dest))
{
case _Block::block_type:
_Block::assign_values_from_block(dest, src, begin_pos, len);
break;
default:
element_block_func_base::assign_values_from_block(dest, src, begin_pos, len);
}
}
static void prepend_values_from_block(
base_element_block& dest, const base_element_block& src, size_t begin_pos, size_t len)
{
switch (get_block_type(dest))
{
case _Block::block_type:
_Block::prepend_values_from_block(dest, src, begin_pos, len);
break;
default:
element_block_func_base::prepend_values_from_block(dest, src, begin_pos, len);
}
}
static void swap_values(base_element_block& blk1, base_element_block& blk2, size_t pos1, size_t pos2, size_t len)
{
switch (get_block_type(blk1))
{
case _Block::block_type:
_Block::swap_values(blk1, blk2, pos1, pos2, len);
break;
default:
element_block_func_base::swap_values(blk1, blk2, pos1, pos2, len);
}
}
static bool equal_block(const base_element_block& left, const base_element_block& right)
{
if (get_block_type(left) == _Block::block_type)
{
if (get_block_type(right) != _Block::block_type)
return false;
return _Block::get(left) == _Block::get(right);
}
else if (mtv::get_block_type(right) == _Block::block_type)
return false;
return element_block_func::equal_block(left, right);
}
static void overwrite_values(base_element_block& block, size_t pos, size_t len)
{
switch (get_block_type(block))
{
case _Block::block_type:
_Block::overwrite_values(block, pos, len);
break;
default:
element_block_func::overwrite_values(block, pos, len);
}
}
static void shrink_to_fit(base_element_block& block)
{
switch (get_block_type(block))
{
case _Block::block_type:
_Block::shrink_to_fit(block);
break;
default:
element_block_func::shrink_to_fit(block);
}
}
static size_t size(const base_element_block& block)
{
switch (get_block_type(block))
{
case _Block::block_type:
return _Block::size(block);
default:
return element_block_func::size(block);
}
}
};
}} // namespace mdds::mtv
#endif
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

View File

@ -0,0 +1,300 @@
/* -*- 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_CUSTOM_FUNC2_HPP
#define INCLUDED_MDDS_MULTI_TYPE_VECTOR_DIR_CUSTOM_FUNC2_HPP
#include "types.hpp"
#include "trait.hpp"
namespace mdds { namespace mtv {
/**
* Block function template for multi_type_vector with 1 user-defined block.
*/
template<typename _Block1, typename _Block2>
struct custom_block_func2
{
static base_element_block* create_new_block(element_t type, size_t init_size)
{
switch (type)
{
case _Block1::block_type:
return _Block1::create_block(init_size);
case _Block2::block_type:
return _Block2::create_block(init_size);
default:;
}
return element_block_func::create_new_block(type, init_size);
}
static base_element_block* clone_block(const base_element_block& block)
{
switch (get_block_type(block))
{
case _Block1::block_type:
return _Block1::clone_block(block);
case _Block2::block_type:
return _Block2::clone_block(block);
default:;
}
return element_block_func::clone_block(block);
}
static void delete_block(const base_element_block* p)
{
if (!p)
return;
switch (get_block_type(*p))
{
case _Block1::block_type:
_Block1::delete_block(p);
break;
case _Block2::block_type:
_Block2::delete_block(p);
break;
default:
element_block_func::delete_block(p);
}
}
static void resize_block(base_element_block& block, size_t new_size)
{
switch (get_block_type(block))
{
case _Block1::block_type:
_Block1::resize_block(block, new_size);
break;
case _Block2::block_type:
_Block2::resize_block(block, new_size);
break;
default:
element_block_func::resize_block(block, new_size);
}
}
static void print_block(const base_element_block& block)
{
switch (get_block_type(block))
{
case _Block1::block_type:
_Block1::print_block(block);
break;
case _Block2::block_type:
_Block2::print_block(block);
break;
default:
element_block_func::print_block(block);
}
}
static void erase(base_element_block& block, size_t pos)
{
switch (get_block_type(block))
{
case _Block1::block_type:
_Block1::erase_block(block, pos);
break;
case _Block2::block_type:
_Block2::erase_block(block, pos);
break;
default:
element_block_func::erase(block, pos);
}
}
static void erase(base_element_block& block, size_t pos, size_t size)
{
switch (get_block_type(block))
{
case _Block1::block_type:
_Block1::erase_block(block, pos, size);
break;
case _Block2::block_type:
_Block2::erase_block(block, pos, size);
break;
default:
element_block_func_base::erase(block, pos, size);
}
}
static void append_values_from_block(base_element_block& dest, const base_element_block& src)
{
switch (get_block_type(dest))
{
case _Block1::block_type:
_Block1::append_values_from_block(dest, src);
break;
case _Block2::block_type:
_Block2::append_values_from_block(dest, src);
break;
default:
element_block_func_base::append_values_from_block(dest, src);
}
}
static void append_values_from_block(
base_element_block& dest, const base_element_block& src, size_t begin_pos, size_t len)
{
switch (get_block_type(dest))
{
case _Block1::block_type:
_Block1::append_values_from_block(dest, src, begin_pos, len);
break;
case _Block2::block_type:
_Block2::append_values_from_block(dest, src, begin_pos, len);
break;
default:
element_block_func_base::append_values_from_block(dest, src, begin_pos, len);
}
}
static void assign_values_from_block(
base_element_block& dest, const base_element_block& src, size_t begin_pos, size_t len)
{
switch (get_block_type(dest))
{
case _Block1::block_type:
_Block1::assign_values_from_block(dest, src, begin_pos, len);
break;
case _Block2::block_type:
_Block2::assign_values_from_block(dest, src, begin_pos, len);
break;
default:
element_block_func_base::assign_values_from_block(dest, src, begin_pos, len);
}
}
static void prepend_values_from_block(
base_element_block& dest, const base_element_block& src, size_t begin_pos, size_t len)
{
switch (get_block_type(dest))
{
case _Block1::block_type:
_Block1::prepend_values_from_block(dest, src, begin_pos, len);
break;
case _Block2::block_type:
_Block2::prepend_values_from_block(dest, src, begin_pos, len);
break;
default:
element_block_func_base::prepend_values_from_block(dest, src, begin_pos, len);
}
}
static void swap_values(base_element_block& blk1, base_element_block& blk2, size_t pos1, size_t pos2, size_t len)
{
switch (get_block_type(blk1))
{
case _Block1::block_type:
_Block1::swap_values(blk1, blk2, pos1, pos2, len);
break;
case _Block2::block_type:
_Block2::swap_values(blk1, blk2, pos1, pos2, len);
break;
default:
element_block_func_base::swap_values(blk1, blk2, pos1, pos2, len);
}
}
static bool equal_block(const base_element_block& left, const base_element_block& right)
{
if (get_block_type(left) == _Block1::block_type)
{
if (get_block_type(right) != _Block1::block_type)
return false;
return _Block1::get(left) == _Block1::get(right);
}
else if (mtv::get_block_type(right) == _Block1::block_type)
return false;
if (get_block_type(left) == _Block2::block_type)
{
if (get_block_type(right) != _Block2::block_type)
return false;
return _Block2::get(left) == _Block2::get(right);
}
else if (mtv::get_block_type(right) == _Block2::block_type)
return false;
return element_block_func::equal_block(left, right);
}
static void overwrite_values(base_element_block& block, size_t pos, size_t len)
{
switch (get_block_type(block))
{
case _Block1::block_type:
_Block1::overwrite_values(block, pos, len);
break;
case _Block2::block_type:
_Block2::overwrite_values(block, pos, len);
break;
default:
element_block_func::overwrite_values(block, pos, len);
}
}
static void shrink_to_fit(base_element_block& block)
{
switch (get_block_type(block))
{
case _Block1::block_type:
_Block1::shrink_to_fit(block);
break;
case _Block2::block_type:
_Block2::shrink_to_fit(block);
break;
default:
element_block_func::shrink_to_fit(block);
}
}
static size_t size(const base_element_block& block)
{
switch (get_block_type(block))
{
case _Block1::block_type:
return _Block1::size(block);
case _Block2::block_type:
return _Block2::size(block);
default:
return element_block_func::size(block);
}
}
};
}} // namespace mdds::mtv
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
#endif

View File

@ -0,0 +1,348 @@
/* -*- 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_CUSTOM_FUNC3_HPP
#define INCLUDED_MDDS_MULTI_TYPE_VECTOR_DIR_CUSTOM_FUNC3_HPP
#include "types.hpp"
#include "trait.hpp"
namespace mdds { namespace mtv {
template<typename _Block1, typename _Block2, typename _Block3>
struct custom_block_func3
{
static base_element_block* create_new_block(element_t type, size_t init_size)
{
switch (type)
{
case _Block1::block_type:
return _Block1::create_block(init_size);
case _Block2::block_type:
return _Block2::create_block(init_size);
case _Block3::block_type:
return _Block3::create_block(init_size);
default:;
}
return element_block_func::create_new_block(type, init_size);
}
static base_element_block* clone_block(const base_element_block& block)
{
switch (get_block_type(block))
{
case _Block1::block_type:
return _Block1::clone_block(block);
case _Block2::block_type:
return _Block2::clone_block(block);
case _Block3::block_type:
return _Block3::clone_block(block);
default:;
}
return element_block_func::clone_block(block);
}
static void delete_block(const base_element_block* p)
{
if (!p)
return;
switch (get_block_type(*p))
{
case _Block1::block_type:
_Block1::delete_block(p);
break;
case _Block2::block_type:
_Block2::delete_block(p);
break;
case _Block3::block_type:
_Block3::delete_block(p);
break;
default:
element_block_func::delete_block(p);
}
}
static void resize_block(base_element_block& block, size_t new_size)
{
switch (get_block_type(block))
{
case _Block1::block_type:
_Block1::resize_block(block, new_size);
break;
case _Block2::block_type:
_Block2::resize_block(block, new_size);
break;
case _Block3::block_type:
_Block3::resize_block(block, new_size);
break;
default:
element_block_func::resize_block(block, new_size);
}
}
static void print_block(const base_element_block& block)
{
switch (get_block_type(block))
{
case _Block1::block_type:
_Block1::print_block(block);
break;
case _Block2::block_type:
_Block2::print_block(block);
break;
case _Block3::block_type:
_Block3::print_block(block);
break;
default:
element_block_func::print_block(block);
}
}
static void erase(base_element_block& block, size_t pos)
{
switch (get_block_type(block))
{
case _Block1::block_type:
_Block1::erase_block(block, pos);
break;
case _Block2::block_type:
_Block2::erase_block(block, pos);
break;
case _Block3::block_type:
_Block3::erase_block(block, pos);
break;
default:
element_block_func::erase(block, pos);
}
}
static void erase(base_element_block& block, size_t pos, size_t size)
{
switch (get_block_type(block))
{
case _Block1::block_type:
_Block1::erase_block(block, pos, size);
break;
case _Block2::block_type:
_Block2::erase_block(block, pos, size);
break;
case _Block3::block_type:
_Block3::erase_block(block, pos, size);
break;
default:
element_block_func_base::erase(block, pos, size);
}
}
static void append_values_from_block(base_element_block& dest, const base_element_block& src)
{
switch (get_block_type(dest))
{
case _Block1::block_type:
_Block1::append_values_from_block(dest, src);
break;
case _Block2::block_type:
_Block2::append_values_from_block(dest, src);
break;
case _Block3::block_type:
_Block3::append_values_from_block(dest, src);
break;
default:
element_block_func_base::append_values_from_block(dest, src);
}
}
static void append_values_from_block(
base_element_block& dest, const base_element_block& src, size_t begin_pos, size_t len)
{
switch (get_block_type(dest))
{
case _Block1::block_type:
_Block1::append_values_from_block(dest, src, begin_pos, len);
break;
case _Block2::block_type:
_Block2::append_values_from_block(dest, src, begin_pos, len);
break;
case _Block3::block_type:
_Block3::append_values_from_block(dest, src, begin_pos, len);
break;
default:
element_block_func_base::append_values_from_block(dest, src, begin_pos, len);
}
}
static void assign_values_from_block(
base_element_block& dest, const base_element_block& src, size_t begin_pos, size_t len)
{
switch (get_block_type(dest))
{
case _Block1::block_type:
_Block1::assign_values_from_block(dest, src, begin_pos, len);
break;
case _Block2::block_type:
_Block2::assign_values_from_block(dest, src, begin_pos, len);
break;
case _Block3::block_type:
_Block3::assign_values_from_block(dest, src, begin_pos, len);
break;
default:
element_block_func_base::assign_values_from_block(dest, src, begin_pos, len);
}
}
static void prepend_values_from_block(
base_element_block& dest, const base_element_block& src, size_t begin_pos, size_t len)
{
switch (get_block_type(dest))
{
case _Block1::block_type:
_Block1::prepend_values_from_block(dest, src, begin_pos, len);
break;
case _Block2::block_type:
_Block2::prepend_values_from_block(dest, src, begin_pos, len);
break;
case _Block3::block_type:
_Block3::prepend_values_from_block(dest, src, begin_pos, len);
break;
default:
element_block_func_base::prepend_values_from_block(dest, src, begin_pos, len);
}
}
static void swap_values(base_element_block& blk1, base_element_block& blk2, size_t pos1, size_t pos2, size_t len)
{
switch (get_block_type(blk1))
{
case _Block1::block_type:
_Block1::swap_values(blk1, blk2, pos1, pos2, len);
break;
case _Block2::block_type:
_Block2::swap_values(blk1, blk2, pos1, pos2, len);
break;
case _Block3::block_type:
_Block3::swap_values(blk1, blk2, pos1, pos2, len);
break;
default:
element_block_func_base::swap_values(blk1, blk2, pos1, pos2, len);
}
}
static bool equal_block(const base_element_block& left, const base_element_block& right)
{
if (get_block_type(left) == _Block1::block_type)
{
if (get_block_type(right) != _Block1::block_type)
return false;
return _Block1::get(left) == _Block1::get(right);
}
else if (mtv::get_block_type(right) == _Block1::block_type)
return false;
if (get_block_type(left) == _Block2::block_type)
{
if (get_block_type(right) != _Block2::block_type)
return false;
return _Block2::get(left) == _Block2::get(right);
}
else if (mtv::get_block_type(right) == _Block2::block_type)
return false;
if (get_block_type(left) == _Block3::block_type)
{
if (get_block_type(right) != _Block3::block_type)
return false;
return _Block3::get(left) == _Block3::get(right);
}
else if (mtv::get_block_type(right) == _Block3::block_type)
return false;
return element_block_func::equal_block(left, right);
}
static void overwrite_values(base_element_block& block, size_t pos, size_t len)
{
switch (get_block_type(block))
{
case _Block1::block_type:
_Block1::overwrite_values(block, pos, len);
break;
case _Block2::block_type:
_Block2::overwrite_values(block, pos, len);
break;
case _Block3::block_type:
_Block3::overwrite_values(block, pos, len);
break;
default:
element_block_func::overwrite_values(block, pos, len);
}
}
static void shrink_to_fit(base_element_block& block)
{
switch (get_block_type(block))
{
case _Block1::block_type:
_Block1::shrink_to_fit(block);
break;
case _Block2::block_type:
_Block2::shrink_to_fit(block);
break;
case _Block3::block_type:
_Block3::shrink_to_fit(block);
break;
default:
element_block_func::shrink_to_fit(block);
}
}
static size_t size(const base_element_block& block)
{
switch (get_block_type(block))
{
case _Block1::block_type:
return _Block1::size(block);
case _Block2::block_type:
return _Block2::size(block);
case _Block3::block_type:
return _Block3::size(block);
default:
return element_block_func::size(block);
}
}
};
}} // namespace mdds::mtv
#endif
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

View File

@ -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: */

View File

@ -0,0 +1,189 @@
/*************************************************************************
*
* 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
#define MDDS_MTV_DEFINE_ELEMENT_CALLBACKS(_type_, _type_id_, _empty_val_, _block_) \
\
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_val_; \
} \
\
inline void mdds_mtv_set_value(mdds::mtv::base_element_block& block, size_t pos, const _type_& val) \
{ \
_block_::set_value(block, pos, val); \
} \
\
inline void mdds_mtv_get_value(const mdds::mtv::base_element_block& block, size_t pos, _type_& val) \
{ \
_block_::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_::set_values(block, pos, it_begin, it_end); \
} \
\
inline void mdds_mtv_append_value(mdds::mtv::base_element_block& block, const _type_& val) \
{ \
_block_::append_value(block, val); \
} \
\
inline void mdds_mtv_prepend_value(mdds::mtv::base_element_block& block, const _type_& val) \
{ \
_block_::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_::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_::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_::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_::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_::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_::create_block_with_values(it_begin, it_end); \
}
#define MDDS_MTV_DEFINE_ELEMENT_CALLBACKS_PTR(_type_, _type_id_, _empty_val_, _block_) \
\
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_val_; \
} \
\
inline void mdds_mtv_set_value(mdds::mtv::base_element_block& block, size_t pos, _type_* val) \
{ \
_block_::set_value(block, pos, val); \
} \
\
inline void mdds_mtv_get_value(const mdds::mtv::base_element_block& block, size_t pos, _type_*& val) \
{ \
_block_::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_::set_values(block, pos, it_begin, it_end); \
} \
\
inline void mdds_mtv_append_value(mdds::mtv::base_element_block& block, _type_* val) \
{ \
_block_::append_value(block, val); \
} \
\
inline void mdds_mtv_prepend_value(mdds::mtv::base_element_block& block, _type_* val) \
{ \
_block_::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_::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_::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_::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_::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_::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_::create_block_with_values(it_begin, it_end); \
}
#endif

View File

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

View File

@ -0,0 +1,543 @@
# 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)/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__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@
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@
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@
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)
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."
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 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:

View File

@ -0,0 +1,620 @@
/* -*- 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(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: */

View File

@ -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 Trait>
class iterator_updater
{
protected:
using parent_type = typename Trait::parent;
using positions_type = typename Trait::positions_type;
using sizes_type = typename Trait::sizes_type;
using element_blocks_type = typename Trait::element_blocks_type;
using size_type = typename Trait::parent::size_type;
using node = mdds::detail::mtv::iterator_value_node<parent_type, size_type>;
using positions_iterator_type = typename Trait::positions_iterator_type;
using sizes_iterator_type = typename Trait::sizes_iterator_type;
using element_blocks_iterator_type = typename Trait::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 Trait>
class iterator_base : public iterator_updater<Trait>
{
using parent_type = typename Trait::parent;
using node_update_func = typename Trait::private_data_update;
using updater = iterator_updater<Trait>;
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 Trait, typename NonConstItrBase>
class const_iterator_base : public iterator_updater<Trait>
{
using parent_type = typename Trait::parent;
using node_update_func = typename Trait::private_data_update;
using updater = iterator_updater<Trait>;
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 Trait>
std::ostream& operator<<(std::ostream& os, const iterator_base<Trait>& it)
{
it._print_state(os);
return os;
}
template<typename Trait, typename NonConstItrBase>
std::ostream& operator<<(std::ostream& os, const const_iterator_base<Trait, NonConstItrBase>& it)
{
it._print_state(os);
return os;
}
}}}} // namespace mdds::mtv::soa::detail
#endif
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,753 @@
/* -*- 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_TRAIT_2_HPP
#define INCLUDED_MDDS_MULTI_TYPE_VECTOR_TRAIT_2_HPP
#include "types.hpp"
#include <vector>
namespace mdds { namespace mtv {
struct element_block_func_base
{
inline static base_element_block* create_new_block(element_t type, size_t init_size);
inline static base_element_block* clone_block(const base_element_block& block);
inline static void delete_block(const base_element_block* p);
inline static void resize_block(base_element_block& block, size_t new_size);
inline static void print_block(const base_element_block& block);
inline static void erase(base_element_block& block, size_t pos);
inline static void erase(base_element_block& block, size_t pos, size_t size);
inline static void append_values_from_block(base_element_block& dest, const base_element_block& src);
inline static void append_values_from_block(
base_element_block& dest, const base_element_block& src, size_t begin_pos, size_t len);
inline static void assign_values_from_block(
base_element_block& dest, const base_element_block& src, size_t begin_pos, size_t len);
inline static void prepend_values_from_block(
base_element_block& dest, const base_element_block& src, size_t begin_pos, size_t len);
inline static void swap_values(
base_element_block& blk1, base_element_block& blk2, size_t pos1, size_t pos2, size_t len);
inline static bool equal_block(const base_element_block& left, const base_element_block& right);
/**
* This method gets called when cell values are being overwritten by new
* values. This provides the client code an opportunity to delete
* overwritten instances in case the block stores pointers to managed
* objects. For blocks that don't need to manage their stored objects (or
* store primitive values), this method can be left empty.
*/
inline static void overwrite_values(base_element_block& block, size_t pos, size_t len);
inline static void shrink_to_fit(base_element_block& block);
inline static size_t size(const base_element_block& block);
};
base_element_block* element_block_func_base::create_new_block(element_t type, size_t init_size)
{
switch (type)
{
case element_type_float:
return float_element_block::create_block(init_size);
case element_type_double:
return double_element_block::create_block(init_size);
case element_type_string:
return string_element_block::create_block(init_size);
case element_type_int16:
return int16_element_block::create_block(init_size);
case element_type_uint16:
return uint16_element_block::create_block(init_size);
case element_type_int32:
return int32_element_block::create_block(init_size);
case element_type_uint32:
return uint32_element_block::create_block(init_size);
case element_type_int64:
return int64_element_block::create_block(init_size);
case element_type_uint64:
return uint64_element_block::create_block(init_size);
case element_type_boolean:
return boolean_element_block::create_block(init_size);
case element_type_int8:
return int8_element_block::create_block(init_size);
case element_type_uint8:
return uint8_element_block::create_block(init_size);
default:
throw general_error("create_new_block: failed to create a new block of unknown type.");
}
}
base_element_block* element_block_func_base::clone_block(const base_element_block& block)
{
switch (get_block_type(block))
{
case element_type_float:
return float_element_block::clone_block(block);
case element_type_double:
return double_element_block::clone_block(block);
case element_type_string:
return string_element_block::clone_block(block);
case element_type_int16:
return int16_element_block::clone_block(block);
case element_type_uint16:
return uint16_element_block::clone_block(block);
case element_type_int32:
return int32_element_block::clone_block(block);
case element_type_uint32:
return uint32_element_block::clone_block(block);
case element_type_int64:
return int64_element_block::clone_block(block);
case element_type_uint64:
return uint64_element_block::clone_block(block);
case element_type_boolean:
return boolean_element_block::clone_block(block);
case element_type_int8:
return int8_element_block::clone_block(block);
case element_type_uint8:
return uint8_element_block::clone_block(block);
default:
throw general_error("clone_block: failed to clone a block of unknown type.");
}
}
void element_block_func_base::delete_block(const base_element_block* p)
{
if (!p)
return;
switch (get_block_type(*p))
{
case element_type_float:
float_element_block::delete_block(p);
break;
case element_type_double:
double_element_block::delete_block(p);
break;
case element_type_string:
string_element_block::delete_block(p);
break;
case element_type_int16:
int16_element_block::delete_block(p);
break;
case element_type_uint16:
uint16_element_block::delete_block(p);
break;
case element_type_int32:
int32_element_block::delete_block(p);
break;
case element_type_uint32:
uint32_element_block::delete_block(p);
break;
case element_type_int64:
int64_element_block::delete_block(p);
break;
case element_type_uint64:
uint64_element_block::delete_block(p);
break;
case element_type_boolean:
boolean_element_block::delete_block(p);
break;
case element_type_int8:
int8_element_block::delete_block(p);
break;
case element_type_uint8:
uint8_element_block::delete_block(p);
break;
default:
{
#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
// We sould not throw an exception here as this gets called from a
// destructor and destructors should not throw exceptions.
std::ostringstream os;
os << __FILE__ << "#" << __LINE__ << " (element_block_func_base:delete_block): "
<< "failed to delete a block of unknown type (" << get_block_type(*p) << ")" << std::endl;
throw general_error(os.str());
#else
throw general_error("delete_block: failed to delete a block of unknown type.");
#endif
}
}
}
void element_block_func_base::resize_block(base_element_block& block, size_t new_size)
{
switch (get_block_type(block))
{
case element_type_float:
float_element_block::resize_block(block, new_size);
break;
case element_type_double:
double_element_block::resize_block(block, new_size);
break;
case element_type_string:
string_element_block::resize_block(block, new_size);
break;
case element_type_int16:
int16_element_block::resize_block(block, new_size);
break;
case element_type_uint16:
uint16_element_block::resize_block(block, new_size);
break;
case element_type_int32:
int32_element_block::resize_block(block, new_size);
break;
case element_type_uint32:
uint32_element_block::resize_block(block, new_size);
break;
case element_type_int64:
int64_element_block::resize_block(block, new_size);
break;
case element_type_uint64:
uint64_element_block::resize_block(block, new_size);
break;
case element_type_boolean:
boolean_element_block::resize_block(block, new_size);
break;
case element_type_int8:
int8_element_block::resize_block(block, new_size);
break;
case element_type_uint8:
uint8_element_block::resize_block(block, new_size);
break;
default:
throw general_error("resize_block: failed to resize a block of unknown type.");
}
}
void element_block_func_base::print_block(const base_element_block& block)
{
switch (get_block_type(block))
{
case element_type_float:
float_element_block::print_block(block);
break;
case element_type_double:
double_element_block::print_block(block);
break;
case element_type_string:
string_element_block::print_block(block);
break;
case element_type_int16:
int16_element_block::print_block(block);
break;
case element_type_uint16:
uint16_element_block::print_block(block);
break;
case element_type_int32:
int32_element_block::print_block(block);
break;
case element_type_uint32:
uint32_element_block::print_block(block);
break;
case element_type_int64:
int64_element_block::print_block(block);
break;
case element_type_uint64:
uint64_element_block::print_block(block);
break;
case element_type_boolean:
boolean_element_block::print_block(block);
break;
case element_type_int8:
int8_element_block::print_block(block);
break;
case element_type_uint8:
uint8_element_block::print_block(block);
break;
default:
throw general_error("print_block: failed to print a block of unknown type.");
}
}
void element_block_func_base::erase(base_element_block& block, size_t pos)
{
switch (get_block_type(block))
{
case element_type_float:
float_element_block::erase_block(block, pos);
break;
case element_type_double:
double_element_block::erase_block(block, pos);
break;
case element_type_string:
string_element_block::erase_block(block, pos);
break;
case element_type_int16:
int16_element_block::erase_block(block, pos);
break;
case element_type_uint16:
uint16_element_block::erase_block(block, pos);
break;
case element_type_int32:
int32_element_block::erase_block(block, pos);
break;
case element_type_uint32:
uint32_element_block::erase_block(block, pos);
break;
case element_type_int64:
int64_element_block::erase_block(block, pos);
break;
case element_type_uint64:
uint64_element_block::erase_block(block, pos);
break;
case element_type_boolean:
boolean_element_block::erase_block(block, pos);
break;
case element_type_int8:
int8_element_block::erase_block(block, pos);
break;
case element_type_uint8:
uint8_element_block::erase_block(block, pos);
break;
default:
throw general_error("erase: failed to erase an element from a block of unknown type.");
}
}
void element_block_func_base::erase(base_element_block& block, size_t pos, size_t size)
{
switch (get_block_type(block))
{
case element_type_float:
float_element_block::erase_block(block, pos, size);
break;
case element_type_double:
double_element_block::erase_block(block, pos, size);
break;
case element_type_string:
string_element_block::erase_block(block, pos, size);
break;
case element_type_int16:
int16_element_block::erase_block(block, pos, size);
break;
case element_type_uint16:
uint16_element_block::erase_block(block, pos, size);
break;
case element_type_int32:
int32_element_block::erase_block(block, pos, size);
break;
case element_type_uint32:
uint32_element_block::erase_block(block, pos, size);
break;
case element_type_int64:
int64_element_block::erase_block(block, pos, size);
break;
case element_type_uint64:
uint64_element_block::erase_block(block, pos, size);
break;
case element_type_boolean:
boolean_element_block::erase_block(block, pos, size);
break;
case element_type_int8:
int8_element_block::erase_block(block, pos, size);
break;
case element_type_uint8:
uint8_element_block::erase_block(block, pos, size);
break;
default:
throw general_error("erase: failed to erase elements from a block of unknown type.");
}
}
void element_block_func_base::append_values_from_block(base_element_block& dest, const base_element_block& src)
{
switch (get_block_type(dest))
{
case element_type_float:
float_element_block::append_values_from_block(dest, src);
break;
case element_type_double:
double_element_block::append_values_from_block(dest, src);
break;
case element_type_string:
string_element_block::append_values_from_block(dest, src);
break;
case element_type_int16:
int16_element_block::append_values_from_block(dest, src);
break;
case element_type_uint16:
uint16_element_block::append_values_from_block(dest, src);
break;
case element_type_int32:
int32_element_block::append_values_from_block(dest, src);
break;
case element_type_uint32:
uint32_element_block::append_values_from_block(dest, src);
break;
case element_type_int64:
int64_element_block::append_values_from_block(dest, src);
break;
case element_type_uint64:
uint64_element_block::append_values_from_block(dest, src);
break;
case element_type_boolean:
boolean_element_block::append_values_from_block(dest, src);
break;
case element_type_int8:
int8_element_block::append_values_from_block(dest, src);
break;
case element_type_uint8:
uint8_element_block::append_values_from_block(dest, src);
break;
default:
throw general_error("append_values: failed to append values to a block of unknown type.");
}
}
void element_block_func_base::append_values_from_block(
base_element_block& dest, const base_element_block& src, size_t begin_pos, size_t len)
{
switch (get_block_type(dest))
{
case element_type_float:
float_element_block::append_values_from_block(dest, src, begin_pos, len);
break;
case element_type_double:
double_element_block::append_values_from_block(dest, src, begin_pos, len);
break;
case element_type_string:
string_element_block::append_values_from_block(dest, src, begin_pos, len);
break;
case element_type_int16:
int16_element_block::append_values_from_block(dest, src, begin_pos, len);
break;
case element_type_uint16:
uint16_element_block::append_values_from_block(dest, src, begin_pos, len);
break;
case element_type_int32:
int32_element_block::append_values_from_block(dest, src, begin_pos, len);
break;
case element_type_uint32:
uint32_element_block::append_values_from_block(dest, src, begin_pos, len);
break;
case element_type_int64:
int64_element_block::append_values_from_block(dest, src, begin_pos, len);
break;
case element_type_uint64:
uint64_element_block::append_values_from_block(dest, src, begin_pos, len);
break;
case element_type_boolean:
boolean_element_block::append_values_from_block(dest, src, begin_pos, len);
break;
case element_type_int8:
int8_element_block::append_values_from_block(dest, src, begin_pos, len);
break;
case element_type_uint8:
uint8_element_block::append_values_from_block(dest, src, begin_pos, len);
break;
default:
throw general_error("append_values: failed to append values to a block of unknown type.");
}
}
void element_block_func_base::assign_values_from_block(
base_element_block& dest, const base_element_block& src, size_t begin_pos, size_t len)
{
switch (get_block_type(dest))
{
case element_type_float:
float_element_block::assign_values_from_block(dest, src, begin_pos, len);
break;
case element_type_double:
double_element_block::assign_values_from_block(dest, src, begin_pos, len);
break;
case element_type_string:
string_element_block::assign_values_from_block(dest, src, begin_pos, len);
break;
case element_type_int16:
int16_element_block::assign_values_from_block(dest, src, begin_pos, len);
break;
case element_type_uint16:
uint16_element_block::assign_values_from_block(dest, src, begin_pos, len);
break;
case element_type_int32:
int32_element_block::assign_values_from_block(dest, src, begin_pos, len);
break;
case element_type_uint32:
uint32_element_block::assign_values_from_block(dest, src, begin_pos, len);
break;
case element_type_int64:
int64_element_block::assign_values_from_block(dest, src, begin_pos, len);
break;
case element_type_uint64:
uint64_element_block::assign_values_from_block(dest, src, begin_pos, len);
break;
case element_type_boolean:
boolean_element_block::assign_values_from_block(dest, src, begin_pos, len);
break;
case element_type_int8:
int8_element_block::assign_values_from_block(dest, src, begin_pos, len);
break;
case element_type_uint8:
uint8_element_block::assign_values_from_block(dest, src, begin_pos, len);
break;
default:
throw general_error("assign_values_from_block: failed to assign values to a block of unknown type.");
}
}
void element_block_func_base::prepend_values_from_block(
base_element_block& dest, const base_element_block& src, size_t begin_pos, size_t len)
{
switch (get_block_type(dest))
{
case element_type_float:
float_element_block::prepend_values_from_block(dest, src, begin_pos, len);
break;
case element_type_double:
double_element_block::prepend_values_from_block(dest, src, begin_pos, len);
break;
case element_type_string:
string_element_block::prepend_values_from_block(dest, src, begin_pos, len);
break;
case element_type_int16:
int16_element_block::prepend_values_from_block(dest, src, begin_pos, len);
break;
case element_type_uint16:
uint16_element_block::prepend_values_from_block(dest, src, begin_pos, len);
break;
case element_type_int32:
int32_element_block::prepend_values_from_block(dest, src, begin_pos, len);
break;
case element_type_uint32:
uint32_element_block::prepend_values_from_block(dest, src, begin_pos, len);
break;
case element_type_int64:
int64_element_block::prepend_values_from_block(dest, src, begin_pos, len);
break;
case element_type_uint64:
uint64_element_block::prepend_values_from_block(dest, src, begin_pos, len);
break;
case element_type_boolean:
boolean_element_block::prepend_values_from_block(dest, src, begin_pos, len);
break;
case element_type_int8:
int8_element_block::prepend_values_from_block(dest, src, begin_pos, len);
break;
case element_type_uint8:
uint8_element_block::prepend_values_from_block(dest, src, begin_pos, len);
break;
default:
throw general_error("prepend_values_from_block: failed to prepend values to a block of unknown type.");
}
}
void element_block_func_base::swap_values(
base_element_block& blk1, base_element_block& blk2, size_t pos1, size_t pos2, size_t len)
{
element_t blk1_type = get_block_type(blk1);
assert(blk1_type == get_block_type(blk2));
switch (blk1_type)
{
case element_type_float:
float_element_block::swap_values(blk1, blk2, pos1, pos2, len);
break;
case element_type_double:
double_element_block::swap_values(blk1, blk2, pos1, pos2, len);
break;
case element_type_string:
string_element_block::swap_values(blk1, blk2, pos1, pos2, len);
break;
case element_type_int16:
int16_element_block::swap_values(blk1, blk2, pos1, pos2, len);
break;
case element_type_uint16:
uint16_element_block::swap_values(blk1, blk2, pos1, pos2, len);
break;
case element_type_int32:
int32_element_block::swap_values(blk1, blk2, pos1, pos2, len);
break;
case element_type_uint32:
uint32_element_block::swap_values(blk1, blk2, pos1, pos2, len);
break;
case element_type_int64:
int64_element_block::swap_values(blk1, blk2, pos1, pos2, len);
break;
case element_type_uint64:
uint64_element_block::swap_values(blk1, blk2, pos1, pos2, len);
break;
case element_type_boolean:
boolean_element_block::swap_values(blk1, blk2, pos1, pos2, len);
break;
case element_type_int8:
int8_element_block::swap_values(blk1, blk2, pos1, pos2, len);
break;
case element_type_uint8:
uint8_element_block::swap_values(blk1, blk2, pos1, pos2, len);
break;
default:
throw general_error("swap_values: block of unknown type.");
}
}
bool element_block_func_base::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;
switch (block_type)
{
case element_type_float:
return float_element_block::get(left) == float_element_block::get(right);
case element_type_double:
return double_element_block::get(left) == double_element_block::get(right);
case element_type_string:
return string_element_block::get(left) == string_element_block::get(right);
case element_type_int16:
return int16_element_block::get(left) == int16_element_block::get(right);
case element_type_uint16:
return uint16_element_block::get(left) == uint16_element_block::get(right);
case element_type_int32:
return int32_element_block::get(left) == int32_element_block::get(right);
case element_type_uint32:
return uint32_element_block::get(left) == uint32_element_block::get(right);
case element_type_int64:
return int64_element_block::get(left) == int64_element_block::get(right);
case element_type_uint64:
return uint64_element_block::get(left) == uint64_element_block::get(right);
case element_type_boolean:
return boolean_element_block::get(left) == boolean_element_block::get(right);
case element_type_int8:
return int8_element_block::get(left) == int8_element_block::get(right);
case element_type_uint8:
return uint8_element_block::get(left) == uint8_element_block::get(right);
default:;
}
return false;
}
void element_block_func_base::overwrite_values(base_element_block&, size_t, size_t)
{
// Do nothing for the standard types.
}
void element_block_func_base::shrink_to_fit(base_element_block& block)
{
switch (get_block_type(block))
{
case element_type_float:
float_element_block::shrink_to_fit(block);
break;
case element_type_double:
double_element_block::shrink_to_fit(block);
break;
case element_type_string:
string_element_block::shrink_to_fit(block);
break;
case element_type_int16:
int16_element_block::shrink_to_fit(block);
break;
case element_type_uint16:
uint16_element_block::shrink_to_fit(block);
break;
case element_type_int32:
int32_element_block::shrink_to_fit(block);
break;
case element_type_uint32:
uint32_element_block::shrink_to_fit(block);
break;
case element_type_int64:
int64_element_block::shrink_to_fit(block);
break;
case element_type_uint64:
uint64_element_block::shrink_to_fit(block);
break;
case element_type_boolean:
boolean_element_block::shrink_to_fit(block);
break;
case element_type_int8:
int8_element_block::shrink_to_fit(block);
break;
case element_type_uint8:
uint8_element_block::shrink_to_fit(block);
break;
default:
throw general_error("shrink_to_fit: failed to print a block of unknown type.");
}
}
size_t element_block_func_base::size(const base_element_block& block)
{
switch (get_block_type(block))
{
case element_type_float:
return float_element_block::size(block);
case element_type_double:
return double_element_block::size(block);
case element_type_string:
return string_element_block::size(block);
case element_type_int16:
return int16_element_block::size(block);
case element_type_uint16:
return uint16_element_block::size(block);
case element_type_int32:
return int32_element_block::size(block);
case element_type_uint32:
return uint32_element_block::size(block);
case element_type_int64:
return int64_element_block::size(block);
case element_type_uint64:
return uint64_element_block::size(block);
case element_type_boolean:
return boolean_element_block::size(block);
case element_type_int8:
return int8_element_block::size(block);
case element_type_uint8:
return uint8_element_block::size(block);
default:
throw general_error("size: failed to print a block of unknown type.");
}
}
/**
* Default cell block function definitions. Implementation can use this if
* it only uses the default block types implemented by the library.
*/
struct element_block_func : public element_block_func_base
{
};
}} // namespace mdds::mtv
#endif
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

View File

@ -0,0 +1,783 @@
/* -*- 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 <algorithm>
#include <cassert>
#include <memory>
#include <cstdint>
#ifdef MDDS_MULTI_TYPE_VECTOR_USE_DEQUE
#include <deque>
#else
#include <vector>
#endif
#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_boolean = 0;
constexpr element_t element_type_int8 = 1;
constexpr element_t element_type_uint8 = 2;
constexpr element_t element_type_int16 = 3;
constexpr element_t element_type_uint16 = 4;
constexpr element_t element_type_int32 = 5;
constexpr element_t element_type_uint32 = 6;
constexpr element_t element_type_int64 = 7;
constexpr element_t element_type_uint64 = 8;
constexpr element_t element_type_float = 9;
constexpr element_t element_type_double = 10;
constexpr element_t element_type_string = 11;
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)
{}
};
template<typename _Self, element_t _TypeId, typename _Data>
class element_block : public base_element_block
{
#ifdef MDDS_UNIT_TEST
struct print_block_array
{
void operator()(const _Data& val) const
{
std::cout << val << " ";
}
};
#endif
protected:
#ifdef MDDS_MULTI_TYPE_VECTOR_USE_DEQUE
typedef std::deque<_Data> store_type;
#else
typedef std::vector<_Data> store_type;
#endif
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 _Data& 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:
static const element_t block_type = _TypeId;
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 _Data value_type;
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 _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 _Data& val)
{
get(blk).m_array[pos] = val;
}
static void get_value(const base_element_block& blk, size_t pos, _Data& 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 _Data& val)
{
get(blk).m_array.push_back(val);
}
static void prepend_value(base_element_block& blk, const _Data& 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 < (st.capacity() / 2))
st.shrink_to_fit();
}
#ifdef MDDS_UNIT_TEST
static void print_block(const base_element_block& blk)
{
const store_type& blk2 = get(blk).m_array;
std::for_each(blk2.begin(), blk2.end(), print_block_array());
std::cout << std::endl;
}
#else
static void print_block(const base_element_block&)
{}
#endif
static void erase_block(base_element_block& blk, size_t pos)
{
store_type& blk2 = get(blk).m_array;
blk2.erase(blk2.begin() + pos);
}
static void erase_block(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_values_from_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);
#ifndef MDDS_MULTI_TYPE_VECTOR_USE_DEQUE
d.reserve(d.size() + len);
#endif
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);
#ifndef MDDS_MULTI_TYPE_VECTOR_USE_DEQUE
d.reserve(d.size() + len);
#endif
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)
{
#ifdef MDDS_MULTI_TYPE_VECTOR_USE_DEQUE
std::swap(*it1, *it2);
#else
value_type v1 = *it1, v2 = *it2;
*it1 = v2;
*it2 = v1;
#endif
}
}
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)
{
#ifdef MDDS_MULTI_TYPE_VECTOR_USE_DEQUE
return 0;
#else
const store_type& blk = get(block).m_array;
return blk.capacity();
#endif
}
static void shrink_to_fit(base_element_block& block)
{
#ifndef MDDS_MULTI_TYPE_VECTOR_USE_DEQUE
get(block).m_array.shrink_to_fit();
#endif
}
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 _Data>
class copyable_element_block : public element_block<_Self, _TypeId, _Data>
{
typedef element_block<_Self, _TypeId, _Data> base_type;
protected:
copyable_element_block() : base_type()
{}
copyable_element_block(size_t n) : base_type(n)
{}
copyable_element_block(size_t n, const _Data& 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 _Data>
class noncopyable_element_block : public element_block<_Self, _TypeId, _Data>
{
typedef element_block<_Self, _TypeId, _Data> base_type;
protected:
noncopyable_element_block() : base_type()
{}
noncopyable_element_block(size_t n) : base_type(n)
{}
noncopyable_element_block(size_t n, const _Data& 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 _Data>
struct default_element_block : public copyable_element_block<default_element_block<_TypeId, _Data>, _TypeId, _Data>
{
typedef copyable_element_block<default_element_block, _TypeId, _Data> base_type;
typedef default_element_block<_TypeId, _Data> self_type;
default_element_block() : base_type()
{}
default_element_block(size_t n) : base_type(n)
{}
default_element_block(size_t n, const _Data& 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 _Data& 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 _Data>
struct managed_element_block : public copyable_element_block<managed_element_block<_TypeId, _Data>, _TypeId, _Data*>
{
typedef copyable_element_block<managed_element_block<_TypeId, _Data>, _TypeId, _Data*> base_type;
typedef managed_element_block<_TypeId, _Data> self_type;
using base_type::get;
using base_type::m_array;
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)
{
#ifndef MDDS_MULTI_TYPE_VECTOR_USE_DEQUE
m_array.reserve(r.m_array.size());
#endif
typename managed_element_block::store_type::const_iterator it = r.m_array.begin(), it_end = r.m_array.end();
for (; it != it_end; ++it)
m_array.push_back(new _Data(**it));
}
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<_Data>());
}
static self_type* create_block_with_value(size_t init_size, _Data* 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<_Data>());
}
};
template<element_t _TypeId, typename _Data>
struct noncopyable_managed_element_block
: public noncopyable_element_block<noncopyable_managed_element_block<_TypeId, _Data>, _TypeId, _Data*>
{
typedef noncopyable_element_block<noncopyable_managed_element_block<_TypeId, _Data>, _TypeId, _Data*> base_type;
typedef managed_element_block<_TypeId, _Data> self_type;
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<_Data>());
}
static self_type* create_block_with_value(size_t init_size, _Data* 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<_Data>());
}
};
using boolean_element_block = default_element_block<mtv::element_type_boolean, bool>;
using int8_element_block = default_element_block<mtv::element_type_int8, int8_t>;
using uint8_element_block = default_element_block<mtv::element_type_uint8, uint8_t>;
using int16_element_block = default_element_block<mtv::element_type_int16, int16_t>;
using uint16_element_block = default_element_block<mtv::element_type_uint16, uint16_t>;
using int32_element_block = default_element_block<mtv::element_type_int32, int32_t>;
using uint32_element_block = default_element_block<mtv::element_type_uint32, uint32_t>;
using int64_element_block = default_element_block<mtv::element_type_int64, int64_t>;
using uint64_element_block = default_element_block<mtv::element_type_uint64, uint64_t>;
using float_element_block = default_element_block<mtv::element_type_float, float>;
using double_element_block = default_element_block<mtv::element_type_double, double>;
using string_element_block = default_element_block<mtv::element_type_string, std::string>;
}} // namespace mdds::mtv
#endif
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

View File

@ -0,0 +1,277 @@
/* -*- 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 "./types.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_trait
{
/**
* 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;
};
} // namespace mtv
namespace detail { namespace mtv {
#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 Trait>
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)
Trait::trace(props);
}
void operator()(const ::mdds::mtv::trace_method_properties_t& props) const
{
call(has_trace<Trait>{}, 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;
}
template<typename _Blk>
inline typename _Blk::value_type get_block_element_at(const mdds::mtv::base_element_block& data, size_t offset)
{
return _Blk::at(data, offset);
}
#ifndef MDDS_MULTI_TYPE_VECTOR_USE_DEQUE
template<>
inline bool get_block_element_at<mdds::mtv::boolean_element_block>(
const mdds::mtv::base_element_block& data, size_t offset)
{
auto it = mdds::mtv::boolean_element_block::cbegin(data);
std::advance(it, offset);
return *it;
}
#endif
}} // namespace detail::mtv
} // namespace mdds
#ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
#define MDDS_MTV_TRACE(method_type) \
::mdds::detail::mtv::call_trace<Trait> 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::detail::mtv::call_trace<Trait> 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: */

View File

@ -0,0 +1,33 @@
/*************************************************************************
*
* Copyright (c) 2013-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_CUSTOM_FUNC1_HPP
#define INCLUDED_MDDS_MULTI_TYPE_VECTOR_CUSTOM_FUNC1_HPP
#include "./multi_type_vector/custom_func1.hpp"
#endif

View File

@ -0,0 +1,33 @@
/*************************************************************************
*
* Copyright (c) 2013-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_MULTI_TYPE_VECTOR_CUSTOM_FUNC2_HPP
#define INCLUDED_MDDS_MULTI_TYPE_VECTOR_CUSTOM_FUNC2_HPP
#include "./multi_type_vector/custom_func2.hpp"
#endif

View File

@ -0,0 +1,33 @@
/*************************************************************************
*
* Copyright (c) 2013-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_MULTI_TYPE_VECTOR_CUSTOM_FUNC3_HPP
#define INCLUDED_MULTI_TYPE_VECTOR_CUSTOM_FUNC3_HPP
#include "./multi_type_vector/custom_func3.hpp"
#endif

View File

@ -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: */

View File

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

View File

@ -0,0 +1,33 @@
/*************************************************************************
*
* Copyright (c) 2012-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_MULTI_TYPE_VECTOR_TRAIT_HPP
#define INCLUDED_MDDS_MULTI_TYPE_VECTOR_TRAIT_HPP
#include "./multi_type_vector/trait.hpp"
#endif

View File

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

490
include/mdds/node.hpp Normal file
View File

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

File diff suppressed because it is too large Load Diff

375
include/mdds/quad_node.hpp Normal file
View File

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

80
include/mdds/ref_pair.hpp Normal file
View File

@ -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: */

830
include/mdds/rtree.hpp Normal file
View File

@ -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_trait
{
/**
* 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 _Key, typename _Value, typename _Trait = detail::rtree::default_rtree_trait>
class rtree
{
using trait_type = _Trait;
constexpr static size_t max_dist_size = trait_type::max_node_size - trait_type::min_node_size * 2 + 2;
public:
using key_type = _Key;
using value_type = _Value;
struct point_type
{
key_type d[trait_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 = trait_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 _Func>
void walk(_Func 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 _Func>
void descend_with_func(_Func 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: */

2624
include/mdds/rtree_def.inl Normal file

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@ -0,0 +1,100 @@
/*************************************************************************
*
* Copyright (c) 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_SORTED_STRING_MAP_HPP
#define MDDS_SORTED_STRING_MAP_HPP
#include <cstdlib>
namespace mdds {
/**
* 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>
class sorted_string_map
{
public:
typedef _ValueT value_type;
typedef size_t size_type;
/**
* Single key-value entry. Caller must provide at compile time a static
* array of these entries.
*/
struct entry
{
const char* key;
size_type keylen;
value_type value;
};
/**
* 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;
/**
* 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"
#endif

View File

@ -0,0 +1,92 @@
/*************************************************************************
*
* Copyright (c) 2014-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 <cstring>
#include <algorithm>
#include <cassert>
namespace mdds {
namespace detail {
// don't use it!
// Implementation detail!
template<typename _ValueT>
bool compare(
const typename sorted_string_map<_ValueT>::entry& entry1, const typename sorted_string_map<_ValueT>::entry& entry2)
{
if (entry1.keylen != entry2.keylen)
{
typename sorted_string_map<_ValueT>::size_type keylen = std::min(entry1.keylen, entry2.keylen);
int ret = std::memcmp(entry1.key, entry2.key, keylen);
if (ret == 0)
return entry1.keylen < entry2.keylen;
return ret < 0;
}
else
{
return std::memcmp(entry1.key, entry2.key, entry1.keylen) < 0;
}
}
} // namespace detail
template<typename _ValueT>
sorted_string_map<_ValueT>::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)
{
#if defined(_GLIBCXX_DEBUG) || defined(MDDS_ASSERT_STRING_MAP)
assert(std::is_sorted(m_entries, m_entry_end, detail::compare<_ValueT>));
#endif
}
template<typename _ValueT>
typename sorted_string_map<_ValueT>::value_type sorted_string_map<_ValueT>::find(const char* input, size_type len) const
{
if (m_entry_size == 0)
return m_null_value;
entry ent;
ent.key = input;
ent.keylen = len;
const entry* val = std::lower_bound(m_entries, m_entry_end, ent, detail::compare<_ValueT>);
if (val == m_entry_end || val->keylen != len || std::memcmp(val->key, input, len))
return m_null_value;
return val->value;
}
template<typename _ValueT>
typename sorted_string_map<_ValueT>::size_type sorted_string_map<_ValueT>::size() const
{
return m_entry_size;
}
} // namespace mdds

712
include/mdds/trie_map.hpp Normal file
View File

@ -0,0 +1,712 @@
/* -*- 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_trait
{
/** 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_trait = std_container_trait<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<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 _KeyTrait, 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 _KeyTrait, typename _ValueT>
class trie_map
{
friend class packed_trie_map<_KeyTrait, _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<_KeyTrait, _ValueT> packed_type;
typedef _KeyTrait key_trait_type;
typedef typename key_trait_type::key_type key_type;
typedef typename key_trait_type::key_buffer_type key_buffer_type;
typedef typename key_trait_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 = bool_constant<_IsConst>;
using child_pos_type = typename get_iterator_type<typename trie_node::children_type, _is_const>::type;
using trie_node_type = typename 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, 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 _KeyTrait, 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 _KeyTrait key_trait_type;
typedef typename key_trait_type::key_type key_type;
typedef typename key_trait_type::key_buffer_type key_buffer_type;
typedef typename key_trait_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_trait_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 _Func = 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 _Func = 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<_KeyTrait, _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<_KeyTrait, _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: */

File diff suppressed because it is too large Load Diff

View File

@ -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 = 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 = const_t<typename trie_type::trie_node, _IsConst>;
using trie_node_child_pos_type =
typename get_iterator_type<typename std::remove_const<trie_node_type>::type::children_type, _is_const>::type;
using key_trait_type = typename trie_type::key_trait_type;
using key_type = typename key_trait_type::key_type;
using key_buffer_type = typename key_trait_type::key_buffer_type;
using trie_value_type = typename 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_trait_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_trait_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_trait_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_trait_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_trait_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_trait_type = typename trie_type::key_trait_type;
using key_type = typename key_trait_type::key_type;
using key_buffer_type = typename key_trait_type::key_buffer_type;
using key_unit_type = typename key_trait_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_trait_type = typename trie_type::key_trait_type;
using key_type = typename key_trait_type::key_type;
using key_buffer_type = typename key_trait_type::key_buffer_type;
using key_unit_type = typename key_trait_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_trait_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_trait_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_trait_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_trait_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_trait_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_trait_type = typename trie_type::key_trait_type;
using key_type = typename key_trait_type::key_type;
using key_buffer_type = typename key_trait_type::key_buffer_type;
using key_unit_type = typename key_trait_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

541
install-sh Executable file
View File

@ -0,0 +1,541 @@
#!/bin/sh
# install - install a program, script, or datafile
scriptversion=2020-11-14.01; # UTC
# This originates from X11R5 (mit/util/scripts/install.sh), which was
# later released in X11R6 (xc/config/util/install.sh) with the
# following copyright and license.
#
# Copyright (C) 1994 X Consortium
#
# 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
# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
# Except as contained in this notice, the name of the X Consortium shall not
# be used in advertising or otherwise to promote the sale, use or other deal-
# ings in this Software without prior written authorization from the X Consor-
# tium.
#
#
# FSF changes to this file are in the public domain.
#
# Calling this script install-sh is preferred over install.sh, to prevent
# 'make' implicit rules from creating a file called install from it
# when there is no Makefile.
#
# This script is compatible with the BSD install script, but was written
# from scratch.
tab=' '
nl='
'
IFS=" $tab$nl"
# Set DOITPROG to "echo" to test this script.
doit=${DOITPROG-}
doit_exec=${doit:-exec}
# Put in absolute file names if you don't have them in your path;
# or use environment vars.
chgrpprog=${CHGRPPROG-chgrp}
chmodprog=${CHMODPROG-chmod}
chownprog=${CHOWNPROG-chown}
cmpprog=${CMPPROG-cmp}
cpprog=${CPPROG-cp}
mkdirprog=${MKDIRPROG-mkdir}
mvprog=${MVPROG-mv}
rmprog=${RMPROG-rm}
stripprog=${STRIPPROG-strip}
posix_mkdir=
# Desired mode of installed file.
mode=0755
# Create dirs (including intermediate dirs) using mode 755.
# This is like GNU 'install' as of coreutils 8.32 (2020).
mkdir_umask=22
backupsuffix=
chgrpcmd=
chmodcmd=$chmodprog
chowncmd=
mvcmd=$mvprog
rmcmd="$rmprog -f"
stripcmd=
src=
dst=
dir_arg=
dst_arg=
copy_on_change=false
is_target_a_directory=possibly
usage="\
Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
or: $0 [OPTION]... SRCFILES... DIRECTORY
or: $0 [OPTION]... -t DIRECTORY SRCFILES...
or: $0 [OPTION]... -d DIRECTORIES...
In the 1st form, copy SRCFILE to DSTFILE.
In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
In the 4th, create DIRECTORIES.
Options:
--help display this help and exit.
--version display version info and exit.
-c (ignored)
-C install only if different (preserve data modification time)
-d create directories instead of installing files.
-g GROUP $chgrpprog installed files to GROUP.
-m MODE $chmodprog installed files to MODE.
-o USER $chownprog installed files to USER.
-p pass -p to $cpprog.
-s $stripprog installed files.
-S SUFFIX attempt to back up existing files, with suffix SUFFIX.
-t DIRECTORY install into DIRECTORY.
-T report an error if DSTFILE is a directory.
Environment variables override the default commands:
CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
RMPROG STRIPPROG
By default, rm is invoked with -f; when overridden with RMPROG,
it's up to you to specify -f if you want it.
If -S is not specified, no backups are attempted.
Email bug reports to bug-automake@gnu.org.
Automake home page: https://www.gnu.org/software/automake/
"
while test $# -ne 0; do
case $1 in
-c) ;;
-C) copy_on_change=true;;
-d) dir_arg=true;;
-g) chgrpcmd="$chgrpprog $2"
shift;;
--help) echo "$usage"; exit $?;;
-m) mode=$2
case $mode in
*' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*)
echo "$0: invalid mode: $mode" >&2
exit 1;;
esac
shift;;
-o) chowncmd="$chownprog $2"
shift;;
-p) cpprog="$cpprog -p";;
-s) stripcmd=$stripprog;;
-S) backupsuffix="$2"
shift;;
-t)
is_target_a_directory=always
dst_arg=$2
# Protect names problematic for 'test' and other utilities.
case $dst_arg in
-* | [=\(\)!]) dst_arg=./$dst_arg;;
esac
shift;;
-T) is_target_a_directory=never;;
--version) echo "$0 $scriptversion"; exit $?;;
--) shift
break;;
-*) echo "$0: invalid option: $1" >&2
exit 1;;
*) break;;
esac
shift
done
# We allow the use of options -d and -T together, by making -d
# take the precedence; this is for compatibility with GNU install.
if test -n "$dir_arg"; then
if test -n "$dst_arg"; then
echo "$0: target directory not allowed when installing a directory." >&2
exit 1
fi
fi
if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
# When -d is used, all remaining arguments are directories to create.
# When -t is used, the destination is already specified.
# Otherwise, the last argument is the destination. Remove it from $@.
for arg
do
if test -n "$dst_arg"; then
# $@ is not empty: it contains at least $arg.
set fnord "$@" "$dst_arg"
shift # fnord
fi
shift # arg
dst_arg=$arg
# Protect names problematic for 'test' and other utilities.
case $dst_arg in
-* | [=\(\)!]) dst_arg=./$dst_arg;;
esac
done
fi
if test $# -eq 0; then
if test -z "$dir_arg"; then
echo "$0: no input file specified." >&2
exit 1
fi
# It's OK to call 'install-sh -d' without argument.
# This can happen when creating conditional directories.
exit 0
fi
if test -z "$dir_arg"; then
if test $# -gt 1 || test "$is_target_a_directory" = always; then
if test ! -d "$dst_arg"; then
echo "$0: $dst_arg: Is not a directory." >&2
exit 1
fi
fi
fi
if test -z "$dir_arg"; then
do_exit='(exit $ret); exit $ret'
trap "ret=129; $do_exit" 1
trap "ret=130; $do_exit" 2
trap "ret=141; $do_exit" 13
trap "ret=143; $do_exit" 15
# Set umask so as not to create temps with too-generous modes.
# However, 'strip' requires both read and write access to temps.
case $mode in
# Optimize common cases.
*644) cp_umask=133;;
*755) cp_umask=22;;
*[0-7])
if test -z "$stripcmd"; then
u_plus_rw=
else
u_plus_rw='% 200'
fi
cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
*)
if test -z "$stripcmd"; then
u_plus_rw=
else
u_plus_rw=,u+rw
fi
cp_umask=$mode$u_plus_rw;;
esac
fi
for src
do
# Protect names problematic for 'test' and other utilities.
case $src in
-* | [=\(\)!]) src=./$src;;
esac
if test -n "$dir_arg"; then
dst=$src
dstdir=$dst
test -d "$dstdir"
dstdir_status=$?
# Don't chown directories that already exist.
if test $dstdir_status = 0; then
chowncmd=""
fi
else
# Waiting for this to be detected by the "$cpprog $src $dsttmp" command
# might cause directories to be created, which would be especially bad
# if $src (and thus $dsttmp) contains '*'.
if test ! -f "$src" && test ! -d "$src"; then
echo "$0: $src does not exist." >&2
exit 1
fi
if test -z "$dst_arg"; then
echo "$0: no destination specified." >&2
exit 1
fi
dst=$dst_arg
# If destination is a directory, append the input filename.
if test -d "$dst"; then
if test "$is_target_a_directory" = never; then
echo "$0: $dst_arg: Is a directory" >&2
exit 1
fi
dstdir=$dst
dstbase=`basename "$src"`
case $dst in
*/) dst=$dst$dstbase;;
*) dst=$dst/$dstbase;;
esac
dstdir_status=0
else
dstdir=`dirname "$dst"`
test -d "$dstdir"
dstdir_status=$?
fi
fi
case $dstdir in
*/) dstdirslash=$dstdir;;
*) dstdirslash=$dstdir/;;
esac
obsolete_mkdir_used=false
if test $dstdir_status != 0; then
case $posix_mkdir in
'')
# With -d, create the new directory with the user-specified mode.
# Otherwise, rely on $mkdir_umask.
if test -n "$dir_arg"; then
mkdir_mode=-m$mode
else
mkdir_mode=
fi
posix_mkdir=false
# The $RANDOM variable is not portable (e.g., dash). Use it
# here however when possible just to lower collision chance.
tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
trap '
ret=$?
rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null
exit $ret
' 0
# Because "mkdir -p" follows existing symlinks and we likely work
# directly in world-writeable /tmp, make sure that the '$tmpdir'
# directory is successfully created first before we actually test
# 'mkdir -p'.
if (umask $mkdir_umask &&
$mkdirprog $mkdir_mode "$tmpdir" &&
exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1
then
if test -z "$dir_arg" || {
# Check for POSIX incompatibilities with -m.
# HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
# other-writable bit of parent directory when it shouldn't.
# FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
test_tmpdir="$tmpdir/a"
ls_ld_tmpdir=`ls -ld "$test_tmpdir"`
case $ls_ld_tmpdir in
d????-?r-*) different_mode=700;;
d????-?--*) different_mode=755;;
*) false;;
esac &&
$mkdirprog -m$different_mode -p -- "$test_tmpdir" && {
ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"`
test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
}
}
then posix_mkdir=:
fi
rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir"
else
# Remove any dirs left behind by ancient mkdir implementations.
rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null
fi
trap '' 0;;
esac
if
$posix_mkdir && (
umask $mkdir_umask &&
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
)
then :
else
# mkdir does not conform to POSIX,
# or it failed possibly due to a race condition. Create the
# directory the slow way, step by step, checking for races as we go.
case $dstdir in
/*) prefix='/';;
[-=\(\)!]*) prefix='./';;
*) prefix='';;
esac
oIFS=$IFS
IFS=/
set -f
set fnord $dstdir
shift
set +f
IFS=$oIFS
prefixes=
for d
do
test X"$d" = X && continue
prefix=$prefix$d
if test -d "$prefix"; then
prefixes=
else
if $posix_mkdir; then
(umask $mkdir_umask &&
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
# Don't fail if two instances are running concurrently.
test -d "$prefix" || exit 1
else
case $prefix in
*\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
*) qprefix=$prefix;;
esac
prefixes="$prefixes '$qprefix'"
fi
fi
prefix=$prefix/
done
if test -n "$prefixes"; then
# Don't fail if two instances are running concurrently.
(umask $mkdir_umask &&
eval "\$doit_exec \$mkdirprog $prefixes") ||
test -d "$dstdir" || exit 1
obsolete_mkdir_used=true
fi
fi
fi
if test -n "$dir_arg"; then
{ test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
{ test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
else
# Make a couple of temp file names in the proper directory.
dsttmp=${dstdirslash}_inst.$$_
rmtmp=${dstdirslash}_rm.$$_
# Trap to clean up those temp files at exit.
trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
# Copy the file name to the temp name.
(umask $cp_umask &&
{ test -z "$stripcmd" || {
# Create $dsttmp read-write so that cp doesn't create it read-only,
# which would cause strip to fail.
if test -z "$doit"; then
: >"$dsttmp" # No need to fork-exec 'touch'.
else
$doit touch "$dsttmp"
fi
}
} &&
$doit_exec $cpprog "$src" "$dsttmp") &&
# and set any options; do chmod last to preserve setuid bits.
#
# If any of these fail, we abort the whole thing. If we want to
# ignore errors from any of these, just make sure not to ignore
# errors from the above "$doit $cpprog $src $dsttmp" command.
#
{ test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
{ test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
{ test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
# If -C, don't bother to copy if it wouldn't change the file.
if $copy_on_change &&
old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
set -f &&
set X $old && old=:$2:$4:$5:$6 &&
set X $new && new=:$2:$4:$5:$6 &&
set +f &&
test "$old" = "$new" &&
$cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
then
rm -f "$dsttmp"
else
# If $backupsuffix is set, and the file being installed
# already exists, attempt a backup. Don't worry if it fails,
# e.g., if mv doesn't support -f.
if test -n "$backupsuffix" && test -f "$dst"; then
$doit $mvcmd -f "$dst" "$dst$backupsuffix" 2>/dev/null
fi
# Rename the file to the real destination.
$doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
# The rename failed, perhaps because mv can't rename something else
# to itself, or perhaps because mv is so ancient that it does not
# support -f.
{
# Now remove or move aside any old file at destination location.
# We try this two ways since rm can't unlink itself on some
# systems and the destination file might be busy for other
# reasons. In this case, the final cleanup might fail but the new
# file should still install successfully.
{
test ! -f "$dst" ||
$doit $rmcmd "$dst" 2>/dev/null ||
{ $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
{ $doit $rmcmd "$rmtmp" 2>/dev/null; :; }
} ||
{ echo "$0: cannot unlink or rename $dst" >&2
(exit 1); exit 1
}
} &&
# Now rename the file to the real destination.
$doit $mvcmd "$dsttmp" "$dst"
}
fi || exit 1
trap '' 0
fi
done
# Local variables:
# eval: (add-hook 'before-save-hook 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC0"
# time-stamp-end: "; # UTC"
# End:

Some files were not shown because too many files have changed in this diff Show More