The Pedigree Project 0.1
RangeList.h
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#ifndef KERNEL_UTILITIES_RANGELIST_H
21#define KERNEL_UTILITIES_RANGELIST_H
22
23#include "pedigree/kernel/compiler.h"
24#include "pedigree/kernel/processor/types.h"
25#include "pedigree/kernel/utilities/StaticString.h"
26#include "pedigree/kernel/utilities/Vector.h"
27
34template <typename T, bool Reversed = false>
35class EXPORTED_PUBLIC RangeList {
36 public:
38 inline RangeList() : m_List(), m_bPreferUsed(false) {}
40 inline RangeList(bool preferUsed) : m_List(), m_bPreferUsed(preferUsed) {}
44 RangeList(T Address, T Length, bool XXX, bool preferUsed = false)
45 : m_List(), m_bPreferUsed(preferUsed) {
46 m_List.pushBack(Range(Address, Length));
47 }
50
52 RangeList& operator=(const RangeList& l);
53
55 struct Range {
56 Range() = default;
58 Range(T Address, T Length) : address(Address), length(Length) {}
59
64
65 bool operator==(const Range& other) const {
66 return (address == other.address) && (length == other.length);
67 }
68 };
69
75 void free(T address, T length, bool merge = true);
76 bool tryFree(T address, T length, bool merge = true);
78 bool freeWithoutAllocation(T address, T length);
79 bool allocateWithoutAllocation(T length, T& address);
80 bool allocateSpecificWithoutAllocation(T address, T length);
81
87 bool allocate(T length, T& address);
92 bool allocateSpecific(T address, T length);
93 void clear();
95 void swap(RangeList& other) noexcept {
96 m_List.swap(other.m_List);
97 const bool preferUsed = m_bPreferUsed;
98 m_bPreferUsed = other.m_bPreferUsed;
99 other.m_bPreferUsed = preferUsed;
100 }
101
104 inline size_t size() const {
105 return m_List.count();
106 }
108 bool getRange(size_t index, Range& range) const;
109
111 void sweep();
112
114 void dump(void (*emit_line)(const char* s)) const;
115
116 private:
119
122
123 typedef typename decltype(m_List)::Iterator Iterator;
124 typedef typename decltype(m_List)::ConstIterator ConstIterator;
125 typedef typename decltype(m_List)::ReverseIterator ReverseIterator;
126 typedef typename decltype(m_List)::ConstReverseIterator ConstReverseIterator;
127};
128
132template <typename T, bool Reversed>
134 : m_List(), m_bPreferUsed(other.m_bPreferUsed) {
135 for (ConstIterator it = other.m_List.begin(); it != other.m_List.end(); ++it) {
136 m_List.pushBack(*it);
137 }
138}
139
140template <typename T, bool Reversed>
142 if (this == &other) {
143 return *this;
144 }
145 // Need to clean up all our existing ranges.
146 clear();
147 m_bPreferUsed = other.m_bPreferUsed;
148
149 for (ConstIterator it = other.m_List.begin(); it != other.m_List.end(); ++it) {
150 m_List.pushBack(*it);
151 }
152
153 return *this;
154}
155
156template <typename T, bool Reversed>
157void RangeList<T, Reversed>::free(T address, T length, bool merge) {
158 if (merge) {
159 Iterator cur = m_List.begin();
160 ConstIterator end = m_List.end();
161
162 // Try and find a place to merge immediately.
163 bool needsNew = true;
164 for (; cur != end; ++cur) {
165 // Region ends at our freed address.
166 if ((cur->address + cur->length) == address) {
167 // Update - all done.
168 cur->length += length;
169 needsNew = false;
170 break;
171 }
172 // Region starts after our address.
173 else if (cur->address == (address + length)) {
174 // Expand.
175 cur->address -= length;
176 cur->length += length;
177 needsNew = false;
178 break;
179 }
180 }
181
182 if (!needsNew)
183 return;
184 }
185
186 // Couldn't find a merge or didn't want one, so we need to add a new region.
187
188 // Add the range back to our list, but in such a way that it is allocated
189 // last rather than first (if another allocation of the same length comes
190 // later).
191 Range range(address, length);
192
193 // Decide which side of the list to push to. If we prefer used ranges over
194 // fresh ranges, we want to invert the push decision.
195 bool front = Reversed;
196 if (m_bPreferUsed)
197 front = !front;
198
199 if (front)
200 m_List.pushFront(range);
201 else
202 m_List.pushBack(range);
203
204 // NOTE: we defer sweeping to allocate(), and only if a first attempt at
205 // allocate() fails to successfully find a range it can use. This saves
206 // time when freeing regions and is useful for RangeLists that are not
207 // heavily utilized.
208}
209
210template <typename T, bool Reversed>
211bool RangeList<T, Reversed>::tryFree(T address, T length, bool merge) {
212 if (merge && freeWithoutAllocation(address, length)) {
213 return true;
214 }
215 if (!m_List.tryReserve(m_List.count() + 1)) {
216 return false;
217 }
218 Range range(address, length);
219 if (Reversed != m_bPreferUsed) {
220 m_List.pushFront(range);
221 } else {
222 m_List.pushBack(range);
223 }
224 return true;
225}
226
227template <typename T, bool Reversed>
229 if (!length) {
230 return true;
231 }
232 size_t empty = m_List.count();
233 for (size_t i = 0; i < m_List.count(); ++i) {
234 Range* range = &m_List[i];
235 if (!range->length) {
236 empty = i;
237 } else if (range->address + range->length == address) {
238 range->length += length;
239 return true;
240 } else if (range->address == address + length) {
241 range->address = address;
242 range->length += length;
243 return true;
244 }
245 }
246 if (empty == m_List.count()) {
247 return false;
248 }
249 m_List.erase(empty);
250 Range range(address, length);
251 if (Reversed != m_bPreferUsed) {
252 m_List.pushFront(range);
253 } else {
254 m_List.pushBack(range);
255 }
256 return true;
257}
258
259template <typename T, bool Reversed>
260bool RangeList<T, Reversed>::allocateWithoutAllocation(T length, T& address) {
261 if (!length) {
262 return false;
263 }
264 for (size_t i = 0; i < m_List.count(); ++i) {
265 Range* range = &m_List[Reversed ? m_List.count() - 1 - i : i];
266 if (range->length < length) {
267 continue;
268 }
269 address = Reversed ? range->address + range->length - length : range->address;
270 if (!Reversed) {
271 range->address += length;
272 }
273 range->length -= length;
274 return true;
275 }
276 return false;
277}
278
279template <typename T, bool Reversed>
281 if (!length) {
282 return false;
283 }
284 for (size_t i = 0; i < m_List.count(); ++i) {
285 Range* range = &m_List[i];
286 if (address < range->address || address - range->address > range->length ||
287 length > range->length - (address - range->address)) {
288 continue;
289 }
290 const T prefix = address - range->address;
291 const T suffix = range->length - prefix - length;
292 if (prefix && suffix) {
293 Range* spare = nullptr;
294 for (size_t j = 0; j < m_List.count(); ++j) {
295 if (!m_List[j].length) {
296 spare = &m_List[j];
297 break;
298 }
299 }
300 if (!spare) {
301 return false;
302 }
303 spare->address = address + length;
304 spare->length = suffix;
305 range->length = prefix;
306 } else if (prefix) {
307 range->length = prefix;
308 } else {
309 range->address += length;
310 range->length = suffix;
311 }
312 return true;
313 }
314 return false;
315}
316
317template <typename T, bool Reversed>
318bool RangeList<T, Reversed>::allocate(T length, T& address) {
319 bool bSuccess = false;
320
321 for (int i = 0; i < 2; ++i) {
322 for (size_t j = 0; j < m_List.count(); ++j) {
323 const size_t index = Reversed ? m_List.count() - 1 - j : j;
324 Range& range = m_List[index];
325 if (range.length < length) {
326 continue;
327 }
328
329 if (Reversed) {
330 // Big enough. Cut into the END of this range.
331 T offset = range.length - length;
332 address = range.address + offset;
333 } else {
334 address = range.address;
335 range.address += length;
336 }
337 range.length -= length;
338
339 // Remove if the entry no longer exists.
340 if (!range.length) {
341 m_List.erase(index);
342 }
343
344 bSuccess = true;
345 break;
346 }
347
348 if (bSuccess) {
349 return true;
350 } else if (!i) {
351 // Failed on first pass, try another pass after a sweep.
352 // The sweep could merge some regions and let us allocate.
353 // This is better than sweeping on every single allocation, which
354 // could be really slow and unnecessary.
355 sweep();
356 }
357 }
358
359 return bSuccess;
360}
361
362template <typename T, bool Reversed>
364 bool bSuccess = false;
365 for (int i = 0; i < 2; ++i) {
366 for (size_t j = 0; j < m_List.count(); ++j) {
367 Range* cur = &m_List[j];
368 // Precise match.
369 if (cur->address == address && cur->length == length) {
370 m_List.erase(j);
371 bSuccess = true;
372 break;
373 }
374
375 // Match at end.
376 else if (address > cur->address && (cur->address + cur->length) == (address + length)) {
377 cur->length -= length;
378 bSuccess = true;
379 break;
380 }
381
382 // Match at start.
383 else if (cur->address == address && cur->length > length) {
384 cur->address += length;
385 cur->length -= length;
386 bSuccess = true;
387 break;
388 }
389
390 // Match within.
391 else if (address > cur->address && (cur->address + cur->length) > (address + length)) {
392 // Need to split the range.
393 const T suffix = cur->address + cur->length - address - length;
394 const T prefix = address - cur->address;
395 if (!m_List.tryReserve(m_List.count() + 1)) {
396 return false;
397 }
398 // Growing the value buffer can move the original range.
399 m_List[j].length = prefix;
400 m_List.pushBack(Range(address + length, suffix));
401 bSuccess = true;
402 break;
403 }
404 }
405
406 if (bSuccess) {
407 return bSuccess;
408 } else if (!i) {
409 // Failed in the first pass, sweep to merge potential regions and
410 // then we'll try again.
411 sweep();
412 }
413 }
414
415 return bSuccess;
416}
417
418template <typename T, bool Reversed>
419bool RangeList<T, Reversed>::getRange(size_t index, Range& range) const {
420 range = Range(0, 0);
421 if (index >= m_List.count())
422 return false;
423
424 range = m_List[index];
425 return true;
426}
427
428template <typename T, bool Reversed>
432
433template <typename T, bool Reversed>
435 m_List.clear();
436}
437
438template <typename T, bool Reversed>
440 if (m_List.count() < 2) {
441 return;
442 }
443
444 // Storage order preserves allocation preferences; adjacent addresses need
445 // not be neighboring entries. Keep the first entry's position when merging.
446 for (size_t i = 0; i < m_List.count(); ++i) {
447 for (size_t j = i + 1; j < m_List.count();) {
448 Range* cur = &m_List[i];
449 Range* next = &m_List[j];
450 if (cur->address + cur->length == next->address) {
451 cur->length += next->length;
452 } else if (next->address + next->length == cur->address) {
453 cur->address = next->address;
454 cur->length += next->length;
455 } else {
456 ++j;
457 continue;
458 }
459 m_List.erase(j);
460 // The enlarged range can now reach an entry we passed earlier.
461 j = i + 1;
462 }
463 }
464}
465
466template <typename T, bool Reversed>
467void RangeList<T, Reversed>::dump(void (*emit_line)(const char* s)) const {
468 for (size_t i = 0; i < m_List.count(); ++i) {
469 const Range* range = &m_List[i];
470
472 str.append("range ");
473 str.append(range->address, 16, 16, '0');
474 str.append(" -> ");
475 str.append(range->address + range->length, 16, 16, '0');
476 str.append(" (");
477 str.append(range->length, 10);
478 str.append(" bytes)");
479 emit_line(static_cast<const char*>(str));
480 }
481}
482
483extern template class RangeList<uint64_t>; // IWYU pragma: keep
484extern template class RangeList<uint32_t>; // IWYU pragma: keep
485
486#endif
An iterator applicable for many data structures.
Definition Iterator.h:41
bool getRange(size_t index, Range &range) const
Definition RangeList.h:419
RangeList(T Address, T Length, bool XXX, bool preferUsed=false)
Definition RangeList.h:44
bool allocate(T length, T &address)
Definition RangeList.h:318
size_t size() const
Definition RangeList.h:104
void sweep()
Definition RangeList.h:439
void dump(void(*emit_line)(const char *s)) const
Definition RangeList.h:467
void swap(RangeList &other) noexcept
Definition RangeList.h:95
Vector< Range > m_List
Definition RangeList.h:118
RangeList(const RangeList &)
Definition RangeList.h:133
bool m_bPreferUsed
Definition RangeList.h:121
bool freeWithoutAllocation(T address, T length)
Definition RangeList.h:228
RangeList(bool preferUsed)
Definition RangeList.h:40
void free(T address, T length, bool merge=true)
Definition RangeList.h:157
bool allocateSpecific(T address, T length)
Definition RangeList.h:363
A vector / dynamic array.
Definition Vector.h:33
bool operator==(const Iterator< originalT, Struct, FunctionPrev, FunctionNext, T1 > &x1, const Iterator< originalT, Struct, FunctionPrev, FunctionNext, T2 > &x2)
Definition Iterator.h:256
Range(T Address, T Length)
Definition RangeList.h:58