blob: e61cfcbbb71f226ed727537aa90eb90a1ae1acf5 [file] [log] [blame]
// Copyright 2012 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Declares AddressRange, AddressSpace and AddressRangeMap. AddressRange is
// a primitive used by AddressSpace and AddressRangeMap. AddressSpace is useful
// for maintaining a collection of objects that map to ranges of bytes in some
// finite address-space, ensuring that they do not collide. An AddressRangeMap
// is a specialized version of an AddressSpace where the stored objects are
// themselves AddressRanges, with special semantics for simplifying the
// representation.
#ifndef SYZYGY_CORE_ADDRESS_SPACE_H_
#define SYZYGY_CORE_ADDRESS_SPACE_H_
#include <algorithm>
#include <iosfwd>
#include <map>
#include <utility>
#include <vector>
#include "base/logging.h"
#include "syzygy/core/address_space_internal.h"
#include "syzygy/core/serialization.h"
namespace core {
// Forward declaration.
template <typename AddressType, typename SizeType> class AddressRange;
// An address space is a mapping from a set of non-overlapping address ranges
// (AddressSpace::Range), each of non-zero size, to an ItemType.
template <typename AddressType, typename SizeType, typename ItemType>
class AddressSpace {
public:
// Typedef we use for convenience throughout.
typedef AddressRange<AddressType, SizeType> Range;
typedef std::map<Range, ItemType> RangeMap;
typedef typename std::map<Range, ItemType>::iterator RangeMapIter;
typedef typename std::map<Range, ItemType>::const_iterator RangeMapConstIter;
typedef std::pair<RangeMapConstIter, RangeMapConstIter> RangeMapConstIterPair;
typedef std::pair<RangeMapIter, RangeMapIter> RangeMapIterPair;
// STL-like type definitions
// @{
typedef typename RangeMapIter iterator;
typedef typename RangeMapConstIter const_iterator;
typedef typename RangeMap::value_type value_type;
// @}
// Create an empty address space.
AddressSpace();
// Insert @p range mapping to @p item unless @p range intersects
// an existing range.
// @param range the range to insert.
// @param item the item to associate with @p range.
// @param ret_it on success, returns an iterator to the inserted item.
// @returns true iff @p range inserted.
bool Insert(const Range& range,
const ItemType& item,
typename RangeMap::iterator* ret_it = NULL);
// Insert @p range mapping to @p item or return the existing item exactly
// matching @p range.
//
// @param range the range of the item to get or insert.
// @param item the item to associate with @p range if none already exists.
// @param ret_it on success, returns an iterator to the found or inserted
// item if not NULL.
//
// @returns true if the {range, item} pair is inserted or if there exists
// an item exactly matching range; otherwise false, indicating that a
// conflict/error has been detected.
bool FindOrInsert(const Range& range,
const ItemType& item,
typename RangeMap::iterator* ret_it = NULL);
// Inserts @p range mapping to @p item, unless @p range intersects
// an existing range and does not contain it. Any existing ranges it contains
// will be removed. If a range exists that contains @p range, returns true
// and returns the iterator to that range.
// @param range the range to insert.
// @param item the item to associate with @p range.
// @param ret_it on success, returns an iterator to the inserted item.
// @returns true on success.
//
// Example insertions:
//
// Existing : aaaa bbbb
// Inserting: xxxxxxxxxxxx
// Result : cccccccccccc
//
// Existing : aaaaaa bbbbbb
// Inserting: xxxxxxxxxx
// Result : failure!
bool SubsumeInsert(const Range& range,
const ItemType& item,
typename RangeMap::iterator* ret_it = NULL);
// Inserts @p range mapping to @p item. If this range overlaps any existing
// blocks, all of the overlapping blocks will be merged to form one single
// block. This insertion can never fail. If @p ret_it is non-null, return the
// iterator to the inserted block, or if @p range lies entirely within an
// existing block, returns the iterator to that block.
// @param range the range to insert.
// @param item the item to associate with @p range.
// @param ret_it on success, returns an iterator to the inserted item.
//
// Example insertions:
//
// Existing : aaaa bbbb
// Inserting: xxxxxxxxxxxx
// Result : cccccccccccc
//
// Existing : aaaaaa bbbbbb
// Inserting: xxxxxxxxxx
// Result : cccccccccccccc
void MergeInsert(const Range& range,
const ItemType& item,
typename RangeMap::iterator* ret_it = NULL);
// Remove the range that exactly matches @p range.
// Returns true iff @p range is removed.
bool Remove(const Range& range);
// Remove the item at position @p it.
void Remove(RangeMapIter it) { ranges_.erase(it); }
// Remove the items in the given range.
void Remove(RangeMapIterPair its) { ranges_.erase(its.first, its.second); }
void Remove(RangeMapIter it1, RangeMapIter it2) { ranges_.erase(it1, it2); }
// Remove all items from the address space.
void Clear() { ranges_.clear(); }
const RangeMap& ranges() const { return ranges_; }
const bool empty() const { return ranges_.empty(); }
const size_t size() const { return ranges_.size(); }
// Finds the first contained range that intersects @p range.
RangeMapConstIter FindFirstIntersection(const Range& range) const;
RangeMapIter FindFirstIntersection(const Range& range);
// Caution must be taken in using the non-const version of these! It is up
// to the user not to change the values of any underlying ranges so as to
// invalidate the non-overlapping range property of the address space. The
// non-const iterator access is only intended for deletion of entire ranges.
RangeMapConstIter begin() const { return ranges_.begin(); }
RangeMapIter begin() { return ranges_.begin(); }
RangeMapConstIter end() const { return ranges_.end(); }
RangeMapIter end() { return ranges_.end(); }
// Returns a pair of iterators that iterate over all ranges
// intersecting @p range.
RangeMapConstIterPair FindIntersecting(const Range& range) const;
RangeMapIterPair FindIntersecting(const Range& range);
// Returns true if the given range intersects any range currently in the
// address space.
bool Intersects(const Range& range) const;
bool Intersects(AddressType address, SizeType size = 1) const {
return Intersects(Range(address, size));
}
// Returns true if the given range is contained exactly in the address
// space.
bool ContainsExactly(const Range& range) const;
bool ContainsExactly(AddressType address, SizeType size = 1) const {
return ContainsExactly(Range(address, size));
}
// Returns true if the given range is contained by exactly one range in the
// address space.
bool Contains(const Range& range) const;
bool Contains(AddressType address, SizeType size = 1) const {
return Contains(Range(address, size));
}
// Finds the range that contains @p range.
typename RangeMap::const_iterator FindContaining(const Range& range) const;
typename RangeMap::iterator FindContaining(const Range& range);
private:
// Our ranges and their associated items.
RangeMap ranges_;
};
// An address range has a start address and a size.
// Both types must provide operator <, and it must be possible to
// add a SizeType to an AddressType.
template <typename AddressType, typename SizeType>
class AddressRange {
public:
typedef AddressType Address;
typedef SizeType Size;
AddressRange() : start_(0), size_(0) {
}
AddressRange(const AddressType &start, const SizeType& size)
: start_(start), size_(size) {
}
AddressRange(const AddressRange &other)
: start_(other.start_), size_(other.size_) {
}
void operator=(const AddressRange &other) {
start_ = other.start_;
size_ = other.size_;
}
// @returns true if this range is empty.
bool IsEmpty() const { return size_ == 0; }
// Determines if a given address range is contained within this range.
// @param other The range to check.
// @param addr Start address of the range to check.
// @param size The size of the other range to check.
// @returns true iff @p other is contained within this range.
bool Contains(const AddressRange& other) const {
if (other.start_ < start_ || other.end() > end())
return false;
return true;
}
bool Contains(AddressType addr, SizeType size = 1) const {
return Contains(AddressRange(addr, size));
}
// Determines if a given range intersects this range.
// @param other The range to test.
// @param addr Start address of the range to check.
// @param size The size of the other range to check.
// @returns true iff @p other intersects this range.
bool Intersects(const AddressRange& other) const {
if (other.end() <= start_ || other.start_ >= end())
return false;
return true;
}
bool Intersects(AddressType addr, SizeType size = 1) const {
return Intersects(AddressRange(addr, size));
}
// @name Comparison operators. These are for the purposes of the map that we
// use for tracking the address space.
// @{
bool operator<(const AddressRange& other) const {
// This assumes the Address and Size types provide operator <.
return internal::CompleteAddressRangeLess<AddressRange>()(*this, other);
}
bool operator==(const AddressRange& other) const {
// If neither is less, they have to be equal. That is, they conflict as
// far as the address-space is concerned.
return !(other < *this) && !(*this < other);
}
bool operator!=(const AddressRange& other) const {
return !operator==(other);
}
// @}
AddressType start() const { return start_; }
AddressType end() const { return start_ + size_; }
SizeType size() const { return size_; }
bool Save(OutArchive* out_archive) const {
DCHECK(out_archive != NULL);
return out_archive->Save(start_) && out_archive->Save(size_);
}
bool Load(InArchive* in_archive) {
DCHECK(in_archive != NULL);
return in_archive->Load(&start_) && in_archive->Load(&size_);
}
private:
// Start of address range.
AddressType start_;
// Size of address range.
SizeType size_;
};
// An AddressRangeMap is used for keeping track of data in one address space
// that has some relationship with data in another address space. Mappings are
// stored as pairs of addresses, one from the 'source' address-space and one
// from the 'destination' address-space. The ranges are sorted based on the
// source ranges, and the source ranges must be disjoint. The data structure
// ensures that the representation used is minimal in the following sense:
// a pair of address-range mappings that have the same size in both images and
// are consecutive in each image will be merged. For example, consider a
// mapping containing the two pairs of ranges:
//
// [0, 10) maps to [1000, 1010), and
// [10, 30) maps to [1010, 1030).
//
// This is more succinctly represented as (assuming linearity of the underlying
// relationship):
//
// [0, 30) maps to [1000, 1030).
//
// However, a pair of mappings like:
//
// [0, 10) maps to [1000, 1010), and
// [10, 30) maps to [1010, 1036)
//
// should not be merged, as even though they are contiguous in both address
// spaces, the source and destination ranges are not of the same size for the
// second pair. Thus, we can't imply that the relationship holds for the pair
// [0, 30) and [1000, 1036).
template <typename SourceRangeType, typename DestinationRangeType>
class AddressRangeMap {
public:
typedef SourceRangeType SourceRange;
typedef DestinationRangeType DestinationRange;
typedef std::pair<SourceRange, DestinationRange> RangePair;
typedef std::vector<RangePair> RangePairs;
const RangePairs& range_pairs() const { return range_pairs_; }
const RangePair& range_pair(size_t i) const { return range_pairs_[i]; }
void clear() { range_pairs_.clear(); }
bool empty() const { return range_pairs_.empty(); }
size_t size() const { return range_pairs_.size(); }
bool operator==(const AddressRangeMap& other) const {
return range_pairs_ == other.range_pairs_;
}
bool operator!=(const AddressRangeMap& other) const {
return range_pairs_ != other.range_pairs_;
}
// Determines if this is a simple mapping.
//
// A mapping is simple if there exists exactly one range, and the sizes of the
// source and destination ranges are identical.
//
// @returns true if this mapping is simple, false otherwise.
bool IsSimple() const {
return range_pairs_.size() == 1 &&
range_pairs_.front().first.size() == range_pairs_.front().second.size();
}
// Given a source range finds the range pair that encompasses it, if it
// exists.
//
// @param sec_range the source range to search for.
// @returns a pointer to the range pair, or NULL if none exists.
const RangePair* FindRangePair(const SourceRange& src_range) const;
// Given a source range finds the range pair that encompasses it, if it
// exists.
//
// @param start the beginning of the source range to search for.
// @param size the size of the source range to search for.
// @returns a pointer to the range pair, or NULL if none exists.
const RangePair* FindRangePair(typename SourceRange::Address start,
typename SourceRange::Size size) const {
return FindRangePair(SourceRange(start, size));
}
// Determines if the given source address range is fully mapped.
//
// @param src_range the source range to confirm.
// @returns true if @p src_range is fully mapped, false otherwise.
bool IsMapped(const SourceRange& src_range) const;
// Determines if the given source address range is fully mapped.
//
// @param start the beginning of the source range to confirm.
// @param size the size of the source range to confirm.
// @returns true if the source range is fully mapped, false otherwise.
bool IsMapped(typename SourceRange::Address start,
typename SourceRange::Size size) const {
return IsMapped(SourceRange(start, size));
}
// Adds a new pair of ranges to the map.
//
// This method allows insertions at arbitrary locations, but may consequently
// be slower as a reallocation may be required.
//
// @param src_range the source range of the mapping to be inserted.
// @param dst_range the destination range of the mapping to be inserted.
// @returns true on success, false otherwise.
bool Insert(const SourceRange& src_range, const DestinationRange& dst_range);
// Pushes a new pair of ranges to the tail end of the source address range.
//
// This method is amortized O(1) and is simpler than Insert if the mapping
// is being created in sequential order in the source address space. This will
// fail if @p src_range is not greater than all existing ranges.
//
// @param src_range the source range of the mapping to be inserted.
// @param dst_range the destination range of the mapping to be inserted.
// @returns true on success, false otherwise.
bool Push(const SourceRange& src_range, const DestinationRange& dst_range);
// Computes the inverse of this mapping, returning the number of address
// ranges mappings that were unable to be inverted. If DestinationRange and
// SourceRange are the same type this may be performed in-place.
//
// The inversion is deterministic. When conflicting destination ranges are
// found, earlier start addresses and shorter lengths have priority.
//
// @param inverted a pointer to the AddressRangeMap that will be populated
// with the inverted address range map.
// @returns the number of conflicting address ranges that were unable to be
// inverted.
size_t ComputeInverse(
AddressRangeMap<DestinationRange, SourceRange>* inverted) const;
// Rejigs a mapping by changing the underlying address-space that is being
// mapped. This inserts a range of unmapped data in the source range, pushing
// all mapped ranges that are beyond the newly unmapped data. If a mapped
// source range intersects the unmapped range being inserted, this mapping
// is split. During a split, the first of the two split ranges will maintain
// linearity when possible.
//
// For example, consider the following address range map (expressed as
// [start, end) ranges):
//
// [0, 10) -> [1000, 1010), [20, 30) -> [1020, 1030)
//
// After inserting an unmapped range at offset 25 size 5 the mapping will be:
//
// [0, 10) -> [1000, 1010), [20, 25) -> [1020, 1025), [30, 35) -> [1025, 1030)
//
// Simple splitting can fail in a very unlikely edge case. Consider the case
// where the source range has size N > 1 and the destination range has size 1.
// Now consider splitting the source range. We only have one byte of
// destination range, so how to split that into two? The only solution is to
// duplicate the destination range, which may make the mapping no longer
// invertible.
//
// @param unmapped the unmapped source range to insert.
void InsertUnmappedRange(const SourceRange& unmapped);
// Modifies a mapping by changing the underlying address-space that is being
// mapped. This removes a source range, erasing any mappings over that range
// and shifting all mappings beyond that range to the left, as necessary. If
// any mappings intersect the range being removed they will be split in such a
// way as to keep the individual mappings linear, if possible.
//
// For example, consider the following address range map (expressed as
// [start, end) ranges):
//
// [0, 10) -> [1000, 1010), [20, 30) -> [1020, 1030)
//
// After removing the source range [5, 20), the mapping will be:
//
// [0, 5) -> [1000, 1005), [5, 15) -> [1020, 1030)
//
// @param mapped the mapped source range to remove.
void RemoveMappedRange(const SourceRange& mapped);
// For serialization.
bool Save(OutArchive* out_archive) const {
DCHECK(out_archive != NULL);
return out_archive->Save(range_pairs_);
}
bool Load(InArchive* in_archive) {
DCHECK(in_archive != NULL);
return in_archive->Load(&range_pairs_);
}
private:
// Runs a lower bound search with the provided src_range and a made up
// destination range. The returned iterator either intersects src_range, is
// strictly greater than it, or is 'end()'.
typename RangePairs::const_iterator LowerBound(
const SourceRange& src_range) const;
// Stores the mapping.
RangePairs range_pairs_;
};
template <typename AddressType, typename SizeType, typename ItemType>
AddressSpace<AddressType, SizeType, ItemType>::AddressSpace() {
}
template <typename AddressType, typename SizeType, typename ItemType>
bool AddressSpace<AddressType, SizeType, ItemType>::Insert(
const Range& range,
const ItemType& item,
typename RangeMap::iterator* ret_it) {
// We can't insert empty ranges.
if (range.IsEmpty())
return false;
// Is there an intersecting block?
RangeMap::iterator it = FindFirstIntersection(range);
if (it != ranges_.end())
return false;
std::pair<RangeMap::iterator, bool> inserted =
ranges_.insert(std::make_pair(range, item));
DCHECK(inserted.second);
if (ret_it != NULL)
*ret_it = inserted.first;
return true;
}
template <typename AddressType, typename SizeType, typename ItemType>
bool AddressSpace<AddressType, SizeType, ItemType>::FindOrInsert(
const Range& range,
const ItemType& item,
typename RangeMap::iterator* ret_it) {
// We can't insert empty ranges.
if (range.IsEmpty())
return false;
// Is there already an existing block exactly matching that range? If so,
// return it.
RangeMap::iterator it = FindFirstIntersection(range);
if (it != ranges_.end()) {
if (ret_it != NULL)
*ret_it = it;
return range == it->first && item == it->second;
}
std::pair<RangeMap::iterator, bool> inserted =
ranges_.insert(std::make_pair(range, item));
DCHECK(inserted.second);
if (ret_it != NULL)
*ret_it = inserted.first;
return true;
}
template <typename AddressType, typename SizeType, typename ItemType>
bool AddressSpace<AddressType, SizeType, ItemType>::SubsumeInsert(
const Range& range,
const ItemType& item,
typename RangeMap::iterator* ret_it) {
// We can't insert empty ranges.
if (range.IsEmpty())
return false;
RangeMapIterPair its = FindIntersecting(range);
// We only need to check how we intersect the first and last ranges; we
// are guaranteed to subsume all others.
if (its.first != its.second) {
RangeMapIter it = its.first;
// Check the first range.
DCHECK(range.Intersects(it->first));
// We do not contain the first returned range?
if (!range.Contains(it->first)) {
// We do not contain it, it does not contain us. We have a proper
// intersection with them and the insertion fails.
if (!it->first.Contains(range))
return false;
// They strictly contain us. There should be only one of them, and we
// should return it.
DCHECK_EQ(1, std::distance(its.first, its.second));
if (ret_it != NULL)
*ret_it = its.first;
return true;
}
// We now check the second range. If we got here, the first range is a
// proper subset of the range we're trying to add. We need to contain the
// second range in order for the insertion to proceed. If we do not contain
// it, we know it starts within our range, and finished outside of it and
// therefore it is a proper intersection.
it = its.second;
--it;
DCHECK(range.Intersects(it->first));
if (!range.Contains(it->first))
return false;
}
ranges_.erase(its.first, its.second);
std::pair<RangeMap::iterator, bool> inserted =
ranges_.insert(std::make_pair(range, item));
DCHECK(inserted.second);
if (ret_it != NULL)
*ret_it = inserted.first;
return true;
}
template <typename AddressType, typename SizeType, typename ItemType>
void AddressSpace<AddressType, SizeType, ItemType>::MergeInsert(
const Range& range,
const ItemType& item,
typename RangeMap::iterator* ret_it) {
// We can't insert empty ranges.
if (range.IsEmpty())
return;
RangeMapIterPair its = FindIntersecting(range);
AddressType start_addr = range.start();
size_t length = range.size();
// Have overlap with existing blocks?
if (its.first != its.second) {
// Find start address of new block. This is the min of the requested range,
// or the beginning of the first intersecting block.
RangeMap::iterator it_first = its.first;
DCHECK(it_first != ranges_.end());
start_addr = std::min(range.start(), it_first->first.start());
// Find end address of new block. This is the max of the requested range,
// or the end of the last intersecting block.
RangeMap::iterator it_last = its.second;
--it_last;
DCHECK(it_last != ranges_.end());
AddressType end_addr = std::max(range.end(), it_last->first.end());
// Erase the existing blocks.
length = end_addr - start_addr;
ranges_.erase(its.first, its.second);
}
// Insert the new block.
Range new_range(start_addr, length);
std::pair<RangeMap::iterator, bool> inserted =
ranges_.insert(std::make_pair(new_range, item));
DCHECK(inserted.second);
if (ret_it != NULL)
*ret_it = inserted.first;
return;
}
template <typename AddressType, typename SizeType, typename ItemType>
bool AddressSpace<AddressType, SizeType, ItemType>::Remove(const Range& range) {
// We can't remove empty ranges.
if (range.IsEmpty())
return false;
RangeMap::iterator it = ranges_.find(range);
if (it == ranges_.end())
return false;
ranges_.erase(it);
return true;
}
template <typename AddressType, typename SizeType, typename ItemType>
typename AddressSpace<AddressType, SizeType, ItemType>::RangeMap::const_iterator
AddressSpace<AddressType, SizeType, ItemType>::FindFirstIntersection(
const Range& range) const {
return const_cast<AddressSpace*>(this)->FindFirstIntersection(range);
}
template <typename AddressType, typename SizeType, typename ItemType>
typename AddressSpace<AddressType, SizeType, ItemType>::RangeMap::iterator
AddressSpace<AddressType, SizeType, ItemType>::FindFirstIntersection(
const Range& range) {
// Empty items do not exist in the address-space.
if (range.IsEmpty())
return ranges_.end();
RangeMap::iterator it(ranges_.lower_bound(range));
// There are three cases we need to handle here:
// 1. An exact match.
if (it != ranges_.end() && it->first == range)
return it;
// 2. Intersection with the next earlier (lower address or shorter) range.
// Back up one if we can and test for intersection.
if (it != ranges_.begin()) {
RangeMap::iterator prev(it);
--prev;
if (prev->first.Intersects(range))
return prev;
}
// 3. Intersection to a/the found block.
if (it != ranges_.end() && it->first.Intersects(range))
return it;
return ranges_.end();
}
template <typename AddressType, typename SizeType, typename ItemType>
typename AddressSpace<AddressType, SizeType, ItemType>::RangeMapConstIterPair
AddressSpace<AddressType, SizeType, ItemType>::FindIntersecting(
const Range& range) const {
return const_cast<AddressSpace*>(this)->FindIntersecting(range);
}
template <typename AddressType, typename SizeType, typename ItemType>
typename AddressSpace<AddressType, SizeType, ItemType>::RangeMapIterPair
AddressSpace<AddressType, SizeType, ItemType>::FindIntersecting(
const Range& range) {
// Empty ranges find nothing.
if (range.IsEmpty())
return std::make_pair(ranges_.end(), ranges_.end());
// Find the start of the range first.
RangeMap::iterator begin(FindFirstIntersection(range));
// Then the end.
RangeMap::iterator end(ranges_.lower_bound(
Range(range.start() + range.size(), 1)));
// Ensure that the relationship begin <= end holds, so that we may always
// iterate over the returned range. It is possible that begin == end(),
// and end != end(), which can cause problems. This is specifically the case
// when there is no intersection with @p range, but that there is at least
// one range beyond @p range.
if (begin == ranges_.end())
begin = end;
// Since we search for the first range that starts at or after the end
// of the input range, the range we find should never be intersecting.
DCHECK(end == ranges_.end() || !end->first.Intersects(range));
return std::make_pair(begin, end);
}
template <typename AddressType, typename SizeType, typename ItemType>
bool AddressSpace<AddressType, SizeType, ItemType>::Intersects(
const Range& range) const {
RangeMapConstIterPair its = FindIntersecting(range);
return (its.first != its.second);
}
template <typename AddressType, typename SizeType, typename ItemType>
bool AddressSpace<AddressType, SizeType, ItemType>::ContainsExactly(
const Range& range) const {
RangeMapConstIterPair its = FindIntersecting(range);
if (its.first == its.second)
return false;
return its.first->first == range;
}
template <typename AddressType, typename SizeType, typename ItemType>
bool AddressSpace<AddressType, SizeType, ItemType>::Contains(
const Range& range) const {
RangeMapConstIterPair its = FindIntersecting(range);
if (its.first == its.second)
return false;
return its.first->first.Contains(range);
}
template <typename AddressType, typename SizeType, typename ItemType>
typename AddressSpace<AddressType, SizeType, ItemType>::RangeMap::const_iterator
AddressSpace<AddressType, SizeType, ItemType>::FindContaining(
const Range& range) const {
// If there is a containing range, it must be the first intersection.
RangeMap::const_iterator it(FindFirstIntersection(range));
if (it != ranges_.end() && it->first.Contains(range))
return it;
return ranges_.end();
}
template <typename AddressType, typename SizeType, typename ItemType>
typename AddressSpace<AddressType, SizeType, ItemType>::RangeMap::iterator
AddressSpace<AddressType, SizeType, ItemType>::FindContaining(
const Range& range) {
// If there is a containing range, it must be the first intersection.
RangeMap::iterator it(FindFirstIntersection(range));
if (it != ranges_.end() && it->first.Contains(range))
return it;
return ranges_.end();
}
template <typename SourceRangeType, typename DestinationRangeType>
const std::pair<SourceRangeType, DestinationRangeType>*
AddressRangeMap<SourceRangeType, DestinationRangeType>::FindRangePair(
const SourceRange& src_range) const {
// No empty range exists in the mapping.
if (src_range.IsEmpty())
return NULL;
// Find the first existing source range that is not less than src_range.
// The returned iterator either intersects src_range, or is strictly greater
// than it.
RangePairs::const_iterator it = LowerBound(src_range);
if (it == range_pairs_.end())
return NULL;
if (it->first.Contains(src_range))
return &(*it);
return NULL;
}
template <typename SourceRangeType, typename DestinationRangeType>
bool AddressRangeMap<SourceRangeType, DestinationRangeType>::IsMapped(
const SourceRange& src_range) const {
// By definition no empty range is mapped.
if (src_range.IsEmpty())
return false;
// Find the first existing source range that is not less than src_range.
// The returned iterator either intersects src_range, or is strictly greater
// than it.
RangePairs::const_iterator it = LowerBound(src_range);
// Step through the successive mapped ranges and see if they cover src_range.
typename SourceRange::Address position = src_range.start();
while (true) {
// No more mapped source ranges? Then src_range is not covered.
if (it == range_pairs_.end())
return false;
// Is there an uncovered gap between position and the next mapped source
// range? Then src_range is not covered.
if (position < it->first.start())
return false;
// Step over this mapped source range and see if we're completely covered.
position = it->first.end();
if (position >= src_range.end())
return true;
++it;
}
}
template <typename SourceRangeType, typename DestinationRangeType>
bool AddressRangeMap<SourceRangeType, DestinationRangeType>::Insert(
const SourceRange& src_range, const DestinationRange& dst_range) {
// No empty ranges may be inserted in the mapping.
if (src_range.IsEmpty() || dst_range.IsEmpty())
return false;
// Find the first existing source range that is not less than src_range.
RangePairs::iterator it = std::lower_bound(
range_pairs_.begin(),
range_pairs_.end(),
std::make_pair(src_range, dst_range),
internal::RangePairLess<SourceRange, DestinationRange>());
// The search fell off the end of the vector? Push it to the back.
if (it == range_pairs_.end())
return Push(src_range, dst_range);
// Does this source range overlap at all with the existing one?
if (it->first.Intersects(src_range))
return false;
// At this point we know that 'it' points to a source range that is greater
// than src_range. There are now 4 possibilities:
// 1. It can be merged with the source range to the left, at 'it - 1'.
// 2. It can be merged with the source range to the right, at 'it'.
// 3. It can be merged with both the source range to the left and the right.
// 4. It can't be merged at all, and needs to be inserted.
// Determine in which directions we need to merge.
bool merge_left = false;
bool merge_right = false;
if (src_range.size() == dst_range.size()) {
// If there is an element to the left, see if we can merge with it.
if (it != range_pairs_.begin()) {
RangePairs::iterator it_left = it - 1;
if (it_left->first.size() == it_left->second.size() &&
it_left->first.end() == src_range.start() &&
it_left->second.end() == dst_range.start()) {
merge_left = true;
}
}
if (it->first.size() == it->second.size() &&
src_range.end() == it->first.start() &&
dst_range.end() == it->second.start()) {
merge_right = true;
}
}
// Don't need to change sizes because we're merging in only one direction?
if (merge_left != merge_right) {
SourceRange merged_src_range;
DestinationRange merged_dst_range;
if (merge_left) {
--it;
merged_src_range = SourceRange(it->first.start(),
it->first.size() + src_range.size());
merged_dst_range = DestinationRange(it->second.start(),
it->second.size() + dst_range.size());
} else {
merged_src_range = SourceRange(src_range.start(),
src_range.size() + it->first.size());
merged_dst_range = DestinationRange(dst_range.start(),
dst_range.size() + it->second.size());
}
*it = std::make_pair(merged_src_range, merged_dst_range);
return true;
}
// Merging in both directions and shrinking?
if (merge_left && merge_right) {
RangePairs::iterator it_left = it - 1;
SourceRange merged_src_range(
it_left->first.start(),
it_left->first.size() + src_range.size() + it->first.size());
DestinationRange merged_dst_range(
it_left->second.start(),
it_left->second.size() + dst_range.size() + it->second.size());
*it_left = std::make_pair(merged_src_range, merged_dst_range);
range_pairs_.erase(it);
return true;
}
// If we get here then we're growing.
range_pairs_.insert(it, std::make_pair(src_range, dst_range));
return true;
}
template <typename SourceRangeType, typename DestinationRangeType>
bool AddressRangeMap<SourceRangeType, DestinationRangeType>::Push(
const SourceRange& src_range, const DestinationRange& dst_range) {
// We can't insert empty ranges.
if (src_range.IsEmpty() || dst_range.IsEmpty())
return false;
if (!range_pairs_.empty()) {
SourceRange& last_src_range = range_pairs_.back().first;
// If we already have RangePairs in the list, then src_range must be beyond
// the last SourceRange.
if (!(last_src_range < src_range) || last_src_range.Intersects(src_range))
return false;
// Can we merge this new pair of ranges with the existing last pair of
// ranges?
DestinationRange& last_dst_range = range_pairs_.back().second;
if (last_src_range.size() == last_dst_range.size() &&
src_range.size() == dst_range.size() &&
last_src_range.end() == src_range.start() &&
last_dst_range.end() == dst_range.start()) {
last_src_range = SourceRange(
last_src_range.start(), last_src_range.size() + src_range.size());
last_dst_range = DestinationRange(
last_dst_range.start(), last_dst_range.size() + dst_range.size());
return true;
}
}
range_pairs_.push_back(std::make_pair(src_range, dst_range));
return true;
}
template <typename SourceRangeType, typename DestinationRangeType>
size_t AddressRangeMap<SourceRangeType, DestinationRangeType>::ComputeInverse(
AddressRangeMap<DestinationRangeType, SourceRangeType>* inverted) const {
DCHECK(inverted != NULL);
// Get a list of inverted range pairs.
std::vector<std::pair<DestinationRangeType, SourceRangeType>>
inverted_range_pairs;
inverted_range_pairs.reserve(range_pairs_.size());
for (size_t i = 0; i < range_pairs_.size(); ++i) {
inverted_range_pairs.push_back(
std::make_pair(range_pairs_[i].second, range_pairs_[i].first));
}
// We sort these with a custom sort functor so that a total ordering is
// defined rather than the default partial ordering defined by AddressRange.
std::sort(inverted_range_pairs.begin(),
inverted_range_pairs.end(),
internal::CompleteAddressRangePairLess<DestinationRangeType,
SourceRangeType>());
// Push these back to the inverted address range map and count the conflicts.
size_t conflicts = 0;
inverted->clear();
for (size_t i = 0; i < inverted_range_pairs.size(); ++i) {
if (!inverted->Push(inverted_range_pairs[i].first,
inverted_range_pairs[i].second)) {
++conflicts;
}
}
return conflicts;
}
template <typename SourceRangeType, typename DestinationRangeType>
typename AddressRangeMap<SourceRangeType, DestinationRangeType>::RangePairs::
const_iterator
AddressRangeMap<SourceRangeType, DestinationRangeType>::LowerBound(
const SourceRange& src_range) const {
return std::lower_bound(
range_pairs_.begin(),
range_pairs_.end(),
std::make_pair(src_range,
// We have to manually create a valid DestinationRange with
// a size > 0.
DestinationRange(typename DestinationRange::Address(),
1)),
internal::RangePairLess<SourceRange, DestinationRange>());
}
template <typename SourceRangeType, typename DestinationRangeType>
void AddressRangeMap<SourceRangeType, DestinationRangeType>::
InsertUnmappedRange(const SourceRange& unmapped) {
// Unmapping an empty range is a nop.
if (unmapped.IsEmpty())
return;
typedef typename SourceRange::Size SrcSize;
typedef typename DestinationRange::Size DstSize;
typedef typename DestinationRange::Address DstAddr;
// Walk backwards through the range pairs, fixing them as we go.
for (size_t i = range_pairs_.size(); i > 0; --i) {
RangePair& range_pair = range_pairs_[i - 1];
SourceRange& src = range_pair.first;
DestinationRange& dst = range_pair.second;
// This range pair starts before the unmapped source range? We may have to
// split it, but we can stop looking at earlier ranges.
if (src.start() < unmapped.start()) {
// Do we need a split?
if (src.end() > unmapped.start()) {
SrcSize src_size_before =
static_cast<SrcSize>(unmapped.start() - src.start());
SrcSize src_size_after =
static_cast<SrcSize>(src.size() - src_size_before);
DstAddr dst_start_before = dst.start();
DstSize dst_size_before = src_size_before;
DstAddr dst_start_after(0);
DstSize dst_size_after(0);
// Special case: The destination size is 1, so indivisible. In this
// case we simply duplicate the destination range.
if (dst.size() == 1) {
dst_start_after = dst_start_before;
dst_size_after = dst_size_before;
} else {
// If the destination range is smaller than the source range, it is
// possible that dst_size_before consumes too much. In this case send
// as much as possible to the left (so it is as close to linear as
// possible), but leave some for the after the split.
if (dst_size_before >= dst.size()) {
dst_size_before = dst.size() - 1;
dst_size_after = 1;
}
dst_start_after = dst_start_before + dst_size_before;
dst_size_after = static_cast<DstSize>(dst.size() - dst_size_before);
}
// Create the range for after the split.
RangePair pair_after(
SourceRange(src.start() + src_size_before + unmapped.size(),
src_size_after),
DestinationRange(dst_start_after, dst_size_after));
// Fix the existing pair, which is now the pair before the split.
src = SourceRange(src.start(), src_size_before);
dst = DestinationRange(dst_start_before, dst_size_before);
// Insert the the new range. This invalidates range_pair, src and dst
// hence the need to do it at the very end.
range_pairs_.insert(range_pairs_.begin() + i, pair_after);
}
return;
}
// Shift this range to the right.
src = SourceRange(src.start() + unmapped.size(), src.size());
}
}
template <typename SourceRangeType, typename DestinationRangeType>
void AddressRangeMap<SourceRangeType, DestinationRangeType>::
RemoveMappedRange(const SourceRange& mapped) {
// Removing an empty range is a nop.
if (mapped.IsEmpty())
return;
typedef typename SourceRange::Size SrcSize;
typedef typename DestinationRange::Size DstSize;
typedef typename DestinationRange::Address DstAddr;
// Special case: no source ranges to modify.
if (range_pairs_.size() == 0)
return;
// Walk backwards through the range pairs, fixing them as we go.
size_t i = range_pairs_.size();
for (; i > 0; --i) {
RangePair& range_pair = range_pairs_[i - 1];
SourceRange& src = range_pair.first;
DestinationRange& dst = range_pair.second;
// This range pair starts before the end of the range we want to unmap?
// Then we've finished fixing ranges that simply need to be shifted.
if (src.start() < mapped.end())
break;
// Shift this range to the left.
src = SourceRange(src.start() - mapped.size(), src.size());
}
// At this point we've found the first range that is not beyond the
// end of mapped. Now we want to find the first range that is completely
// before mapped.
size_t end_affected_ranges = i;
for (; i > 0; --i) {
RangePair& range_pair = range_pairs_[i - 1];
SourceRange& src = range_pair.first;
DestinationRange& dst = range_pair.second;
if (src.end() <= mapped.start())
break;
}
size_t begin_affected_ranges = i;
// It's possible that the affected ranges are off the end of the vector,
// in which case there is absolutely nothing to do.
if (begin_affected_ranges >= range_pairs_.size())
return;
// At this point the ith through (end_affected_ranges - 1)th ranges intersect
// the range to be removed. The two endpoints may need to be split, but
// everything between them needs to be deleted. We inspect each endpoint and
// split them if need be.
// Does the ith range need to split?
if (range_pairs_[begin_affected_ranges].first.start() < mapped.start()) {
RangePair& range_pair = range_pairs_[begin_affected_ranges];
SourceRange& src = range_pair.first;
DestinationRange& dst = range_pair.second;
SrcSize src_size_left = mapped.start() - src.start();
DstSize dst_size_left = src_size_left;
if (dst_size_left > dst.size())
dst_size_left = dst.size();
// Special case: this element needs to be both left split and right split.
if (begin_affected_ranges == end_affected_ranges - 1 &&
mapped.end() < src.end()) {
// Split in such as way as to prefer linear mappings. If being linear on
// the left leaves no destination range on the right, shuffle a byte
// between the two.
SrcSize src_size_right = src.end() - mapped.end();
DstSize dst_size_right = src_size_right;
if (dst_size_left + dst_size_right > dst.size()) {
dst_size_right = dst.size() - dst_size_left;
if (dst_size_right == 0) {
++dst_size_right;
--dst_size_left;
}
}
DCHECK_GT(dst_size_left, 0u);
DCHECK_GT(dst_size_right, 0u);
DCHECK_LE(dst_size_left + dst_size_right, dst.size());
DstAddr dst_start_right = dst.end() - dst_size_right;
src = SourceRange(src.start(), src_size_left);
dst = DestinationRange(dst.start(), dst_size_left);
// We do this last as it invalidates src and dst.
range_pairs_.insert(
range_pairs_.begin() + begin_affected_ranges + 1,
std::make_pair(SourceRange(mapped.start(), src_size_right),
DestinationRange(dst_start_right, dst_size_right)));
return;
}
src = SourceRange(src.start(), src_size_left);
dst = DestinationRange(dst.start(), dst_size_left);
++begin_affected_ranges;
}
// Does the (end_affected_ranges - 1)th range need to be split?
if (range_pairs_[end_affected_ranges - 1].first.end() > mapped.end()) {
RangePair& range_pair = range_pairs_[end_affected_ranges - 1];
SourceRange& src = range_pair.first;
DestinationRange& dst = range_pair.second;
SrcSize src_size = src.end() - mapped.end();
DstSize dst_size = src_size;
if (dst_size > dst.size())
dst_size = dst.size();
src = SourceRange(src.end() - src_size - mapped.size(), src_size);
dst = DestinationRange(dst.end() - dst_size, dst_size);
--end_affected_ranges;
}
// Now we have that the ranges [begin_affected_ranges, end_affected_ranges)
// need to simply be erased.
if (begin_affected_ranges < end_affected_ranges)
range_pairs_.erase(range_pairs_.begin() + begin_affected_ranges,
range_pairs_.begin() + end_affected_ranges);
}
// An ostream operator for AddressRanges.
template<typename AddressType, typename SizeType>
std::ostream& operator<<(
std::ostream& str,
const AddressRange<AddressType, SizeType>& range) {
str << "AddressRange(" << range.start() << ", " << range.size() << ")";
return str;
}
} // namespace core
#endif // SYZYGY_CORE_ADDRESS_SPACE_H_