essence-os/shared/bitset.cpp

191 lines
4.7 KiB
C++

// This file is part of the Essence operating system.
// It is released under the terms of the MIT license -- see LICENSE.md.
// Written by: nakst.
struct Bitset {
void Initialise(size_t count, bool mapAll = false);
void PutAll();
uintptr_t Get(size_t count = 1, uintptr_t align = 1, uintptr_t below = 0);
void Put(uintptr_t index);
void Take(uintptr_t index);
bool Read(uintptr_t index);
#define BITSET_GROUP_SIZE (4096)
uint32_t *singleUsage;
uint16_t *groupUsage;
size_t singleCount;
size_t groupCount;
#ifdef DEBUG_BUILD
bool modCheck;
#endif
};
void Bitset::Initialise(size_t count, bool mapAll) {
singleCount = (count + 31) & ~31;
groupCount = singleCount / BITSET_GROUP_SIZE + 1;
singleUsage = (uint32_t *) MMStandardAllocate(kernelMMSpace, (singleCount >> 3) + (groupCount * 2), mapAll ? MM_REGION_FIXED : 0);
groupUsage = (uint16_t *) singleUsage + (singleCount >> 4);
}
void Bitset::PutAll() {
for (uintptr_t i = 0; i < singleCount; i += 32) {
groupUsage[i / BITSET_GROUP_SIZE] += 32;
singleUsage[i / 32] |= 0xFFFFFFFF;
}
}
uintptr_t Bitset::Get(size_t count, uintptr_t align, uintptr_t below) {
#ifdef DEBUG_BUILD
if (modCheck) KernelPanic("Bitset::Allocate - Concurrent modification.\n");
modCheck = true; EsDefer({modCheck = false;});
#endif
uintptr_t returnValue = (uintptr_t) -1;
if (below) {
if (below < count) goto done;
below -= count;
}
if (count == 1 && align == 1) {
for (uintptr_t i = 0; i < groupCount; i++) {
if (groupUsage[i]) {
for (uintptr_t j = 0; j < BITSET_GROUP_SIZE; j++) {
uintptr_t index = i * BITSET_GROUP_SIZE + j;
if (below && index >= below) goto done;
if (singleUsage[index >> 5] & (1 << (index & 31))) {
singleUsage[index >> 5] &= ~(1 << (index & 31));
groupUsage[i]--;
returnValue = index;
goto done;
}
}
}
}
} else if (count == 16 && align == 16) {
for (uintptr_t i = 0; i < groupCount; i++) {
if (groupUsage[i] >= 16) {
for (uintptr_t j = 0; j < BITSET_GROUP_SIZE; j += 16) {
uintptr_t index = i * BITSET_GROUP_SIZE + j;
if (below && index >= below) goto done;
if (((uint16_t *) singleUsage)[index >> 4] == (uint16_t) (-1)) {
((uint16_t *) singleUsage)[index >> 4] = 0;
groupUsage[i] -= 16;
returnValue = index;
goto done;
}
}
}
}
} else if (count == 32 && align == 32) {
for (uintptr_t i = 0; i < groupCount; i++) {
if (groupUsage[i] >= 32) {
for (uintptr_t j = 0; j < BITSET_GROUP_SIZE; j += 32) {
uintptr_t index = i * BITSET_GROUP_SIZE + j;
if (below && index >= below) goto done;
if (singleUsage[index >> 5] == (uint32_t) (-1)) {
singleUsage[index >> 5] = 0;
groupUsage[i] -= 32;
returnValue = index;
goto done;
}
}
}
}
} else {
// TODO Optimise this?
size_t found = 0;
uintptr_t start = 0;
for (uintptr_t i = 0; i < groupCount; i++) {
if (!groupUsage[i]) {
found = 0;
continue;
}
for (uintptr_t j = 0; j < BITSET_GROUP_SIZE; j++) {
uintptr_t index = i * BITSET_GROUP_SIZE + j;
if (singleUsage[index >> 5] & (1 << (index & 31))) {
if (!found) {
if (index >= below && below) goto done;
if (index % align) continue;
start = index;
}
found++;
} else {
found = 0;
}
if (found == count) {
returnValue = start;
for (uintptr_t i = 0; i < count; i++) {
uintptr_t index = start + i;
singleUsage[index >> 5] &= ~(1 << (index & 31));
}
goto done;
}
}
}
}
done:;
return returnValue;
}
bool Bitset::Read(uintptr_t index) {
// We don't want to set modCheck,
// since we allow other bits to be modified while this bit is being read,
// and we allow multiple readers of this bit.
return singleUsage[index >> 5] & (1 << (index & 31));
}
void Bitset::Take(uintptr_t index) {
#ifdef DEBUG_BUILD
if (modCheck) KernelPanic("Bitset::Take - Concurrent modification.\n");
modCheck = true; EsDefer({modCheck = false;});
#endif
uintptr_t group = index / BITSET_GROUP_SIZE;
#ifdef DEBUG_BUILD
if (!(singleUsage[index >> 5] & (1 << (index & 31)))) {
KernelPanic("Bitset::Take - Attempting to take a entry that has already been taken.\n");
}
#endif
groupUsage[group]--;
singleUsage[index >> 5] &= ~(1 << (index & 31));
}
void Bitset::Put(uintptr_t index) {
#ifdef DEBUG_BUILD
if (modCheck) KernelPanic("Bitset::Put - Concurrent modification.\n");
modCheck = true; EsDefer({modCheck = false;});
if (index > singleCount) {
KernelPanic("Bitset::Put - Index greater than single code.\n");
}
if (singleUsage[index >> 5] & (1 << (index & 31))) {
KernelPanic("Bitset::Put - Duplicate entry.\n");
}
#endif
{
singleUsage[index >> 5] |= 1 << (index & 31);
groupUsage[index / BITSET_GROUP_SIZE]++;
}
}