The Pedigree Project 0.1
MappingIndex.cc
1/* Copyright (c) 2026, Pedigree Developers. */
2
3#include "MappingIndex.h"
4#include "pedigree/kernel/utilities/assert.h"
5
6void* __libc_malloc(size_t);
7void __libc_free(void*);
8
9HostedMappingIndex::HostedMappingIndex()
10 : m_Entries(nullptr), m_Capacity(0), m_Live(0), m_Used(0) {}
11
12HostedMappingIndex::~HostedMappingIndex() {
13 if (m_Entries)
14 __libc_free(m_Entries);
15}
16
17size_t HostedMappingIndex::hash(uintptr_t page) {
18 // Mix the address bits so page alignment does not cluster adjacent mappings.
19 uint64_t value = page;
20 value = (value ^ (value >> 30)) * UINT64_C(0xbf58476d1ce4e5b9);
21 value = (value ^ (value >> 27)) * UINT64_C(0x94d049bb133111eb);
22 return static_cast<size_t>(value ^ (value >> 31));
23}
24
25size_t HostedMappingIndex::lookup(uintptr_t page) const {
26 if (!m_Capacity)
27 return Missing;
28
29 size_t index = hash(page) & (m_Capacity - 1);
30 for (size_t probes = 0; probes < m_Capacity; ++probes) {
31 const Entry& entry = m_Entries[index];
32 if (entry.state == Empty)
33 return Missing;
34 if (entry.state == Occupied && entry.page == page)
35 return entry.slot;
36 index = (index + 1) & (m_Capacity - 1);
37 }
38 return Missing;
39}
40
41bool HostedMappingIndex::reserveForInsert() {
42 if (m_Used < m_Capacity / 2)
43 return true;
44
45 size_t capacity = m_Capacity ? m_Capacity : 16;
46 // Rebuild at the same capacity when tombstones, rather than live entries,
47 // account for the load. This bounds probe lengths without retaining growth.
48 if (m_Live >= capacity / 2) {
49 if (capacity > Missing / 2)
50 return false;
51 capacity *= 2;
52 }
53 if (capacity > Missing / sizeof(Entry))
54 return false;
55
56 // The caller holds the address-space lock; SLAM allocation would re-enter it.
57 auto* entries = static_cast<Entry*>(__libc_malloc(capacity * sizeof(Entry)));
58 if (!entries)
59 return false;
60 for (size_t i = 0; i < capacity; ++i)
61 entries[i].state = Empty;
62 for (size_t i = 0; i < m_Capacity; ++i) {
63 if (m_Entries[i].state != Occupied)
64 continue;
65 size_t index = hash(m_Entries[i].page) & (capacity - 1);
66 while (entries[index].state != Empty)
67 index = (index + 1) & (capacity - 1);
68 entries[index] = m_Entries[i];
69 }
70
71 Entry* previous = m_Entries;
72 m_Entries = entries;
73 m_Capacity = capacity;
74 m_Used = m_Live;
75 if (previous)
76 __libc_free(previous);
77 return true;
78}
79
80void HostedMappingIndex::insert(uintptr_t page, size_t slot) {
81 assert(slot != Missing);
82 assert(m_Capacity && m_Used < m_Capacity / 2);
83 if (!m_Capacity || slot == Missing)
84 return;
85
86 size_t index = hash(page) & (m_Capacity - 1);
87 size_t deleted = Missing;
88 for (size_t probes = 0; probes < m_Capacity; ++probes) {
89 Entry& entry = m_Entries[index];
90 if (entry.state == Occupied && entry.page == page) {
91 assert(false);
92 return;
93 }
94 if (entry.state == Deleted && deleted == Missing)
95 deleted = index;
96 if (entry.state == Empty) {
97 const size_t target = deleted == Missing ? index : deleted;
98 m_Entries[target] = {page, slot, Occupied};
99 ++m_Live;
100 if (deleted == Missing)
101 ++m_Used;
102 return;
103 }
104 index = (index + 1) & (m_Capacity - 1);
105 }
106 assert(false);
107}
108
109void HostedMappingIndex::erase(uintptr_t page) {
110 if (!m_Capacity)
111 return;
112
113 size_t index = hash(page) & (m_Capacity - 1);
114 for (size_t probes = 0; probes < m_Capacity; ++probes) {
115 Entry& entry = m_Entries[index];
116 if (entry.state == Empty)
117 return;
118 if (entry.state == Occupied && entry.page == page) {
119 entry.state = Deleted;
120 --m_Live;
121 return;
122 }
123 index = (index + 1) & (m_Capacity - 1);
124 }
125}
126
127void HostedMappingIndex::clear() {
128 for (size_t i = 0; i < m_Capacity; ++i)
129 m_Entries[i].state = Empty;
130 m_Live = 0;
131 m_Used = 0;
132}
#define assert(x)
Definition assert.h:39