1344 lines
54 KiB
Plaintext
1344 lines
54 KiB
Plaintext
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// 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) {
|
|
DebugStub.Write("\n{0:x6}: " , __arglist(i));
|
|
int todo = Math.Min(Length - i, BPS);
|
|
for (int j = 0; j < todo; j++) {
|
|
if ((j & 15) == 8)
|
|
DebugStub.Write(" ");
|
|
|
|
DebugStub.Write(IsAllocated(i + j) ? "1" : "0");
|
|
}
|
|
}
|
|
DebugStub.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
|
|
}
|
|
|