216 lines
4.4 KiB
C
216 lines
4.4 KiB
C
|
#pragma once
|
||
|
|
||
|
#include <map>
|
||
|
|
||
|
// 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 <typename t_key, typename t_data>
|
||
|
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 <t_key, Entry> 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 <Entry *>(reinterpret_cast <UInt8 *>(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;
|
||
|
};
|