forked from openkylin/mdds
Import Upstream version 2.0.3
This commit is contained in:
commit
58c1ba07ea
|
@ -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>
|
|
@ -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.
|
|
@ -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.
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
|
@ -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.
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -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:
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||
==============================================================================
|
||||
])
|
|
@ -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 |
|
@ -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')
|
File diff suppressed because it is too large
Load Diff
|
@ -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:
|
|
@ -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`
|
||||
|
|
@ -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
|
|
@ -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:
|
|
@ -0,0 +1,10 @@
|
|||
.. highlight:: cpp
|
||||
|
||||
Point Quad Tree
|
||||
===============
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. doxygenclass:: mdds::point_quad_tree
|
||||
:members:
|
|
@ -0,0 +1,11 @@
|
|||
.. highlight:: cpp
|
||||
|
||||
Segment Tree
|
||||
============
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. doxygenclass:: mdds::segment_tree
|
||||
:members:
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
.. highlight:: cpp
|
||||
|
||||
Sorted String Map
|
||||
=================
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. doxygenclass:: mdds::sorted_string_map
|
||||
:members:
|
||||
|
|
@ -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:
|
|
@ -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)
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -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: */
|
|
@ -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: */
|
|
@ -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: */
|
|
@ -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: */
|
|
@ -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: */
|
|
@ -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: */
|
||||
|
|
@ -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: */
|
|
@ -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: */
|
|
@ -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: */
|
|
@ -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: */
|
|
@ -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: */
|
||||
|
|
@ -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: */
|
|
@ -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: */
|
|
@ -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: */
|
|
@ -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: */
|
|
@ -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: */
|
|
@ -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: */
|
|
@ -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: */
|
|
@ -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: */
|
|
@ -0,0 +1,2 @@
|
|||
SUBDIRS = mdds
|
||||
|
|
@ -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:
|
|
@ -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
|
|
@ -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:
|
|
@ -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: */
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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:
|
|
@ -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
|
|
@ -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:
|
|
@ -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: */
|
|
@ -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
|
@ -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
|
|
@ -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
|
|
@ -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: */
|
|
@ -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
|
|
@ -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: */
|
|
@ -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: */
|
|
@ -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
|
|
@ -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
|
|
@ -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:
|
|
@ -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: */
|
|
@ -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
|
@ -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: */
|
|
@ -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: */
|
|
@ -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: */
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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: */
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
@ -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
|
|
@ -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: */
|
|
@ -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: */
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
@ -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
|
|
@ -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
Loading…
Reference in New Issue