mirror of https://gitlab.com/nakst/essence
191 lines
4.7 KiB
C++
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]++;
|
|
}
|
|
}
|