//===-- 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_ #include #include "lldb/lldb-private.h" #include "llvm/ADT/SmallVector.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 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; } 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()); } bool Overlap (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 class RangeArray { public: typedef B BaseType; typedef S SizeType; typedef Range Entry; typedef llvm::SmallVector Collection; RangeArray () : m_entries () { } ~RangeArray() { } void Append (const Entry &entry) { m_entries.push_back (entry); } 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->Overlap(*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->Overlap(*pos)) minimal_ranges.back().SetRangeEnd (std::max(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 { if (iContains(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 NULL; } 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 NULL; } protected: Collection m_entries; }; template class RangeVector { public: typedef B BaseType; typedef S SizeType; typedef Range Entry; typedef std::vector Collection; RangeVector () : m_entries () { } ~RangeVector() { } void Append (const Entry &entry) { m_entries.push_back (entry); } 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->Overlap(*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->Overlap(*pos)) minimal_ranges.back().SetRangeEnd (std::max(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 { if (iContains(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 NULL; } 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 NULL; } 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 struct RangeData : public Range { typedef T DataType; DataType data; RangeData () : Range (), data () { } RangeData (B base, S size) : Range (base, size), data () { } RangeData (B base, S size, DataType d) : Range (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 class RangeDataArray { public: typedef RangeData Entry; typedef llvm::SmallVector Collection; RangeDataArray () { } ~RangeDataArray() { } 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 { if (iContains(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 NULL; } 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 NULL; } 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 NULL; } Entry * Back() { if (!m_entries.empty()) return &m_entries.back(); return NULL; } const Entry * Back() const { if (!m_entries.empty()) return &m_entries.back(); return NULL; } 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 class RangeDataVector { public: typedef RangeData Entry; typedef std::vector Collection; RangeDataVector () { } ~RangeDataVector() { } 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 () { #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; } } } } } 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 { if (iContains(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); while(pos != begin && pos[-1].Contains(addr)) --pos; if (pos != end && pos->Contains(addr)) return &(*pos); } return NULL; } 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 NULL; } 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 NULL; } Entry * Back() { if (!m_entries.empty()) return &m_entries.back(); return NULL; } const Entry * Back() const { if (!m_entries.empty()) return &m_entries.back(); return NULL; } 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 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 class AddressDataArray { public: typedef AddressData Entry; typedef llvm::SmallVector Collection; AddressDataArray () { } ~AddressDataArray() { } 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 { if (iaddr == addr || !exact_match_only) return &(*pos); } } return NULL; } const Entry * FindNextEntry (const Entry *entry) { if (entry >= &*m_entries.begin() && entry + 1 < &*m_entries.end()) return entry + 1; return NULL; } Entry * Back() { if (!m_entries.empty()) return &m_entries.back(); return NULL; } const Entry * Back() const { if (!m_entries.empty()) return &m_entries.back(); return NULL; } protected: Collection m_entries; }; } // namespace lldb_private #endif // liblldb_RangeMap_h_