#pragma once #include // t_key must be a numeric type // ### you can't create a range taking up the entire range of t_key // ### (could be done by switching from start/length -> start/end) template class IRangeMap { public: struct Entry { bool Contains(t_key addr, t_key base) { return (addr >= base) && (addr <= (base + length - 1)); } t_key length; t_data data; }; typedef std::map EntryMapType; typedef typename EntryMapType::iterator Iterator; IRangeMap() { // } virtual ~IRangeMap() { // } void Clear(void) { m_entries.clear(); } t_data * Add(t_key start, t_key length) { t_data * result = NULL; Entry * entry = NULL; t_key end = start + length - 1; if(end >= start) // check for overflow ### should also check for overflow on length - 1, but that's pedantic { // special-case empty lists if(m_entries.empty()) { entry = &m_entries[start]; } else { // collision check EntryMapType::iterator iter = m_entries.lower_bound(start); // iter contains the first entry at or after start (or null) if(iter == m_entries.begin()) { // there can't be anything before this entry // so we only need to check if it's colliding with us if(iter->first > end) { // can't provide a hint because we're inserting at the top entry = &m_entries[start]; } } else { // see if this entry doesn't collide // can be null (null entries don't collide) if((iter == m_entries.end()) || (iter->first > end)) { // we didn't get the first entry in the map // and there is at least one entry in the map // therefore there's an entry before iter EntryMapType::iterator preIter = iter; preIter--; // check if this collides // guaranteed to be the first entry before start t_key preEnd = preIter->first + preIter->second.length - 1; if(preEnd < start) { // cool, everything's fine, allocate it EntryMapType::iterator newEntry = m_entries.insert(preIter, EntryMapType::value_type(start, Entry())); entry = &newEntry->second; } } } } } // set up the entry if(entry) { entry->length = length; result = &entry->data; } return result; } t_data * Lookup(t_key addr, t_key * base = NULL, t_key * length = NULL) { t_data * result = NULL; EntryMapType::iterator iter = LookupIter(addr); if(iter != m_entries.end()) { if(base) *base = iter->first; if(length) *length = iter->second.length; result = &iter->second.data; } return result; } bool Erase(t_key addr, t_key * base = NULL, t_key * length = NULL) { bool result = false; EntryMapType::iterator iter = LookupIter(addr); if(iter != m_entries.end()) { if(base) *base = iter->first; if(length) *length = iter->second.length; m_entries.erase(iter); result = true; } return result; } t_key GetDataRangeLength(t_data * data) { Entry * entry = reinterpret_cast (reinterpret_cast (data) - offsetof(Entry, data)); return entry->length; } typename EntryMapType::iterator LookupIter(t_key addr) { EntryMapType::iterator result = m_entries.end(); if(!m_entries.empty()) { // we need to find the last entry less than or equal to addr // find the first entry not less than addr EntryMapType::iterator iter = m_entries.lower_bound(addr); // iter is either equal to addr, greater than addr, or the end if(iter == m_entries.end()) { // iter is the end // can only be in the entry before this // which does exist because map isn't empty --iter; if(iter->second.Contains(addr, iter->first)) { result = iter; } } // at this point iter must be valid else if(iter->first > addr) { // iter is greater than addr // can only be in the entry before this // but there may not be an entry before this if(iter != m_entries.begin()) { --iter; if(iter->second.Contains(addr, iter->first)) { result = iter; } } } else { // iter is equal to addr and matches result = iter; } } return result; } typename EntryMapType::iterator Begin(void) { return m_entries.begin(); } typename EntryMapType::iterator End(void) { return m_entries.end(); } private: EntryMapType m_entries; };