blob: 118fdfd85fa9fd176a5ec226279ba1ecc4e771ef [file] [log] [blame]
//===-- RangeMap.h ----------------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLDB_UTILITY_RANGEMAP_H
#define LLDB_UTILITY_RANGEMAP_H
#include <algorithm>
#include <vector>
#include "llvm/ADT/SmallVector.h"
#include "lldb/lldb-private.h"
// Uncomment to make sure all Range objects are sorted when needed
//#define ASSERT_RANGEMAP_ARE_SORTED
namespace lldb_private {
// Templatized classes for dealing with generic ranges and also collections of
// ranges, or collections of ranges that have associated data.
// A simple range class where you get to define the type of the range
// base "B", and the type used for the range byte size "S".
template <typename B, typename S> struct Range {
typedef B BaseType;
typedef S SizeType;
BaseType base;
SizeType size;
Range() : base(0), size(0) {}
Range(BaseType b, SizeType s) : base(b), size(s) {}
void Clear(BaseType b = 0) {
base = b;
size = 0;
}
// Set the start value for the range, and keep the same size
BaseType GetRangeBase() const { return base; }
void SetRangeBase(BaseType b) { base = b; }
void Slide(BaseType slide) { base += slide; }
bool Union(const Range &rhs) {
if (DoesAdjoinOrIntersect(rhs)) {
auto new_end = std::max<BaseType>(GetRangeEnd(), rhs.GetRangeEnd());
base = std::min<BaseType>(base, rhs.base);
size = new_end - base;
return true;
}
return false;
}
BaseType GetRangeEnd() const { return base + size; }
void SetRangeEnd(BaseType end) {
if (end > base)
size = end - base;
else
size = 0;
}
SizeType GetByteSize() const { return size; }
void SetByteSize(SizeType s) { size = s; }
bool IsValid() const { return size > 0; }
bool Contains(BaseType r) const {
return (GetRangeBase() <= r) && (r < GetRangeEnd());
}
bool ContainsEndInclusive(BaseType r) const {
return (GetRangeBase() <= r) && (r <= GetRangeEnd());
}
bool Contains(const Range &range) const {
return Contains(range.GetRangeBase()) &&
ContainsEndInclusive(range.GetRangeEnd());
}
// Returns true if the two ranges adjoing or intersect
bool DoesAdjoinOrIntersect(const Range &rhs) const {
const BaseType lhs_base = this->GetRangeBase();
const BaseType rhs_base = rhs.GetRangeBase();
const BaseType lhs_end = this->GetRangeEnd();
const BaseType rhs_end = rhs.GetRangeEnd();
bool result = (lhs_base <= rhs_end) && (lhs_end >= rhs_base);
return result;
}
// Returns true if the two ranges intersect
bool DoesIntersect(const Range &rhs) const {
const BaseType lhs_base = this->GetRangeBase();
const BaseType rhs_base = rhs.GetRangeBase();
const BaseType lhs_end = this->GetRangeEnd();
const BaseType rhs_end = rhs.GetRangeEnd();
bool result = (lhs_base < rhs_end) && (lhs_end > rhs_base);
return result;
}
bool operator<(const Range &rhs) const {
if (base == rhs.base)
return size < rhs.size;
return base < rhs.base;
}
bool operator==(const Range &rhs) const {
return base == rhs.base && size == rhs.size;
}
bool operator!=(const Range &rhs) const {
return base != rhs.base || size != rhs.size;
}
};
template <typename B, typename S, unsigned N = 0> class RangeVector {
public:
typedef B BaseType;
typedef S SizeType;
typedef Range<B, S> Entry;
typedef llvm::SmallVector<Entry, N> Collection;
RangeVector() = default;
~RangeVector() = default;
void Append(const Entry &entry) { m_entries.push_back(entry); }
void Append(B base, S size) { m_entries.emplace_back(base, size); }
// Insert an item into a sorted list and optionally combine it with any
// adjacent blocks if requested.
void Insert(const Entry &entry, bool combine) {
if (m_entries.empty()) {
m_entries.push_back(entry);
return;
}
auto begin = m_entries.begin();
auto end = m_entries.end();
auto pos = std::lower_bound(begin, end, entry);
if (combine) {
if (pos != end && pos->Union(entry)) {
CombinePrevAndNext(pos);
return;
}
if (pos != begin) {
auto prev = pos - 1;
if (prev->Union(entry)) {
CombinePrevAndNext(prev);
return;
}
}
}
m_entries.insert(pos, entry);
}
bool RemoveEntryAtIndex(uint32_t idx) {
if (idx < m_entries.size()) {
m_entries.erase(m_entries.begin() + idx);
return true;
}
return false;
}
void Sort() {
if (m_entries.size() > 1)
std::stable_sort(m_entries.begin(), m_entries.end());
}
#ifdef ASSERT_RANGEMAP_ARE_SORTED
bool IsSorted() const {
typename Collection::const_iterator pos, end, prev;
// First we determine if we can combine any of the Entry objects so we
// don't end up allocating and making a new collection for no reason
for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end;
prev = pos++) {
if (prev != end && *pos < *prev)
return false;
}
return true;
}
#endif
void CombineConsecutiveRanges() {
#ifdef ASSERT_RANGEMAP_ARE_SORTED
assert(IsSorted());
#endif
auto first_intersect = std::adjacent_find(
m_entries.begin(), m_entries.end(), [](const Entry &a, const Entry &b) {
return a.DoesAdjoinOrIntersect(b);
});
if (first_intersect == m_entries.end())
return;
// We we can combine at least one entry, then we make a new collection and
// populate it accordingly, and then swap it into place.
auto pos = std::next(first_intersect);
Collection minimal_ranges(m_entries.begin(), pos);
for (; pos != m_entries.end(); ++pos) {
Entry &back = minimal_ranges.back();
if (back.DoesAdjoinOrIntersect(*pos))
back.SetRangeEnd(std::max(back.GetRangeEnd(), pos->GetRangeEnd()));
else
minimal_ranges.push_back(*pos);
}
m_entries.swap(minimal_ranges);
}
BaseType GetMinRangeBase(BaseType fail_value) const {
#ifdef ASSERT_RANGEMAP_ARE_SORTED
assert(IsSorted());
#endif
if (m_entries.empty())
return fail_value;
// m_entries must be sorted, so if we aren't empty, we grab the first
// range's base
return m_entries.front().GetRangeBase();
}
BaseType GetMaxRangeEnd(BaseType fail_value) const {
#ifdef ASSERT_RANGEMAP_ARE_SORTED
assert(IsSorted());
#endif
if (m_entries.empty())
return fail_value;
// m_entries must be sorted, so if we aren't empty, we grab the last
// range's end
return m_entries.back().GetRangeEnd();
}
void Slide(BaseType slide) {
typename Collection::iterator pos, end;
for (pos = m_entries.begin(), end = m_entries.end(); pos != end; ++pos)
pos->Slide(slide);
}
void Clear() { m_entries.clear(); }
void Reserve(typename Collection::size_type size) { m_entries.reserve(size); }
bool IsEmpty() const { return m_entries.empty(); }
size_t GetSize() const { return m_entries.size(); }
const Entry *GetEntryAtIndex(size_t i) const {
return ((i < m_entries.size()) ? &m_entries[i] : nullptr);
}
// Clients must ensure that "i" is a valid index prior to calling this
// function
Entry &GetEntryRef(size_t i) { return m_entries[i]; }
const Entry &GetEntryRef(size_t i) const { return m_entries[i]; }
Entry *Back() { return (m_entries.empty() ? nullptr : &m_entries.back()); }
const Entry *Back() const {
return (m_entries.empty() ? nullptr : &m_entries.back());
}
static bool BaseLessThan(const Entry &lhs, const Entry &rhs) {
return lhs.GetRangeBase() < rhs.GetRangeBase();
}
uint32_t FindEntryIndexThatContains(B addr) const {
#ifdef ASSERT_RANGEMAP_ARE_SORTED
assert(IsSorted());
#endif
if (!m_entries.empty()) {
Entry entry(addr, 1);
typename Collection::const_iterator begin = m_entries.begin();
typename Collection::const_iterator end = m_entries.end();
typename Collection::const_iterator pos =
std::lower_bound(begin, end, entry, BaseLessThan);
if (pos != end && pos->Contains(addr)) {
return std::distance(begin, pos);
} else if (pos != begin) {
--pos;
if (pos->Contains(addr))
return std::distance(begin, pos);
}
}
return UINT32_MAX;
}
const Entry *FindEntryThatContains(B addr) const {
#ifdef ASSERT_RANGEMAP_ARE_SORTED
assert(IsSorted());
#endif
if (!m_entries.empty()) {
Entry entry(addr, 1);
typename Collection::const_iterator begin = m_entries.begin();
typename Collection::const_iterator end = m_entries.end();
typename Collection::const_iterator pos =
std::lower_bound(begin, end, entry, BaseLessThan);
if (pos != end && pos->Contains(addr)) {
return &(*pos);
} else if (pos != begin) {
--pos;
if (pos->Contains(addr)) {
return &(*pos);
}
}
}
return nullptr;
}
const Entry *FindEntryThatContains(const Entry &range) const {
#ifdef ASSERT_RANGEMAP_ARE_SORTED
assert(IsSorted());
#endif
if (!m_entries.empty()) {
typename Collection::const_iterator begin = m_entries.begin();
typename Collection::const_iterator end = m_entries.end();
typename Collection::const_iterator pos =
std::lower_bound(begin, end, range, BaseLessThan);
if (pos != end && pos->Contains(range)) {
return &(*pos);
} else if (pos != begin) {
--pos;
if (pos->Contains(range)) {
return &(*pos);
}
}
}
return nullptr;
}
using const_iterator = typename Collection::const_iterator;
const_iterator begin() const { return m_entries.begin(); }
const_iterator end() const { return m_entries.end(); }
protected:
void CombinePrevAndNext(typename Collection::iterator pos) {
// Check if the prev or next entries in case they need to be unioned with
// the entry pointed to by "pos".
if (pos != m_entries.begin()) {
auto prev = pos - 1;
if (prev->Union(*pos))
m_entries.erase(pos);
pos = prev;
}
auto end = m_entries.end();
if (pos != end) {
auto next = pos + 1;
if (next != end) {
if (pos->Union(*next))
m_entries.erase(next);
}
}
return;
}
Collection m_entries;
};
// A simple range with data class where you get to define the type of
// the range base "B", the type used for the range byte size "S", and the type
// for the associated data "T".
template <typename B, typename S, typename T>
struct RangeData : public Range<B, S> {
typedef T DataType;
DataType data;
RangeData() : Range<B, S>(), data() {}
RangeData(B base, S size) : Range<B, S>(base, size), data() {}
RangeData(B base, S size, DataType d) : Range<B, S>(base, size), data(d) {}
};
// We can treat the vector as a flattened Binary Search Tree, augmenting it
// with upper bounds (max of range endpoints) for every index allows us to
// query for range containment quicker.
template <typename B, typename S, typename T>
struct AugmentedRangeData : public RangeData<B, S, T> {
B upper_bound;
AugmentedRangeData(const RangeData<B, S, T> &rd)
: RangeData<B, S, T>(rd), upper_bound() {}
};
template <typename B, typename S, typename T, unsigned N = 0,
class Compare = std::less<T>>
class RangeDataVector {
public:
typedef lldb_private::Range<B, S> Range;
typedef RangeData<B, S, T> Entry;
typedef AugmentedRangeData<B, S, T> AugmentedEntry;
typedef llvm::SmallVector<AugmentedEntry, N> Collection;
RangeDataVector(Compare compare = Compare()) : m_compare(compare) {}
~RangeDataVector() = default;
void Append(const Entry &entry) { m_entries.emplace_back(entry); }
void Sort() {
if (m_entries.size() > 1)
std::stable_sort(m_entries.begin(), m_entries.end(),
[&compare = m_compare](const Entry &a, const Entry &b) {
if (a.base != b.base)
return a.base < b.base;
if (a.size != b.size)
return a.size < b.size;
return compare(a.data, b.data);
});
if (!m_entries.empty())
ComputeUpperBounds(0, m_entries.size());
}
#ifdef ASSERT_RANGEMAP_ARE_SORTED
bool IsSorted() const {
typename Collection::const_iterator pos, end, prev;
for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end;
prev = pos++) {
if (prev != end && *pos < *prev)
return false;
}
return true;
}
#endif
void CombineConsecutiveEntriesWithEqualData() {
#ifdef ASSERT_RANGEMAP_ARE_SORTED
assert(IsSorted());
#endif
typename Collection::iterator pos;
typename Collection::iterator end;
typename Collection::iterator prev;
bool can_combine = false;
// First we determine if we can combine any of the Entry objects so we
// don't end up allocating and making a new collection for no reason
for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end;
prev = pos++) {
if (prev != end && prev->data == pos->data) {
can_combine = true;
break;
}
}
// We we can combine at least one entry, then we make a new collection and
// populate it accordingly, and then swap it into place.
if (can_combine) {
Collection minimal_ranges;
for (pos = m_entries.begin(), end = m_entries.end(), prev = end;
pos != end; prev = pos++) {
if (prev != end && prev->data == pos->data)
minimal_ranges.back().SetRangeEnd(pos->GetRangeEnd());
else
minimal_ranges.push_back(*pos);
}
// Use the swap technique in case our new vector is much smaller. We must
// swap when using the STL because std::vector objects never release or
// reduce the memory once it has been allocated/reserved.
m_entries.swap(minimal_ranges);
}
}
void Clear() { m_entries.clear(); }
bool IsEmpty() const { return m_entries.empty(); }
size_t GetSize() const { return m_entries.size(); }
const Entry *GetEntryAtIndex(size_t i) const {
return ((i < m_entries.size()) ? &m_entries[i] : nullptr);
}
Entry *GetMutableEntryAtIndex(size_t i) {
return ((i < m_entries.size()) ? &m_entries[i] : nullptr);
}
// Clients must ensure that "i" is a valid index prior to calling this
// function
Entry &GetEntryRef(size_t i) { return m_entries[i]; }
const Entry &GetEntryRef(size_t i) const { return m_entries[i]; }
static bool BaseLessThan(const Entry &lhs, const Entry &rhs) {
return lhs.GetRangeBase() < rhs.GetRangeBase();
}
uint32_t FindEntryIndexThatContains(B addr) const {
const AugmentedEntry *entry =
static_cast<const AugmentedEntry *>(FindEntryThatContains(addr));
if (entry)
return std::distance(m_entries.begin(), entry);
return UINT32_MAX;
}
uint32_t FindEntryIndexesThatContain(B addr, std::vector<uint32_t> &indexes) {
#ifdef ASSERT_RANGEMAP_ARE_SORTED
assert(IsSorted());
#endif
if (!m_entries.empty())
FindEntryIndexesThatContain(addr, 0, m_entries.size(), indexes);
return indexes.size();
}
Entry *FindEntryThatContains(B addr) {
return const_cast<Entry *>(
static_cast<const RangeDataVector *>(this)->FindEntryThatContains(
addr));
}
const Entry *FindEntryThatContains(B addr) const {
return FindEntryThatContains(Entry(addr, 1));
}
const Entry *FindEntryThatContains(const Entry &range) const {
#ifdef ASSERT_RANGEMAP_ARE_SORTED
assert(IsSorted());
#endif
if (!m_entries.empty()) {
typename Collection::const_iterator begin = m_entries.begin();
typename Collection::const_iterator end = m_entries.end();
typename Collection::const_iterator pos =
std::lower_bound(begin, end, range, BaseLessThan);
while (pos != begin && pos[-1].Contains(range))
--pos;
if (pos != end && pos->Contains(range))
return &(*pos);
}
return nullptr;
}
const Entry *FindEntryStartsAt(B addr) const {
#ifdef ASSERT_RANGEMAP_ARE_SORTED
assert(IsSorted());
#endif
if (!m_entries.empty()) {
auto begin = m_entries.begin(), end = m_entries.end();
auto pos = std::lower_bound(begin, end, Entry(addr, 1), BaseLessThan);
if (pos != end && pos->base == addr)
return &(*pos);
}
return nullptr;
}
// This method will return the entry that contains the given address, or the
// entry following that address. If you give it an address of 0 and the
// first entry starts at address 0x100, you will get the entry at 0x100.
//
// For most uses, FindEntryThatContains is the correct one to use, this is a
// less commonly needed behavior. It was added for core file memory regions,
// where we want to present a gap in the memory regions as a distinct region,
// so we need to know the start address of the next memory section that
// exists.
const Entry *FindEntryThatContainsOrFollows(B addr) const {
#ifdef ASSERT_RANGEMAP_ARE_SORTED
assert(IsSorted());
#endif
if (!m_entries.empty()) {
typename Collection::const_iterator begin = m_entries.begin();
typename Collection::const_iterator end = m_entries.end();
typename Collection::const_iterator pos =
std::lower_bound(m_entries.begin(), end, addr,
[](const Entry &lhs, B rhs_base) -> bool {
return lhs.GetRangeEnd() <= rhs_base;
});
while (pos != begin && pos[-1].Contains(addr))
--pos;
if (pos != end)
return &(*pos);
}
return nullptr;
}
Entry *Back() { return (m_entries.empty() ? nullptr : &m_entries.back()); }
const Entry *Back() const {
return (m_entries.empty() ? nullptr : &m_entries.back());
}
protected:
Collection m_entries;
Compare m_compare;
private:
// Compute extra information needed for search
B ComputeUpperBounds(size_t lo, size_t hi) {
size_t mid = (lo + hi) / 2;
AugmentedEntry &entry = m_entries[mid];
entry.upper_bound = entry.base + entry.size;
if (lo < mid)
entry.upper_bound =
std::max(entry.upper_bound, ComputeUpperBounds(lo, mid));
if (mid + 1 < hi)
entry.upper_bound =
std::max(entry.upper_bound, ComputeUpperBounds(mid + 1, hi));
return entry.upper_bound;
}
// This is based on the augmented tree implementation found at
// https://en.wikipedia.org/wiki/Interval_tree#Augmented_tree
void FindEntryIndexesThatContain(B addr, size_t lo, size_t hi,
std::vector<uint32_t> &indexes) {
size_t mid = (lo + hi) / 2;
const AugmentedEntry &entry = m_entries[mid];
// addr is greater than the rightmost point of any interval below mid
// so there are cannot be any matches.
if (addr > entry.upper_bound)
return;
// Recursively search left subtree
if (lo < mid)
FindEntryIndexesThatContain(addr, lo, mid, indexes);
// If addr is smaller than the start of the current interval it
// cannot contain it nor can any of its right subtree.
if (addr < entry.base)
return;
if (entry.Contains(addr))
indexes.push_back(entry.data);
// Recursively search right subtree
if (mid + 1 < hi)
FindEntryIndexesThatContain(addr, mid + 1, hi, indexes);
}
};
// A simple range with data class where you get to define the type of
// the range base "B", the type used for the range byte size "S", and the type
// for the associated data "T".
template <typename B, typename T> struct AddressData {
typedef B BaseType;
typedef T DataType;
BaseType addr;
DataType data;
AddressData() : addr(), data() {}
AddressData(B a, DataType d) : addr(a), data(d) {}
bool operator<(const AddressData &rhs) const {
if (this->addr == rhs.addr)
return this->data < rhs.data;
return this->addr < rhs.addr;
}
bool operator==(const AddressData &rhs) const {
return this->addr == rhs.addr && this->data == rhs.data;
}
bool operator!=(const AddressData &rhs) const {
return this->addr != rhs.addr || this->data == rhs.data;
}
};
template <typename B, typename T, unsigned N> class AddressDataArray {
public:
typedef AddressData<B, T> Entry;
typedef llvm::SmallVector<Entry, N> Collection;
AddressDataArray() = default;
~AddressDataArray() = default;
void Append(const Entry &entry) { m_entries.push_back(entry); }
void Sort() {
if (m_entries.size() > 1)
std::stable_sort(m_entries.begin(), m_entries.end());
}
#ifdef ASSERT_RANGEMAP_ARE_SORTED
bool IsSorted() const {
typename Collection::const_iterator pos, end, prev;
// First we determine if we can combine any of the Entry objects so we
// don't end up allocating and making a new collection for no reason
for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end;
prev = pos++) {
if (prev != end && *pos < *prev)
return false;
}
return true;
}
#endif
void Clear() { m_entries.clear(); }
bool IsEmpty() const { return m_entries.empty(); }
size_t GetSize() const { return m_entries.size(); }
const Entry *GetEntryAtIndex(size_t i) const {
return ((i < m_entries.size()) ? &m_entries[i] : nullptr);
}
// Clients must ensure that "i" is a valid index prior to calling this
// function
const Entry &GetEntryRef(size_t i) const { return m_entries[i]; }
static bool BaseLessThan(const Entry &lhs, const Entry &rhs) {
return lhs.addr < rhs.addr;
}
Entry *FindEntry(B addr, bool exact_match_only) {
#ifdef ASSERT_RANGEMAP_ARE_SORTED
assert(IsSorted());
#endif
if (!m_entries.empty()) {
Entry entry;
entry.addr = addr;
typename Collection::iterator begin = m_entries.begin();
typename Collection::iterator end = m_entries.end();
typename Collection::iterator pos =
std::lower_bound(begin, end, entry, BaseLessThan);
while (pos != begin && pos[-1].addr == addr)
--pos;
if (pos != end) {
if (pos->addr == addr || !exact_match_only)
return &(*pos);
}
}
return nullptr;
}
const Entry *FindNextEntry(const Entry *entry) {
if (entry >= &*m_entries.begin() && entry + 1 < &*m_entries.end())
return entry + 1;
return nullptr;
}
Entry *Back() { return (m_entries.empty() ? nullptr : &m_entries.back()); }
const Entry *Back() const {
return (m_entries.empty() ? nullptr : &m_entries.back());
}
protected:
Collection m_entries;
};
} // namespace lldb_private
#endif // LLDB_UTILITY_RANGEMAP_H