Files
TheRock/rocm-systems/projects/rocprofiler-systems/external/dyninst/dataflowAPI/rose/util/AddressMap.h
T
derek.holloway 7502bf64a4
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
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

1859 lines
85 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_AddressMap_H
#define Sawyer_AddressMap_H
#include "Access.h"
#include "AddressSegment.h"
#include "Assert.h"
#include "BitVector.h"
#include "Callbacks.h"
#include "Interval.h"
#include "IntervalMap.h"
#include "IntervalSet.h"
#include "Sawyer.h"
#include <ostream>
#include <stddef.h>
#include <stdint.h>
#include <string>
#include <utility>
#include <vector>
#include <dyncompat/algorithm/string/predicate.hpp>
#include <dyncompat/cstdint.hpp>
#include <dyncompat/foreach.hpp>
#include <dyncompat/integer_traits.hpp>
#include <dyncompat/lexical_cast.hpp>
namespace Sawyer {
namespace Container {
template<class AddressMap>
struct AddressMapTraits {
typedef typename AddressMap::NodeIterator NodeIterator;
typedef typename AddressMap::SegmentIterator SegmentIterator;
};
template<class AddressMap>
struct AddressMapTraits<const AddressMap> {
typedef typename AddressMap::ConstNodeIterator NodeIterator;
typedef typename AddressMap::ConstSegmentIterator SegmentIterator;
};
/** Flags for matching constraints. */
typedef unsigned MatchFlags;
static const MatchFlags MATCH_BACKWARD = 0x00000001; /**< Match in backward direction. */
static const MatchFlags MATCH_CONTIGUOUS = 0x00000002; /**< Force contiguous matching for methods that default otherwise. */
static const MatchFlags MATCH_NONCONTIGUOUS = 0x00000004; /**< Allow non-contiguous matching for methods that are contiguous. */
static const MatchFlags MATCH_WHOLE = 0x00000008; /**< Anchor at both specified ends of address interval. */
/** Base class for testing segment constraints. */
template<class A, class T>
class SegmentPredicate {
public:
struct Args {
const Sawyer::Container::Interval<A> &interval;
const AddressSegment<A, T> &segment;
Args(const Sawyer::Container::Interval<A> &interval, const AddressSegment<A, T> &segment)
: interval(interval), segment(segment) {}
};
virtual ~SegmentPredicate() {}
virtual bool operator()(bool chain, const Args &) = 0;
};
/** Constraints are used to select addresses from a memory map.
*
* Users don't normally see this class since it's almost always created as a temporary. In fact, most of the public methods in
* this class are also present in the AddressMap class, and that's where they're documented.
*
* The purpose of this class is to curry the arguments that would otherwise need to be passed to the various map I/O methods
* and which would significantly complicate the API since many of these arguments are optional. */
template<typename AddressMap>
class AddressMapConstraints {
public:
typedef typename AddressMap::Address Address;
typedef typename AddressMap::Value Value;
typedef Sawyer::Container::Interval<Address> AddressInterval;
private:
AddressMap *map_; // AddressMap<> to which these constraints are bound
bool never_; // never match anything (e.g., when least_ > greatest_)
// address constraints
Optional<Address> least_; // least possible valid address
Optional<Address> greatest_; // greatest possible valid address
Optional<AddressInterval> anchored_; // anchored least or greatest depending on direction
// constraints requiring iteration
size_t maxSize_; // result size is limited
bool singleSegment_; // do not cross a segment boundary
unsigned requiredAccess_; // access bits that must be set in the segment
unsigned prohibitedAccess_; // access bits that must be clear in the segment
std::string nameSubstring_; // segment name must contain substring
Callbacks<SegmentPredicate<Address, Value>*> segmentPredicates_; // user-supplied segment predicates
public:
/** Construct a constraint that matches everything. */
AddressMapConstraints(AddressMap *map)
: map_(map), never_(false), maxSize_(size_t(-1)), singleSegment_(false), requiredAccess_(0), prohibitedAccess_(0) {}
// Implicitly construct constraints for a const AddressMap from a non-const address map.
operator AddressMapConstraints<const AddressMap>() const {
AddressMapConstraints<const AddressMap> cc(map_);
if (neverMatches())
cc = cc.none();
if (isAnchored()) {
if (anchored().isSingleton()) {
cc = cc.at(anchored().least());
} else {
cc = cc.at(anchored());
}
}
if (least())
cc = cc.atOrAfter(*least());
if (greatest())
cc = cc.atOrBefore(*greatest());
cc = cc.limit(limit());
if (isSingleSegment())
cc = cc.singleSegment();
cc = cc.require(required());
cc = cc.prohibit(prohibited());
cc = cc.substr(substr());
cc = cc.segmentPredicates(segmentPredicates());
return cc;
}
/** Print constraints in a human readable form. */
void print(std::ostream &out) const {
out <<"{map=" <<map_;
if (never_)
out <<", never";
if (least_)
out <<", least=" <<*least_;
if (greatest_)
out <<", greatest=" <<*greatest_;
if (anchored_) {
out <<", anchored=";
AddressInterval a = *anchored_;
if (a.isEmpty()) {
out <<"empty";
} else if (a.least()==a.greatest()) {
out <<a.least();
} else {
out <<"{" <<a.least() <<".." <<a.greatest() <<"}";
}
}
if (maxSize_ != size_t(-1))
out <<", limit=" <<maxSize_;
if (singleSegment_)
out <<", single-segment";
if (requiredAccess_!=0) {
out <<", required="
<<((requiredAccess_ & Access::READABLE) ? "r" : "")
<<((requiredAccess_ & Access::WRITABLE) ? "w" : "")
<<((requiredAccess_ & Access::EXECUTABLE) ? "x" : "")
<<((requiredAccess_ & Access::IMMUTABLE) ? "i" : "");
unsigned other = requiredAccess_ & ~(Access::READABLE|Access::WRITABLE|Access::EXECUTABLE|Access::IMMUTABLE);
if (other)
out <<"+0x" <<BitVector(8*sizeof requiredAccess_).fromInteger(other).toHex();
}
if (prohibitedAccess_!=0) {
out <<", required="
<<((prohibitedAccess_ & Access::READABLE) ? "r" : "")
<<((prohibitedAccess_ & Access::WRITABLE) ? "w" : "")
<<((prohibitedAccess_ & Access::EXECUTABLE) ? "x" : "")
<<((prohibitedAccess_ & Access::IMMUTABLE) ? "i" : "");
unsigned other = prohibitedAccess_ & ~(Access::READABLE|Access::WRITABLE|Access::EXECUTABLE|Access::IMMUTABLE);
if (other)
out <<"+0x" <<BitVector(8*sizeof prohibitedAccess_).fromInteger(other).toHex();
}
if (!nameSubstring_.empty()) {
out <<", substr=\"";
DYN_FOREACH (char ch, nameSubstring_) {
switch (ch) {
case '\a': out <<"\\a"; break;
case '\b': out <<"\\b"; break;
case '\t': out <<"\\t"; break;
case '\n': out <<"\\n"; break;
case '\v': out <<"\\v"; break;
case '\f': out <<"\\f"; break;
case '\r': out <<"\\r"; break;
case '\"': out <<"\\\""; break;
case '\\': out <<"\\\\"; break;
default:
if (isprint(ch)) {
out <<ch;
} else {
char buf[8];
sprintf(buf, "\\%03o", (unsigned)(unsigned char)ch);
out <<buf;
}
break;
}
}
}
if (!segmentPredicates_.isEmpty())
out <<", user-def";
out <<"}";
}
/** Print constraints in a human readable form. */
friend std::ostream& operator<<(std::ostream &out, const AddressMapConstraints &x) {
x.print(out);
return out;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Constraint modifiers
public:
/** Require certain access permissions. See @ref AddressMap::require. */
AddressMapConstraints require(unsigned bits) const {
AddressMapConstraints retval = *this;
retval.requiredAccess_ |= bits;
return retval;
}
/** Prohibit certain access permissions. See @ref AddressMap::prohibit. */
AddressMapConstraints prohibit(unsigned bits) const {
AddressMapConstraints retval = *this;
retval.prohibitedAccess_ |= bits;
return retval;
}
/** Require and prohibit certain access permissions. See @ref AddressMap::access. */
AddressMapConstraints access(unsigned bits) const {
return require(bits).prohibit(~bits);
}
/** Require certain segment names. See @ref AddressMap::substr. */
AddressMapConstraints substr(const std::string &s) const {
ASSERT_require(nameSubstring_.empty() || nameSubstring_==s);// substring conjunction not supported
AddressMapConstraints retval = *this;
retval.nameSubstring_ = s;
return retval;
}
/** No constraints. See @ref AddressMap::any. */
AddressMapConstraints any() const {
return *this;
}
/** Constraints that match nothing. See @ref AddressMap::none. */
AddressMapConstraints none() const {
AddressMapConstraints retval = *this;
retval.never_ = true;
return retval;
}
/** Anchor at a certain address. See @ref AddressMap::at. */
AddressMapConstraints at(Address x) const {
AddressMapConstraints retval = *this;
retval.anchored_ = anchored_ ? *anchored_ & AddressInterval(x) : AddressInterval(x);
return retval.anchored_->isEmpty() ? retval.none() : retval;
}
/** Anchor at a certain interval. See @ref AddressMap::at. */
AddressMapConstraints at(const Sawyer::Container::Interval<Address> &x) const {
AddressMapConstraints retval = *this;
retval.anchored_ = anchored_ ? *anchored_ & x : x;
return retval.anchored_->isEmpty() ? retval.none() : retval.atOrAfter(x.least()).atOrBefore(x.greatest());
}
/** Limit matching length. See @ref AddressMap::limit. */
AddressMapConstraints limit(size_t x) const {
AddressMapConstraints retval = *this;
retval.maxSize_ = std::min(maxSize_, x);
return 0 == retval.maxSize_ ? retval.none() : retval;
}
/** Limit addresses. See @ref AddressMap::atOrAfter. */
AddressMapConstraints atOrAfter(Address least) const {
AddressMapConstraints retval = *this;
if (least_) {
retval.least_ = std::max(*least_, least);
} else {
retval.least_ = least;
}
if (greatest_ && *greatest_ < *retval.least_)
retval.none();
return retval;
}
/** Limit addresses. See @ref AddressMap::atOrBefore. */
AddressMapConstraints atOrBefore(Address greatest) const {
AddressMapConstraints retval = *this;
if (greatest_) {
retval.greatest_ = std::min(*greatest_, greatest);
} else {
retval.greatest_ = greatest;
}
if (least_ && *least_ > *retval.greatest_)
retval.none();
return retval;
}
/** Limit addresses. See @ref AddressMap::within. */
AddressMapConstraints within(const Sawyer::Container::Interval<Address> &x) const {
return x.isEmpty() ? none() : atOrAfter(x.least()).atOrBefore(x.greatest());
}
/** Limit addresses. See @ref AddressMap::within. */
AddressMapConstraints within(Address lo, Address hi) const {
return lo<=hi ? within(Sawyer::Container::Interval<Address>::hull(lo, hi)) : none();
}
/** Limit addresses. See @ref AddressMap::baseSize. */
AddressMapConstraints baseSize(Address base, Address size) const {
return size>0 ? atOrAfter(base).atOrBefore(base+size-1) : none();
}
/** Limit addresses. See @ref AddressMap::after. */
AddressMapConstraints after(Address x) const {
return x==dyncompat::integer_traits<Address>::const_max ? none() : atOrAfter(x+1);
}
/** Limit addreses. See @ref AddressMap::before. */
AddressMapConstraints before(Address x) const {
return x==dyncompat::integer_traits<Address>::const_min ? none() : atOrBefore(x-1);
}
/** Limit matching to single segment. See @ref AddressMap::singleSegment. */
AddressMapConstraints singleSegment() const {
AddressMapConstraints retval = *this;
retval.singleSegment_ = true;
return retval;
}
/** Limit segments. See @ref AddressMap::segmentPredicate. */
AddressMapConstraints segmentPredicate(SegmentPredicate<Address, Value> *p) const {
if (!p)
return none();
AddressMapConstraints retval = *this;
retval.segmentPredicates_.append(p);
return retval;
}
AddressMapConstraints segmentPredicates(const Callbacks<SegmentPredicate<Address, Value>*> &predicates) const {
AddressMapConstraints retval = *this;
retval.segmentPredicates_.append(predicates);
return retval;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Constraint queries
public:
/** Accessibility bits that are required to be set. */
unsigned required() const {
return requiredAccess_;
}
/** Accessibility bits that are required to be clear. */
unsigned prohibited() const {
return prohibitedAccess_;
}
/** Section name substring. Returns the string that must be present as a substring in a section name. */
const std::string& substr() const {
return nameSubstring_;
}
/** Returns true if the constraint is not allowed to match anything. This returns true if the @ref none constraint is
* set, but it doesn't necessarily return true if a combination of other constraints results in something that cannot
* match. */
bool neverMatches() const {
return never_;
}
/** Determines whether constraints are anchored to an address. */
bool isAnchored() const {
return anchored_;
}
/** Returns the anchor points. Returns the anchor points as an interval, where the anchor(s) is an endpoint of the returned
* interval. For instance, if the @ref at method was called, then @ref anchored will return a singleton interval whose
* value was the @ref at argument. The return value will be an empty interval if mutually exclusive anchors are set. This
* method should not be called without first calling @ref isAnchored. */
AddressInterval anchored() const {
ASSERT_require(isAnchored());
return *anchored_;
}
/** Size limit. Returns the size limit or the maximum possible size_t value if not limit is in effect. */
size_t limit() const {
return maxSize_;
}
/** Least possible address. Returns the least possible address or nothing. The least address is in effect for the @ref
* atOrAfter constraint. */
const Optional<Address>& least() const {
return least_;
}
/** Greatest possible address. Returns the greatest possible address or nothing. The greatest address is in effect for the
* @ref atOrBefore constraint. */
const Optional<Address>& greatest() const {
return greatest_;
}
/** Returns true if the single-segment constraint is set. */
bool isSingleSegment() const {
return singleSegment_;
}
/** Returns the segment predicates. */
const Callbacks<SegmentPredicate<Address, Value>*> segmentPredicates() const {
return segmentPredicates_;
}
/** Returns a pointer to the memory map for this constraint object. */
AddressMap* map() const {
return map_;
}
/** Determines whether non-address constraints are present. The presence of non-address constraints requires iteration
* over matching addresses to find applicable regions of the address space. */
bool hasNonAddressConstraints() const {
return (!never_ &&
(requiredAccess_ || prohibitedAccess_ || !nameSubstring_.empty() || maxSize_!=size_t(-1) ||
singleSegment_ || !segmentPredicates_.isEmpty()));
}
/** Construct new constraints from existing address constraints. Constructs a new constraints object from an existing
* but using only the address constraints of the existing object and none of its non-address constraints. */
AddressMapConstraints addressConstraints() const {
AddressMapConstraints c(map_);
c.least_ = least_;
c.greatest_ = greatest_;
c.anchored_ = anchored_;
c.maxSize_ = maxSize_;
return c;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Methods that directly call the AddressMap
public:
dyncompat::iterator_range<typename AddressMapTraits<AddressMap>::NodeIterator>
nodes(MatchFlags flags=0) const {
return map_->nodes(*this, flags);
}
dyncompat::iterator_range<typename AddressMapTraits<AddressMap>::SegmentIterator>
segments(MatchFlags flags=0) const {
return map_->segments(*this, flags);
}
Optional<typename AddressMap::Address>
next(MatchFlags flags=0) const {
return map_->next(*this, flags);
}
Sawyer::Container::Interval<Address>
available(MatchFlags flags=0) const {
return map_->available(*this, flags);
}
bool
exists(MatchFlags flags=0) const {
return map_->exists(*this, flags);
}
typename AddressMapTraits<AddressMap>::NodeIterator
findNode(MatchFlags flags=0) const {
return map_->findNode(*this, flags);
}
template<typename Functor>
void
traverse(Functor &functor, MatchFlags flags=0) const {
return map_->traverse(functor, *this, flags);
}
void
traverse(typename AddressMap::Visitor &visitor, MatchFlags flags=0) const {
return map_->template traverse<typename AddressMap::Visitor>(visitor, *this, flags);
}
Sawyer::Container::Interval<Address>
read(typename AddressMap::Value *buf /*out*/, MatchFlags flags=0) const {
return map_->read(buf, *this, flags);
}
Sawyer::Container::Interval<Address>
read(std::vector<typename AddressMap::Value> &buf /*out*/,
MatchFlags flags=0) const {
return map_->read(buf, *this, flags);
}
Sawyer::Container::Interval<Address>
write(const typename AddressMap::Value *buf, MatchFlags flags=0) const {
return map_->write(buf, *this, flags);
}
Sawyer::Container::Interval<Address>
write(const std::vector<typename AddressMap::Value> &buf, MatchFlags flags=0) {
return map_->write(buf, *this, flags);
}
void
prune(MatchFlags flags=0) const {
return map_->prune(*this, flags);
}
void
keep(MatchFlags flags=0) const {
return map_->keep(*this, flags);
}
void
changeAccess(unsigned requiredAccess, unsigned prohibitedAccess, MatchFlags flags=0) const {
return map_->changeAccess(requiredAccess, prohibitedAccess, *this, flags);
}
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Implementation details
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AddressMapImpl {
// Used internally to split and merge segments
template<class A, class T>
class SegmentMergePolicy {
public:
typedef A Address;
typedef T Value;
typedef AddressSegment<A, T> Segment;
bool merge(const Sawyer::Container::Interval<Address> &leftInterval, Segment &leftSegment,
const Sawyer::Container::Interval<Address> &rightInterval, Segment &rightSegment) {
ASSERT_forbid(leftInterval.isEmpty());
ASSERT_forbid(rightInterval.isEmpty());
ASSERT_require(leftInterval.greatest() + 1 == rightInterval.least());
return (leftSegment.accessibility() == rightSegment.accessibility() &&
leftSegment.name() == rightSegment.name() &&
leftSegment.buffer() == rightSegment.buffer() &&
leftSegment.offset() + leftInterval.size() == rightSegment.offset());
}
Segment split(const Sawyer::Container::Interval<Address> &interval, Segment &segment, Address splitPoint) {
ASSERT_forbid(interval.isEmpty());
ASSERT_require(interval.isContaining(splitPoint));
Segment right = segment;
right.offset(segment.offset() + splitPoint - interval.least());
return right;
}
void truncate(const Sawyer::Container::Interval<Address> &interval, Segment &segment, Address splitPoint) {
ASSERT_forbid(interval.isEmpty());
ASSERT_require(interval.isContaining(splitPoint));
}
};
// The results for matching constraints
template<class AddressMap>
struct MatchedConstraints {
typedef typename AddressMap::Address Address;
Sawyer::Container::Interval<Address> interval_;
typedef typename AddressMapTraits<AddressMap>::NodeIterator NodeIterator;
dyncompat::iterator_range<NodeIterator> nodes_;
};
// Finds the minimum possible address and node iterator for the specified constraints in this map and returns that
// iterator. Returns the end iterator if the constraints match no address. If a non-end iterator is returned then minAddr
// is adjusted to be the minimum address that satisfies the constraint (it will be an address within the returned node, but
// not necessarily the least address for the node). If useAnchor is set and the constraints specify an anchor, then the
// anchor address must be present in the map and satisfy any address constraints that might also be present.
template<class AddressMap>
typename AddressMapTraits<AddressMap>::NodeIterator
constraintLowerBound(AddressMap &amap, const AddressMapConstraints<AddressMap> &c, bool useAnchor,
typename AddressMap::Address &minAddr) {
typedef typename AddressMapTraits<AddressMap>::NodeIterator Iterator;
if (amap.isEmpty() || c.neverMatches())
return amap.nodes().end();
if (useAnchor && c.isAnchored()) { // forward matching if useAnchor is set
if ((c.least() && *c.least() > c.anchored().least()) || (c.greatest() && *c.greatest() < c.anchored().greatest()))
return amap.nodes().end(); // anchor is outside of allowed interval
Iterator lb = amap.lowerBound(c.anchored().least());
if (lb==amap.nodes().end() || c.anchored().least() < lb->key().least())
return amap.nodes().end(); // anchor is not present in this map
minAddr = c.anchored().least();
return lb;
}
if (c.least()) {
Iterator lb = amap.lowerBound(*c.least());
if (lb==amap.nodes().end())
return lb; // least is above all segments
minAddr = std::max(*c.least(), lb->key().least());
return lb;
}
Iterator lb = amap.nodes().begin();
if (lb!=amap.nodes().end())
minAddr = lb->key().least();
return lb;
}
// Finds the maximum possible address and node for the specified constraints in this map, and returns an iterator to the
// following node. Returns the begin iterator if the constraints match no address. If a non-begin iterator is returned
// then maxAddr is adjusted to be the maximum address that satisfies the constraint (it will be an address that belongs to
// the node immediately prior to the one pointed to by the returned iterator, but not necessarily the greatest address for
// that node). If useAnchor is set and the constraints specify an anchor, then the anchor address must be present in the
// map and satisfy any address constraints that might also be present.
template<class AddressMap>
typename AddressMapTraits<AddressMap>::NodeIterator
constraintUpperBound(AddressMap &amap, const AddressMapConstraints<AddressMap> &c, bool useAnchor,
typename AddressMap::Address &maxAddr) {
typedef typename AddressMapTraits<AddressMap>::NodeIterator Iterator;
if (amap.isEmpty() || c.neverMatches())
return amap.nodes().begin();
if (useAnchor && c.isAnchored()) { // backward matching if useAnchor is set
if ((c.least() && *c.least() > c.anchored().least()) || (c.greatest() && *c.greatest() < c.anchored().greatest()))
return amap.nodes().begin(); // anchor is outside allowed interval
Iterator ub = amap.findPrior(c.anchored().greatest());
if (ub==amap.nodes().end() || c.anchored().greatest() > ub->key().greatest())
return amap.nodes().begin(); // anchor is not present in this map
maxAddr = c.anchored().greatest();
return ++ub; // return node after the one containing the anchor
}
if (c.greatest()) {
Iterator ub = amap.findPrior(*c.greatest());
if (ub==amap.nodes().end())
return amap.nodes().begin(); // greatest is below all segments
maxAddr = std::min(ub->key().greatest(), *c.greatest());
return ++ub; // return node after the one containing the maximum
}
maxAddr = amap.hull().greatest();
return amap.nodes().end();
}
// Returns true if the segment satisfies the non-address constraints in c.
template<class AddressMap>
bool
isSatisfied(const typename AddressMap::Node &node, const AddressMapConstraints<AddressMap> &c) {
typedef typename AddressMap::Address Address;
typedef typename AddressMap::Value Value;
typedef typename AddressMap::Segment Segment;
const Segment &segment = node.value();
if (!segment.isAccessible(c.required(), c.prohibited()))
return false; // wrong segment permissions
if (!dyncompat::contains(segment.name(), c.substr()))
return false; // wrong segment name
if (!c.segmentPredicates().apply(true, typename SegmentPredicate<Address, Value>::Args(node.key(), node.value())))
return false; // user-supplied predicates failed
return true;
}
// Matches constraints against contiguous addresses in a forward direction.
template<class AddressMap>
MatchedConstraints<AddressMap>
matchForward(AddressMap &amap, const AddressMapConstraints<AddressMap> &c, MatchFlags flags) {
typedef typename AddressMap::Address Address;
typedef typename AddressMapTraits<AddressMap>::NodeIterator Iterator;
MatchedConstraints<AddressMap> retval;
retval.nodes_ = dyncompat::iterator_range<Iterator>(amap.nodes().end(), amap.nodes().end());
if (c.neverMatches() || amap.isEmpty())
return retval;
// Find a lower bound for the minimum address
Address minAddr = 0;
Iterator begin = constraintLowerBound(amap, c, true, minAddr /*out*/);
if (begin == amap.nodes().end())
return retval;
// Find an upper bound for the maximum address.
Address maxAddr = 0;
Iterator end = constraintUpperBound(amap, c, false, maxAddr /*out*/);
if (end==amap.nodes().begin())
return retval;
// Advance the lower-bound until it satisfies the other (non-address) constraints
while (begin!=end && !isSatisfied(*begin, c)) {
if (c.isAnchored())
return retval; // match is anchored to minAddr
++begin;
}
if (begin==end)
return retval;
minAddr = std::max(minAddr, begin->key().least());
// Iterate forward until the constraints are no longer satisfied
if ((flags & MATCH_CONTIGUOUS)!=0 || c.hasNonAddressConstraints()) {
Address addr = minAddr;
Iterator iter = begin;
size_t nElmtsFound = 0;
for (/*void*/; iter!=end; ++iter) {
if (iter!=begin) { // already tested the first node above
if (c.isSingleSegment())
break; // we crossed a segment boundary
if ((flags & MATCH_CONTIGUOUS)!=0 && addr+1 != iter->key().least())
break; // gap between segments
if (!isSatisfied(*iter, c)) {
if ((flags & MATCH_WHOLE)!=0)
return retval; // match is anchored to maxAddr
break; // segment does not satisfy constraints
}
}
size_t nElmtsHere = iter->key().greatest() + 1 - std::max(minAddr, iter->key().least());
if (nElmtsFound + nElmtsHere >= c.limit()) {
size_t nNeed = c.limit() - nElmtsFound;
addr = std::max(minAddr, iter->key().least()) + nNeed - 1;
++iter;
break; // too many values
}
addr = iter->key().greatest();
nElmtsFound += nElmtsHere;
}
end = iter;
maxAddr = std::min(maxAddr, addr);
}
// Build the result
retval.interval_ = Sawyer::Container::Interval<Address>::hull(minAddr, maxAddr);
retval.nodes_ = dyncompat::iterator_range<Iterator>(begin, end);
return retval;
}
// Matches constraints against contiguous addresses in a backward direction.
template<class AddressMap>
MatchedConstraints<AddressMap>
matchBackward(AddressMap &amap, const AddressMapConstraints<AddressMap> &c, MatchFlags flags) {
typedef typename AddressMap::Address Address;
typedef typename AddressMapTraits<AddressMap>::NodeIterator Iterator;
MatchedConstraints<AddressMap> retval;
retval.nodes_ = dyncompat::iterator_range<Iterator>(amap.nodes().end(), amap.nodes().end());
if (c.neverMatches() || amap.isEmpty())
return retval;
// Find a lower bound for the minimum address
Address minAddr = 0;
Iterator begin = constraintLowerBound(amap, c, false, minAddr /*out*/);
if (begin == amap.nodes().end())
return retval;
// Find an upper bound for the maximum address.
Address maxAddr = 0;
Iterator end = constraintUpperBound(amap, c, true, maxAddr /*out*/);
if (end==amap.nodes().begin())
return retval;
// Decrement the upper bound until constraints are met. End always points to one-past the last matching node.
while (end!=begin) {
Iterator prev = end; --prev;
if (isSatisfied(*prev, c)) {
maxAddr = std::min(maxAddr, prev->key().greatest());
break;
}
if (c.isAnchored())
return retval; // match is anchored to maxAddr
end = prev;
}
if (end==begin)
return retval;
// Iterate backward until the constraints are no longer satisfied. Within the loop, iter always points to on-past the
// node in question. When the loop exits, iter points to the first node satisfying constraints.
if ((flags & MATCH_CONTIGUOUS)!=0 || c.hasNonAddressConstraints()) {
Address addr = maxAddr;
Iterator iter = end;
size_t nElmtsFound = 0;
for (/*void*/; iter!=begin; --iter) {
Iterator prev = iter; --prev; // prev points to the node in question
if (iter!=end) { // already tested last node above
if (c.isSingleSegment())
break; // we crossed a segment boundary
if ((flags & MATCH_CONTIGUOUS)!=0 && prev->key().greatest()+1 != addr)
break; // gap between segments
if (!isSatisfied(*prev, c)) {
if ((flags & MATCH_WHOLE)!=0)
return retval; // match is anchored to minAddr
break; // segment does not satisfy constraints
}
}
size_t nElmtsHere = std::min(maxAddr, prev->key().greatest()) - prev->key().least() + 1;
if (nElmtsFound + nElmtsHere >= c.limit()) {
size_t nNeed = c.limit() - nElmtsFound;
addr = std::min(maxAddr, prev->key().greatest()) - nNeed + 1;
iter = prev;
break;
}
addr = prev->key().least();
nElmtsFound += nElmtsHere;
}
begin = iter; // iter points to first matching node
minAddr = std::max(minAddr, addr);
}
// Build the result
retval.interval_ = Sawyer::Container::Interval<Address>::hull(minAddr, maxAddr);
retval.nodes_ = dyncompat::iterator_range<Iterator>(begin, end);
return retval;
}
// Match constraints forward or backward
template<class AddressMap>
MatchedConstraints<AddressMap>
matchConstraints(AddressMap &amap, const AddressMapConstraints<AddressMap> &c, MatchFlags flags) {
if ((flags & MATCH_BACKWARD) != 0)
return matchBackward(amap, c, flags);
return matchForward(amap, c, flags);
}
} // namespace
/** A mapping from address space to values.
*
* This object maps addresses (actually, intervals thereof) to values. Addresses must be an integral unsigned type but values
* may be any type as long as they are default constructible and copyable. The address type is usually an integral type whose
* width is the log base 2 of the size of the address space; the value type is often unsigned 8-bit bytes.
*
* An address map accomplishes the mapping by inheriting from an @ref IntervalMap, whose intervals are
* <code>Interval<A></code> and whose values are <code>AddressSegment<A,T></code>. The @ref AddressSegment objects
* point to reference-counted @ref Buffer objects that hold the values. The same values can be mapped at different addresses
* by inserting segments at those addresses that point to a common buffer.
*
* An address map implements read and write concepts for copying values between user-supplied buffers and the storage areas
* referenced by the map. Many of the address map methods operate over a region of the map described by a set of constraints
* that are matched within the map. Constraints are indicated by listing them before the map I/O operation, but they do not
* modify the map in any way--they exist outside the map. Constraints are combined by logical conjunction. For instance, the
* @ref AddressMap::next method returns the lowest address that satisfies the given constraints, or nothing. If we wanted
* to search for the lowest address beteen 1000 and 1999 inclusive, that has read access but not write access, we would
* do so like this:
*
* @code
* Optional<Address> x = map.within(1000,1999).require(READABLE).prohibit(WRITABLE).next();
* @endcode
*
* In fact, by making use of the @ref Sawyer::Optional API, we can write a loop that iterates over such addresses, although
* there may be more efficient ways to do this than one address at a time:
*
* @code
* for (Address x=1000; map.within(x,1999).require(READABLE).prohibit(WRITABLE).next().assignTo(x); ++x) ...
* @endcode
*
* The next example shows how to read a buffer's worth of values anchored at a particular starting value. The @ref read
* method returns an address interval to indicate what addresses were accessed, but in this case we're only interested in the
* number of such addresses since we know the starting address.
*
* @code
* Value buf[10];
* size_t nAccessed = map.at(1000).limit(10).read(buf).size();
* @endcode
*
* An interval return value is more useful when we don't know where the operation occurs until after it occurs. For instance,
* to read up to 10 values that are readable at or beyond some address:
*
* @code
* Value buf[10]
* Interval<Address> accessed = map.atOrAfter(1000).limit(10).require(READABLE).read(buf);
* @endcode
*
* Since all map operations take the same constraints, it is possible to rewrite the previous @c for loop so that instead of
* searching for an address it actually reads data. Here's a loop that prints all the data that's readable but not writable
* and falls between two addresses, regardless of what other segments also exist in that same interval:
*
* @code
* static const size_t bufsz = 256;
* Value buf[bufsz];
* Address addr = 1000;
* while (Interval accessed = map.within(addr, 1999).require(READABLE).prohibit(WRITABLE).limit(bufsz).read(buf)) {
* for (Address i=accessed.least(); i<=access.greatest(); ++i)
* std::cout <<i <<": " <<buf[i-accessed.least()] <<"\n";
* addr += accessed.size();
* }
* @endcode
*
* Most I/O methods require that constraints match only contiguous addresses. If there is an intervening address that does
* not satisfy the constraint, including addresses that are not mapped, then the matched range terminates at the non-matching
* address. However, the @c MATCH_NONCONTIGUOUS flag can be used to relax this, in which case the matched interval of
* addresses may include addresses that are not mapped. Regardless of whether contiguous addresses are required, the returned
* interval of addresses will never contain an address that is mapped and does not also satisfy the constraints. I/O
* operations (read and write) require contiguous addresses, but some other methods don't. For instance, the expressions
*
* @code
* Interval<Address> found1 = map.within(100,200).require(READABLE).available(MATCH_CONTIGUOUS);
* Interval<Address> found2 = map.within(100,200).require(READABLE).available(MATCH_NONCONTIGUOUS);
* @endcode
*
* are the same except the second one returns an interval that might include non-mapped addresses. A few methods go even
* further and are able to operate across mapped addresses that don't satisfy the segment constraints, skipping over the
* addresses that don't satisfy the constraints. For instance, the @ref prune and @ref keep functions operate this way so
* that a call like:
*
* @code
* map.within(100,200).require(READABLE).prohibit(WRITABLE).keep();
* @endcode
*
* will discard all addresses except keeping those which are between 100 and 200 (inclusive) and which are readable but not
* writable. The documentation for each method states whether the default is contiguous matching, non-contiguous matching, or
* skipping over whole segments, and the method can take a bit flag (@ref MatchFlags) to change its default behavior (with
* some restrictions).
*
* One of the @ref MatchFlags bits indicates whether the constraint should match the lowest or highest possible addresses. The
* default is to match the constraint at the lowest possible addresses. Matching at the highest addresses is useful when
* iterating backward. For instance, if one wants to read up to 1024 values that end at address 1023 but is not sure how many
* prior addresses are readable, he could use backward matching. This is much more efficient than the alternative of
* searching backward one address at a time, and is simpler than doing an explicit binary search:
*
* @code
* Value buf[1024];
* Interval<Address> accessed = map.at(1023).limit(1024).read(buf, MATCH_BACKWARD);
* @endcode
*
* Backward and forward I/O behaves identically as far as which array element holds which value. In all cases array element
* zero contains the value at the lowest address read or written.
*
* Here's an example that creates two buffers (they happen to point to arrays that the Buffer objects do not own), maps them
* at addresses in such a way that part of the smaller of the two buffers occludes the larger buffer, and then
* performs a write operation that touches parts of both buffers. We then rewrite part of the mapping and do another write
* operation:
*
* @code
* using namespace Sawyer::Container;
*
* typedef unsigned Address;
* typedef Interval<Address> Addresses;
* typedef Buffer<Address, char>::Ptr BufferPtr;
* typedef AddressSegment<Address, char> Segment;
* typedef AddressMap<Address, char> MemoryMap;
*
* // Create some buffer objects
* char data1[15];
* memcpy(data1, "---------------", 15);
* BufferPtr buf1 = Sawyer::Container::StaticBuffer<Address, char>::instance(data1, 15);
* char data2[5];
* memcpy(data2, "##########", 10);
* BufferPtr buf2 = Sawyer::Container::StaticBuffer<Address, char>::instance(data2, 5); // using only first 5 bytes
*
* // Map data2 into the middle of data1
* MemoryMap map;
* map.insert(Addresses::baseSize(1000, 15), Segment(buf1));
* map.insert(Addresses::baseSize(1005, 5), Segment(buf2));
*
* // Write across both buffers and check that data2 occluded data1
* Addresses accessed = map.at(1001).limit(13).write("bcdefghijklmn");
* ASSERT_always_require(accessed.size()==13);
* ASSERT_always_require(0==memcmp(data1, "-bcde-----klmn-", 15));
* ASSERT_always_require(0==memcmp(data2, "fghij#####", 10));
*
* // Map the middle of data1 over the top of data2 again and check that the mapping has one element. I.e., the three
* // separate parts were recombined into a single entry since they are three consecutive areas of a single buffer.
* map.insert(Addresses::baseSize(1005, 5), Segment(buf1, 5));
* ASSERT_always_require(map.nSegments()==1);
*
* // Write some data again
* accessed = map.at(1001).limit(13).write("BCDEFGHIJKLMN");
* ASSERT_always_require(accessed.size()==13);
* ASSERT_always_require(0==memcmp(data1, "-BCDEFGHIJKLMN-", 15));
* ASSERT_always_require(0==memcmp(data2, "fghij#####", 10));
* @endcode
*
* @section errors Microsoft C++ compilers
*
* Microsoft Visual Studio 12 2013 (MSVC 18.0.30501.0) and possibly other versions look up non-dependent names in template base
* classes in violation of C++ standards and apparently no switch to make their behavior compliant with the standard. This
* causes problems for AddressMap because unqualified references to <tt>%Interval</tt> should refer to the
* Sawyer::Container::Interval class template, but instead they refer to the @ref IntervalMap::Interval "Interval" typedef in
* the base class. Our work around is to qualify all occurrences of <tt>%Interval</tt> where Microsoft compilers go wrong. */
template<class A, class T = uint8_t>
class AddressMap: public IntervalMap<Interval<A>, AddressSegment<A, T>, AddressMapImpl::SegmentMergePolicy<A, T> > {
// "Interval" is qualified to work around bug in Microsoft compilers. See doxygen note above.
typedef IntervalMap<Sawyer::Container::Interval<A>, AddressSegment<A, T>, AddressMapImpl::SegmentMergePolicy<A, T> > Super;
public:
typedef A Address; /**< Type for addresses. This should be an unsigned type. */
typedef T Value; /**< Type of data stored in the address space. */
typedef AddressSegment<A, T> Segment; /**< Type of segments stored by this map. */
typedef Sawyer::Container::Buffer<Address, Value> Buffer;
typedef typename Super::Node Node; /**< Storage node containing interval/segment pair. */
typedef typename Super::ValueIterator SegmentIterator; /**< Iterates over segments in the map. */
typedef typename Super::ConstValueIterator ConstSegmentIterator; /**< Iterators over segments in the map. */
typedef typename Super::ConstIntervalIterator ConstIntervalIterator; /**< Iterates over address intervals in the map. */
typedef typename Super::NodeIterator NodeIterator; /**< Iterates over address interval, segment pairs in the map. */
typedef typename Super::ConstNodeIterator ConstNodeIterator; /**< Iterates over address interval/segment pairs in the map. */
/** Constructs an empty address map. */
AddressMap() {}
/** Copy constructor.
*
* The new address map has the same addresses mapped to the same buffers as the @p other map. The buffers themselves are
* not copied since they are reference counted.
*
* If @p copyOnWrite is set then the buffers are marked so that any subsequent write to that buffer via the @ref write
* method from any AddressMap object will cause a new copy to be created and used by the AddressMap that's doing the
* writing. One should be careful when buffers are intended to be shared because setting the copy-on-write bit on the
* buffer will cause the sharing to be broken. For example, if map1 is created and then copied into map2 with the
* copy-on-write bit cleared, then any writes to the buffer via map1 will be visible when reading from map2 and vice
* versa. However, if map3 is then created by copying either map1 or map2 with the copy-on-write bit set, then writes to
* any of the three maps will cause that map to obtain an independent copy of the buffer, effectively removing the sharing
* that was intended between map1 and map2. Another thing to be aware of is that some buffer types will return a
* different buffer type when they're copied. For instance, copying a @ref StaticBuffer or @ref MappedBuffer will return
* an @ref AllocatingBuffer. */
AddressMap(const AddressMap &other, bool copyOnWrite=false): Super(other) {
if (copyOnWrite) {
DYN_FOREACH (Segment &segment, this->values()) {
if (const typename Buffer::Ptr &buffer = segment.buffer())
buffer->copyOnWrite(true);
}
}
}
/** Constraint: required access bits.
*
* Constrains address to those that have all of the access bits that are set in @p x.
*
* @{ */
AddressMapConstraints<const AddressMap> require(unsigned x) const {
return AddressMapConstraints<const AddressMap>(this).require(x);
}
AddressMapConstraints<AddressMap> require(unsigned x) {
return AddressMapConstraints<AddressMap>(this).require(x);
}
/** @} */
/** Constraint: prohibited access bits.
*
* Constrains addresses to those that have none of the access bits that are set in @p x.
*
* @{ */
AddressMapConstraints<const AddressMap> prohibit(unsigned x) const {
return AddressMapConstraints<const AddressMap>(this).prohibit(x);
}
AddressMapConstraints<AddressMap> prohibit(unsigned x) {
return AddressMapConstraints<AddressMap>(this).prohibit(x);
}
/** @} */
/** Constraint: required and prohibited access bits.
*
* Constrains address to those that have the specified access bits. This method is the same as calling @ref require with
* the specified bit vector, and @ref prohibit with the inverted bit vector.
*
* @{ */
AddressMapConstraints<const AddressMap> access(unsigned x) const {
return AddressMapConstraints<const AddressMap>(this).access(x);
}
AddressMapConstraints<AddressMap> access(unsigned x) {
return AddressMapConstraints<AddressMap>(this).access(x);
}
/** @} */
/** Constraint: segment name substring.
*
* Constrains addresses to those that belong to a segment that contains string @p x as part of its name.
*
* @{ */
AddressMapConstraints<const AddressMap> substr(const std::string &x) const {
return AddressMapConstraints<const AddressMap>(this).substr(x);
}
AddressMapConstraints<AddressMap> substr(const std::string &x) {
return AddressMapConstraints<AddressMap>(this).substr(x);
}
/** @} */
/** Constraint: anchor point.
*
* Constrains addresses to a sequence that begins at @p x. If address @p x is not part of the addresses matched by the
* other constraints, then no address matches.
*
* @{ */
AddressMapConstraints<const AddressMap> at(Address x) const {
return AddressMapConstraints<const AddressMap>(this).at(x);
}
AddressMapConstraints<AddressMap> at(Address x) {
return AddressMapConstraints<AddressMap>(this).at(x);
}
/** @} */
/** Constraint: anchored interval.
*
* Constrains addresses so that the lowest or highest matched address is the specified anchor point. When matching
* constraints in the forward direction (the default) then the anchor must be the lowest address, and when matching in the
* backward direction the anchor must be the highest address. The direction is specified by an argument to the
* operation.
*
* For instance:
*
* @code
* map.at(100).limit(10).read(buf); // 1
* map.at(100).limit(10).read(buf, MATCH_BACKWARD); // 2
* @endcode
*
* Expression 1 reads up to 10 values such that the lowest value read is at address 100, while expression 2 reads up to 10
* values such that the highest value read is at address 100. In both cases, if address 100 is not mapped (or otherwise
* does not satisfy the constraints) then nothing is read.
*
* @{ */
AddressMapConstraints<const AddressMap> at(const Sawyer::Container::Interval<Address> &x) const {
return AddressMapConstraints<const AddressMap>(this).at(x);
}
AddressMapConstraints<AddressMap> at(const Sawyer::Container::Interval<Address> &x) {
return AddressMapConstraints<AddressMap>(this).at(x);
}
/** @} */
/** Constraint: limit matched size.
*
* Constrains the matched addresses so that at most @p x addresses match. Forward matching matches the first @p x
* addresses while backward matching matches the last @p x addresses.
*
* @{ */
AddressMapConstraints<const AddressMap> limit(size_t x) const {
return AddressMapConstraints<const AddressMap>(this).limit(x);
}
AddressMapConstraints<AddressMap> limit(size_t x) {
return AddressMapConstraints<AddressMap>(this).limit(x);
}
/** @} */
/** Constraint: address lower bound.
*
* Constrains matched addresses so that they are all greater than or equal to @p x.
*
* @{ */
AddressMapConstraints<const AddressMap> atOrAfter(Address x) const {
return AddressMapConstraints<const AddressMap>(this).atOrAfter(x);
}
AddressMapConstraints<AddressMap> atOrAfter(Address x) {
return AddressMapConstraints<AddressMap>(this).atOrAfter(x);
}
/** @} */
/** Constraint: address upper bound.
*
* Constrains matched addresses so that they are all less than or equal to @p x.
*
* @{ */
AddressMapConstraints<const AddressMap> atOrBefore(Address x) const {
return AddressMapConstraints<const AddressMap>(this).atOrBefore(x);
}
AddressMapConstraints<AddressMap> atOrBefore(Address x) {
return AddressMapConstraints<AddressMap>(this).atOrBefore(x);
}
/** @} */
/** Constraint: address lower and upper bounds.
*
* Constrains matched addresses so they are all within the specified interval.
*
* @{ */
AddressMapConstraints<const AddressMap> within(const Sawyer::Container::Interval<Address> &x) const {
return AddressMapConstraints<const AddressMap>(this).within(x);
}
AddressMapConstraints<AddressMap> within(const Sawyer::Container::Interval<Address> &x) {
return AddressMapConstraints<AddressMap>(this).within(x);
}
/** @} */
/** Constraint: address lower and upper bounds.
*
* Constrains matched addresses so they are all greater than or equal to @p x and less than or equal to @p y.
*
* @{ */
AddressMapConstraints<const AddressMap> within(Address x, Address y) const {
return AddressMapConstraints<const AddressMap>(this).within(x, y);
}
AddressMapConstraints<AddressMap> within(Address x, Address y) {
return AddressMapConstraints<AddressMap>(this).within(x, y);
}
/** @} */
/** Constraint: address lower and upper bounds.
*
* Specifies lower and upper bounds. The upper bound is specified indirectly by a size.
*
* @{ */
AddressMapConstraints<const AddressMap> baseSize(Address base, Address size) const {
return AddressMapConstraints<const AddressMap>(this).baseSize(base, size);
}
AddressMapConstraints<AddressMap> baseSize(Address base, Address size) {
return AddressMapConstraints<AddressMap>(this).baseSize(base, size);
}
/** @} */
/** Constraint: address lower bound.
*
* Constrains matched addresses so that they are all greater than @p x.
*
* @{ */
AddressMapConstraints<const AddressMap> after(Address x) const {
return AddressMapConstraints<const AddressMap>(this).after(x);
}
AddressMapConstraints<AddressMap> after(Address x) {
return AddressMapConstraints<AddressMap>(this).after(x);
}
/** @} */
/** Constraint: address upper bound.
*
* Constrains matched addresses so that they are all less than @p x.
*
* @{ */
AddressMapConstraints<const AddressMap> before(Address x) const {
return AddressMapConstraints<const AddressMap>(this).before(x);
}
AddressMapConstraints<AddressMap> before(Address x) {
return AddressMapConstraints<AddressMap>(this).before(x);
}
/** @} */
/** Constraint: single segment.
*
* Constrains matched addresses so that they all come from the same segment.
*
* @{ */
AddressMapConstraints<const AddressMap> singleSegment() const {
return AddressMapConstraints<const AddressMap>(this).singleSegment();
}
AddressMapConstraints<AddressMap> singleSegment() {
return AddressMapConstraints<AddressMap>(this).singleSegment();
}
/** @} */
/** Constraint: arbitrary segment constraint.
*
* Constraints matched addresses to those for which the chain of segment predicates return true.
*
* @{ */
AddressMapConstraints<const AddressMap> segmentPredicate(SegmentPredicate<Address, Value> *p) const {
return AddressMapConstraints<const AddressMap>(this).segmentPredicate(p);
}
AddressMapConstraints<AddressMap> segmentPredicate(SegmentPredicate<Address, Value> *p) {
return AddressMapConstraints<AddressMap>(this).segmentPredicate(p);
}
/** @} */
/** Constraint: matches anything.
*
* The null constraint matches any mapped address.
*
* @{ */
AddressMapConstraints<const AddressMap> any() const {
return AddressMapConstraints<const AddressMap>(this);
}
AddressMapConstraints<AddressMap> any() {
return AddressMapConstraints<AddressMap>(this);
}
/** @} */
/** Constraint: matches nothing.
*
* Constrains addresses so that none of them can match.
*
* @{ */
AddressMapConstraints<const AddressMap> none() const {
return AddressMapConstraints<const AddressMap>(this).none();
}
AddressMapConstraints<AddressMap> none() {
return AddressMapConstraints<AddressMap>(this).none();
}
/** @} */
/** Check map consistency.
*
* Performs the following consistency checks and throws an <code>std::runtime_error</code> if something is wrong.
*
* @li A segment should not have a null buffer pointer.
*
* @li Checks that the buffers of the map are appropriate sizes for the address interval in which they're mapped. */
void checkConsistency() const {
DYN_FOREACH (const Node &node, nodes()) {
const Sawyer::Container::Interval<Address> &interval = node.key();
const Segment &segment = node.value();
if (segment.buffer()==NULL) {
throw std::runtime_error("AddressMap null buffer for interval [" +
dyncompat::lexical_cast<std::string>(interval.least()) +
"," + dyncompat::lexical_cast<std::string>(interval.greatest()) + "]");
}
Address bufAvail = segment.buffer()->available(segment.offset());
if (bufAvail < interval.size()) {
throw std::runtime_error("AddressMap segment at [" + dyncompat::lexical_cast<std::string>(interval.least()) +
"," + dyncompat::lexical_cast<std::string>(interval.greatest()) + "] points to only " +
dyncompat::lexical_cast<std::string>(bufAvail) + (1==bufAvail?" value":" values") +
" but the interval size is " + dyncompat::lexical_cast<std::string>(interval.size()));
}
}
}
/** Number of segments contained in the map.
*
* Multiple segments may be pointing to the same underlying buffer, and the number of segments is not necessarily the same
* as the net number of segments inserted and erased. For instance, if a segment is inserted for addresses [0,99] and
* then a different segment is inserted at [50,59], the map will contain three segments at addresses [0,49], [50,59], and
* [60,99], although the first and third segment point into different parts of the same buffer. */
Address nSegments() const { return this->nIntervals(); }
/** Iterator range for all segments.
*
* This is just an alias for the @ref values method defined in the super class.
*
* @{ */
dyncompat::iterator_range<SegmentIterator> segments() { return this->values(); }
dyncompat::iterator_range<ConstSegmentIterator> segments() const { return this->values(); }
/** Segments that overlap with constraints.
*
* Returns an iterator range for the first longest sequence of segments that all at least partly satisfy the specified
* constraints. Constraints are always matched at the address level and the return value consists of those segments that
* contain at least one matched address. Constraints normally match contiguous addresses, and therefore the returned list
* will be segments that are contiguous. Disabling the contiguous constraint with the @c MATCH_NONCONTIGUOUS flag relaxes
* the requirement that addresses be contiguous, although it still enforces that the matched interval contains only
* addresses that satisfy the constraints or addresses that are not mapped.
*
* The following example finds the first sequence of one or more segments having "IAT" as a substring in their name and
* returns the longest sequence at that position. The sequence is then used to remove execute permission from each
* segment.
*
* @code
* typedef AddressMap<Address,Value>::Segment Segment;
* DYN_FOREACH (Segment &segment, map.substr("IAT").segments(MATCH_NONCONTIGUOUS))
* segment.accessibility(segment.accessibility() & ~EXECUTABLE);
* @endcode
*
* @{ */
dyncompat::iterator_range<ConstSegmentIterator>
segments(const AddressMapConstraints<const AddressMap> &c, MatchFlags flags=0) const {
using namespace AddressMapImpl;
if (0==(flags & (MATCH_CONTIGUOUS|MATCH_NONCONTIGUOUS)))
flags |= MATCH_CONTIGUOUS;
MatchedConstraints<const AddressMap> m = matchConstraints(*this, c, flags);
return dyncompat::iterator_range<ConstSegmentIterator>(m.nodes_.begin(), m.nodes_.end());
}
dyncompat::iterator_range<SegmentIterator>
segments(const AddressMapConstraints<AddressMap> &c, MatchFlags flags=0) {
using namespace AddressMapImpl;
if (0==(flags & (MATCH_CONTIGUOUS|MATCH_NONCONTIGUOUS)))
flags |= MATCH_CONTIGUOUS;
MatchedConstraints<AddressMap> m = matchConstraints(*this, c, flags);
return dyncompat::iterator_range<SegmentIterator>(m.nodes_);
}
/** @} */
/** Iterator range for nodes.
*
* This is just an alias for the @ref nodes method defined in the super class. See also the overloaded method of the same
* name that takes a constraint and thus returns only some nodes.
*
* @{ */
dyncompat::iterator_range<NodeIterator> nodes() { return Super::nodes(); }
dyncompat::iterator_range<ConstNodeIterator> nodes() const { return Super::nodes(); }
/** @} */
/** Nodes that overlap with constraints.
*
* Returns an iterator range for the first longest sequence of interval/segment nodes that all at least partly satisfy the
* specified constraints. Constraints are always matched at the address level and the return value consists of those
* nodes that contain at least one matched address. Constraints normally match contiguous addresses, and therefore the
* returned list will be nodes that are contiguous. Disabling the contiguous constraint with the @c MATCH_NONCONTIGUOUS
* flag relaxes the requirement that addresses be contiguous, although it still enforces that the matched interval
* contains only addresses that satisfy the constraints or addresses that are not mapped.
*
* The following example finds the first sequence of one or more segments having addresses between 1000 and 2000 and "IAT"
* as part of their name and prints their address interval and name:
*
* @code
* typedef AddressMap<Address,Value>::Node Node;
* DYN_FOREACH (const Node &node, map.within(1000,2000).substr("IAT").nodes(MATCH_NONCONTIGUOUS))
* std::cout <<"segment at " <<node.key() <<" named " <<node.value().name() <<"\n";
* @endcode
*
* @{ */
dyncompat::iterator_range<ConstNodeIterator>
nodes(const AddressMapConstraints<const AddressMap> &c, MatchFlags flags=0) const {
using namespace AddressMapImpl;
if (0==(flags & (MATCH_CONTIGUOUS|MATCH_NONCONTIGUOUS)))
flags |= MATCH_CONTIGUOUS;
MatchedConstraints<const AddressMap> m = matchConstraints(*this, c, flags);
return m.nodes_;
}
dyncompat::iterator_range<NodeIterator>
nodes(const AddressMapConstraints<AddressMap> &c, MatchFlags flags=0) {
using namespace AddressMapImpl;
if (0==(flags & (MATCH_CONTIGUOUS|MATCH_NONCONTIGUOUS)))
flags |= MATCH_CONTIGUOUS;
MatchedConstraints<AddressMap> m = matchConstraints(*this, c, flags);
return m.nodes_;
}
/** @} */
/** Minimum or maximum address that satisfies constraints.
*
* This method returns the minimum or maximum address that satisfies the constraints, depending on whether the
* direction is forward or backward. It is named "next" because it is often used in loops that iterate over addresses.
* For instance, the following loop iterates over all readable addresses one at a time (there are more efficient ways to
* do this).
*
* @code
* typedef AddressMap<Address,Value> Map;
* Map map = ...;
* for (Address a=0; map.atOrAfter(a).require(READABLE).next().assignTo(a); ++a) {
* ...
* if (a == map.hull().greatest())
* break;
* }
* @endcode
*
* The conditional break at the end of the loop is to handle the case where @c a is the largest possible address, and
* incrementing it would result in an overflow back to a smaller address. The @ref hull method returns in constant time,
* but a slightly faster test (that is also more self-documenting) is:
*
* @code
* if (a == dyncompat::integer_traits<Address>::const_max)
* break;
* @endcode
*
* Backward iterating is similar:
*
* @code
* typedef AddressMap<Address,Value> Map;
* Map map = ...;
* for (Address a=map.hull().greatest(); map.atOrBefore(a).require(READABLE).next(MATCH_BACKWARD).assignTo(a); --a) {
* ...
* if (a == map.hull().least())
* break;
* }
* @endcode */
Optional<Address>
next(const AddressMapConstraints<const AddressMap> &c, MatchFlags flags=0) const {
using namespace AddressMapImpl;
if (0==(flags & (MATCH_CONTIGUOUS|MATCH_NONCONTIGUOUS)))
flags |= MATCH_CONTIGUOUS;
MatchedConstraints<const AddressMap> m = matchConstraints(*this, c.limit(1), flags);
return m.interval_.isEmpty() ? Optional<Address>() : Optional<Address>(m.interval_.least());
}
/** Adress interval that satisfies constraints.
*
* Returns the lowest or highest (depending on direction) largest address interval that satisfies the specified
* constraints. The interval can be contiguous (the default), or it may contain unmapped addresses. In any case, all
* mapped addresses in the returned interval satisfy the constraints. */
Sawyer::Container::Interval<Address>
available(const AddressMapConstraints<const AddressMap> &c, MatchFlags flags=0) const {
using namespace AddressMapImpl;
if (0==(flags & (MATCH_CONTIGUOUS|MATCH_NONCONTIGUOUS)))
flags |= MATCH_CONTIGUOUS;
return matchConstraints(*this, c, flags).interval_;
}
/** Determines if an address exists with the specified constraints.
*
* Checking for existence is just a wrapper around next. For instance, these two statements both check whether the
* address 1000 exists and has execute permission:
*
* @code
* if (map.at(1000).require(EXECUTABLE).exists()) ...
* if (map.at(1000).require(EXECUTABLE).next()) ...
* @endcode */
bool exists(const AddressMapConstraints<const AddressMap> &c, MatchFlags flags=0) const {
return next(c, flags);
}
/** Find node containing address.
*
* Finds the node that contains the first (or last, depending on direction) address that satisfies the constraints.
*
* @{ */
ConstNodeIterator findNode(const AddressMapConstraints<const AddressMap> &c, MatchFlags flags=0) const {
return nodes(c.limit(1), flags).begin();
}
NodeIterator findNode(const AddressMapConstraints<AddressMap> &c, MatchFlags flags=0) {
return nodes(c.limit(1), flags).begin();
}
/** @} */
/** Find unmapped interval.
*
* Searches for the lowest (or highest if direction is @c MATCH_BACKWARD) interval that is not mapped and returns its
* address and size. The returned interval will not contain addresses that are less than (or greater than) than @p
* boundary. If no such unmapped intervals exist then the empty interval is returned.
*
* This method does not use constraints since it searches for addresses that do not exist in the map. */
Sawyer::Container::Interval<Address>
unmapped(Address boundary, MatchFlags flags=0) const {
return (flags & MATCH_BACKWARD) != 0 ? this->lastUnmapped(boundary) : this->firstUnmapped(boundary);
}
/** Find free space.
*
* Finds a suitable region of unmapped address space in which @p nValues values can be mapped. The return value is either
* an address where the values can be mapped, or nothing if no such unmapped region is available. The @p restriction can
* be used to restrict which addresses are considered. The return value will have the specified alignment and will be
* either the lowest or highest possible address depending on whether direction is forward or backward.
*
* This method does not use constraints since it searches for addresses that do not exist in the map. */
Optional<Address>
findFreeSpace(size_t nValues, size_t alignment=1,
Sawyer::Container::Interval<Address> restriction = Sawyer::Container::Interval<Address>::whole(),
MatchFlags flags=0) const {
static const Sawyer::Container::Interval<Address> whole = Sawyer::Container::Interval<Address>::whole();
ASSERT_forbid2(nValues == 0, "cannot determine if this is an overflow or intentional");
if (restriction.isEmpty())
return Nothing();
if (0 == (flags & MATCH_BACKWARD)) {
Address minAddr = restriction.least();
while (minAddr <= restriction.greatest()) {
Sawyer::Container::Interval<Address> interval = unmapped(minAddr, 0 /*forward*/);
if (interval.isEmpty())
return Nothing();
minAddr = alignUp(interval.least(), alignment);
Address maxAddr = minAddr + (nValues-1);
if ((nValues <= interval.size() || 0==interval.size()/*overflow*/) &&
minAddr >= interval.least()/*overflow*/ && maxAddr >= interval.least()/*overflow*/ &&
maxAddr <= interval.greatest()) {
return minAddr;
}
if (interval.greatest() == whole.greatest())
return Nothing(); // to avoid overflow in next statement
minAddr = interval.greatest() + 1;
}
return Nothing();
}
ASSERT_require((flags & MATCH_BACKWARD) != 0);
Address maxAddr = restriction.greatest();
while (maxAddr >= restriction.least()) {
Sawyer::Container::Interval<Address> interval = unmapped(maxAddr, MATCH_BACKWARD);
if (interval.isEmpty())
return Nothing();
Address minAddr = alignDown(interval.greatest() - (nValues-1), alignment);
maxAddr = minAddr + (nValues-1);
if ((nValues <= interval.size() || 0==interval.size()/*overflow*/) &&
minAddr >= interval.least()/*overflow*/ && maxAddr >= interval.least()/*overflow*/ &&
maxAddr <= interval.greatest()) {
return minAddr;
}
if (interval.least() == whole.least())
return Nothing(); // to avoid overflow in next statement
maxAddr = interval.least() - 1;
}
return Nothing();
}
/** Base class for traversals. */
class Visitor {
public:
virtual ~Visitor() {}
virtual bool operator()(const AddressMap&, const Sawyer::Container::Interval<Address>&) = 0;
};
/** Invoke a function on each address interval.
*
* The functor is invoked with the following arguments: the memory map and an interval. If the functor returns false then
* the traversal is terminated. To facilitate the use of function-local types for the functor without requiring the use
* of explicit template parameters, one may pass a subclass of @ref Visitor as the functor.
*
* This example shows one way to print the names of segments that overlap with a given interval:
*
* @code
* typedef AddressMap<unsigned, char> Map;
* struct: Visitor {
* bool operator()(const Map &map, const Interval<unsigned> &interval) {
* const Map::Segment &segment = map.at(interval.least()).findNode()->value();
* std::cerr <<"segment \"" <<segment.name() <<"\"\n";
* return true;
* }
* } visitor;
* Map map = ...;
* Interval<unsigned> where = ...;
* map.within(where).traverse(visitor);
* @endcode
*
* @{ */
template<typename Functor>
void traverse(Functor &functor, const AddressMapConstraints<const AddressMap> &c, MatchFlags flags=0) const {
using namespace AddressMapImpl;
MatchedConstraints<const AddressMap> m = matchConstraints(*this, c, flags);
DYN_FOREACH (const Node &node, m.nodes_) {
Sawyer::Container::Interval<Address> part = m.interval_ & node.key();
if (!functor(*this, part))
return;
}
return;
}
template<typename Functor>
void traverse(Functor &functor, const AddressMapConstraints<AddressMap> &c, MatchFlags flags=0) {
using namespace AddressMapImpl;
MatchedConstraints<AddressMap> m = matchConstraints(*this, c, flags);
DYN_FOREACH (const Node &node, m.nodes_) {
Sawyer::Container::Interval<Address> part = m.interval_ & node.key();
if (!functor(*this, part))
return;
}
return;
}
void traverse(Visitor &visitor, const AddressMapConstraints<const AddressMap> &c, MatchFlags flags=0) const {
traverse<Visitor>(visitor, c, flags);
}
void traverse(Visitor &visitor, const AddressMapConstraints<AddressMap> &c, MatchFlags flags=0) {
traverse<Visitor>(visitor, c, flags);
}
/** @} */
/** Reads data into the supplied buffer.
*
* Reads data into an array or STL vector according to the specified constraints. If the array is a null pointer then no
* data is read or copied and the return value indicates what addresses would have been accessed. When the buffer is an
* STL vector the constraints are augmented by also limiting the number of items accessed; the caller must do that
* explicitly for arrays. The return value is the interval of addresses that were read.
*
* The constraints are usually curried before the actual read call, as in this example that reads up to 10 values starting
* at some address and returns the number of values read:
*
* @code
* Value buf[10];
* size_t nRead = map.at(start).limit(10).read(buf).size();
* @endcode
*
* The following loop reads and prints all the readable values from a memory map using a large buffer for efficiency:
*
* @code
* std::vector<Value> buf(1024);
* while (Interval<Address> accessed = map.atOrAfter(a).read(buf)) {
* a = accessed.least();
* DYN_FOREACH (const Value &v, buf)
* std::cout <<a++ <<": " <<v <<"\n";
* if (accessed.greatest()==map.hull().greatest())
* break; // to handle case when a++ overflowed
* }
* @endcode
*
* Reading can also be performed backward, such as this example that reads up to ten values such that the last value read
* is at address 999. The buffer will always contain results in address order, with the first element of the buffer being
* the value that was read with the lowest address.
*
* @code
* Value buf[10];
* size_t nRead = map.at(999).limit(10).read(buf, MATCH_BACKWARD).size();
* @endcode
*
* @{ */
Sawyer::Container::Interval<Address>
read(Value *buf /*out*/, const AddressMapConstraints<const AddressMap> &c, MatchFlags flags=0) const {
using namespace AddressMapImpl;
ASSERT_require2(0 == (flags & MATCH_NONCONTIGUOUS), "only contiguous addresses can be read");
if (0==(flags & (MATCH_CONTIGUOUS|MATCH_NONCONTIGUOUS)))
flags |= MATCH_CONTIGUOUS;
MatchedConstraints<const AddressMap> m = matchConstraints(*this, c, flags);
if (buf) {
DYN_FOREACH (const Node &node, m.nodes_) {
Sawyer::Container::Interval<Address> part = m.interval_ & node.key(); // part of segment to read
ASSERT_forbid(part.isEmpty());
Address bufferOffset = part.least() - node.key().least() + node.value().offset();
Address nValues = node.value().buffer()->read(buf, bufferOffset, part.size());
if (nValues != part.size()) {
checkConsistency();
ASSERT_not_reachable("something is wrong with the memory map");
}
buf += nValues;
}
}
return m.interval_;
}
Sawyer::Container::Interval<Address>
read(std::vector<Value> &buf /*out*/, const AddressMapConstraints<const AddressMap> &c, MatchFlags flags=0) const {
return buf.empty() ? Sawyer::Container::Interval<Address>() : read(&buf[0], c.limit(buf.size()), flags);
}
/** @} */
/** Writes data from the supplied buffer.
*
* Copies data from an array or STL vector into the underlying address map buffers corresponding to the specified
* constraints. If the array is a null pointer then no data is written and the return value indicates what addresses
* would have been accessed. The constraints are agumented by also requiring that the addresses be contiguous
* and lack the IMMUTABLE bit, and in the case of STL vectors that not more data is written thn what is in the vector.
* The return value is the interval of addresses that were written.
*
* The Access::IMMUTABLE bit is usually used to indicate that a buffer cannot be modified (for instance, the buffer is
* memory allocated with read-only access by POSIX @c mmap).
*
* The constraints are usually curried before the actual read call, as in this example that writes the vector's values
* into the map at the first writable address greater than or equal to 1000.
*
* @code
* std::vector<Value> buffer = {...};
* Interval<Address> written = map.atOrAfter(1000).require(WRITABLE).write(buffer);
* @endcode
*
* Writing can also be performed backward, such as this example that writes up to ten values such that the last value
* written is at address 999. The buffer contains values in their address order.
*
* @code
* Value buf[10] = { ... };
* size_t nWritten = map.at(999).limit(10).write(buf, MATCH_BACKWARD).size();
* @endcode
*
* @todo FIXME[Robb Matzke 2014-09-01]: The order of values in the buffer being written by AddressMap::write when writing
* in a backward direction is not all that useful. Perhaps the write should consume values from the end of the buffer
* instead of the beginning.
*
* @{ */
Sawyer::Container::Interval<Address>
write(const Value *buf, const AddressMapConstraints<AddressMap> &c, MatchFlags flags=0) {
using namespace AddressMapImpl;
ASSERT_require2(0 == (flags & MATCH_NONCONTIGUOUS), "only contiguous addresses can be written");
if (0==(flags & (MATCH_CONTIGUOUS|MATCH_NONCONTIGUOUS)))
flags |= MATCH_CONTIGUOUS;
MatchedConstraints<AddressMap> m = matchConstraints(*this, c.prohibit(Access::IMMUTABLE), flags);
if (buf) {
DYN_FOREACH (Node &node, m.nodes_) {
Segment &segment = node.value();
Sawyer::Container::Interval<Address> part = m.interval_ & node.key(); // part of segment to write
ASSERT_forbid(part.isEmpty());
typename Buffer::Ptr buffer = segment.buffer();
ASSERT_not_null(buffer);
if (buffer->copyOnWrite()) {
typename Buffer::Ptr newBuffer = buffer->copy(); // copyOnWrite is cleared in newBuffer
ASSERT_not_null(newBuffer);
for (NodeIterator iter=this->lowerBound(node.key().least()); iter!=nodes().end(); ++iter) {
if (iter->value().buffer() == buffer)
iter->value().buffer(newBuffer);
}
buffer = newBuffer;
}
Address bufferOffset = part.least() - node.key().least() + segment.offset();
Address nValues = buffer->write(buf, bufferOffset, part.size());
if (nValues != part.size()) {
checkConsistency();
ASSERT_not_reachable("something is wrong with the memory map");
}
buf += nValues;
}
}
return m.interval_;
}
Sawyer::Container::Interval<Address>
write(const std::vector<Value> &buf, const AddressMapConstraints<AddressMap> &c, MatchFlags flags=0) {
return buf.empty() ? Sawyer::Container::Interval<Address>() : write(&buf[0], c.limit(buf.size()), flags);
}
/** @} */
/** Prune away addresses that match constraints.
*
* Removes all addresses for which the constraints match. The addresses need not be contiguous in memory (in fact,
* noncontiguous is the default), and the matching segments need not be consecutive segments. In other words, the
* interval over which this function operates can include segments that do not satisfy the constraints (and are not
* pruned). For instance, to remove all segments that are writable regardless of whether other segments are interspersed:
*
* @code
* map.require(WRITABLE).contiguous(false).prune();
* @endcode
*
* @sa keep */
void prune(const AddressMapConstraints<AddressMap> &c, MatchFlags flags=0) {
using namespace AddressMapImpl;
IntervalSet<Sawyer::Container::Interval<Address> > toErase;
if (0==(flags & (MATCH_CONTIGUOUS|MATCH_NONCONTIGUOUS)))
flags |= MATCH_NONCONTIGUOUS;
MatchedConstraints<AddressMap> m = matchConstraints(*this, c.addressConstraints(), flags);
DYN_FOREACH (const Node &node, m.nodes_) {
if (isSatisfied(node, c))
toErase.insert(node.key() & m.interval_);
}
DYN_FOREACH (const Sawyer::Container::Interval<Address> &interval, toErase.intervals())
this->erase(interval);
}
/** Keep only addresses that match constraints.
*
* Keeps only those addresses that satisfy the given constraints, discarding all others. The addresses need not be
* contiguous (in fact, noncontiguous is the default), and the matching segments need not be consecutive segments. In
* other words, the interval over which this function operates can include segments that do not satisfy the constraints
* (and are pruned). For instance, to remove all segments that are not writable regardless of whether other segments are
* interspersed:
*
* @code
* map.require(WRITABLE).contiguous(false).keep();
* @endcode
*
* @sa prune */
void keep(const AddressMapConstraints<AddressMap> &c, MatchFlags flags=0) {
using namespace AddressMapImpl;
if (0==(flags & (MATCH_CONTIGUOUS|MATCH_NONCONTIGUOUS)))
flags |= MATCH_NONCONTIGUOUS;
IntervalSet<Sawyer::Container::Interval<Address> > toKeep;
MatchedConstraints<AddressMap> m = matchConstraints(*this, c.addressConstraints(), flags);
DYN_FOREACH (const Node &node, m.nodes_) {
if (isSatisfied(node, c))
toKeep.insert(node.key() & m.interval_);
}
toKeep.invert();
DYN_FOREACH (const Sawyer::Container::Interval<Address> &interval, toKeep.intervals())
this->erase(interval);
}
/** Change access bits for addresses that match constraints.
*
* For all addresses that satisfy the specified constraints, add the @p requiredAccess and remove the @p prohibitedAccess
* bits. The addresses need not be contiguous (in fact, noncontiguous is the default), and the matching segments need not
* be consecutive segments. In other words, the interval over which this function operates can include addresses that do
* not satisfy the constraints and whose access bits are not modified. For instance, to add execute permission and remove
* write permission for all segments containing the string ".text":
*
* @code
* map.substr(".text").changeAccess(EXECUTABLE, WRITABLE);
* @endcode
*
* To set access bits to a specific value, supply the complement as the second argument. The following code changes all
* addresses between a specified range so that only the READABLE and WRITABLE bits are set and no others:
*
* @code
* unsigned newAccess = READABLE | WRITABLE;
* map.within(100,200).changeAccess(newAccess, ~newAccess);
* @endcode */
void changeAccess(unsigned requiredAccess, unsigned prohibitedAccess, const AddressMapConstraints<AddressMap> &c,
MatchFlags flags=0) {
using namespace AddressMapImpl;
if (0==(flags & (MATCH_CONTIGUOUS|MATCH_NONCONTIGUOUS)))
flags |= MATCH_NONCONTIGUOUS;
typedef std::pair<Sawyer::Container::Interval<Address>, Segment> ISPair;
std::vector<ISPair> newSegments;
MatchedConstraints<AddressMap> m = matchConstraints(*this, c.addressConstraints(), flags);
DYN_FOREACH (Node &node, m.nodes_) {
Segment &segment = node.value();
if (isSatisfied(node, c)) {
unsigned newAccess = (segment.accessibility() | requiredAccess) & ~prohibitedAccess;
Sawyer::Container::Interval<Address> toChange = node.key() & m.interval_;
if (toChange == node.key()) { // all addresses in segment are selected; change segment in place
segment.accessibility(newAccess);
} else { // insert a new segment, replacing part of the existing one
Segment newSegment(segment);
newSegment.accessibility(newAccess);
newSegment.offset(segment.offset() + toChange.least() - node.key().least());
newSegments.push_back(ISPair(toChange, newSegment));
}
}
}
DYN_FOREACH (const ISPair &pair, newSegments)
this->insert(pair.first, pair.second);
}
private:
// Increment x if necessary so it is aligned.
static Address alignUp(Address x, Address alignment) {
return alignment>0 && x%alignment!=0 ? ((x+alignment-1)/alignment)*alignment : x;
}
static Address alignDown(Address x, Address alignment) {
return alignment>0 && x%alignment!=0 ? (x/alignment)*alignment : x;
}
};
} // namespace
} // namespace
#endif