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
1859 lines
85 KiB
C++
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
|