singrdk/base/Services/Fat/Fs/Bitmap.sg

1344 lines
54 KiB
Plaintext
Raw Normal View History

2008-03-05 09:52:00 -05:00
///////////////////////////////////////////////////////////////////////////////
//
// Microsoft Research Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// File: Bitmap.sg
//
// Note:
//
// The Bitmap class provides a bitmap allocator for fixed size
// spaces. It's used in the FAT filesystem to maintain
// information on free FAT clusters and to keep track of
// allocated entries in directories which helps in creating and
// finding files.
//
// For Compilation with C#/Spec# of test program:
// {csc,ssc} /D:DEBUG /DEBUG /D:TEST_BITMAP bitmap.sg
//
// TODO:
// - Consider optimizing set/clear bit search based on summaries. It is
// presently expensive for sparse cases.
using System;
using System.Diagnostics;
#if TEST_BITMAP
using System.Collections;
#endif
namespace Microsoft.Singularity.Services.Fat.Fs
{
internal sealed class Bitmap
{
#region tables
// Upper 4-bits number free on left, lower 4bits number
// free on right.
private static readonly byte [] LRContiguousTable = {
/* 0x00 */ 0x88, 0x70, 0x61, 0x60, 0x52, 0x50, 0x51, 0x50,
/* 0x08 */ 0x43, 0x40, 0x41, 0x40, 0x42, 0x40, 0x41, 0x40,
/* 0x10 */ 0x34, 0x30, 0x31, 0x30, 0x32, 0x30, 0x31, 0x30,
/* 0x18 */ 0x33, 0x30, 0x31, 0x30, 0x32, 0x30, 0x31, 0x30,
/* 0x20 */ 0x25, 0x20, 0x21, 0x20, 0x22, 0x20, 0x21, 0x20,
/* 0x28 */ 0x23, 0x20, 0x21, 0x20, 0x22, 0x20, 0x21, 0x20,
/* 0x30 */ 0x24, 0x20, 0x21, 0x20, 0x22, 0x20, 0x21, 0x20,
/* 0x38 */ 0x23, 0x20, 0x21, 0x20, 0x22, 0x20, 0x21, 0x20,
/* 0x40 */ 0x16, 0x10, 0x11, 0x10, 0x12, 0x10, 0x11, 0x10,
/* 0x48 */ 0x13, 0x10, 0x11, 0x10, 0x12, 0x10, 0x11, 0x10,
/* 0x50 */ 0x14, 0x10, 0x11, 0x10, 0x12, 0x10, 0x11, 0x10,
/* 0x58 */ 0x13, 0x10, 0x11, 0x10, 0x12, 0x10, 0x11, 0x10,
/* 0x60 */ 0x15, 0x10, 0x11, 0x10, 0x12, 0x10, 0x11, 0x10,
/* 0x68 */ 0x13, 0x10, 0x11, 0x10, 0x12, 0x10, 0x11, 0x10,
/* 0x70 */ 0x14, 0x10, 0x11, 0x10, 0x12, 0x10, 0x11, 0x10,
/* 0x78 */ 0x13, 0x10, 0x11, 0x10, 0x12, 0x10, 0x11, 0x10,
/* 0x80 */ 0x07, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00,
/* 0x88 */ 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00,
/* 0x90 */ 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00,
/* 0x98 */ 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00,
/* 0xa0 */ 0x05, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00,
/* 0xa8 */ 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00,
/* 0xb0 */ 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00,
/* 0xb8 */ 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00,
/* 0xc0 */ 0x06, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00,
/* 0xc8 */ 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00,
/* 0xd0 */ 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00,
/* 0xd8 */ 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00,
/* 0xe0 */ 0x05, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00,
/* 0xe8 */ 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00,
/* 0xf0 */ 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00,
/* 0xf8 */ 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00
};
// Upper for bits equals start index (0=msb,7=lsb).
// Lower four bits are the number free.
private static readonly byte [] MaxFreeTable = {
/* 0x00 */ 0x08, 0x07, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05,
/* 0x08 */ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
/* 0x10 */ 0x44, 0x43, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
/* 0x18 */ 0x53, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
/* 0x20 */ 0x35, 0x34, 0x33, 0x33, 0x62, 0x32, 0x32, 0x32,
/* 0x28 */ 0x53, 0x52, 0x02, 0x02, 0x62, 0x02, 0x02, 0x02,
/* 0x30 */ 0x44, 0x43, 0x42, 0x42, 0x62, 0x02, 0x02, 0x02,
/* 0x38 */ 0x53, 0x52, 0x02, 0x02, 0x62, 0x02, 0x02, 0x02,
/* 0x40 */ 0x26, 0x25, 0x24, 0x24, 0x23, 0x23, 0x23, 0x23,
/* 0x48 */ 0x53, 0x52, 0x22, 0x22, 0x62, 0x22, 0x22, 0x22,
/* 0x50 */ 0x44, 0x43, 0x42, 0x42, 0x62, 0x61, 0x71, 0x41,
/* 0x58 */ 0x53, 0x52, 0x71, 0x51, 0x62, 0x61, 0x71, 0x21,
/* 0x60 */ 0x35, 0x34, 0x33, 0x33, 0x62, 0x32, 0x32, 0x32,
/* 0x68 */ 0x53, 0x52, 0x71, 0x51, 0x62, 0x61, 0x71, 0x31,
/* 0x70 */ 0x44, 0x43, 0x42, 0x42, 0x62, 0x61, 0x71, 0x41,
/* 0x78 */ 0x53, 0x52, 0x71, 0x51, 0x62, 0x61, 0x71, 0x01,
/* 0x80 */ 0x17, 0x16, 0x15, 0x15, 0x14, 0x14, 0x14, 0x14,
/* 0x88 */ 0x53, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
/* 0x90 */ 0x44, 0x43, 0x42, 0x42, 0x62, 0x12, 0x12, 0x12,
/* 0x98 */ 0x53, 0x52, 0x12, 0x12, 0x62, 0x12, 0x12, 0x12,
/* 0xa0 */ 0x35, 0x34, 0x33, 0x33, 0x62, 0x32, 0x32, 0x32,
/* 0xa8 */ 0x53, 0x52, 0x71, 0x51, 0x62, 0x61, 0x71, 0x31,
/* 0xb0 */ 0x44, 0x43, 0x42, 0x42, 0x62, 0x61, 0x71, 0x41,
/* 0xb8 */ 0x53, 0x52, 0x71, 0x51, 0x62, 0x61, 0x71, 0x11,
/* 0xc0 */ 0x26, 0x25, 0x24, 0x24, 0x23, 0x23, 0x23, 0x23,
/* 0xc8 */ 0x53, 0x52, 0x22, 0x22, 0x62, 0x22, 0x22, 0x22,
/* 0xd0 */ 0x44, 0x43, 0x42, 0x42, 0x62, 0x61, 0x71, 0x41,
/* 0xd8 */ 0x53, 0x52, 0x71, 0x51, 0x62, 0x61, 0x71, 0x21,
/* 0xe0 */ 0x35, 0x34, 0x33, 0x33, 0x62, 0x32, 0x32, 0x32,
/* 0xe8 */ 0x53, 0x52, 0x71, 0x51, 0x62, 0x61, 0x71, 0x31,
/* 0xf0 */ 0x44, 0x43, 0x42, 0x42, 0x62, 0x61, 0x71, 0x41,
/* 0xf8 */ 0x53, 0x52, 0x71, 0x51, 0x62, 0x61, 0x71, 0x80
};
// Left Edge Contiguous Table.
//
// Assumes left most bits are set if present. Table
// entries represent the contiguous number free from the
// first free left-bit.
private static readonly byte [] LeftEdgeContiguousTable = {
/* 0x00 */ 0x08, 0x07, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05,
/* 0x08 */ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
/* 0x10 */ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
/* 0x18 */ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
/* 0x20 */ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
/* 0x28 */ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
/* 0x30 */ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
/* 0x38 */ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
/* 0x40 */ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
/* 0x48 */ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
/* 0x50 */ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
/* 0x58 */ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
/* 0x60 */ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
/* 0x68 */ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
/* 0x70 */ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
/* 0x78 */ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
/* 0x80 */ 0x07, 0x06, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04,
/* 0x88 */ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
/* 0x90 */ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
/* 0x98 */ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
/* 0xa0 */ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
/* 0xa8 */ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
/* 0xb0 */ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
/* 0xb8 */ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
/* 0xc0 */ 0x06, 0x05, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03,
/* 0xc8 */ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
/* 0xd0 */ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
/* 0xd8 */ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
/* 0xe0 */ 0x05, 0x04, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02,
/* 0xe8 */ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
/* 0xf0 */ 0x04, 0x03, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
/* 0xf8 */ 0x03, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01, 0x00,
};
private static readonly byte [] BitCountTable = {
/* 0x00 */ 0x00, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x03,
/* 0x08 */ 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04,
/* 0x10 */ 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04,
/* 0x18 */ 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05,
/* 0x20 */ 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04,
/* 0x28 */ 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05,
/* 0x30 */ 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05,
/* 0x38 */ 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06,
/* 0x40 */ 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04,
/* 0x48 */ 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05,
/* 0x50 */ 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05,
/* 0x58 */ 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06,
/* 0x60 */ 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05,
/* 0x68 */ 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06,
/* 0x70 */ 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06,
/* 0x78 */ 0x04, 0x05, 0x05, 0x06, 0x05, 0x06, 0x06, 0x07,
/* 0x80 */ 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04,
/* 0x88 */ 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05,
/* 0x90 */ 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05,
/* 0x98 */ 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06,
/* 0xa0 */ 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05,
/* 0xa8 */ 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06,
/* 0xb0 */ 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06,
/* 0xb8 */ 0x04, 0x05, 0x05, 0x06, 0x05, 0x06, 0x06, 0x07,
/* 0xc0 */ 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05,
/* 0xc8 */ 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06,
/* 0xd0 */ 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06,
/* 0xd8 */ 0x04, 0x05, 0x05, 0x06, 0x05, 0x06, 0x06, 0x07,
/* 0xe0 */ 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06,
/* 0xe8 */ 0x04, 0x05, 0x05, 0x06, 0x05, 0x06, 0x06, 0x07,
/* 0xf0 */ 0x04, 0x05, 0x05, 0x06, 0x05, 0x06, 0x06, 0x07,
/* 0xf8 */ 0x05, 0x06, 0x06, 0x07, 0x06, 0x07, 0x07, 0x08,
};
#endregion tables
// Constants for logarithmic summary totals
private const int SummaryFanOut = 256;
private const int Log2SummaryFanOut = 9;
/// <summary> Maximum length of a single allocation. </summary>
public const int MaxAllocationLength = 64;
private const int SmallAllocationLength = 8;
// Padding at end of array to simplify end cases
private const int PadBytes = MaxAllocationLength / 8;
internal readonly int totalBits;
private int totalFreeBits;
private byte[] map;
private int [][] summary; // logarithmic summary totals
[ Microsoft.Contracts.NotDelayed ]
internal Bitmap(int numberOfBits)
{
totalBits = numberOfBits;
totalFreeBits = 0;
int mapLength = (numberOfBits + 7) / 8 + PadBytes;
this.map = new byte[mapLength];
int logSummaryLevels = Log2Up(mapLength) / Log2SummaryFanOut;
if (logSummaryLevels == 0) {
logSummaryLevels = 1;
}
this.summary = new int [logSummaryLevels][];
int n = mapLength;
for (int i = (logSummaryLevels - 1); i >= 0; i--) {
n = (n + SummaryFanOut - 1) / SummaryFanOut;
summary[i] = new int [n];
}
this.Initialize();
}
private void Initialize()
{
// This is a little gratuitous - we make
// two passes over the map when initializing. This enables
// getting the logarithmic summary table information correct in
// a trivial manner, but is obviously sub-optimal.
totalFreeBits = 0;
for (int i = 0; i < map.Length; i++) {
this.map[i] = 0xff;
}
for (int i = 0; i < totalBits / 8; i++) {
ClearBits(i, 0, 8);
}
// Clean up trailing bits
if ((totalBits & 0x07) != 0) {
ClearBits((totalBits / 8), 0, totalBits & 0x07);
}
}
private static int LeadingFree(byte value)
{
return ((int)LRContiguousTable[value]) >> 4;
}
private static int TrailingFree(byte value)
{
return ((int)LRContiguousTable[value]) & 0x0f;
}
private static int MaxFreeStart(byte value)
{
return ((int)MaxFreeTable[value]) >> 4;
}
private static int MaxFree(byte value)
{
return ((int)MaxFreeTable[value]) & 0xf;
}
private static int BitCount(byte value)
{
return (int)BitCountTable[value];
}
private static int MakeMask(int startIndex, int length)
/*^ requires startIndex >= 0 && startIndex + length <= 8; ^*/
/*^ requires length >= 0 && length <= 8; ^*/
{
int a = 0xff >> startIndex;
int b = 0xff << (8 - length - startIndex);
return a & b;
}
private void SetBits(int byteIndex, int bitIndex, int length)
{
Debug.Assert(length >= 0 && length <= 8);
Debug.Assert(bitIndex >= 0 && bitIndex + length <= 8);
int tmp = (int)this.map[byteIndex];
int mask = MakeMask(bitIndex, length);
tmp ^= mask;
Debug.Assert((tmp & mask) == mask);
this.map[byteIndex] = (byte) tmp;
SummarizeAlloc(byteIndex, length);
}
private void ClearBits(int byteIndex, int bitIndex, int length)
/*^ requires length <= SmallAllocationLength; ^*/
{
Debug.Assert(length >= 0 && length <= 8);
Debug.Assert(bitIndex >= 0 && bitIndex + length <= 8);
int tmp = (int)this.map[byteIndex];
int mask = MakeMask(bitIndex, length);
tmp ^= mask;
Debug.Assert((tmp & mask) == 0);
this.map[byteIndex] = (byte) tmp;
SummarizeFree(byteIndex, length);
}
private void SetRegion(int byteIndex, int bitIndex, int length)
/*^ requires byteIndex >= 0; ^*/
/*^ requires bitIndex >= 0 && bitIndex <= 7; ^*/
/*^ requires length > SmallAllocationLength; ^*/
{
int lhs = 8 - bitIndex;
SetBits(byteIndex, bitIndex, lhs);
int fill8 = (length - lhs) / 8;
for (int i = 1; i <= fill8; i++) {
SetBits(byteIndex + i, 0, 8);
}
int rhs = length - (lhs + fill8 * 8);
if (rhs != 0) {
SetBits(byteIndex + fill8 + 1, 0, rhs);
}
}
private void ClearRegion(int byteIndex, int bitIndex, int length)
/*^ requires byteIndex >= 0; ^*/
/*^ requires bitIndex >= 0 && bitIndex <= 7; ^*/
/*^ requires length > SmallAllocationLength; ^*/
{
int lhs = 8 - bitIndex;
ClearBits(byteIndex, bitIndex, lhs);
int fill8 = (length - lhs) / 8;
for (int i = 1; i <= fill8; i++) {
ClearBits(byteIndex + i, 0, 8);
}
int rhs = length - (lhs + fill8 * 8);
if (rhs != 0) {
ClearBits(byteIndex + fill8 + 1, 0, rhs);
}
}
private bool IsAllocated(int bitIndex)
{
int mask = 0x80 >> (bitIndex & 0x7);
int value = (int) this.map[bitIndex / 8];
return (value & mask) == mask;
}
private int GetLeadingClearBytes(int startByte, int maxLength)
/*^ requires startByte >= 0; ^*/
/*^ requires maxLength >= 0; ^*/
/*^ requires startByte + maxLength <= this.map.Length; ^*/
{
for (int i = 0; i < maxLength; i++) {
if (this.map[startByte + i] != 0) {
return i;
}
}
return maxLength;
}
private bool HintAllocateSmall(int requestLength,
int hintStart,
out int allocStart,
out int allocLength)
{
Debug.Assert(this.IsAllocated(hintStart) == false);
Debug.Assert(requestLength <= SmallAllocationLength);
int byteIndex = hintStart / 8;
int bitIndex = hintStart - byteIndex * 8;
int edgeMask = (0xff00 >> bitIndex) & 0xff;
int lhs = LeftEdgeContiguousTable[ this.map[byteIndex] | edgeMask ];
if (lhs >= requestLength) {
// Allocation confined to single byte in bitmap
allocStart = hintStart;
allocLength = requestLength;
SetBits(byteIndex, bitIndex, allocLength);
return true;
}
int rhs = LeadingFree( this.map[byteIndex + 1] );
if (lhs == (8 - bitIndex) && lhs + rhs >= requestLength) {
// Allocation spans two bytes in bitmap
allocStart = hintStart;
allocLength = requestLength;
SetBits(byteIndex, 8 - lhs, lhs);
SetBits(byteIndex + 1, 0, requestLength - lhs);
return true;
}
return ScanAllocateSmall(byteIndex + 1, requestLength,
out allocStart, out allocLength);
}
private bool HintAllocateLarge(int requestLength,
int hintStart,
out int allocStart,
out int allocLength)
{
// Set bit just before hint start if not already set so
// ScanAllocateLarge starts from at least hintStart.
bool didFiddle = false;
int bitIndex = 0;
int byteIndex = 0;
if (hintStart > 0 && !IsAllocated(hintStart - 1)) {
byteIndex = (hintStart - 1) / 8;
bitIndex = (hintStart - 1) - byteIndex * 8;
SetBits(byteIndex, bitIndex, 1);
didFiddle = true;
}
try {
return ScanAllocateLarge(hintStart / 8, requestLength,
out allocStart, out allocLength);
}
finally {
if (didFiddle) {
ClearBits(byteIndex, bitIndex, 1);
}
}
}
private bool HintAllocate(int requestLength,
int hintStart,
out int allocStart,
out int allocLength)
{
if (requestLength <= SmallAllocationLength) {
return HintAllocateSmall(requestLength, hintStart,
out allocStart, out allocLength);
}
else {
return HintAllocateLarge(requestLength, hintStart,
out allocStart, out allocLength);
}
}
private bool ScanAllocateSmall(int startByte,
int requestLength,
out int allocStart,
out int allocLength)
/*^ requires requestLength <= SmallAllocationLength; ^*/
{
if (startByte >= this.map.Length) {
allocStart = 0;
allocLength = 0;
return false;
}
int endByte = startByte + SummaryFanOut;
if (endByte >= map.Length) {
endByte = map.Length;
}
for (int i = startByte; i < endByte; i++) {
int maxFree = MaxFree(this.map[i]);
if (maxFree == 0) {
continue;
}
int startFree = MaxFreeStart(this.map[i]);
if (maxFree >= requestLength) {
allocStart = i * 8 + startFree;
allocLength = requestLength;
SetBits(i, MaxFreeStart(this.map[i]), requestLength);
return true;
}
int lhs = TrailingFree(this.map[i]);
int rhs = LeadingFree(this.map[i + 1]);
if (lhs + rhs >= requestLength) {
Debug.Assert(lhs != requestLength);
allocStart = i * 8 + 8 - lhs;
allocLength = requestLength;
SetBits(i, 8 - lhs, lhs);
SetBits(i + 1, 0, requestLength - lhs);
return true;
}
}
allocStart = 0;
allocLength = 0;
return false;
}
private bool ScanAllocateLarge(int startByte,
int requestLength,
out int allocStart,
out int allocLength)
/*^ requires requestLength > SmallAllocationLength; ^*/
/*^ requires requestLength <= MaxAllocationLength; ^*/
{
if (startByte >= this.map.Length) {
allocStart = 0;
allocLength = 0;
return false;
}
int maxRangeBytes = (requestLength + 14) / 8;
int endByte = startByte + SummaryFanOut;
if (endByte > this.map.Length - maxRangeBytes) {
endByte = this.map.Length - maxRangeBytes;
}
for (int i = startByte; i < endByte; i++) {
int lhs = TrailingFree(this.map[i]);
if (lhs == 0) {
continue;
}
int clearBytes = GetLeadingClearBytes(i + 1,
maxRangeBytes - 1);
if (lhs + clearBytes * 8 >= requestLength) {
SetRegion(i, 8 - lhs, requestLength);
allocStart = i * 8 + (8 - lhs);
allocLength = requestLength;
return true;
}
int rhs = LeadingFree(this.map[i + clearBytes + 1]);
if (rhs + lhs + clearBytes * 8 >= requestLength) {
SetRegion(i, 8 - lhs, requestLength);
allocStart = i * 8 + (8 - lhs);
allocLength = requestLength;
return true;
}
else {
i += maxRangeBytes - 2;
}
}
allocStart = 0;
allocLength = 0;
return false;
}
private bool ScanAllocate(int startByte,
int requestLength,
out int allocStart,
out int allocLength)
{
if (requestLength < SmallAllocationLength) {
return ScanAllocateSmall(startByte, requestLength,
out allocStart, out allocLength);
}
else {
return ScanAllocateLarge(startByte, requestLength,
out allocStart, out allocLength);
}
}
/// <summary> Allocate contiguous block of specified length near
/// requested start position.
/// <para> This method attempts to find contiguous block
/// of specified length near the hinted start position.
/// </para>
/// <returns> true on success, false if allocation fails. </returns>
/// </summary>
internal bool Allocate(int requestStart,
int requestLength,
out int allocStart,
out int allocLength)
/*^ requires requestLength > 0; ^*/
/*^ requires requestStart < this.totalBits; ^*/
{
if (requestLength > MaxAllocationLength || totalFreeBits == 0) {
allocStart = 0;
allocLength = 0;
return false;
}
if (requestStart <= totalBits &&
IsAllocated(requestStart) == false &&
HintAllocate(requestLength, requestStart,
out allocStart, out allocLength)) {
CheckInvariants();
return true;
}
return Allocate(requestLength, out allocStart, out allocLength);
}
// <summary> This is a recursive method that examines
// the summary information held in the logarithmically
// arranged summary data and explores regions that have
// sufficient reserve to potentially fulfill the
// allocation request. </summary>
private bool DrillDownScan(int depth,
int position,
int requestLength,
out int allocStart,
out int allocLength)
{
if (depth == summary.Length) {
return ScanAllocate(position, requestLength,
out allocStart, out allocLength);
}
int [] /*!*/ totals = /*^ (!) ^*/ summary[depth];
int end = Math.Min(position + SummaryFanOut, totals.Length);
for (int i = position; i < end; i++) {
// Explore lower level if this cell has a total geq the
// target length, or, this cell and its neighbor have
// combined total geq the target length.
if ((totals[i] >= requestLength) ||
(totals.Length > i + 1 &&
totals[i] + totals[i + 1] >= requestLength)) {
if (DrillDownScan(depth + 1, i * SummaryFanOut,
requestLength,
out allocStart, out allocLength)) {
return true;
}
}
}
allocStart = 0;
allocLength = 0;
return false;
}
/// <summary> Allocate contiguous block of specified length.
/// <para> This method attempts to allocate region of specified length.
/// </para>
/// <returns> true on success, false if allocation fails.</returns>
/// </summary>
internal bool Allocate(int requestLength,
out int allocStart,
out int allocLength)
/*^ requires requestLength > 0; ^*/
/*^ requires requestLength <= MaxAllocationLength; ^*/
{
if (requestLength > MaxAllocationLength || totalFreeBits == 0) {
allocStart = 0;
allocLength = 0;
return false;
}
CheckInvariants();
try {
return DrillDownScan(0, 0, requestLength,
out allocStart, out allocLength);
}
finally {
CheckInvariants();
}
}
private void FreeSmall(int start, int length)
{
Debug.Assert(length <= SmallAllocationLength);
int byteIndex = start / 8;
int bitIndex = start - byteIndex * 8;
if (bitIndex + length <= 8) {
ClearBits(byteIndex, bitIndex, length);
}
else {
ClearBits(byteIndex, bitIndex, 8 - bitIndex);
ClearBits(byteIndex + 1, 0, length - (8 - bitIndex));
}
}
private void FreeLarge(int start, int length)
{
int byteIndex = start / 8;
int bitIndex = start - byteIndex * 8;
ClearRegion(byteIndex, bitIndex, length);
}
internal void Free(int start, int length)
{
if (length <= SmallAllocationLength) {
FreeSmall(start, length);
}
else {
FreeLarge(start, length);
}
}
/// <summary> Scans bitmap for first set bit. Scan starts
/// from supplied position. </summary>
// <returns> value >= 0 on success, -1 on failure. </returns>
/*^ [Microsoft.Contracts.Pure] ^*/
internal int GetFirstSetBitFrom(int startBit)
{
int byteIndex = startBit / 8;
int bitIndex = startBit - byteIndex * 8;
int m =
MakeMask(bitIndex, 8 - bitIndex) & (int)this.map[byteIndex];
int skip = LeadingFree((byte)m);
Debug.Assert(skip >= bitIndex);
if (skip != 8) {
return startBit - bitIndex + skip;
}
for (int i = byteIndex + 1; i < map.Length; i++) {
skip = LeadingFree(this.map[i]);
if (skip != 8) {
return 8 * i + skip;
}
}
return -1;
}
/// <summary> Scans bitmap for first clear bit. Scan
/// starts from supplied position. </summary>
// <returns> value >= 0 on success, -1 on failure. </returns>
internal int GetFirstClearBitFrom(int startBit)
{
int byteIndex = startBit / 8;
int bitIndex = startBit - byteIndex * 8;
int m = MakeMask(bitIndex, 8 - bitIndex) & ~(int)this.map[byteIndex];
int skip = LeadingFree((byte)m);
Debug.Assert(skip >= bitIndex);
if (skip != 8) {
return startBit - bitIndex + skip;
}
for (int i = byteIndex + 1; i < map.Length; i++) {
int tmp = ~(int)this.map[i];
skip = LeadingFree((byte)tmp);
if (skip != 8) {
return 8 * i + skip;
}
}
return -1;
}
/// <summary> Scans bitmap for first set bit. Scan
/// starts from supplied position. </summary>
// <returns> value >= 0 on success, -1 on failure. </returns>
internal int GetFirstBitSetBefore(int startBit)
requires (startBit < this.totalBits);
{
int byteIndex = startBit / 8;
int bitIndex = startBit - byteIndex * 8;
int m = MakeMask(0, bitIndex) & (int)this.map[byteIndex];
int r = TrailingFree((byte)m);
if (r != 8) {
return (byteIndex * 8) + (7 - r);
}
for (int i = byteIndex - 1; i >= 0; i--) {
r = TrailingFree(this.map[i]);
if (r != 8) {
return 8 * i + (7 - r);
}
}
return -1;
}
internal int Length
{
get { return totalBits; }
}
internal int FreeCount
{
get { return totalFreeBits; }
}
internal int UsedCount
{
get { return Length - FreeCount; }
}
private void SummarizeAlloc(int byteIndex, int numberOfBits)
{
totalFreeBits -= numberOfBits;
int n = byteIndex;
for (int i = summary.Length - 1; i >= 0; i--) {
n = n / SummaryFanOut;
(/*^ (!) ^*/summary[i])[n] -= numberOfBits;
}
CheckInvariants();
}
private void SummarizeFree(int byteIndex, int numberOfBits)
{
totalFreeBits += numberOfBits;
int n = byteIndex;
for (int i = summary.Length - 1; i >= 0; i--) {
n = n / SummaryFanOut;
(/*^ (!) ^*/summary[i])[n] += numberOfBits;
}
CheckInvariants();
}
[ Conditional("CHECK_INVARIANTS") ]
private static void FastBitCount(byte [] /*!*/ data, out int sum)
{
const int GroupCount = 64;
const int GroupRound = -64;
// sgc croaks if GroupRound is assigned
// using:
// const int GroupRound = ~(GroupCount - 1);
// const int GroupRound = ~63;
int rounded = data.Length & GroupRound;
int i = 0;
sum = 0;
while (i < rounded) {
sum += (int) BitCountTable[ data[i + 0] ];
sum += (int) BitCountTable[ data[i + 1] ];
sum += (int) BitCountTable[ data[i + 2] ];
sum += (int) BitCountTable[ data[i + 3] ];
sum += (int) BitCountTable[ data[i + 4] ];
sum += (int) BitCountTable[ data[i + 5] ];
sum += (int) BitCountTable[ data[i + 6] ];
sum += (int) BitCountTable[ data[i + 7] ];
sum += (int) BitCountTable[ data[i + 8] ];
sum += (int) BitCountTable[ data[i + 9] ];
sum += (int) BitCountTable[ data[i + 10] ];
sum += (int) BitCountTable[ data[i + 11] ];
sum += (int) BitCountTable[ data[i + 12] ];
sum += (int) BitCountTable[ data[i + 13] ];
sum += (int) BitCountTable[ data[i + 14] ];
sum += (int) BitCountTable[ data[i + 15] ];
sum += (int) BitCountTable[ data[i + 16] ];
sum += (int) BitCountTable[ data[i + 17] ];
sum += (int) BitCountTable[ data[i + 18] ];
sum += (int) BitCountTable[ data[i + 19] ];
sum += (int) BitCountTable[ data[i + 20] ];
sum += (int) BitCountTable[ data[i + 21] ];
sum += (int) BitCountTable[ data[i + 22] ];
sum += (int) BitCountTable[ data[i + 23] ];
sum += (int) BitCountTable[ data[i + 24] ];
sum += (int) BitCountTable[ data[i + 25] ];
sum += (int) BitCountTable[ data[i + 26] ];
sum += (int) BitCountTable[ data[i + 27] ];
sum += (int) BitCountTable[ data[i + 28] ];
sum += (int) BitCountTable[ data[i + 29] ];
sum += (int) BitCountTable[ data[i + 30] ];
sum += (int) BitCountTable[ data[i + 31] ];
sum += (int) BitCountTable[ data[i + 32] ];
sum += (int) BitCountTable[ data[i + 33] ];
sum += (int) BitCountTable[ data[i + 34] ];
sum += (int) BitCountTable[ data[i + 35] ];
sum += (int) BitCountTable[ data[i + 36] ];
sum += (int) BitCountTable[ data[i + 37] ];
sum += (int) BitCountTable[ data[i + 38] ];
sum += (int) BitCountTable[ data[i + 39] ];
sum += (int) BitCountTable[ data[i + 40] ];
sum += (int) BitCountTable[ data[i + 41] ];
sum += (int) BitCountTable[ data[i + 42] ];
sum += (int) BitCountTable[ data[i + 43] ];
sum += (int) BitCountTable[ data[i + 44] ];
sum += (int) BitCountTable[ data[i + 45] ];
sum += (int) BitCountTable[ data[i + 46] ];
sum += (int) BitCountTable[ data[i + 47] ];
sum += (int) BitCountTable[ data[i + 48] ];
sum += (int) BitCountTable[ data[i + 49] ];
sum += (int) BitCountTable[ data[i + 50] ];
sum += (int) BitCountTable[ data[i + 51] ];
sum += (int) BitCountTable[ data[i + 52] ];
sum += (int) BitCountTable[ data[i + 53] ];
sum += (int) BitCountTable[ data[i + 54] ];
sum += (int) BitCountTable[ data[i + 55] ];
sum += (int) BitCountTable[ data[i + 56] ];
sum += (int) BitCountTable[ data[i + 57] ];
sum += (int) BitCountTable[ data[i + 58] ];
sum += (int) BitCountTable[ data[i + 59] ];
sum += (int) BitCountTable[ data[i + 60] ];
sum += (int) BitCountTable[ data[i + 61] ];
sum += (int) BitCountTable[ data[i + 62] ];
sum += (int) BitCountTable[ data[i + 63] ];
i += GroupCount;
}
while (i != data.Length) {
sum += BitCount(data[i++]);
}
#if ALTERNATE_FAST_COUNT
// Neither method here seems particularly efficient. The
// unrolled loop still entails bounds checks on every access and
// the cost of calling the bit converter appears to be moderately
// high.
sum = 0;
int i = 0;
int max = data.Length & 0x3;
while (i < max) {
uint v = BitConverter.ToUInt32(data, i);
uint w = v - ((v >> 1) & 0x55555555);
uint x = (w & 0x33333333) + ((w >> 2) & 0x33333333);
uint c = ((x + (x >> 4) & 0xF0F0F0F) * 0x1010101) >> 24;
sum += (int) c;
i += 4;
}
while (i != data.Length) {
sum += BitCount(data[i++]);
}
#endif
}
[ Conditional("CHECK_INVARIANTS") ]
private void CheckInvariants()
{
for (int i = 0; i < summary.Length; i++) {
int [] /*!*/ s = /*^ (!) ^*/ summary[i];
int totalSummary = 0;
for (int j = 0; j < s.Length; j++) {
totalSummary += s[j];
Debug.Assert(s[j] >= 0);
}
Debug.Assert(totalSummary == totalFreeBits);
}
int totalMap = 0;
FastBitCount(this.map, out totalMap);
Debug.Assert(totalMap == map.Length * 8 - totalFreeBits);
}
private static int Log2Up(int x)
/*^ requires x >= 0 && x <= 0x40000000; ^*/
{
// Calculate log2 of x rounded up to nearest power
int i = 0;
for (i = 0; i <= 30; i++) {
if ((1 << i) >= x) {
return i;
}
}
// not reached
Debug.Assert(false);
return 0;
}
[ System.Diagnostics.Conditional("TEST_BITMAP") ]
internal void Dump()
{
const int BPS = 64;
for (int i = 0; i < Length; i += BPS) {
Console.Write("\n{0:x6}: " , i);
int todo = Math.Min(Length - i, BPS);
for (int j = 0; j < todo; j++) {
if ((j & 15) == 8)
Console.Write(" ");
Console.Write("{0}", IsAllocated(i + j) ? 1 : 0);
}
}
Console.Write("\n");
}
}
///////////////////////////////////////////////////////////////////////////
// Test code
#if TEST_BITMAP
internal class BitmapTest
{
internal class AllocationRecord
{
internal int Start;
internal int Length;
internal AllocationRecord() {}
internal AllocationRecord(int start, int length)
{
Start = start;
Length = length; }
}
private static void TestSearch()
{
System.Console.WriteLine("TestSearch()");
const int BitCount = 1024;
Random rng = new Random(0);
bool failed = false;
for (int i = 0; i < 1000 && !failed; i++) {
Bitmap b = new Bitmap(BitCount);
int start;
int length;
if (i == 0) {
// This forces the initial byte checks in
// GetFirst{Set,Clear}BitFrom().
start = 3;
length = 2;
}
else if (i == 1) {
// This forces code past initial byte checks in
// GetFirst{Set,Clear}BitFrom().
start = 13;
length = Bitmap.MaxAllocationLength;
}
else {
start = rng.Next(BitCount);
length = 1 + rng.Next(Math.Min(BitCount - start - 1, Bitmap.MaxAllocationLength - 1));
}
int theStart, theLength;
b.Allocate(start, length, out theStart, out theLength);
Debug.Assert(start == theStart);
Debug.Assert(length == theLength);
Debug.Assert(b.GetFirstSetBitFrom(0) == theStart);
for (int j = 0; j < length; j++) {
Debug.Assert(
b.GetFirstSetBitFrom(theStart + j) == theStart + j);
}
for (int j = theStart + theLength; j < b.Length; j++) {
Debug.Assert(b.GetFirstClearBitFrom(j) == j);
}
}
}
private static void TestSearch2()
{
Console.WriteLine("TestSearch2()");
const int BitCount = 1024;
Random rng = new Random(0);
ArrayList allocations = new ArrayList(BitCount / 8);
for (int r = 0; r < 1000; r++) {
Bitmap b = new Bitmap(BitCount);
int i = r % Bitmap.MaxAllocationLength;
while (i < BitCount) {
int length = 1 + Math.Min(
b.Length - i - 1,
rng.Next(Bitmap.MaxAllocationLength - 1)
);
AllocationRecord a = new AllocationRecord();
b.Allocate(i, length, out a.Start, out a.Length);
Debug.Assert(a.Start == i && a.Length == length);
allocations.Add(a);
i += 1 + length + rng.Next(Bitmap.MaxAllocationLength - 1);
}
i = 0;
while (i < BitCount) {
while (i < BitCount && b.GetFirstClearBitFrom(i) == i) {
i++;
}
if (i >= BitCount) {
break;
}
AllocationRecord /*!*/ a =
/*^(!)^*/ (AllocationRecord)allocations[0];
allocations.RemoveAt(0);
Debug.Assert(i == a.Start);
int nextClear = b.GetFirstClearBitFrom(i);
Debug.Assert(nextClear == i + a.Length ||
i + a.Length >= BitCount);
int j = i;
while (j < BitCount && b.GetFirstSetBitFrom(j) == j) {
j++;
}
Debug.Assert(j == i + a.Length);
i = j;
}
Debug.Assert(allocations.Count == 0);
}
}
private static void TestFillThenEmpty(int bitmapSize,
int allocationSize)
{
System.Console.WriteLine("TestFillThenEmpty({0}, {1})",
bitmapSize, allocationSize);
Debug.Assert((bitmapSize % allocationSize) == 0);
Bitmap b = new Bitmap(bitmapSize);
Debug.Assert(b.FreeCount == bitmapSize);
// Allocate all bits in map
for (int i = 0; i < bitmapSize; i += allocationSize) {
int start;
int length;
if (! b.Allocate(allocationSize, out start, out length)) {
b.Dump();
Console.WriteLine("Allocation failed (i = {0}, length = {1})", i, allocationSize);
Debug.Assert(false);
}
Debug.Assert(start == i);
Debug.Assert(length == allocationSize);
Debug.Assert(b.FreeCount == b.Length - i - allocationSize);
}
// Try allocation when full
Debug.Assert(b.FreeCount == 0);
int dummyStart, dummyLength;
Debug.Assert(! b.Allocate(1, out dummyStart, out dummyLength));
Debug.Assert(b.FreeCount == 0);
// Free all bits in map
for (int i = 0; i < bitmapSize; i += allocationSize) {
b.Free(i, allocationSize);
Debug.Assert(b.FreeCount == i + allocationSize);
}
Debug.Assert(b.FreeCount == b.Length);
// Allocate all bits in specific positions
for (int i = 0; i < bitmapSize; i += allocationSize) {
int start;
int length;
b.Allocate(i, allocationSize, out start, out length);
Debug.Assert(start == i);
Debug.Assert(length == allocationSize);
Debug.Assert(b.FreeCount == b.Length - i - allocationSize);
}
// Try allocation when full
Debug.Assert(b.FreeCount == 0);
Debug.Assert(! b.Allocate(1, out dummyStart, out dummyLength));
Debug.Assert(b.FreeCount == 0);
// Free all bits in map
for (int i = 0; i < bitmapSize; i += allocationSize) {
b.Free(i, allocationSize);
Debug.Assert(b.FreeCount == i + allocationSize);
}
Debug.Assert(b.FreeCount == b.Length);
}
private static void TestSkipFillThenEmpty(int bitmapSize,
int allocationSize)
{
System.Console.WriteLine("TestSkipFillThenEmpty({0}, {1})",
bitmapSize, allocationSize);
for (int skip = 0; skip < allocationSize; skip++) {
int size = bitmapSize - (bitmapSize % (allocationSize + skip));
Bitmap b = new Bitmap(size);
int start = 0;
int length;
int canDo = bitmapSize / (allocationSize + skip);
for (int i = 0; i < canDo; i++) {
bool success =
b.Allocate(i * (allocationSize + skip) + skip,
allocationSize, out start, out length);
Debug.Assert(success);
Debug.Assert(length == allocationSize);
}
Debug.Assert(b.Allocate(allocationSize, out start, out length) == false);
for (int i = 0; i < canDo; i++) {
int oldFree = b.FreeCount;
b.Free(i * (allocationSize + skip) + skip, allocationSize);
Debug.Assert(oldFree == b.FreeCount - allocationSize);
}
}
}
private static void Hammer(int bitmapSize, double allocProbability)
{
Bitmap b = new Bitmap(bitmapSize);
ArrayList allocations = new ArrayList();
Console.WriteLine("Hammer {0}, {1}", bitmapSize, allocProbability);
Random rng = new Random(0);
for (int i = 0; i < bitmapSize; i++) {
if (rng.NextDouble() > (1.0 - allocProbability)) {
int request = Math.Min(Bitmap.MaxAllocationLength, b.FreeCount);
if (request == 0)
continue;
AllocationRecord a = new AllocationRecord();
int oldFree = b.FreeCount;
if (b.Allocate(request, out a.Start, out a.Length) == true) {
allocations.Add(a);
Debug.Assert(a.Length == request);
Debug.Assert(b.FreeCount + a.Length == oldFree);
}
}
else {
int count = allocations.Count;
if (count > 0) {
int index = rng.Next(count);
AllocationRecord /*!*/ a =
/*^(!)^*/ (AllocationRecord)allocations[index];
allocations.RemoveAt(index);
int oldFree = b.FreeCount;
b.Free(a.Start, a.Length);
Debug.Assert(oldFree == b.FreeCount - a.Length);
}
}
}
}
private static void HammerFixedHint(int bitmapSize, double allocProbability)
{
Bitmap b = new Bitmap(bitmapSize);
ArrayList allocations = new ArrayList();
Console.WriteLine("HammerFixedHint {0}, {1}", bitmapSize, allocProbability);
Random rng = new Random(0);
for (int i = 0; i < bitmapSize; i++) {
if (rng.NextDouble() > (1.0 - allocProbability)) {
int request = 1 + Math.Min(Bitmap.MaxAllocationLength - 1, b.FreeCount - 1);
if (request == 0)
continue;
int oldFree = b.FreeCount;
AllocationRecord a = new AllocationRecord();
if (b.Allocate(bitmapSize / 3, request,
out a.Start, out a.Length) == true) {
allocations.Add(a);
Debug.Assert(a.Length == request);
Debug.Assert(oldFree == b.FreeCount + a.Length);
}
}
else {
int count = allocations.Count;
if (count > 0) {
int index = rng.Next(count);
int oldFree = b.FreeCount;
AllocationRecord /*!*/ a =
/*^(!)^*/(AllocationRecord)allocations[index];
allocations.RemoveAt(index);
b.Free(a.Start, a.Length);
Debug.Assert(oldFree == b.FreeCount - a.Length);
}
}
}
}
private static void HammerBadHint(int bitmapSize,
double allocProbability)
{
Bitmap b = new Bitmap(bitmapSize);
ArrayList allocations = new ArrayList();
Console.WriteLine("HammerBadHint {0}, {1}",
bitmapSize, allocProbability);
Random rng = new Random(0);
for (int i = 0; i < bitmapSize; i++) {
if (rng.NextDouble() > (1.0 - allocProbability)) {
int length = Math.Min(Bitmap.MaxAllocationLength, b.FreeCount);
if (length == 0)
continue;
AllocationRecord a = new AllocationRecord();
int start = rng.Next(bitmapSize);
int oldFree = b.FreeCount;
if (b.Allocate(start, length,
out a.Start, out a.Length) == true) {
allocations.Add(a);
Debug.Assert(a.Length == length);
Debug.Assert(oldFree == b.FreeCount + a.Length);
}
}
else {
int count = allocations.Count;
if (count > 0) {
int index = rng.Next(count);
int oldFree = b.FreeCount;
AllocationRecord /*!*/ a =
/*^(!)^*/(AllocationRecord)allocations[index];
allocations.RemoveAt(index);
b.Free(a.Start, a.Length);
Debug.Assert(oldFree == b.FreeCount - a.Length);
}
}
}
}
internal static void Main()
{
#if !DEBUG
Console.WriteLine("Program needs to be compiles with /D:DEBUG /DEBUG flags");
Environment.Exit(-1);
#endif
TestSearch();
TestSearch2();
TestFillThenEmpty(333, 3);
TestFillThenEmpty(28, 2);
TestFillThenEmpty(256, 8);
TestFillThenEmpty(13 * 17, 13);
TestFillThenEmpty(17 * 17, 17);
TestFillThenEmpty(1700, 17);
TestFillThenEmpty(8 * 8 * 1024 * 2, 8);
TestFillThenEmpty(8 * 8 * 1024 * 2, 32);
TestFillThenEmpty(8 * 8 * 1024 * 2, 1);
for (int i = 3; i < Bitmap.MaxAllocationLength; i++) {
int s = 3 * 256 * 256 + Bitmap.MaxAllocationLength;
if ((s % i) != 0) {
s -= s % i;
}
TestFillThenEmpty(s, i);
}
for (int i = 1; i < Bitmap.MaxAllocationLength; i++) {
TestSkipFillThenEmpty(13 * 1024 + i, i);
}
foreach (int s in new int [] { 172, 4247, 16384}) {
foreach (double p in new double [] { 0.1, 0.5, 0.85, 0.99 }) {
Hammer(s, p);
HammerFixedHint(s, p);
HammerBadHint(s, p);
}
}
}
}
#endif // TEST_BITMAP
}