Files
GTASource/rage/scaleform/Src/GKernel/GHeapBitSet2.h
expvintl 419f2e4752 init
2025-02-23 17:40:52 +08:00

286 lines
9.4 KiB
C++

/**********************************************************************
Filename : GHeapBitSet2.h
Content :
Created : 2009
Authors : Maxim Shemanarev
Copyright : (c) 2001-2009 Scaleform Corp. All Rights Reserved.
Notes : Allocator bit-set maintanance
Licensees may use this file in accordance with the valid Scaleform
Commercial License Agreement provided with the software.
This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR ANY PURPOSE.
For information regarding Commercial License Agreements go to:
online - http://www.scaleform.com/licensing.html or
email - sales@scaleform.com
**********************************************************************/
#ifndef INC_GHeapBitSet2_H
#define INC_GHeapBitSet2_H
#include "GTypes.h"
#include <string.h>
// ***** GHeapBitSet2
//
// This class represents a number of static functions for manipulating
// with allocation bit-sets. It uses two bits per block, that is, for the
// block size = 16 bytes, it will take 1K per every 64K of the payload.
// In average the bookkeeping information is less than the losses for minimal
// alignment itself. The idea is simple and straightforward. We have two
// bits per block that mean the following:
//
// Variant without alignment shift:
//------------------
// 00 - empty,
// 01 - 1 block
// 10 01 - 2 blocks
// 11 00 01 - 3 blocks
// 11 01 xx 01 - 4 blocks
// 11 10 xx xx 01 - 5 blocks
//
// 11 11 0n nn nn . . . 01 - 6 to 37 blocks. "n nn nn" - is a 5-bit number
// of blocks minus 6. 0 means 6 blocks.
//
// 11 11 11 . . . . . . 01 - 38 or more blocks. Since we have a at least
// 68 bits (2*38-6-2), it's guaranteed we can place
// a 32-bit number directly. And of course,
// it's aligned to 32-bit.
//
// Variant with alignment shift:
//------------------
// 00 - empty,
// 01 - 1 block
// 10 aa - 2 blocks, aa - alignment, 01 or 10
// 11 00 aa - 3 blocks, aa - alignment, 01 or 10
// 11 01 xx aa - 4 blocks, aa - alignment, 01, 10 or 11
// 11 10 xx xx aa - 5 blocks, aa - alignment, 01, 10 or 11
//
// 11 11 0n nn nn aa - 6 blocks. "n nn nn" - is a 5-bit number
// of blocks minus 6. "0 00 00" means 6 blocks.
// aa - alignment: 01, 10 or 11.
//
// 11 11 0n nn nn xx aa - 7 blocks. "n nn nn" equals "0 00 01" in this case.
// aa - alignment: 01, 10 or 11
//
// 11 11 0n nn nn aa aa a1 - 8 to 37 blocks. "aa aa a" - is a 5-bit alignment
// shift value, that is, 0:1, 1:2, 2:4, 3:8, etc.
//
// 11 11 11 . . . aa aa a1 - 38 or more blocks. Since we have a at least
// 64 bits (2*38-6-6), it's guaranteed we can place
// a 32-bit number directly. And of course,
// it's aligned to 32-bit.
//------------------------------------------------------------------------
class GHeapBitSet2
{
public:
// Calculate the number of 32-bit words needed for the
// bit-set with the particular data alignment. For example,
// 16-byte blocks must have alignmentShift=4.
//--------------------------------------------------------------------
static UPInt GetBitSetSize(UPInt dataSize, UPInt alignmentShift)
{
UPInt alignmentMask = (UPInt(1) << alignmentShift) - 1;
return (((dataSize + alignmentMask) >> alignmentShift) + 15) / 16;
}
static void Clear(UInt32* buf, UPInt numWords)
{
memset(buf, 0, sizeof(UInt32) * numWords);
}
// Get the two-bit value at the particular idx.
//--------------------------------------------------------------------
GINLINE static UInt GetValue(const UInt32* buf, UPInt idx)
{
return (buf[idx >> 4] >> (2*idx & 30)) & 3;
}
// Clear the two-bit value at the particular idx.
//--------------------------------------------------------------------
GINLINE static void ClearValue(UInt32* buf, UPInt idx)
{
buf[idx >> 4] &= ~(UInt32(3) << (2*idx & 30));
}
// Set the two-bit value at the particular idx.
//--------------------------------------------------------------------
GINLINE static void SetValue(UInt32* buf, UPInt idx, UInt val)
{
UPInt wrd = idx >> 4;
UPInt rem = 2*idx & 30;
buf[wrd] = (buf[wrd] & ~(UInt32(3) << rem)) | (UInt32(val) << rem);
}
// Mark the array of blocks as empty. We need to mark the first
// and the last values as 00. It's necessary for proper merging.
//--------------------------------------------------------------------
GINLINE static void MarkFree(UInt32* buf, UPInt start, UPInt num)
{
ClearValue(buf, start);
ClearValue(buf, start+num-1);
}
// Mark the array of blocks as busy without alignment info.
// The first and the last values must be non-zero for proper merging.
// The other bits are set according to the described above encoding method.
//--------------------------------------------------------------------
static void MarkBusy(UInt32* buf, UPInt start, UPInt num)
{
switch(num)
{
case 0:
case 1:
SetValue(buf, start, 1);
return;
case 2:
SetValue(buf, start, 2);
break;
case 3:
case 4:
case 5:
SetValue(buf, start, 3);
SetValue(buf, start+1, unsigned(num-3));
break;
default:
if (num < 38)
{
unsigned n6 = unsigned(num-6);
SetValue(buf, start, 3);
SetValue(buf, start+1, 3);
SetValue(buf, start+2, n6 >> 4);
SetValue(buf, start+3, (n6 >> 2) & 3);
SetValue(buf, start+4, n6 & 3);
}
else
{
SetValue(buf, start, 3);
SetValue(buf, start+1, 3);
SetValue(buf, start+2, 3);
buf[(2*start + 6 + 31) >> 5] = UInt32(num);
}
break;
}
SetValue(buf, start+num-1, 1);
}
// Mark the array of blocks as busy. The first and the last values
// must be non-zero for proper merging. The other bits are set
// according to the described above encoding method.
//--------------------------------------------------------------------
static void MarkBusy(UInt32* buf, UPInt start, UPInt num, UPInt alignShift)
{
switch(num)
{
case 0:
case 1:
SetValue(buf, start, 1);
return;
case 2:
SetValue(buf, start, 2);
SetValue(buf, start+1, UInt(alignShift+1));
return;
case 3:
case 4:
case 5:
SetValue(buf, start, 3);
SetValue(buf, start+1, UInt(num-3));
SetValue(buf, start+num-1, UInt(alignShift+1));
return;
case 6:
case 7:
SetValue(buf, start, 3);
SetValue(buf, start+1, 3);
SetValue(buf, start+2, 0);
SetValue(buf, start+3, 0);
SetValue(buf, start+4, UInt(num-6));
SetValue(buf, start+num-1, UInt(alignShift+1));
return;
default:
{
if (num < 38)
{
UInt n6 = UInt(num-6);
SetValue(buf, start, 3);
SetValue(buf, start+1, 3);
SetValue(buf, start+2, n6 >> 4);
SetValue(buf, start+3, (n6 >> 2) & 3);
SetValue(buf, start+4, n6 & 3);
}
else
{
SetValue(buf, start, 3);
SetValue(buf, start+1, 3);
SetValue(buf, start+2, 3);
buf[(2*start + 6 + 31) >> 5] = UInt32(num);
}
UInt a = UInt(alignShift << 1) | 1;
SetValue(buf, start+num-3, a >> 4);
SetValue(buf, start+num-2, (a >> 2) & 3);
SetValue(buf, start+num-1, a & 3);
}
return;
}
}
// Calculate the block size according to the described above
// encoding method.
//--------------------------------------------------------------------
static UPInt GetBlockSize(const UInt32* buf, UPInt start)
{
UInt size;
size = GetValue(buf, start);
if (size < 3)
{
return size;
}
size = GetValue(buf, start+1);
if (size < 3)
{
return size+3;
}
size = GetValue(buf, start+2);
if (size < 3)
{
return 6 + ((size << 4) |
(GetValue(buf, start+3) << 2) |
GetValue(buf, start+4));
}
return buf[(2*start + 6 + 31) >> 5];
}
//--------------------------------------------------------------------
static UInt GetAlignShift(const UInt32* buf, UPInt start, UPInt num)
{
if (num < 8)
{
return GetValue(buf, start+num-1) - 1;
}
else
{
return (GetValue(buf, start+num-1) >> 1)|
(GetValue(buf, start+num-2) << 1)|
(GetValue(buf, start+num-3) << 3);
}
}
};
#endif