Skip to content

Commit c979399

Browse files
committed
Move GenericRange and GenericRangeMap to api
1 parent 36c49a4 commit c979399

File tree

1 file changed

+244
-0
lines changed

1 file changed

+244
-0
lines changed

genericrange.h

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
// Copyright (c) 2015-2024 Vector 35 Inc
2+
//
3+
// Permission is hereby granted, free of charge, to any person obtaining a copy
4+
// of this software and associated documentation files (the "Software"), to
5+
// deal in the Software without restriction, including without limitation the
6+
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7+
// sell copies of the Software, and to permit persons to whom the Software is
8+
// furnished to do so, subject to the following conditions:
9+
//
10+
// The above copyright notice and this permission notice shall be included in
11+
// all copies or substantial portions of the Software.
12+
//
13+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18+
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19+
// IN THE SOFTWARE.
20+
21+
22+
#pragma once
23+
24+
#ifdef BINARYNINJACORE_LIBRARY
25+
#include "binaryninjacore_global.h"
26+
namespace BinaryNinjaCore
27+
{
28+
#else
29+
using namespace std;
30+
#endif
31+
32+
template <typename T>
33+
class GenericRange
34+
{
35+
uint64_t m_start;
36+
uint64_t m_end;
37+
vector<T> m_items;
38+
39+
public:
40+
GenericRange(uint64_t s) : m_start(s), m_end(0) { }
41+
GenericRange(uint64_t s, uint64_t e, const T& item) : m_start(s), m_end(e), m_items{item} {}
42+
GenericRange(uint64_t s, uint64_t e, const vector<T>& items) : m_start(s), m_end(e), m_items{items} {}
43+
44+
bool operator<(const GenericRange& other) const
45+
{
46+
if (m_start != other.m_start)
47+
return m_start < other.m_start;
48+
return m_end < other.m_end;
49+
}
50+
51+
uint64_t GetStart() const { return m_start; }
52+
uint64_t GetEnd() const { return m_end; }
53+
const vector<T>& GetItems() const { return m_items; }
54+
vector<T>& GetMutableItems() { return m_items; }
55+
56+
bool overlaps(const GenericRange& other) const { return !(other.m_start > m_end || m_start > other.m_end); }
57+
58+
vector<GenericRange> split(const GenericRange& nextInterval) const
59+
{
60+
vector<GenericRange> result;
61+
if (overlaps(nextInterval))
62+
{
63+
// Find overlap start and end
64+
uint64_t intersectionStart = std::max(m_start, nextInterval.m_start);
65+
uint64_t intersectionEnd = std::min(m_end, nextInterval.m_end);
66+
67+
// Add part of this section to before the intersecting region if it starts earlier
68+
if (m_start < intersectionStart)
69+
result.push_back({m_start, intersectionStart - 1, m_items});
70+
71+
// Add the intersecting range, plus both sets of items
72+
GenericRange intersection(intersectionStart, intersectionEnd, m_items);
73+
intersection.m_items.insert(intersection.m_items.end(), nextInterval.m_items.begin(), nextInterval.m_items.end());
74+
result.push_back(intersection);
75+
76+
// If the an interval's end is after the intersection (only up to one will be) add it after
77+
if (nextInterval.m_end > intersectionEnd)
78+
result.push_back({intersectionEnd + 1, nextInterval.m_end, nextInterval.m_items});
79+
else if (m_end > intersectionEnd)
80+
result.push_back({intersectionEnd + 1, m_end, m_items});
81+
}
82+
83+
return result;
84+
}
85+
};
86+
87+
// A map of ranges to items. The ranges are flattened and sorted, and the map is used to quickly find the items. Range values are inclusive.
88+
template <typename T>
89+
class GenericRangeMap
90+
{
91+
vector<GenericRange<T>> m_sourceRanges;
92+
vector<GenericRange<T>> m_flattenedRanges;
93+
map<uint64_t, GenericRange<T>> m_rangeMap;
94+
95+
void populateRangeMap()
96+
{
97+
uint64_t nextStart = 0;
98+
for (const auto& i : m_flattenedRanges)
99+
{
100+
if (i.GetStart() > nextStart)
101+
m_rangeMap.emplace(nextStart, GenericRange<T>(nextStart, i.GetStart() - 1, vector<T>()));
102+
103+
m_rangeMap.emplace(i.GetStart(), GenericRange<T>(i.GetStart(), i.GetEnd(), i.GetItems()));
104+
nextStart = i.GetEnd();
105+
if (nextStart != std::numeric_limits<uint64_t>::max())
106+
nextStart++;
107+
}
108+
109+
if (nextStart != std::numeric_limits<uint64_t>::max())
110+
m_rangeMap.emplace(nextStart, GenericRange<T>(nextStart, std::numeric_limits<uint64_t>::max(), vector<T>()));
111+
}
112+
113+
public:
114+
static void flatten(vector<GenericRange<T>>& intervals)
115+
{
116+
// Make a flat list of intervals, with each interval having all elements found in it
117+
// TODO: using a vector isn't ideal, since each modification not at front or back is O(n)
118+
std::sort(intervals.begin(), intervals.end());
119+
auto itr = intervals.begin();
120+
while (itr != intervals.end())
121+
{
122+
auto currentRange = *itr;
123+
auto nextRange = std::next(itr);
124+
if (nextRange == intervals.end()) // This is the last interval
125+
break;
126+
127+
if (auto splitRanges = currentRange.split(*nextRange); splitRanges.size())
128+
{
129+
itr = intervals.erase(itr, std::next(nextRange)); // Remove the two source ranges that were split
130+
size_t resetIndex = intervals.size() + splitRanges.size() - 1; // This is where the iterator will be moved to after inserting new ranges
131+
for (const auto& range : splitRanges)
132+
{
133+
// For each split range, insert it in its sorted position
134+
auto rangeInsertItr = std::upper_bound(intervals.begin(), intervals.end(), range);
135+
size_t rangeInsertIndex = rangeInsertItr - intervals.begin();
136+
intervals.insert(rangeInsertItr, range);
137+
// Move the reset index to before the lowest inserted range's index; everything before is still sorted
138+
resetIndex = std::min(resetIndex, rangeInsertIndex == 0 ? 0 : rangeInsertIndex - 1);
139+
}
140+
itr = intervals.begin() + resetIndex;
141+
}
142+
else
143+
++itr;
144+
}
145+
}
146+
147+
GenericRangeMap()
148+
{
149+
populateRangeMap();
150+
}
151+
152+
GenericRangeMap(const vector<GenericRange<T>>& ranges)
153+
{
154+
m_sourceRanges = ranges;
155+
m_flattenedRanges = ranges;
156+
flatten(m_flattenedRanges);
157+
populateRangeMap();
158+
}
159+
160+
GenericRangeMap(const vector<GenericRange<T>>& ranges, std::function<void(vector<T>&)> orderingStrategy)
161+
{
162+
m_sourceRanges = ranges;
163+
m_flattenedRanges = ranges;
164+
flatten(m_flattenedRanges);
165+
if (orderingStrategy)
166+
{
167+
for (auto& i : m_flattenedRanges)
168+
orderingStrategy(i.GetMutableItems());
169+
}
170+
populateRangeMap();
171+
}
172+
173+
const vector<GenericRange<T>>& GetSourceRanges() const { return m_sourceRanges; }
174+
const vector<GenericRange<T>>& GetRanges() const { return m_flattenedRanges; }
175+
176+
const vector<T>& GetItemsAt(uint64_t addr) const
177+
{
178+
if (auto itr = m_rangeMap.upper_bound(addr); itr != m_rangeMap.begin())
179+
{
180+
--itr;
181+
return itr->second.GetItems();
182+
}
183+
184+
throw std::out_of_range("GenericRangeMap::GetItemsAt - Address not found in any range!");
185+
}
186+
187+
const GenericRange<T>& GetGenericRangeAt(uint64_t addr) const
188+
{
189+
if (auto itr = m_rangeMap.upper_bound(addr); itr != m_rangeMap.begin())
190+
{
191+
--itr;
192+
return itr->second;
193+
}
194+
195+
throw std::out_of_range("GenericRangeMap::GetGenericRangeAt - Address not found in any range!");
196+
}
197+
198+
GenericRange<T>& GetMutableGenericRangeAt(uint64_t addr)
199+
{
200+
if (auto itr = m_rangeMap.upper_bound(addr); itr != m_rangeMap.begin())
201+
{
202+
--itr;
203+
return itr->second;
204+
}
205+
206+
throw std::out_of_range("GenericRangeMap::GetMutableGenericRangeAt - Address not found in any range!");
207+
}
208+
209+
std::optional<std::pair<uint64_t, uint64_t>> GetNextValidRange(uint64_t addr, std::function<bool(const GenericRange<T>&)> predicate) const
210+
{
211+
auto itr = m_rangeMap.upper_bound(addr);
212+
if (itr != m_rangeMap.begin())
213+
--itr;
214+
215+
while (itr != m_rangeMap.end())
216+
{
217+
if (predicate(itr->second))
218+
return std::make_pair(itr->second.GetStart(), itr->second.GetEnd());
219+
++itr;
220+
}
221+
222+
return std::nullopt;
223+
}
224+
225+
std::optional<std::pair<uint64_t, uint64_t>> GetPreviousValidRange(uint64_t addr, std::function<bool(const GenericRange<T>&)> predicate) const
226+
{
227+
auto itr = m_rangeMap.upper_bound(addr);
228+
if (itr != m_rangeMap.begin())
229+
--itr;
230+
231+
while (itr != m_rangeMap.begin())
232+
{
233+
if (predicate(itr->second))
234+
return std::make_pair(itr->second.GetStart(), itr->second.GetEnd());
235+
--itr;
236+
}
237+
238+
return std::nullopt;
239+
}
240+
};
241+
242+
#ifdef BINARYNINJACORE_LIBRARY
243+
}
244+
#endif

0 commit comments

Comments
 (0)