The Pedigree Project 0.1
MemoryMappedFile-map.cc
1/* Copyright (c) 2026, Pedigree Developers. */
2#include "pedigree/kernel/LockGuard.h"
3#include "pedigree/kernel/process/Process.h"
4#include "pedigree/kernel/process/Thread.h"
5#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
6#include "pedigree/kernel/processor/Processor.h"
7#include "pedigree/kernel/processor/ProcessorInformation.h"
8#include "pedigree/kernel/utilities/Vector.h"
9
10#include "File.h"
11#include "MemoryMappedFile.h"
12
13namespace {
15using Status = MemoryMapManager::MapStatus;
16using Placement = MemoryMapManager::Placement;
17MemoryAllocator* allocatorFor(Snapshot& snapshot, VirtualAddressSpace& space, uintptr_t base,
18 size_t length) {
19 if (space.getDynamicStart() && base >= space.getDynamicStart() &&
20 base + length <= space.getDynamicEnd())
21 return &snapshot.dynamic;
22 if (base >= space.getUserStart() && base + length <= space.getUserReservedStart())
23 return &snapshot.normal;
24 return nullptr;
25}
26bool reserveFree(MemoryAllocator& allocator, uintptr_t base, size_t length) {
27 while (true) {
28 bool found = false;
29 for (size_t i = 0; i < allocator.size(); ++i) {
30 MemoryAllocator::Range range(0, 0);
31 if (!allocator.getRange(i, range))
32 continue;
33 const uintptr_t first = range.address > base ? range.address : base;
34 const uintptr_t rangeEnd = range.length > ~uintptr_t(0) - range.address
35 ? ~uintptr_t(0)
36 : range.address + range.length;
37 const uintptr_t last = rangeEnd < base + length ? rangeEnd : base + length;
38 if (first >= last)
39 continue;
40 if (!allocator.allocateSpecific(first, last - first))
41 return false;
42 found = true;
43 break;
44 }
45 if (!found)
46 return true;
47 }
48}
49Status place(Snapshot& snapshot, VirtualAddressSpace& space, uintptr_t& address, size_t length,
50 Placement placement, size_t mask) {
51 if (address) {
52 auto* allocator = allocatorFor(snapshot, space, address, length);
53 if (allocator && allocator->allocateSpecific(address, length))
54 return Status::Success;
55 if (placement == Placement::FixedNoReplace)
56 return Status::AddressInUse;
57 if (placement == Placement::FixedReplace)
58 return reserveFree(snapshot.dynamic, address, length) &&
59 reserveFree(snapshot.normal, address, length)
60 ? Status::Success
61 : Status::NoMemory;
62 }
63 MemoryAllocator* allocators[] = {&snapshot.dynamic, &snapshot.normal};
64 for (auto* allocator : allocators) {
65 uintptr_t allocation = 0;
66 if (!allocator->allocate(length + mask, allocation))
67 continue;
68 address = (allocation + mask) & ~mask;
69 if (address != allocation && !allocator->tryFree(allocation, address - allocation))
70 return Status::NoMemory;
71 const uintptr_t end = allocation + length + mask;
72 if (address + length < end && !allocator->tryFree(address + length, end - address - length))
73 return Status::NoMemory;
74 return Status::Success;
75 }
76 return Status::NoMemory;
77}
78struct MappingPlan {
79 MappingList<MemoryMappedObject>* replacement = nullptr;
80 Vector<MemoryMappedObject*> staged, retired;
81 MemoryMappedObject* inserted = nullptr;
82 bool committed = false;
83 ~MappingPlan() {
84 for (auto* object : committed ? retired : staged)
85 delete object;
86 delete replacement;
87 }
88 bool appendSlice(MemoryMappedObject* owner, uintptr_t first, uintptr_t end) {
89 if (first >= end)
90 return true;
91 // Only surviving tracked pages need staging; sparse virtual extent is
92 // not a useful bound on the metadata needed to split or retire an owner.
93 auto* object = owner->stageSlice(first, end - first, first, end - first);
94 if (!object)
95 return false;
96 staged.pushBack(object);
97 return replacement->tryPushBack(object);
98 }
99};
100
101class DirectReservation {
102 public:
103 DirectReservation()
104 : m_Process(nullptr),
105 m_Region(Process::UserRegion::Normal),
106 m_Base(0),
107 m_Length(0),
108 m_Committed(false) {}
109
110 void arm(Process* process, Process::UserRegion region, uintptr_t base, size_t length) {
111 m_Process = process;
112 m_Region = region;
113 m_Base = base;
114 m_Length = length;
115 }
116
117 void commit() {
118 m_Committed = true;
119 }
120
121 ~DirectReservation() {
122 if (m_Process && !m_Committed)
123 m_Process->freeUserRange(m_Region, m_Base, m_Length);
124 }
125
126 private:
127 Process* m_Process;
128 Process::UserRegion m_Region;
129 uintptr_t m_Base;
130 size_t m_Length;
131 bool m_Committed;
132};
133
134bool allocateDirect(Process& process, VirtualAddressSpace& space, size_t length, size_t mask,
135 uintptr_t& address, Process::UserRegion& region) {
136 const size_t allocationLength = length + mask;
137 auto allocate = [&](Process::UserRegion candidate) {
138 uintptr_t allocation = 0;
139 if (!process.allocateUserRange(candidate, allocationLength, allocation))
140 return false;
141 if (allocation > ~uintptr_t(0) - mask) {
142 process.freeUserRange(candidate, allocation, allocationLength);
143 return false;
144 }
145 const uintptr_t aligned = (allocation + mask) & ~mask;
146 if (aligned > ~uintptr_t(0) - length || allocationLength > ~uintptr_t(0) - allocation) {
147 process.freeUserRange(candidate, allocation, allocationLength);
148 return false;
149 }
150 const uintptr_t allocationEnd = allocation + allocationLength;
151 const uintptr_t usedEnd = aligned + length;
152 if (aligned != allocation)
153 process.freeUserRange(candidate, allocation, aligned - allocation);
154 if (usedEnd != allocationEnd)
155 process.freeUserRange(candidate, usedEnd, allocationEnd - usedEnd);
156 address = aligned;
157 region = candidate;
158 return true;
159 };
160
161 if (space.getDynamicStart() && allocate(Process::UserRegion::Dynamic))
162 return true;
163 return allocate(Process::UserRegion::Normal);
164}
165} // namespace
166
167MemoryMappedObject* MemoryMapManager::mapFile(File* file, uintptr_t& address, size_t length,
168 MemoryMappedObject::Permissions perms, size_t offset,
169 bool copyOnWrite) {
170 return mapFile(file, address, length, perms, offset, copyOnWrite, Placement::FixedReplace,
171 nullptr);
172}
173MemoryMappedObject* MemoryMapManager::mapFile(File* file, uintptr_t& address, size_t length,
174 MemoryMappedObject::Permissions perms, size_t offset,
175 bool copyOnWrite, Placement placement,
176 MapStatus* status,
178 const SharedPointer<MappingAttachment>& attachment,
179 MemoryLockMode requestedLock,
180 const FileMappingOrigin& origin) {
181 OperationGuard operation(*this);
182 bool mayWrite = maximumPerms & MemoryMappedObject::Write;
183 if (!file->allowMapping(!copyOnWrite, perms & MemoryMappedObject::Write, mayWrite)) {
184 if (status)
185 *status = MapStatus::PolicyDenied;
186 return nullptr;
187 }
188 if (!mayWrite)
189 maximumPerms &= ~MemoryMappedObject::Write;
190 return publishMapping(file, address, length, perms, offset, copyOnWrite, placement, status,
191 maximumPerms, attachment, requestedLock, origin);
192}
193MemoryMappedObject* MemoryMapManager::mapAnon(uintptr_t& address, size_t length,
195 return mapAnon(address, length, perms, Placement::FixedReplace, nullptr);
196}
197MemoryMappedObject* MemoryMapManager::mapAnon(uintptr_t& address, size_t length,
199 Placement placement, MapStatus* status,
200 MemoryLockMode requestedLock) {
201 OperationGuard operation(*this);
202 return publishMapping(
203 nullptr, address, length, perms, 0, true, placement, status,
204 MemoryMappedObject::Read | MemoryMappedObject::Write | MemoryMappedObject::Exec,
205 SharedPointer<MappingAttachment>(), requestedLock);
206}
207
208MemoryMappedObject* MemoryMapManager::publishMapping(
209 File* file, uintptr_t& address, size_t length, MemoryMappedObject::Permissions perms,
210 size_t offset, bool copyOnWrite, Placement placement, MapStatus* status,
212 const SharedPointer<MappingAttachment>& attachment, MemoryLockMode requestedLock,
213 const FileMappingOrigin& origin) {
214 if (status)
215 *status = MapStatus::NoMemory;
216 const size_t pageSize = PhysicalMemoryManager::getPageSize(), mask = pageSize - 1;
217 const size_t actualLength = length;
218 if (!length || length > ~size_t(0) - mask)
219 return nullptr;
220 length = (length + mask) & ~mask;
221 if (length > ~size_t(0) - mask || length > ~uintptr_t(0) - address)
222 return nullptr;
223 auto& space = Processor::information().getVirtualAddressSpace();
224 auto* process = Processor::information().getCurrentThread()->getParent();
225#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
226 process->recordBenchmarkVmCounter(Process::VmPublishCalls);
227#endif
228 auto* account = space.memoryLockAccount();
229 const MemoryLockMode mode = requestedLock != MemoryLockMode::None ? requestedLock
230 : account ? account->futureMode()
231 : MemoryLockMode::None;
232 auto* objects = m_MmObjectLists.lookup(&space);
233 // Preallocate the empty registry entry before a reservation or PTE changes.
234 if (!objects) {
235 auto* empty = new MmObjectList;
236 if (!empty)
237 return nullptr;
238 if (!m_MmObjectLists.tryInsert(&space, empty)) {
239 delete empty;
240 return nullptr;
241 }
242 objects = empty;
243 }
244#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
245 process->recordBenchmarkVmCounter(Process::VmPublishObjectCount, objects->count());
246#endif
247 const uintptr_t requested = address;
248 const bool directPlacement = requested == 0 && placement == Placement::Hint;
249 for (size_t attempt = 0; attempt < 32; ++attempt) {
250 Snapshot snapshot;
251 uintptr_t destination = requested;
252 Process::UserRegion directRegion = Process::UserRegion::Normal;
253 DirectReservation directReservation;
254 bool direct = false;
255 if (directPlacement) {
256 direct = allocateDirect(*process, space, length, mask, destination, directRegion);
257 if (!direct)
258 return nullptr;
259 directReservation.arm(process, directRegion, destination, length);
260 } else {
261 if (!process->snapshotUserReservations(snapshot))
262 return nullptr;
263 auto placementStatus = place(snapshot, space, destination, length, placement, mask);
264 if (placementStatus != MapStatus::Success) {
265 if (status)
266 *status = placementStatus;
267 return nullptr;
268 }
269 }
270 if (space.runtimeMappingPages(destination, length)) {
271 if (status)
272 *status = MapStatus::PolicyDenied;
273 return nullptr;
274 }
276 auto rawStatus = space.rawUserMemory().prepareReplacement(destination, length, raw);
277 if (rawStatus != MemoryLockStatus::Success)
278 return nullptr;
279 bool overlaps = false;
280#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
281 size_t objectVisits = 0;
282#endif
283 if (!direct)
284 for (auto* object : *objects) {
285#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
286 ++objectVisits;
287#endif
288 const uintptr_t end = (object->address() + object->length() + mask) & ~mask;
289 if (destination < end && object->address() < destination + length) {
290 overlaps = true;
291 break;
292 }
293 }
294#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
295 process->recordBenchmarkVmCounter(Process::VmPublishOverlapProbeVisits, objectVisits);
296 if (overlaps)
297 process->recordBenchmarkVmCounter(Process::VmPublishOverlapHits);
298#endif
299 MappingPlan plan;
300 if (!plan.staged.tryReserve(overlaps ? objects->count() * 2 + 1 : 1))
301 return nullptr;
302 size_t removedPages = 0;
303 if (overlaps) {
304 if (!plan.retired.tryReserve(objects->count()))
305 return nullptr;
306 plan.replacement = new MmObjectList;
307 if (!plan.replacement)
308 return nullptr;
309 for (auto* object : *objects) {
310 const uintptr_t end = (object->address() + object->length() + mask) & ~mask;
311 if (destination >= end || object->address() >= destination + length) {
312 if (!plan.replacement->tryPushBack(object))
313 return nullptr;
314 continue;
315 }
316 if (placement != Placement::FixedReplace)
317 return nullptr;
318 const uintptr_t first = object->address() > destination ? object->address() : destination;
319 const uintptr_t last = end < destination + length ? end : destination + length;
320 if (object->m_LockMode != MemoryLockMode::None)
321 removedPages += (last - first) / pageSize;
322 plan.retired.pushBack(object);
323 if (!plan.appendSlice(object, object->address(), first) ||
324 !plan.appendSlice(object, last, end))
325 return nullptr;
326 }
327 }
328 auto charge = account ? account->charge() : MemoryLockCharge{};
329 if (account) {
330 const size_t rawRemovedPages = raw ? raw.get()->removedPages() : 0;
331 assert(removedPages <= charge.managedPages && rawRemovedPages <= charge.rawPages);
332 charge.managedPages -= removedPages;
333 charge.rawPages -= rawRemovedPages;
334 if (mode != MemoryLockMode::None) {
335 const size_t added = length / pageSize;
336 if (added > ~size_t(0) - charge.managedPages)
337 return nullptr;
338 charge.managedPages += added;
339 if (charge.rawPages > ~size_t(0) - charge.managedPages ||
340 !account->permitsTotalPages(charge.managedPages + charge.rawPages,
341 process->getEffectiveUserId() == 0)) {
342 if (status)
343 *status = MapStatus::LockLimit;
344 return nullptr;
345 }
346 }
347 }
348 plan.inserted =
349 file ? static_cast<MemoryMappedObject*>(
350 new MemoryMappedFile(destination, actualLength, offset, file, copyOnWrite, perms,
351 maximumPerms, attachment, origin))
352 : static_cast<MemoryMappedObject*>(new AnonymousMemoryMap(destination, length, perms));
353 if (!plan.inserted)
354 return nullptr;
355 plan.inserted->m_OwnerProcess = process->addressSpaceOwner();
356 plan.inserted->m_OwnsMappings = false;
357 plan.inserted->m_LockMode = mode;
358 plan.staged.pushBack(plan.inserted);
359 if (file && !static_cast<MemoryMappedFile*>(plan.inserted)->m_MountAdmitted) {
360 if (status) {
361 *status = MapStatus::PolicyDenied;
362 }
363 return nullptr;
364 }
365 if (file && !static_cast<MemoryMappedFile*>(plan.inserted)->m_UseAdmitted) {
366 if (status)
367 *status = MapStatus::TextBusy;
368 return nullptr;
369 }
370 auto* publication = overlaps ? plan.replacement : objects;
371 if (!publication->tryPushBack(plan.inserted))
372 return nullptr;
373 if (!direct && !process->commitUserReservations(snapshot.generation, snapshot)) {
374#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
375 process->recordBenchmarkVmCounter(Process::VmPublishCommitRetries);
376#endif
377 // The operation gate excludes other executions; the allocation-free
378 // reservation commit cannot reenter this registry. Undo the provisional
379 // append before destroying the staged object.
380 if (!overlaps) {
381 [[maybe_unused]] auto* removed = objects->popBack();
382 assert(removed == plan.inserted);
383 }
384 continue;
385 }
386 directReservation.commit();
387 // Every recoverable preparation failure precedes retirement. Latest PTEs
388 // are detached by each owner, preserving independent stale CoW cache loans.
389 for (auto* object : plan.retired) {
390 const uintptr_t end = (object->address() + object->length() + mask) & ~mask;
391 const uintptr_t first = object->address() > destination ? object->address() : destination;
392 const uintptr_t last = end < destination + length ? end : destination + length;
393 object->discardRange(space, first, last - first);
394 object->m_OwnsMappings = false;
395 }
396 if (raw)
397 raw.get()->commit();
398 for (auto* object : plan.staged)
399 object->m_OwnsMappings = true;
400 if (overlaps) {
402 m_MmObjectLists.insert(&space, plan.replacement);
403 }
404 plan.replacement = nullptr;
405 plan.committed = true;
406 if (overlaps)
407 delete objects;
408 if (account)
409 account->publish(charge, account->futureMode());
410 address = destination;
411 if (status)
412 *status = MapStatus::Success;
413 if (mode == MemoryLockMode::Eager)
414 for (uintptr_t page = destination; page < destination + length; page += pageSize)
415 plan.inserted->populatePage(space, page);
416 return plan.inserted;
417 }
418 return nullptr;
419}
420
421size_t MemoryMapManager::removeInternal(uintptr_t base, size_t length, bool releaseReservations,
422 VmStatus* status) {
423 OperationGuard operation(*this);
424 if (status)
425 *status = VmStatus::InvalidRange;
426 const size_t pageSize = PhysicalMemoryManager::getPageSize(), mask = pageSize - 1;
427 if (!length || (base & mask) || length > ~size_t(0) - mask)
428 return 0;
429 length = (length + mask) & ~mask;
430 if (length > ~uintptr_t(0) - base)
431 return 0;
432 auto& space = Processor::information().getVirtualAddressSpace();
433 auto* objects = m_MmObjectLists.lookup(&space);
434 if (!objects) {
435 if (status)
436 *status = VmStatus::Success;
437 return 0;
438 }
439 if (status)
440 *status = VmStatus::NoMemory;
441 auto* process = Processor::information().getCurrentThread()->getParent();
442#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
443 process->recordBenchmarkVmCounter(Process::VmRemoveCalls);
444 process->recordBenchmarkVmCounter(Process::VmRemoveObjectCount, objects->count());
445 size_t objectVisits = 0;
446#endif
447
448 // Mmap users normally unmap the exact mapping they just created.
449 // The registry is append-ordered, so find and retire that common case from
450 // the tail without rebuilding a reservation or mapping snapshot.
451 for (auto it = objects->rbegin(); it != objects->rend(); ++it) {
452 auto* object = *it;
453#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
454 ++objectVisits;
455#endif
456 const uintptr_t objectEnd = (object->address() + object->length() + mask) & ~mask;
457 if (object->address() != base || objectEnd != base + length)
458 continue;
459
460 const size_t removedPages =
461 object->m_LockMode == MemoryLockMode::None ? 0 : (objectEnd - base) / pageSize;
462 object->discardRange(space, base, length);
463 object->m_OwnsMappings = false;
464 if (releaseReservations)
465 releaseReservation(process, space, base, length);
466 objects->erase(it);
467 delete object;
468 retireLockedPages(space, removedPages);
469#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
470 process->recordBenchmarkVmCounter(Process::VmRemoveObjectVisits, objectVisits);
471 process->recordBenchmarkVmCounter(Process::VmRemoveAffectedObjects, 1);
472#endif
473 if (status)
474 *status = VmStatus::Success;
475 return 1;
476 }
477
478#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
479 objectVisits = 0;
480#endif
481 bool needsSlices = false;
482 for (auto* object : *objects) {
483#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
484 ++objectVisits;
485#endif
486 const uintptr_t end = (object->address() + object->length() + mask) & ~mask;
487 if (object->address() < base + length && base < end &&
488 (object->address() < base || end > base + length)) {
489 needsSlices = true;
490 break;
491 }
492 }
493#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
494 process->recordBenchmarkVmCounter(Process::VmRemoveObjectVisits, objectVisits);
495#endif
496 if (!needsSlices) {
497 size_t affected = 0, removedPages = 0;
498#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
499 objectVisits = 0;
500#endif
501 for (auto it = objects->begin(); it != objects->end();) {
502#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
503 ++objectVisits;
504#endif
505 auto* object = *it;
506 const uintptr_t first = object->address();
507 const uintptr_t end = (first + object->length() + mask) & ~mask;
508 if (first >= base + length || end <= base) {
509 ++it;
510 continue;
511 }
512 if (object->m_LockMode != MemoryLockMode::None)
513 removedPages += (end - first) / pageSize;
514 object->discardRange(space, first, end - first);
515 object->m_OwnsMappings = false;
516 if (releaseReservations)
517 releaseReservation(process, space, first, end - first);
518 it = objects->erase(it);
519 delete object;
520 ++affected;
521 }
522#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
523 process->recordBenchmarkVmCounter(Process::VmRemoveObjectVisits, objectVisits);
524 process->recordBenchmarkVmCounter(Process::VmRemoveAffectedObjects, affected);
525#endif
526 retireLockedPages(space, removedPages);
527 if (status)
528 *status = VmStatus::Success;
529 return affected;
530 }
531 MappingPlan plan;
532 if (!plan.staged.tryReserve(objects->count() * 2) || !plan.retired.tryReserve(objects->count()))
533 return 0;
534 plan.replacement = new MmObjectList;
535 if (!plan.replacement)
536 return 0;
537 size_t removedPages = 0;
538#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
539 objectVisits = 0;
540#endif
541 for (auto* object : *objects) {
542#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
543 ++objectVisits;
544#endif
545 const uintptr_t end = (object->address() + object->length() + mask) & ~mask;
546 const uintptr_t first = object->address() > base ? object->address() : base;
547 const uintptr_t last = end < base + length ? end : base + length;
548 if (first >= last) {
549 if (!plan.replacement->tryPushBack(object))
550 return 0;
551 continue;
552 }
553 if (object->m_LockMode != MemoryLockMode::None)
554 removedPages += (last - first) / pageSize;
555 plan.retired.pushBack(object);
556 if (!plan.appendSlice(object, object->address(), first) || !plan.appendSlice(object, last, end))
557 return 0;
558 }
559#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
560 process->recordBenchmarkVmCounter(Process::VmRemoveObjectVisits, objectVisits);
561 process->recordBenchmarkVmCounter(Process::VmRemoveSliceCalls);
562 process->recordBenchmarkVmCounter(Process::VmRemoveAffectedObjects, plan.retired.count());
563#endif
564 for (auto* object : plan.retired) {
565 const uintptr_t end = (object->address() + object->length() + mask) & ~mask;
566 const uintptr_t first = object->address() > base ? object->address() : base;
567 const uintptr_t last = end < base + length ? end : base + length;
568 object->discardRange(space, first, last - first);
569 object->m_OwnsMappings = false;
570 if (releaseReservations)
571 releaseReservation(process, space, first, last - first);
572 }
573 for (auto* object : plan.staged)
574 object->m_OwnsMappings = true;
575 {
577 m_MmObjectLists.insert(&space, plan.replacement);
578 }
579 plan.replacement = nullptr;
580 plan.committed = true;
581 delete objects;
582 retireLockedPages(space, removedPages);
583 if (status)
584 *status = VmStatus::Success;
585 return plan.retired.count();
586}
Memory-mapped file interface.
Definition File.h:75
virtual bool allowMapping(bool shared, bool writeRequested, bool &mayWrite)
Definition File.cc:1177
Tree< VirtualAddressSpace *, MmObjectList * > m_MmObjectLists
MemoryMappedObject * mapAnon(uintptr_t &address, size_t length, MemoryMappedObject::Permissions perms)
MemoryMappedObject * mapFile(File *pFile, uintptr_t &address, size_t length, MemoryMappedObject::Permissions perms, size_t offset=0, bool bCopyOnWrite=true)
Process * getParent()
Definition Process.h:620
Process * addressSpaceOwner()
Definition Process.h:545
bool commitUserReservations(uint64_t expectedGeneration, UserReservationSnapshot &replacement)
static ProcessorInformation & information()
bool getRange(size_t index, Range &range) const
Definition RangeList.h:419
bool allocate(T length, T &address)
Definition RangeList.h:318
size_t size() const
Definition RangeList.h:104
bool allocateSpecific(T address, T length)
Definition RangeList.h:363
MemoryLockStatus prepareReplacement(uintptr_t base, size_t length, UniquePointer< PreparedMemoryLock > &result)
A vector / dynamic array.
Definition Vector.h:33
virtual uintptr_t getUserReservedStart() const =0
virtual uintptr_t getUserStart() const =0
void pushBack(const T &value)
Definition Vector.h:275