private long Alloc(long size)
{
if (size < 0)
throw new IOException("Negative size allocation");
// Add 16 bytes for headers
size = size + 16;
// If size < 32, make size = 32
if (size < 32)
size = 32;
// Round all sizes up to the nearest 8
long d = size & 0x07L;
if (d != 0)
size = size + (8 - d);
long realAllocSize = size;
// Search the free bin list for the first bin that matches the given size.
int binChainIndex;
if (size > MaxBinSize) {
binChainIndex = BinSizeEntries;
} else {
int i = MinimumBinSizeIndex(size);
binChainIndex = i;
}
// Search the bins until we find the first area that is the nearest fit to
// the size requested.
int foundBinIndex = -1;
long prevOffset = -1;
bool first = true;
for (int i = binChainIndex;
i < BinSizeEntries + 1 && foundBinIndex == -1;
++i) {
long curOffset = freeBinList[i];
if (curOffset != -1) {
if (!first) {
// Pick this..
foundBinIndex = i;
prevOffset = -1;
} else {
// Search this bin for the first that's big enough.
// We only search the first 12 entries input the bin before giving up.
long lastOffset = -1;
int searches = 0;
while (curOffset != -1 &&
foundBinIndex == -1 &&
searches < 12) {
var headerInfo = new long[2];
ReadAreaHeader(curOffset, headerInfo);
long areaSize = (headerInfo[0] & ActiveFlag);
// Is this area is greater or equal than the required size
// and is not the wilderness area, pick it.
if (curOffset != WildernessOffset && areaSize >= size) {
foundBinIndex = i;
prevOffset = lastOffset;
}
// Go to next input chain.
lastOffset = curOffset;
curOffset = headerInfo[1];
++searches;
}
}
}
first = false;
}
// If no area can be recycled,
if (foundBinIndex == -1) {
// Allocate a new area of the given size.
// If there is a wilderness, grow the wilderness area to the new size,
long workingOffset;
long sizeToGrow;
long currentAreaSize;
if (WildernessOffset != -1) {
workingOffset = WildernessOffset;
var headerInfo = new long[2];
ReadAreaHeader(WildernessOffset, headerInfo);
long wildernessSize = (headerInfo[0] & ActiveFlag);
// Remove this from the bins
RemoveFromBinChain(workingOffset, wildernessSize);
// For safety, we set wilderness_pointer to -1
WildernessOffset = -1;
sizeToGrow = size - wildernessSize;
currentAreaSize = wildernessSize;
} else {
// wilderness_pointer == -1 so add to the end of the data area.
workingOffset = DataAreaEndOffset;
sizeToGrow = size;
currentAreaSize = 0;
}
long expandedSize = 0;
if (sizeToGrow > 0) {
// Expand the data area to the new size.
expandedSize = ExpandDataArea(sizeToGrow);
}
// Coalesce the new area to the given size
CoalesceArea(workingOffset, currentAreaSize + expandedSize);
// crop the area
CropArea(workingOffset, size);
// Add to the total allocated space
totalAllocatedSpace += realAllocSize;
return workingOffset;
} else {
// An area is taken from the bins,
long freeAreaOffset;
var headerInfo = new long[2];
// Remove this area from the bin chain and possibly add any excess space
// left over to a new bin.
if (prevOffset == -1) {
freeAreaOffset = freeBinList[foundBinIndex];
ReadAreaHeader(freeAreaOffset, headerInfo);
freeBinList[foundBinIndex] = headerInfo[1];
WriteBinIndex(foundBinIndex);
} else {
var headerInfo2 = new long[2];
ReadAreaHeader(prevOffset, headerInfo2);
freeAreaOffset = headerInfo2[1];
ReadAreaHeader(freeAreaOffset, headerInfo);
headerInfo2[1] = headerInfo[1];
ReboundArea(prevOffset, headerInfo2, false);
}
// Reset the header of the recycled area.
headerInfo[0] = (headerInfo[0] & ActiveFlag);
ReboundArea(freeAreaOffset, headerInfo, true);
// Crop the area to the given size.
CropArea(freeAreaOffset, size);
// Add to the total allocated space
totalAllocatedSpace += realAllocSize;
return freeAreaOffset;
}
}