// 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 #include #include #include #include #include #include #include #include #include #include namespace Sawyer { namespace Container { template struct AddressMapTraits { typedef typename AddressMap::NodeIterator NodeIterator; typedef typename AddressMap::SegmentIterator SegmentIterator; }; template struct AddressMapTraits { 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 SegmentPredicate { public: struct Args { const Sawyer::Container::Interval &interval; const AddressSegment &segment; Args(const Sawyer::Container::Interval &interval, const AddressSegment &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 class AddressMapConstraints { public: typedef typename AddressMap::Address Address; typedef typename AddressMap::Value Value; typedef Sawyer::Container::Interval
AddressInterval; private: AddressMap *map_; // AddressMap<> to which these constraints are bound bool never_; // never match anything (e.g., when least_ > greatest_) // address constraints Optional
least_; // least possible valid address Optional
greatest_; // greatest possible valid address Optional 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*> 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 { AddressMapConstraints 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=" <isEmpty() ? retval.none() : retval; } /** Anchor at a certain interval. See @ref AddressMap::at. */ AddressMapConstraints at(const Sawyer::Container::Interval
&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
&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
::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
::const_max ? none() : atOrAfter(x+1); } /** Limit addreses. See @ref AddressMap::before. */ AddressMapConstraints before(Address x) const { return x==dyncompat::integer_traits
::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 *p) const { if (!p) return none(); AddressMapConstraints retval = *this; retval.segmentPredicates_.append(p); return retval; } AddressMapConstraints segmentPredicates(const Callbacks*> &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
& 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
& greatest() const { return greatest_; } /** Returns true if the single-segment constraint is set. */ bool isSingleSegment() const { return singleSegment_; } /** Returns the segment predicates. */ const Callbacks*> 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::NodeIterator> nodes(MatchFlags flags=0) const { return map_->nodes(*this, flags); } dyncompat::iterator_range::SegmentIterator> segments(MatchFlags flags=0) const { return map_->segments(*this, flags); } Optional next(MatchFlags flags=0) const { return map_->next(*this, flags); } Sawyer::Container::Interval
available(MatchFlags flags=0) const { return map_->available(*this, flags); } bool exists(MatchFlags flags=0) const { return map_->exists(*this, flags); } typename AddressMapTraits::NodeIterator findNode(MatchFlags flags=0) const { return map_->findNode(*this, flags); } template 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(visitor, *this, flags); } Sawyer::Container::Interval
read(typename AddressMap::Value *buf /*out*/, MatchFlags flags=0) const { return map_->read(buf, *this, flags); } Sawyer::Container::Interval
read(std::vector &buf /*out*/, MatchFlags flags=0) const { return map_->read(buf, *this, flags); } Sawyer::Container::Interval
write(const typename AddressMap::Value *buf, MatchFlags flags=0) const { return map_->write(buf, *this, flags); } Sawyer::Container::Interval
write(const std::vector &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 SegmentMergePolicy { public: typedef A Address; typedef T Value; typedef AddressSegment Segment; bool merge(const Sawyer::Container::Interval
&leftInterval, Segment &leftSegment, const Sawyer::Container::Interval
&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
&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
&interval, Segment &segment, Address splitPoint) { ASSERT_forbid(interval.isEmpty()); ASSERT_require(interval.isContaining(splitPoint)); } }; // The results for matching constraints template struct MatchedConstraints { typedef typename AddressMap::Address Address; Sawyer::Container::Interval
interval_; typedef typename AddressMapTraits::NodeIterator NodeIterator; dyncompat::iterator_range 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 typename AddressMapTraits::NodeIterator constraintLowerBound(AddressMap &amap, const AddressMapConstraints &c, bool useAnchor, typename AddressMap::Address &minAddr) { typedef typename AddressMapTraits::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 typename AddressMapTraits::NodeIterator constraintUpperBound(AddressMap &amap, const AddressMapConstraints &c, bool useAnchor, typename AddressMap::Address &maxAddr) { typedef typename AddressMapTraits::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 bool isSatisfied(const typename AddressMap::Node &node, const AddressMapConstraints &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::Args(node.key(), node.value()))) return false; // user-supplied predicates failed return true; } // Matches constraints against contiguous addresses in a forward direction. template MatchedConstraints matchForward(AddressMap &amap, const AddressMapConstraints &c, MatchFlags flags) { typedef typename AddressMap::Address Address; typedef typename AddressMapTraits::NodeIterator Iterator; MatchedConstraints retval; retval.nodes_ = dyncompat::iterator_range(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
::hull(minAddr, maxAddr); retval.nodes_ = dyncompat::iterator_range(begin, end); return retval; } // Matches constraints against contiguous addresses in a backward direction. template MatchedConstraints matchBackward(AddressMap &amap, const AddressMapConstraints &c, MatchFlags flags) { typedef typename AddressMap::Address Address; typedef typename AddressMapTraits::NodeIterator Iterator; MatchedConstraints retval; retval.nodes_ = dyncompat::iterator_range(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
::hull(minAddr, maxAddr); retval.nodes_ = dyncompat::iterator_range(begin, end); return retval; } // Match constraints forward or backward template MatchedConstraints matchConstraints(AddressMap &amap, const AddressMapConstraints &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 * Interval and whose values are AddressSegment. 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
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
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 < found1 = map.within(100,200).require(READABLE).available(MATCH_CONTIGUOUS); * Interval
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
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
Addresses; * typedef Buffer::Ptr BufferPtr; * typedef AddressSegment Segment; * typedef AddressMap MemoryMap; * * // Create some buffer objects * char data1[15]; * memcpy(data1, "---------------", 15); * BufferPtr buf1 = Sawyer::Container::StaticBuffer::instance(data1, 15); * char data2[5]; * memcpy(data2, "##########", 10); * BufferPtr buf2 = Sawyer::Container::StaticBuffer::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 %Interval 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 %Interval where Microsoft compilers go wrong. */ template class AddressMap: public IntervalMap, AddressSegment, AddressMapImpl::SegmentMergePolicy > { // "Interval" is qualified to work around bug in Microsoft compilers. See doxygen note above. typedef IntervalMap, AddressSegment, AddressMapImpl::SegmentMergePolicy > 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 Segment; /**< Type of segments stored by this map. */ typedef Sawyer::Container::Buffer 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 require(unsigned x) const { return AddressMapConstraints(this).require(x); } AddressMapConstraints require(unsigned x) { return AddressMapConstraints(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 prohibit(unsigned x) const { return AddressMapConstraints(this).prohibit(x); } AddressMapConstraints prohibit(unsigned x) { return AddressMapConstraints(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 access(unsigned x) const { return AddressMapConstraints(this).access(x); } AddressMapConstraints access(unsigned x) { return AddressMapConstraints(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 substr(const std::string &x) const { return AddressMapConstraints(this).substr(x); } AddressMapConstraints substr(const std::string &x) { return AddressMapConstraints(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 at(Address x) const { return AddressMapConstraints(this).at(x); } AddressMapConstraints at(Address x) { return AddressMapConstraints(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 at(const Sawyer::Container::Interval
&x) const { return AddressMapConstraints(this).at(x); } AddressMapConstraints at(const Sawyer::Container::Interval
&x) { return AddressMapConstraints(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 limit(size_t x) const { return AddressMapConstraints(this).limit(x); } AddressMapConstraints limit(size_t x) { return AddressMapConstraints(this).limit(x); } /** @} */ /** Constraint: address lower bound. * * Constrains matched addresses so that they are all greater than or equal to @p x. * * @{ */ AddressMapConstraints atOrAfter(Address x) const { return AddressMapConstraints(this).atOrAfter(x); } AddressMapConstraints atOrAfter(Address x) { return AddressMapConstraints(this).atOrAfter(x); } /** @} */ /** Constraint: address upper bound. * * Constrains matched addresses so that they are all less than or equal to @p x. * * @{ */ AddressMapConstraints atOrBefore(Address x) const { return AddressMapConstraints(this).atOrBefore(x); } AddressMapConstraints atOrBefore(Address x) { return AddressMapConstraints(this).atOrBefore(x); } /** @} */ /** Constraint: address lower and upper bounds. * * Constrains matched addresses so they are all within the specified interval. * * @{ */ AddressMapConstraints within(const Sawyer::Container::Interval
&x) const { return AddressMapConstraints(this).within(x); } AddressMapConstraints within(const Sawyer::Container::Interval
&x) { return AddressMapConstraints(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 within(Address x, Address y) const { return AddressMapConstraints(this).within(x, y); } AddressMapConstraints within(Address x, Address y) { return AddressMapConstraints(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 baseSize(Address base, Address size) const { return AddressMapConstraints(this).baseSize(base, size); } AddressMapConstraints baseSize(Address base, Address size) { return AddressMapConstraints(this).baseSize(base, size); } /** @} */ /** Constraint: address lower bound. * * Constrains matched addresses so that they are all greater than @p x. * * @{ */ AddressMapConstraints after(Address x) const { return AddressMapConstraints(this).after(x); } AddressMapConstraints after(Address x) { return AddressMapConstraints(this).after(x); } /** @} */ /** Constraint: address upper bound. * * Constrains matched addresses so that they are all less than @p x. * * @{ */ AddressMapConstraints before(Address x) const { return AddressMapConstraints(this).before(x); } AddressMapConstraints before(Address x) { return AddressMapConstraints(this).before(x); } /** @} */ /** Constraint: single segment. * * Constrains matched addresses so that they all come from the same segment. * * @{ */ AddressMapConstraints singleSegment() const { return AddressMapConstraints(this).singleSegment(); } AddressMapConstraints singleSegment() { return AddressMapConstraints(this).singleSegment(); } /** @} */ /** Constraint: arbitrary segment constraint. * * Constraints matched addresses to those for which the chain of segment predicates return true. * * @{ */ AddressMapConstraints segmentPredicate(SegmentPredicate *p) const { return AddressMapConstraints(this).segmentPredicate(p); } AddressMapConstraints segmentPredicate(SegmentPredicate *p) { return AddressMapConstraints(this).segmentPredicate(p); } /** @} */ /** Constraint: matches anything. * * The null constraint matches any mapped address. * * @{ */ AddressMapConstraints any() const { return AddressMapConstraints(this); } AddressMapConstraints any() { return AddressMapConstraints(this); } /** @} */ /** Constraint: matches nothing. * * Constrains addresses so that none of them can match. * * @{ */ AddressMapConstraints none() const { return AddressMapConstraints(this).none(); } AddressMapConstraints none() { return AddressMapConstraints(this).none(); } /** @} */ /** Check map consistency. * * Performs the following consistency checks and throws an std::runtime_error 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
&interval = node.key(); const Segment &segment = node.value(); if (segment.buffer()==NULL) { throw std::runtime_error("AddressMap null buffer for interval [" + dyncompat::lexical_cast(interval.least()) + "," + dyncompat::lexical_cast(interval.greatest()) + "]"); } Address bufAvail = segment.buffer()->available(segment.offset()); if (bufAvail < interval.size()) { throw std::runtime_error("AddressMap segment at [" + dyncompat::lexical_cast(interval.least()) + "," + dyncompat::lexical_cast(interval.greatest()) + "] points to only " + dyncompat::lexical_cast(bufAvail) + (1==bufAvail?" value":" values") + " but the interval size is " + dyncompat::lexical_cast(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 segments() { return this->values(); } dyncompat::iterator_range 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::Segment Segment; * DYN_FOREACH (Segment &segment, map.substr("IAT").segments(MATCH_NONCONTIGUOUS)) * segment.accessibility(segment.accessibility() & ~EXECUTABLE); * @endcode * * @{ */ dyncompat::iterator_range segments(const AddressMapConstraints &c, MatchFlags flags=0) const { using namespace AddressMapImpl; if (0==(flags & (MATCH_CONTIGUOUS|MATCH_NONCONTIGUOUS))) flags |= MATCH_CONTIGUOUS; MatchedConstraints m = matchConstraints(*this, c, flags); return dyncompat::iterator_range(m.nodes_.begin(), m.nodes_.end()); } dyncompat::iterator_range segments(const AddressMapConstraints &c, MatchFlags flags=0) { using namespace AddressMapImpl; if (0==(flags & (MATCH_CONTIGUOUS|MATCH_NONCONTIGUOUS))) flags |= MATCH_CONTIGUOUS; MatchedConstraints m = matchConstraints(*this, c, flags); return dyncompat::iterator_range(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 nodes() { return Super::nodes(); } dyncompat::iterator_range 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::Node Node; * DYN_FOREACH (const Node &node, map.within(1000,2000).substr("IAT").nodes(MATCH_NONCONTIGUOUS)) * std::cout <<"segment at " < nodes(const AddressMapConstraints &c, MatchFlags flags=0) const { using namespace AddressMapImpl; if (0==(flags & (MATCH_CONTIGUOUS|MATCH_NONCONTIGUOUS))) flags |= MATCH_CONTIGUOUS; MatchedConstraints m = matchConstraints(*this, c, flags); return m.nodes_; } dyncompat::iterator_range nodes(const AddressMapConstraints &c, MatchFlags flags=0) { using namespace AddressMapImpl; if (0==(flags & (MATCH_CONTIGUOUS|MATCH_NONCONTIGUOUS))) flags |= MATCH_CONTIGUOUS; MatchedConstraints 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 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
::const_max) * break; * @endcode * * Backward iterating is similar: * * @code * typedef AddressMap 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
next(const AddressMapConstraints &c, MatchFlags flags=0) const { using namespace AddressMapImpl; if (0==(flags & (MATCH_CONTIGUOUS|MATCH_NONCONTIGUOUS))) flags |= MATCH_CONTIGUOUS; MatchedConstraints m = matchConstraints(*this, c.limit(1), flags); return m.interval_.isEmpty() ? Optional
() : Optional
(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
available(const AddressMapConstraints &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 &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 &c, MatchFlags flags=0) const { return nodes(c.limit(1), flags).begin(); } NodeIterator findNode(const AddressMapConstraints &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
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
findFreeSpace(size_t nValues, size_t alignment=1, Sawyer::Container::Interval
restriction = Sawyer::Container::Interval
::whole(), MatchFlags flags=0) const { static const Sawyer::Container::Interval
whole = Sawyer::Container::Interval
::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
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
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
&) = 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 Map; * struct: Visitor { * bool operator()(const Map &map, const Interval &interval) { * const Map::Segment &segment = map.at(interval.least()).findNode()->value(); * std::cerr <<"segment \"" < where = ...; * map.within(where).traverse(visitor); * @endcode * * @{ */ template void traverse(Functor &functor, const AddressMapConstraints &c, MatchFlags flags=0) const { using namespace AddressMapImpl; MatchedConstraints m = matchConstraints(*this, c, flags); DYN_FOREACH (const Node &node, m.nodes_) { Sawyer::Container::Interval
part = m.interval_ & node.key(); if (!functor(*this, part)) return; } return; } template void traverse(Functor &functor, const AddressMapConstraints &c, MatchFlags flags=0) { using namespace AddressMapImpl; MatchedConstraints m = matchConstraints(*this, c, flags); DYN_FOREACH (const Node &node, m.nodes_) { Sawyer::Container::Interval
part = m.interval_ & node.key(); if (!functor(*this, part)) return; } return; } void traverse(Visitor &visitor, const AddressMapConstraints &c, MatchFlags flags=0) const { traverse(visitor, c, flags); } void traverse(Visitor &visitor, const AddressMapConstraints &c, MatchFlags flags=0) { traverse(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 buf(1024); * while (Interval
accessed = map.atOrAfter(a).read(buf)) { * a = accessed.least(); * DYN_FOREACH (const Value &v, buf) * std::cout < read(Value *buf /*out*/, const AddressMapConstraints &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 m = matchConstraints(*this, c, flags); if (buf) { DYN_FOREACH (const Node &node, m.nodes_) { Sawyer::Container::Interval
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
read(std::vector &buf /*out*/, const AddressMapConstraints &c, MatchFlags flags=0) const { return buf.empty() ? Sawyer::Container::Interval
() : 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 buffer = {...}; * Interval
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
write(const Value *buf, const AddressMapConstraints &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 m = matchConstraints(*this, c.prohibit(Access::IMMUTABLE), flags); if (buf) { DYN_FOREACH (Node &node, m.nodes_) { Segment &segment = node.value(); Sawyer::Container::Interval
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
write(const std::vector &buf, const AddressMapConstraints &c, MatchFlags flags=0) { return buf.empty() ? Sawyer::Container::Interval
() : 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 &c, MatchFlags flags=0) { using namespace AddressMapImpl; IntervalSet > toErase; if (0==(flags & (MATCH_CONTIGUOUS|MATCH_NONCONTIGUOUS))) flags |= MATCH_NONCONTIGUOUS; MatchedConstraints 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
&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 &c, MatchFlags flags=0) { using namespace AddressMapImpl; if (0==(flags & (MATCH_CONTIGUOUS|MATCH_NONCONTIGUOUS))) flags |= MATCH_NONCONTIGUOUS; IntervalSet > toKeep; MatchedConstraints 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
&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 &c, MatchFlags flags=0) { using namespace AddressMapImpl; if (0==(flags & (MATCH_CONTIGUOUS|MATCH_NONCONTIGUOUS))) flags |= MATCH_NONCONTIGUOUS; typedef std::pair, Segment> ISPair; std::vector newSegments; MatchedConstraints 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
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