CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
937 lines
42 KiB
C++
937 lines
42 KiB
C++
// WARNING: Changes to this file must be contributed back to Sawyer or else they will
|
|
// be clobbered by the next update from Sawyer. The Sawyer repository is at
|
|
// https://github.com/matzke1/sawyer.
|
|
|
|
|
|
|
|
|
|
#ifndef Sawyer_IntervalMap_H
|
|
#define Sawyer_IntervalMap_H
|
|
|
|
#include <stddef.h>
|
|
#include <dyncompat/cstdint.hpp>
|
|
#include "Assert.h"
|
|
#include "Map.h"
|
|
#include "Optional.h"
|
|
#include "Sawyer.h"
|
|
|
|
namespace Sawyer {
|
|
namespace Container {
|
|
|
|
/** Traits for IntervalMap. */
|
|
template<class IntervalMap>
|
|
struct IntervalMapTraits {
|
|
typedef typename IntervalMap::NodeIterator NodeIterator; /**< Node iterator. */
|
|
typedef typename IntervalMap::ValueIterator ValueIterator; /**< Value iterator. */
|
|
typedef typename IntervalMap::Value &ValueReference; /**< Reference to value. */
|
|
};
|
|
|
|
template<class IntervalMap>
|
|
struct IntervalMapTraits<const IntervalMap> {
|
|
typedef typename IntervalMap::ConstNodeIterator NodeIterator;
|
|
typedef typename IntervalMap::ConstValueIterator ValueIterator;
|
|
typedef typename IntervalMap::Value const &ValueReference;
|
|
};
|
|
|
|
/** Policy indicating how values are merged and split.
|
|
*
|
|
* Adjacent nodes of an IntervalMap can be joined together provided their values can also be joined together. This joining is
|
|
* a key feature of an IntervalMap since it can significantly reduce the number nodes required in the underlying map. */
|
|
template<typename I, typename T>
|
|
class MergePolicy {
|
|
public:
|
|
typedef I Interval;
|
|
typedef T Value;
|
|
|
|
/** Merge two values if possible.
|
|
*
|
|
* The @p rightValue is merged into the @p leftValue if possible, or this method returns false without changing either
|
|
* value. After a successful merge, the @p rightValue will be removed from the IntervalMap and its destructor called. */
|
|
bool merge(const Interval &/*leftInterval*/, Value &leftValue, const Interval &/*rightInterval*/, Value &rightValue) {
|
|
return leftValue == rightValue;
|
|
}
|
|
|
|
/** Split one value into two values.
|
|
*
|
|
* The IntervalMap calls this method when the @p interval is being split into two smaller, adjacent intervals. The @p
|
|
* splitPoint argument is the split point and becomes the least value of the right interval. The @p value argument is
|
|
* modified in place to become the left value, and the right value is returned. This method is only invoked when the
|
|
* result would be two non-empty intervals. */
|
|
Value split(const Interval &/*interval*/, Value &value, const typename Interval::Value &/*splitPoint*/) { return value; }
|
|
|
|
/** Discard the right part of a value.
|
|
*
|
|
* This method is the same as @ref split except the right part of the resulting value is discarded. This is sometimes
|
|
* more efficient than calling @ref split and then destroying the return value. */
|
|
void truncate(const Interval &/*interval*/, Value &/*value*/, const typename Interval::Value &/*splitPoint*/) {}
|
|
};
|
|
|
|
/** An associative container whose keys are non-overlapping intervals.
|
|
*
|
|
* This container is somewhat like an STL <code>std::map</code> in that it stores key/value pairs. However, it is optimized
|
|
* for the case when many consecutive keys are the same or related. The values may be any type; the keys are any interval
|
|
* type that follows the API and semantics of Sawyer::Container::Interval, namely a closed interval with members
|
|
* <code>least</code> and <code>greatest</code> demarcating the inclusive end points, and a few other methods.
|
|
*
|
|
* The interval/value pair nodes that are stored in this container are managed by the container, automatically joining
|
|
* adjacent nodes when they are inserted, if possible and permitted, and automatically spliting nodes if necessary when
|
|
* something is erased. For the most part, the user can think of this container as associating scalar keys with values, and
|
|
* almost forget that the container uses intervals as an optimization.
|
|
*
|
|
* When two neighboring interval/value nodes are inserted, the container will possibly join them into a single interval/value
|
|
* node. Normally, the merging of two nodes happens if the two values are equal, but this can be influenced by a policy class
|
|
* provided as an argument of the container's constructor. See @ref MergePolicy for details. Similarly, when part of an
|
|
* interval is erased, the container might need to split the affected node into two nodes, which is also handled by the
|
|
* policy.
|
|
*
|
|
* The following examples demonstrates some aspects of the interface:
|
|
*
|
|
* @code
|
|
* typedef Sawyer::Container::Interval<unsigned> Interval; // integral types work best
|
|
* class Stats {...} stats1=..., stats2=...; // needs at least a copy c'tor, assignment, and equality predicate.
|
|
* typedef IntervalMap<Interval, Stats> Map;
|
|
* Map map;
|
|
* map.insert(Interval(1,5), stats1);
|
|
* map.insert(6, stats2);
|
|
* @endcode
|
|
*
|
|
* If the policy allows the two "stats" objects to be merged (the default policy allows them to merge only if they are equal),
|
|
* then the container will end up having one node, the pair ([1,6], merge(stats1,stats2)), otherwise it will have two nodes.
|
|
*
|
|
* @code
|
|
* map.erase(Interval(2,3));
|
|
* @endcode
|
|
*
|
|
* Erasing keys 2 and 3 causes the affected node to split into two discontiguous nodes and a new copy of the node's
|
|
* value. Assuming we started with the two nodes { ([1,5], stats1), (6, stats2) }, then after erasing [2,3] the container will
|
|
* hold three nodes: { (1, stats1), ([4,5], stats1), (6, stats2) }.
|
|
*
|
|
* Iteration over the container returns references to the nodes as @ref Node object that has <code>key</code> and
|
|
* <code>value</code> methods to access the interval key and user-defined value parts of each storage node. For example,
|
|
* here's one way to print the contents of the container, assuming the interval itself doesn't already have a printing
|
|
* function:
|
|
*
|
|
* @code
|
|
* std::cout <<"{";
|
|
* for (Map::ConstNodeIterator iter=map.nodes().begin(); iter!=map.nodes().end(); ++iter) {
|
|
* const Interval &interval = iter->key();
|
|
* const Stats &stats = iter->value();
|
|
* std::cout <<" (";
|
|
* if (interval.isSingleton())
|
|
* std::cout <<interval.least() <<", ";
|
|
* } else {
|
|
* std::cout <<"[" <<interval.least() <<"," <<interval.greatest() <<"], ";
|
|
* }
|
|
* std::cout <<stats <<")";
|
|
* }
|
|
* std::cout <<" }";
|
|
* @endcode
|
|
*
|
|
* Here's another way:
|
|
*
|
|
* @code
|
|
* DYN_FOREACH (const Map::Node &node, map.nodes()) {
|
|
* const Interval &interval = node.key();
|
|
* const Stats &stats = node.value();
|
|
* ...
|
|
* }
|
|
* @endcode
|
|
*
|
|
* Besides <code>nodes()</code>, there's also <code>values()</code> and <code>intervals()</code> that return bidirectional
|
|
* iterators over the user-defined values or the intervals when dereferenced.
|
|
*
|
|
* This class uses CamelCase for all its methods and inner types in conformance with the naming convention for the rest of the
|
|
* library. This includes iterator names (we don't use <code>iterator</code>, <code>const_iterator</code>, etc).
|
|
*
|
|
* @sa
|
|
*
|
|
* See @ref IntervalSetMap for a similar container that stores sets of values per interval. */
|
|
template<typename I, typename T, class Policy = MergePolicy<I, T> >
|
|
class IntervalMap {
|
|
public:
|
|
typedef I Interval; /**< Interval type. */
|
|
typedef T Value; /**< Value type. */
|
|
|
|
private:
|
|
// Nodes of the underlying map are sorted by their last value so that we can use that map's lowerBound method to find the
|
|
// range to which a scalar key might belong. Since the intervals in the map are non-overlapping, sorting by greatest values
|
|
// has the same effect as sorting by least values.
|
|
struct IntervalCompare {
|
|
bool operator()(const Interval &a, const Interval &b) const {
|
|
return a.greatest() < b.greatest();
|
|
}
|
|
};
|
|
|
|
typedef std::pair<Interval, Interval> IntervalPair;
|
|
|
|
public:
|
|
/** Type of the underlying map. */
|
|
typedef Container::Map<Interval, Value, IntervalCompare> Map;
|
|
|
|
/** Storage node.
|
|
*
|
|
* An interval/value pair with methods <code>key</code> and <code>value</code> for accessing the interval key and its
|
|
* associated user-defined value. */
|
|
typedef typename Map::Node Node;
|
|
|
|
/** Interval iterator.
|
|
*
|
|
* This iterator visits the intervals of the container. Dereferencing the iterator returns a reference to a const
|
|
* interval. */
|
|
typedef typename Map::ConstKeyIterator ConstIntervalIterator;
|
|
|
|
/** Value iterator.
|
|
*
|
|
* This iterator visits the values of the container. Dereferencing the iterator returns a reference (const or mutable,
|
|
* depending on the iterator) to a user-defined value.
|
|
*
|
|
* @{ */
|
|
typedef typename Map::ValueIterator ValueIterator;
|
|
typedef typename Map::ConstValueIterator ConstValueIterator;
|
|
/** @} */
|
|
|
|
/** Node iterator.
|
|
*
|
|
* This iterator visits the nodes of the container. Dereferencing the iterator returns a @ref Node reference (const or
|
|
* mutable depending on the iterator), from which the interval key and user-define value can be obtained.
|
|
*
|
|
* @{ */
|
|
typedef typename Map::NodeIterator NodeIterator;
|
|
typedef typename Map::ConstNodeIterator ConstNodeIterator;
|
|
/** @} */
|
|
|
|
private:
|
|
Map map_;
|
|
Policy policy_;
|
|
typename Interval::Value size_; // number of values (map_.size is number of intervals)
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Constructors
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
public:
|
|
/** Default constructor.
|
|
*
|
|
* Creates an empty container. */
|
|
IntervalMap(): size_(0) {}
|
|
|
|
/** Copy constructor.
|
|
*
|
|
* Initialize this container by copying all nodes from the @p other container. This constructor has <em>O(n)</em>
|
|
* complexity, where <em>n</em> is the number of nodes in the container. */
|
|
template<class Interval2, class T2, class Policy2>
|
|
IntervalMap(const IntervalMap<Interval2, T2, Policy2> &other) {
|
|
typedef typename IntervalMap<Interval2, T2, Policy2>::ConstNodeIterator OtherIterator;
|
|
for (OtherIterator otherIter=other.nodes().begin(); other!=other.nodes().end(); ++other)
|
|
insert(Interval(otherIter->key()), Value(otherIter->value()));
|
|
}
|
|
|
|
/** Assignment operator.
|
|
*
|
|
* Makes this container look like the @p other container by clearing this container and then copying all nodes from the
|
|
* other container. */
|
|
template<class Interval2, class T2, class Policy2>
|
|
IntervalMap& operator=(const IntervalMap<Interval2, T2, Policy2> &other) {
|
|
clear();
|
|
typedef typename IntervalMap<Interval2, T2, Policy2>::ConstNodeIterator OtherIterator;
|
|
for (OtherIterator otherIter=other.nodes().begin(); other!=other.nodes().end(); ++other)
|
|
insert(Interval(otherIter->key()), Value(otherIter->value()));
|
|
return *this;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Searching
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
public:
|
|
|
|
/** Iterators for traversing nodes.
|
|
*
|
|
* Returns a range of iterators that traverse storage nodes (key/value pairs) for all nodes of this container. The nodes
|
|
* are traversed in key order.
|
|
*
|
|
* @{ */
|
|
dyncompat::iterator_range<NodeIterator> nodes() { return map_.nodes(); }
|
|
dyncompat::iterator_range<ConstNodeIterator> nodes() const { return map_.nodes(); }
|
|
/** @} */
|
|
|
|
/** Iterators for traversing keys.
|
|
*
|
|
* Returns a range of iteratores that traverse all keys (non-overlapping intervals) of this container according to the
|
|
* order of the intervals. */
|
|
dyncompat::iterator_range<ConstIntervalIterator> intervals() const { return map_.keys(); }
|
|
|
|
/** Iterators for traversing values.
|
|
*
|
|
* Returns a range of iterators that traverse the values (user-defined type) of this container. The values are traversed
|
|
* in the order of their associated keys.
|
|
*
|
|
* @{ */
|
|
dyncompat::iterator_range<ValueIterator> values() { return map_.values(); }
|
|
dyncompat::iterator_range<ConstValueIterator> values() const { return map_.values(); }
|
|
/** @} */
|
|
|
|
/** Find the first node whose interval ends at or above the specified scalar key.
|
|
*
|
|
* Returns an iterator to the node, or the end iterator if no such node exists.
|
|
*
|
|
* @{ */
|
|
NodeIterator lowerBound(const typename Interval::Value &scalar) {
|
|
return map_.lowerBound(Interval(scalar));
|
|
}
|
|
ConstNodeIterator lowerBound(const typename Interval::Value &scalar) const {
|
|
return map_.lowerBound(Interval(scalar));
|
|
}
|
|
/** @} */
|
|
|
|
/** Find the first node whose interval begins above the specified scalar key.
|
|
*
|
|
* Returns an iterator to the node, or the end iterator if no such node exists.
|
|
*
|
|
* @{ */
|
|
NodeIterator upperBound(const typename Interval::Value &scalar) {
|
|
NodeIterator ub = map_.upperBound(Interval(scalar)); // first node that ENDS after scalar
|
|
while (ub!=map_.nodes().end() && ub->key().least() <= scalar)
|
|
++ub;
|
|
return ub;
|
|
}
|
|
ConstNodeIterator upperBound(const typename Interval::Value &scalar) const {
|
|
ConstNodeIterator ub = map_.upperBound(Interval(scalar)); // first node that ENDS after scalar
|
|
while (ub!=map_.nodes().end() && ub->key().least() <= scalar)
|
|
++ub;
|
|
return ub;
|
|
}
|
|
/** @} */
|
|
|
|
/** Find the last node whose interval starts at or below the specified scalar key.
|
|
*
|
|
* Returns an iterator to the node, or the end iterator if no such node exists.
|
|
*
|
|
* @{ */
|
|
NodeIterator findPrior(const typename Interval::Value &scalar) {
|
|
return findPriorImpl(*this, scalar);
|
|
}
|
|
ConstNodeIterator findPrior(const typename Interval::Value &scalar) const {
|
|
return findPriorImpl(*this, scalar);
|
|
}
|
|
|
|
template<class IMap>
|
|
static typename IntervalMapTraits<IMap>::NodeIterator
|
|
findPriorImpl(IMap &imap, const typename Interval::Value &scalar) {
|
|
typedef typename IntervalMapTraits<IMap>::NodeIterator Iter;
|
|
if (imap.isEmpty())
|
|
return imap.nodes().end();
|
|
Iter lb = imap.lowerBound(scalar);
|
|
if (lb!=imap.nodes().end() && lb->key().least() <= scalar)
|
|
return lb;
|
|
if (lb==imap.nodes().begin())
|
|
return imap.nodes().end();
|
|
return --lb;
|
|
}
|
|
/** @} */
|
|
|
|
/** Find the node containing the specified scalar key.
|
|
*
|
|
* Returns an iterator to the matching node, or the end iterator if no such node exists.
|
|
*
|
|
* @{ */
|
|
NodeIterator find(const typename Interval::Value &scalar) {
|
|
return findImpl(*this, scalar);
|
|
}
|
|
ConstNodeIterator find(const typename Interval::Value &scalar) const {
|
|
return findImpl(*this, scalar);
|
|
}
|
|
|
|
template<class IMap>
|
|
static typename IntervalMapTraits<IMap>::NodeIterator
|
|
findImpl(IMap &imap, const typename Interval::Value &scalar) {
|
|
typedef typename IntervalMapTraits<IMap>::NodeIterator Iter;
|
|
Iter found = imap.lowerBound(scalar);
|
|
if (found==imap.nodes().end() || scalar < found->key().least())
|
|
return imap.nodes().end();
|
|
return found;
|
|
}
|
|
/** @} */
|
|
|
|
/** Finds all nodes overlapping the specified interval.
|
|
*
|
|
* Returns an iterator range that enumerates the nodes that overlap with the specified interval.
|
|
*
|
|
* @{ */
|
|
dyncompat::iterator_range<NodeIterator> findAll(const Interval &interval) {
|
|
return findAllImpl(*this, interval);
|
|
}
|
|
dyncompat::iterator_range<ConstNodeIterator> findAll(const Interval &interval) const {
|
|
return findAllImpl(*this, interval);
|
|
}
|
|
|
|
template<class IMap>
|
|
static dyncompat::iterator_range<typename IntervalMapTraits<IMap>::NodeIterator>
|
|
findAllImpl(IMap &imap, const Interval &interval) {
|
|
typedef typename IntervalMapTraits<IMap>::NodeIterator Iter;
|
|
if (interval.isEmpty())
|
|
return dyncompat::iterator_range<Iter>(imap.nodes().end(), imap.nodes().end());
|
|
Iter begin = imap.lowerBound(interval.least());
|
|
if (begin==imap.nodes().end() || begin->key().least() > interval.greatest())
|
|
return dyncompat::iterator_range<Iter>(imap.nodes().end(), imap.nodes().end());
|
|
return dyncompat::iterator_range<Iter>(begin, imap.upperBound(interval.greatest()));
|
|
}
|
|
/** @} */
|
|
|
|
/** Find first interval that overlaps with the specified interval.
|
|
*
|
|
* Returns an iterator to the matching node, or the end iterator if no such node exists.
|
|
*
|
|
* @{ */
|
|
NodeIterator findFirstOverlap(const Interval &interval) {
|
|
return findFirstOverlapImpl(*this, interval);
|
|
}
|
|
ConstNodeIterator findFirstOverlap(const Interval &interval) const {
|
|
return findFirstOverlapImpl(*this, interval);
|
|
}
|
|
|
|
template<class IMap>
|
|
static typename IntervalMapTraits<IMap>::NodeIterator
|
|
findFirstOverlapImpl(IMap &imap, const Interval &interval) {
|
|
typedef typename IntervalMapTraits<IMap>::NodeIterator Iter;
|
|
if (interval.isEmpty())
|
|
return imap.nodes().end();
|
|
Iter lb = imap.lowerBound(interval.least());
|
|
return lb!=imap.nodes().end() && interval.isOverlapping(lb->key()) ? lb : imap.nodes().end();
|
|
}
|
|
/** @} */
|
|
|
|
/** Find first interval that overlaps with any in another container.
|
|
*
|
|
* The @p other container must use the same interval type, but may have different values and merge policies. The search
|
|
* begins at the specified iterators and returns a pair of iterators pointing to the two nodes that overlap. The first
|
|
* member of the pair is an iterator to this container, and the second is an iterator for the @p other container. If no
|
|
* such nodes exist at or after the starting locations, then the return value will be a pair of end iterators for
|
|
* their respective containers.
|
|
*
|
|
* @{ */
|
|
template<typename T2, class Policy2>
|
|
std::pair<NodeIterator, typename IntervalMap<Interval, T2, Policy2>::ConstNodeIterator>
|
|
findFirstOverlap(typename IntervalMap::NodeIterator thisIter, const IntervalMap<Interval, T2, Policy2> &other,
|
|
typename IntervalMap<Interval, T2, Policy2>::ConstNodeIterator otherIter) {
|
|
return findFirstOverlapImpl(*this, thisIter, other, otherIter);
|
|
}
|
|
template<typename T2, class Policy2>
|
|
std::pair<ConstNodeIterator, typename IntervalMap<Interval, T2, Policy2>::ConstNodeIterator>
|
|
findFirstOverlap(typename IntervalMap::ConstNodeIterator thisIter, const IntervalMap<Interval, T2, Policy2> &other,
|
|
typename IntervalMap<Interval, T2, Policy2>::ConstNodeIterator otherIter) const {
|
|
return findFirstOverlapImpl(*this, thisIter, other, otherIter);
|
|
}
|
|
|
|
template<class IMap, typename T2, class Policy2>
|
|
static std::pair<typename IntervalMapTraits<IMap>::NodeIterator,
|
|
typename IntervalMap<Interval, T2, Policy2>::ConstNodeIterator>
|
|
findFirstOverlapImpl(IMap &imap,
|
|
typename IntervalMapTraits<IMap>::NodeIterator thisIter,
|
|
const IntervalMap<Interval, T2, Policy2> &other,
|
|
typename IntervalMap<Interval, T2, Policy2>::ConstNodeIterator otherIter) {
|
|
while (thisIter!=imap.nodes().end() && otherIter!=other.nodes().end()) {
|
|
if (thisIter->key().isOverlapping(otherIter->key()))
|
|
return std::make_pair(thisIter, otherIter);
|
|
if (thisIter->key().greatest() < otherIter->key().greatest()) {
|
|
++thisIter;
|
|
} else {
|
|
++otherIter;
|
|
}
|
|
}
|
|
return std::make_pair(imap.nodes().end(), other.nodes().end());
|
|
}
|
|
/** @} */
|
|
|
|
/** Find the first fit node at or after a starting point.
|
|
*
|
|
* Finds the first node of contiguous values beginning at or after the specified starting iterator, @p start, and which is
|
|
* at least as large as the desired @p size. If there are no such nodes then the end iterator is returned.
|
|
*
|
|
* Caveat emptor: The @p size argument has the name type as the interval end points. If the end points have a signed type,
|
|
* then it is entirely likely that the size will overflow. In fact, it is also possible that unsigned sizes overflow
|
|
* since, for example, an 8-bit unsigned size cannot hold the size of an interval representing the entire 8-bit space.
|
|
* Therefore, use this method with care.
|
|
*
|
|
* @{ */
|
|
NodeIterator firstFit(const typename Interval::Value &size, NodeIterator start) {
|
|
return firstFitImpl(*this, size, start);
|
|
}
|
|
ConstNodeIterator firstFit(const typename Interval::Value &size, ConstNodeIterator start) const {
|
|
return firstFitImpl(*this, size, start);
|
|
}
|
|
|
|
template<class IMap>
|
|
static typename IntervalMapTraits<IMap>::NodeIterator
|
|
firstFitImpl(IMap &imap, const typename Interval::Value &size, typename IntervalMapTraits<IMap>::NodeIterator start) {
|
|
typedef typename IntervalMapTraits<IMap>::NodeIterator Iter;
|
|
for (Iter iter=start; iter!=imap.nodes().end(); ++iter) {
|
|
if (isLarge(iter->key(), size))
|
|
return iter;
|
|
}
|
|
return imap.nodes().end();
|
|
}
|
|
|
|
/** @} */
|
|
|
|
/** Find the best fit node at or after a starting point.
|
|
*
|
|
* Finds a node of contiguous values beginning at or after the specified starting iterator, @p start, and which is at
|
|
* least as large as the desired @p size. If there is more than one such node, then the first smallest such node is
|
|
* returned. If there are no such nodes then the end iterator is returned.
|
|
*
|
|
* Caveat emptor: The @p size argument has the name type as the interval end points. If the end points have a signed type,
|
|
* then it is entirely likely that the size will overflow. In fact, it is also possible that unsigned sizes overflow
|
|
* since, for example, an 8-bit unsigned size cannot hold the size of an interval representing the entire 8-bit space.
|
|
* Therefore, use this method with care.
|
|
*
|
|
* @{ */
|
|
NodeIterator bestFit(const typename Interval::Value &size, NodeIterator start) {
|
|
return bestFitImpl(*this, size, start);
|
|
}
|
|
ConstNodeIterator bestFit(const typename Interval::Value &size, ConstNodeIterator start) const {
|
|
return bestFitImpl(*this, size, start);
|
|
}
|
|
|
|
template<class IMap>
|
|
static typename IntervalMapTraits<IMap>::NodeIterator
|
|
bestFitImpl(IMap &imap, const typename Interval::Value &size, typename IntervalMapTraits<IMap>::NodeIterator start) {
|
|
typedef typename IntervalMapTraits<IMap>::NodeIterator Iter;
|
|
Iter best = imap.nodes().end();
|
|
for (Iter iter=start; iter!=imap.nodes().end(); ++iter) {
|
|
if (iter->key().size()==size && size!=0)
|
|
return iter;
|
|
if (iter->key().size() > size && (best==imap.nodes().end() || iter->key().size() < best->key().size()))
|
|
best = iter;
|
|
}
|
|
return best;
|
|
}
|
|
/** @} */
|
|
|
|
/** Find the first unmapped region.
|
|
*
|
|
* Returns an interval that describes the lowest unmapped interval that begins at or after the specified starting address
|
|
* and ends immediately prior to the mapped address that follows the unmapped interval, or the greatest possible address
|
|
* if there are no following mapped addresses. If @p minAddr is after the last unmapped address then an empty interval is
|
|
* returned. The returned interval will not include addresses less than @p minAddr. */
|
|
Interval firstUnmapped(typename Interval::Value minAddr) const {
|
|
Interval all = Interval::whole();
|
|
for (ConstNodeIterator iter=lowerBound(minAddr); iter!=nodes().end(); ++iter) {
|
|
if (minAddr < iter->key().least()) // minAddr is not mapped
|
|
return Interval::hull(minAddr, iter->key().least()-1);
|
|
if (iter->key().greatest() == all.greatest())
|
|
return Interval(); // no unmapped addresses, prevent potential overflow in next statement
|
|
minAddr = iter->key().greatest() + 1;
|
|
}
|
|
return Interval::hull(minAddr, all.greatest());
|
|
}
|
|
|
|
/** Find the last unmapped region.
|
|
*
|
|
* Returns an interval that describes the lowest unmapped interval that ends at or before the specified maximum address
|
|
* and starts immediately after the next lower mapped address, or the least possible address is no lower mapped address is
|
|
* present. If @p maxAddr is before the first unmapped address then an empty interval is returned. The returned interval
|
|
* will not include addresses greater than @p maxAddr. */
|
|
Interval lastUnmapped(typename Interval::Value maxAddr) const {
|
|
Interval all = Interval::whole();
|
|
for (ConstNodeIterator iter=findPrior(maxAddr); iter!=nodes().begin(); --iter) {
|
|
if (maxAddr > iter->key().greatest()) // maxAddr is not mapped
|
|
return Interval::hull(iter->key().greatest()+1, maxAddr);
|
|
if (iter->key().least() == all.least())
|
|
return Interval(); // no unmapped address, prevent potential overflow in next statement
|
|
maxAddr = iter->key().least() - 1;
|
|
}
|
|
return Interval::hull(all.least(), maxAddr);
|
|
}
|
|
|
|
/** Returns true if element exists.
|
|
*
|
|
* Returns true if and only if the specified key exists in the map. */
|
|
bool exists(const typename Interval::Value &size) const {
|
|
return find(size)!=nodes().end();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Accessors
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
public:
|
|
/** Returns a reference to an existing value.
|
|
*
|
|
* Returns a reference to the value at the node with the specified @p scalar. Unlike <code>std::map</code>, this
|
|
* container does not instantiate a new value if the @p scalar key is not in the map's domain. In other words, the
|
|
* array operator for this class is more like an array operator on arrays or vectors--such objects are not automatically
|
|
* extended if dereferenced with an operand that is outside the domain.
|
|
*
|
|
* If the @p scalar is not part of this map's domain then an <code>std:domain_error</code> is thrown.
|
|
*
|
|
* @{ */
|
|
const Value& operator[](const typename Interval::Value &scalar) const {
|
|
ConstNodeIterator found = find(scalar);
|
|
if (found==nodes().end())
|
|
throw std::domain_error("key lookup failure; key is not in map domain");
|
|
return found->value();
|
|
}
|
|
|
|
const Value& get(const typename Interval::Value &scalar) const {
|
|
ConstNodeIterator found = find(scalar);
|
|
if (found==nodes().end())
|
|
throw std::domain_error("key lookup failure; key is not in map domain");
|
|
return found->value();
|
|
}
|
|
/** @} */
|
|
|
|
/** Lookup and return a value or nothing.
|
|
*
|
|
* Looks up the node with the specified @p scalar key and returns either a copy of its value, or nothing. This method
|
|
* executes in logorithmic time.
|
|
*
|
|
* Here's an example of one convenient way to use this:
|
|
*
|
|
* @code
|
|
* IntervalMap<AddressInterval, FileInfo> files;
|
|
* ...
|
|
* if (Optional<FileInfo> fileInfo = files.getOptional(address))
|
|
* std::cout <<"file info for " <<address <<" is " <<*fileInfo <<"\n";
|
|
* @endcode */
|
|
Optional<Value> getOptional(const typename Interval::Value &scalar) const {
|
|
ConstNodeIterator found = find(scalar);
|
|
return found == nodes().end() ? Optional<Value>() : Optional<Value>(found->value());
|
|
}
|
|
|
|
/** Lookup and return a value or something else.
|
|
*
|
|
* This is similar to the @ref getOptional method, except a default can be provided. If a node with the specified @p
|
|
* scalar key is present in this container, then a reference to that node's value is returned, otherwise the (reference
|
|
* to) supplied default is returned.
|
|
*
|
|
* @{ */
|
|
Value& getOrElse(const typename Interval::Value &scalar, Value &dflt) {
|
|
NodeIterator found = find(scalar);
|
|
return found == nodes().end() ? dflt : found->value();
|
|
}
|
|
const Value& getOrElse(const typename Interval::Value &scalar, const Value &dflt) const {
|
|
ConstNodeIterator found = find(scalar);
|
|
return found == nodes().end() ? dflt : found->value();
|
|
}
|
|
/** @} */
|
|
|
|
/** Lookup and return a value or a default.
|
|
*
|
|
* This is similar to the @ref getOrElse method except when the @p scalar key is not present in the map, a reference to a
|
|
* const, default-constructed value is returned. */
|
|
const Value& getOrDefault(const typename Interval::Value &scalar) const {
|
|
static const Value dflt;
|
|
ConstNodeIterator found = find(scalar);
|
|
return found==nodes().end() ? dflt : found->value();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Capacity
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
public:
|
|
|
|
/** Determine if the container is empty.
|
|
*
|
|
* Returns true if this container has no nodes. */
|
|
bool isEmpty() const {
|
|
return map_.isEmpty();
|
|
}
|
|
|
|
/** Number of nodes in the container.
|
|
*
|
|
* Each node is a pair consisting of an interval and a value. The container normally merges two juxtaposed intervals if
|
|
* their values can be combined. */
|
|
size_t nIntervals() const {
|
|
return map_.size();
|
|
}
|
|
|
|
/** Returns the number of values represented by this container.
|
|
*
|
|
* The number of values in a container is the sum of the widths of all the nodes. This can be calculated in constant
|
|
* time. */
|
|
typename Interval::Value size() const {
|
|
return size_;
|
|
}
|
|
|
|
/** Returns the minimum scalar key. */
|
|
typename Interval::Value least() const {
|
|
ASSERT_forbid(isEmpty());
|
|
return map_.keys().begin()->least();
|
|
}
|
|
|
|
/** Returns the maximum scalar key. */
|
|
typename Interval::Value greatest() const {
|
|
ASSERT_forbid(isEmpty());
|
|
ConstIntervalIterator last = map_.keys().end(); --last;
|
|
return last->greatest();
|
|
}
|
|
|
|
/** Returns the limited-minimum scalar key.
|
|
*
|
|
* Returns the minimum scalar key that exists in the map and which is greater than or equal to @p lowerLimit. If no such
|
|
* value exists then nothing is returned. */
|
|
Optional<typename Interval::Value> least(typename Interval::Value lowerLimit) const {
|
|
ConstNodeIterator found = lowerBound(lowerLimit); // first node ending at or after lowerLimit
|
|
if (found==nodes().end())
|
|
return Nothing();
|
|
return std::max(lowerLimit, found->key().least());
|
|
}
|
|
|
|
/** Returns the limited-maximum scalar key.
|
|
*
|
|
* Returns the maximum scalar key that exists in the map and which is less than or equal to @p upperLimit. If no such
|
|
* value exists then nothing is returned. */
|
|
Optional<typename Interval::Value> greatest(typename Interval::Value upperLimit) const {
|
|
ConstNodeIterator found = findPrior(upperLimit); // last node beginning at or before upperLimit
|
|
if (found==nodes().end())
|
|
return Nothing();
|
|
return std::min(upperLimit, found->key().greatest());
|
|
}
|
|
|
|
/** Returns the limited-minimum unmapped scalar key.
|
|
*
|
|
* Returns the lowest unmapped scalar key equal to or greater than the @p lowerLimit. If no such value exists then
|
|
* nothing is returned. */
|
|
Optional<typename Interval::Value> leastUnmapped(typename Interval::Value lowerLimit) const {
|
|
for (ConstNodeIterator iter = lowerBound(lowerLimit); iter!=nodes().end(); ++iter) {
|
|
if (lowerLimit < iter->key().least())
|
|
return lowerLimit;
|
|
lowerLimit = iter->key().greatest() + 1;
|
|
if (lowerLimit <= iter->key().least()) // sensitive to GCC optimization
|
|
return Nothing(); // overflow
|
|
}
|
|
return lowerLimit;
|
|
}
|
|
|
|
/** Returns the limited-maximum unmapped scalar key.
|
|
*
|
|
* Returns the maximum unmapped scalar key equal to or less than the @p upperLimit. If no such value exists then nothing
|
|
* is returned. */
|
|
Optional<typename Interval::Value> greatestUnmapped(typename Interval::Value upperLimit) const {
|
|
for (ConstNodeIterator iter = findPrior(upperLimit); iter!=nodes().end(); --iter) {
|
|
if (upperLimit > iter->key().greatest())
|
|
return upperLimit;
|
|
upperLimit = iter->key().least() - 1;
|
|
if (upperLimit >= iter->key().greatest()) // sensitive to GCC optimization
|
|
return Nothing(); // overflow
|
|
if (iter==nodes().begin())
|
|
break;
|
|
}
|
|
return upperLimit;
|
|
}
|
|
|
|
/** Returns the range of values in this map. */
|
|
Interval hull() const {
|
|
return isEmpty() ? Interval() : Interval::hull(least(), greatest());
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Mutators
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
public:
|
|
|
|
/** Empties the container. */
|
|
void clear() {
|
|
map_.clear();
|
|
size_ = 0;
|
|
}
|
|
|
|
/** Erase the specified interval. */
|
|
void erase(const Interval &erasure) {
|
|
if (erasure.isEmpty())
|
|
return;
|
|
|
|
// Find what needs to be removed, and create a list of things to insert, but delay actual removing until after
|
|
// the loop since Map::erase doesn't return a next iterator.
|
|
Map insertions; // what needs to be inserted back in
|
|
NodeIterator eraseBegin = nodes().end();
|
|
NodeIterator iter;
|
|
for (iter=lowerBound(erasure.least()); iter!=nodes().end() && !erasure.isLeftOf(iter->key()); ++iter) {
|
|
Interval foundInterval = iter->key();
|
|
Value &v = iter->value();
|
|
if (erasure.isContaining(foundInterval)) {
|
|
// erase entire found interval
|
|
if (eraseBegin==nodes().end())
|
|
eraseBegin = iter;
|
|
size_ -= foundInterval.size();
|
|
} else if (erasure.least()>foundInterval.least() && erasure.greatest()<foundInterval.greatest()) {
|
|
// erase the middle of the node, leaving a left and a right portion
|
|
ASSERT_require(eraseBegin==nodes().end());
|
|
eraseBegin = iter;
|
|
IntervalPair rt = splitInterval(foundInterval, erasure.greatest()+1);
|
|
Value rightValue = policy_.split(foundInterval, v /*in,out*/, rt.second.least());
|
|
insertions.insert(rt.second, rightValue);
|
|
IntervalPair lt = splitInterval(rt.first, erasure.least());
|
|
policy_.truncate(rt.first, v /*in,out*/, erasure.least());
|
|
insertions.insert(lt.first, v);
|
|
size_ -= erasure.size();
|
|
} else if (erasure.least() > foundInterval.least()) {
|
|
// erase the right part of the node
|
|
ASSERT_require(eraseBegin==nodes().end());
|
|
eraseBegin = iter;
|
|
IntervalPair halves = splitInterval(foundInterval, erasure.least());
|
|
policy_.truncate(foundInterval, v /*in,out*/, erasure.least());
|
|
insertions.insert(halves.first, v);
|
|
size_ -= halves.second.size();
|
|
} else if (erasure.greatest() < foundInterval.greatest()) {
|
|
// erase the left part of the node
|
|
if (eraseBegin==nodes().end())
|
|
eraseBegin = iter;
|
|
IntervalPair halves = splitInterval(foundInterval, erasure.greatest()+1);
|
|
Value rightValue = policy_.split(foundInterval, v /*in,out*/, halves.second.least());
|
|
insertions.insert(halves.second, rightValue);
|
|
size_ -= halves.first.size();
|
|
}
|
|
}
|
|
|
|
// Do the actual erasing and insert the new stuff, which is easy now because we know it doesn't overlap with anything.
|
|
if (eraseBegin!=nodes().end())
|
|
map_.eraseAtMultiple(eraseBegin, iter);
|
|
map_.insertMultiple(insertions.nodes());
|
|
}
|
|
|
|
/** Erase intervals specified in another IntervalMap
|
|
*
|
|
* Every interval in @p other is erased from this container. */
|
|
template<typename T2, class Policy2>
|
|
void eraseMultiple(const IntervalMap<Interval, T2, Policy2> &other) {
|
|
ASSERT_forbid2((const void*)&other == (const void*)this, "use clear() instead");
|
|
typedef typename IntervalMap<Interval, T2, Policy2>::ConstNodeIterator OtherIter;
|
|
for (OtherIter oi=other.nodes().begin(); oi!=other.nodes().end(); ++oi)
|
|
erase(oi->key());
|
|
}
|
|
|
|
/** Insert a key/value pair.
|
|
*
|
|
* If @p makeHole is true then the interval being inserted is first erased; otherwise the insertion happens only if none
|
|
* of the interval being inserted already exists in the container. */
|
|
void insert(Interval key, Value value, bool makeHole=true) {
|
|
if (key.isEmpty())
|
|
return;
|
|
if (makeHole) {
|
|
erase(key);
|
|
} else {
|
|
NodeIterator found = lowerBound(key.least());
|
|
if (found!=nodes().end() && key.isOverlapping(found->key()))
|
|
return;
|
|
}
|
|
|
|
// Attempt to merge with a left-adjoining node
|
|
if (key.least() - 1 < key.least()) {
|
|
NodeIterator left = find(key.least() - 1);
|
|
if (left!=nodes().end() &&
|
|
left->key().greatest()+1==key.least() &&
|
|
policy_.merge(left->key(), left->value(), key, value)) {
|
|
key = Interval::hull(left->key().least(), key.greatest());
|
|
std::swap(value, left->value());
|
|
size_ -= left->key().size();
|
|
map_.eraseAt(left);
|
|
}
|
|
}
|
|
|
|
// Attempt to merge with a right-adjoining node
|
|
if (key.greatest() + 1 > key.greatest()) {
|
|
NodeIterator right = find(key.greatest() + 1);
|
|
if (right!=nodes().end() &&
|
|
key.greatest()+1==right->key().least() &&
|
|
policy_.merge(key, value, right->key(), right->value())) {
|
|
key = Interval::hull(key.least(), right->key().greatest());
|
|
size_ -= right->key().size();
|
|
map_.eraseAt(right);
|
|
}
|
|
}
|
|
|
|
map_.insert(key, value);
|
|
size_ += key.size();
|
|
}
|
|
|
|
/** Insert values from another container.
|
|
*
|
|
* The values in the other container must be convertable to values of this container, and the intervals must be the same
|
|
* type. */
|
|
template<typename T2, class Policy2>
|
|
void insertMultiple(const IntervalMap<Interval, T2, Policy2> &other, bool makeHole=true) {
|
|
ASSERT_forbid2((const void*)&other == (const void*)this, "cannot insert a container into itself");
|
|
typedef typename IntervalMap<Interval, T2, Policy>::ConstNodeIterator OtherIter;
|
|
for (OtherIter oi=other.nodes().begin(); oi!=other.nodes().end(); ++oi)
|
|
insert(oi->key(), Value(oi->value()), makeHole);
|
|
}
|
|
|
|
// FIXME[Robb Matzke 2014-04-13]
|
|
// // Intersection
|
|
// void intersect(const Interval&);
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Predicates
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
public:
|
|
bool isOverlapping(const Interval &interval) const {
|
|
return findFirstOverlap(interval)!=nodes().end();
|
|
}
|
|
|
|
template<typename T2, class Policy2>
|
|
bool isOverlapping(const IntervalMap<Interval, T2, Policy2> &other) const {
|
|
return findFirstOverlap(other).first!=nodes().end();
|
|
}
|
|
|
|
bool isDistinct(const Interval &interval) const {
|
|
return !isOverlapping(interval);
|
|
}
|
|
|
|
template<typename T2, class Policy2>
|
|
bool isDistinct(const IntervalMap<Interval, T2, Policy2> &other) const {
|
|
return !isOverlapping(other);
|
|
}
|
|
|
|
bool contains(Interval key) const {
|
|
if (key.isEmpty())
|
|
return true;
|
|
ConstNodeIterator found = find(key.least());
|
|
while (1) {
|
|
if (found==nodes().end())
|
|
return false;
|
|
if (key.least() < found->key().least())
|
|
return false;
|
|
ASSERT_require(key.isOverlapping(found->key()));
|
|
if (key.greatest() <= found->key().greatest())
|
|
return true;
|
|
key = splitInterval(key, found->key().greatest()+1).second;
|
|
++found;
|
|
}
|
|
}
|
|
|
|
template<typename T2, class Policy2>
|
|
bool contains(const IntervalMap<Interval, T2, Policy2> &other) const {
|
|
for (ConstNodeIterator iter=other.nodes().begin(); iter!=other.nodes().end(); ++iter) {
|
|
if (!contains(iter->key()))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Private support methods
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
private:
|
|
static IntervalPair splitInterval(const Interval &interval, const typename Interval::Value &splitPoint) {
|
|
ASSERT_forbid(interval.isEmpty());
|
|
ASSERT_require(splitPoint > interval.least() && splitPoint <= interval.greatest());
|
|
|
|
Interval left = Interval::hull(interval.least(), splitPoint-1);
|
|
Interval right = Interval::hull(splitPoint, interval.greatest());
|
|
return IntervalPair(left, right);
|
|
}
|
|
|
|
// a more convenient way to check whether interval contains at least size items and still handle overflow
|
|
static bool isLarge(const Interval &interval, uint64_t size) {
|
|
return !interval.isEmpty() && (interval.size()==0 || interval.size() >= size);
|
|
}
|
|
};
|
|
|
|
} // namespace
|
|
} // namespace
|
|
|
|
#endif
|