| //===-- RangeMap.h ----------------------------------------------*- C++ -*-===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef liblldb_RangeMap_h_ |
| #define liblldb_RangeMap_h_ |
| |
| // C Includes |
| // C++ Includes |
| #include <algorithm> |
| #include <vector> |
| |
| // Other libraries and framework includes |
| #include "llvm/ADT/SmallVector.h" |
| |
| // Project includes |
| #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; |
| } |
| }; |
| |
| //---------------------------------------------------------------------- |
| // A range array class where you get to define the type of the ranges |
| // that the collection contains. |
| //---------------------------------------------------------------------- |
| |
| template <typename B, typename S, unsigned N> class RangeArray { |
| public: |
| typedef B BaseType; |
| typedef S SizeType; |
| typedef Range<B, S> Entry; |
| typedef llvm::SmallVector<Entry, N> Collection; |
| |
| RangeArray() = default; |
| |
| ~RangeArray() = default; |
| |
| void Append(const Entry &entry) { m_entries.push_back(entry); } |
| |
| void Append(B base, S size) { m_entries.emplace_back(base, size); } |
| |
| bool RemoveEntrtAtIndex(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 |
| // Can't combine if ranges if we have zero or one range |
| if (m_entries.size() > 1) { |
| // The list should be sorted prior to calling this function |
| 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->DoesAdjoinOrIntersect(*pos)) { |
| 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->DoesAdjoinOrIntersect(*pos)) |
| minimal_ranges.back().SetRangeEnd( |
| std::max<BaseType>(prev->GetRangeEnd(), 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); |
| } |
| } |
| } |
| |
| 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(); } |
| |
| 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]; } |
| |
| 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; |
| } |
| |
| protected: |
| Collection m_entries; |
| }; |
| |
| template <typename B, typename S> class RangeVector { |
| public: |
| typedef B BaseType; |
| typedef S SizeType; |
| typedef Range<B, S> Entry; |
| typedef std::vector<Entry> 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 |
| // Can't combine if ranges if we have zero or one range |
| if (m_entries.size() > 1) { |
| // The list should be sorted prior to calling this function |
| 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->DoesAdjoinOrIntersect(*pos)) { |
| 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->DoesAdjoinOrIntersect(*pos)) |
| minimal_ranges.back().SetRangeEnd( |
| std::max<BaseType>(prev->GetRangeEnd(), 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); |
| } |
| } |
| } |
| |
| 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; |
| } |
| |
| 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) {} |
| |
| bool operator<(const RangeData &rhs) const { |
| if (this->base == rhs.base) { |
| if (this->size == rhs.size) |
| return this->data < rhs.data; |
| else |
| return this->size < rhs.size; |
| } |
| return this->base < rhs.base; |
| } |
| |
| bool operator==(const RangeData &rhs) const { |
| return this->GetRangeBase() == rhs.GetRangeBase() && |
| this->GetByteSize() == rhs.GetByteSize() && this->data == rhs.data; |
| } |
| |
| bool operator!=(const RangeData &rhs) const { |
| return this->GetRangeBase() != rhs.GetRangeBase() || |
| this->GetByteSize() != rhs.GetByteSize() || this->data != rhs.data; |
| } |
| }; |
| |
| template <typename B, typename S, typename T, unsigned N> class RangeDataArray { |
| public: |
| typedef RangeData<B, S, T> Entry; |
| typedef llvm::SmallVector<Entry, N> Collection; |
| |
| RangeDataArray() = default; |
| |
| ~RangeDataArray() = 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 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); |
| } |
| |
| // 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.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; |
| } |
| |
| Entry *FindEntryThatContains(B addr) { |
| #ifdef ASSERT_RANGEMAP_ARE_SORTED |
| assert(IsSorted()); |
| #endif |
| if (!m_entries.empty()) { |
| Entry entry; |
| entry.SetRangeBase(addr); |
| entry.SetByteSize(1); |
| 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); |
| |
| if (pos != end && pos->Contains(addr)) { |
| return &(*pos); |
| } else if (pos != begin) { |
| --pos; |
| if (pos->Contains(addr)) { |
| return &(*pos); |
| } |
| } |
| } |
| return nullptr; |
| } |
| |
| const Entry *FindEntryThatContains(B addr) const { |
| #ifdef ASSERT_RANGEMAP_ARE_SORTED |
| assert(IsSorted()); |
| #endif |
| if (!m_entries.empty()) { |
| Entry entry; |
| entry.SetRangeBase(addr); |
| entry.SetByteSize(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; |
| } |
| |
| 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; |
| }; |
| |
| // Same as RangeDataArray, but uses std::vector as to not require static |
| // storage of N items in the class itself |
| template <typename B, typename S, typename T> class RangeDataVector { |
| public: |
| typedef RangeData<B, S, T> Entry; |
| typedef std::vector<Entry> Collection; |
| |
| RangeDataVector() = default; |
| |
| ~RangeDataVector() = 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 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); |
| } |
| } |
| |
| // Calculate the byte size of ranges with zero byte sizes by finding the next |
| // entry with a base address > the current base address |
| void CalculateSizesOfZeroByteSizeRanges(S full_size = 0) { |
| #ifdef ASSERT_RANGEMAP_ARE_SORTED |
| assert(IsSorted()); |
| #endif |
| typename Collection::iterator pos; |
| typename Collection::iterator end; |
| typename Collection::iterator next; |
| for (pos = m_entries.begin(), end = m_entries.end(); pos != end; ++pos) { |
| if (pos->GetByteSize() == 0) { |
| // Watch out for multiple entries with same address and make sure we |
| // find an entry that is greater than the current base address before |
| // we use that for the size |
| auto curr_base = pos->GetRangeBase(); |
| for (next = pos + 1; next != end; ++next) { |
| auto next_base = next->GetRangeBase(); |
| if (next_base > curr_base) { |
| pos->SetByteSize(next_base - curr_base); |
| break; |
| } |
| } |
| if (next == end && full_size > curr_base) |
| pos->SetByteSize(full_size - curr_base); |
| } |
| } |
| } |
| |
| void Clear() { m_entries.clear(); } |
| |
| void Reserve(typename Collection::size_type size) { m_entries.resize(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); |
| } |
| |
| 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 |
| 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 { |
| #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); |
| |
| while (pos != begin && pos[-1].Contains(addr)) |
| --pos; |
| |
| if (pos != end && pos->Contains(addr)) |
| return std::distance(begin, pos); |
| } |
| return UINT32_MAX; |
| } |
| |
| uint32_t FindEntryIndexesThatContain(B addr, |
| std::vector<uint32_t> &indexes) const { |
| #ifdef ASSERT_RANGEMAP_ARE_SORTED |
| assert(IsSorted()); |
| #endif |
| |
| if (!m_entries.empty()) { |
| for (const auto &entry : m_entries) { |
| if (entry.Contains(addr)) |
| indexes.push_back(entry.data); |
| } |
| } |
| return indexes.size(); |
| } |
| |
| Entry *FindEntryThatContains(B addr) { |
| #ifdef ASSERT_RANGEMAP_ARE_SORTED |
| assert(IsSorted()); |
| #endif |
| if (!m_entries.empty()) { |
| Entry entry; |
| entry.SetRangeBase(addr); |
| entry.SetByteSize(1); |
| 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].Contains(addr)) |
| --pos; |
| |
| if (pos != end && pos->Contains(addr)) |
| return &(*pos); |
| } |
| return nullptr; |
| } |
| |
| const Entry *FindEntryThatContains(B addr) const { |
| #ifdef ASSERT_RANGEMAP_ARE_SORTED |
| assert(IsSorted()); |
| #endif |
| if (!m_entries.empty()) { |
| Entry entry; |
| entry.SetRangeBase(addr); |
| entry.SetByteSize(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); |
| |
| while (pos != begin && pos[-1].Contains(addr)) |
| --pos; |
| |
| if (pos != end && 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); |
| |
| 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; |
| }; |
| |
| //---------------------------------------------------------------------- |
| // 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 // liblldb_RangeMap_h_ |