I have this need where I need to store either:
- up to 8
Boolean
values in aByte
- up to 32
Boolean
values in a(U)Int32
- up to 64
Boolean
values in a(U)Int64
Is Byte
more suitable than Char
for 8-bits?
Do I use signed
or unsigned
for 32/64-bit?
Is there a Delphi-specific code sample to convert the Byte
/Integer
to/from an array of Boolean
s? And to set, say, the N-th item to true/false, such as:
SetItemBoolean(ItemNumber: Integer; Value: Boolean);
I found something to convert from a Char
to an array of Boolean
s, I'm just wondering how to do it for Byte
/Integer
so I can support a bigger number of Boolean
values.
https://ibeblog.com/2010/08/20/delphi-binary-data-storage/
CodePudding user response:
Delphi offers TIntegerSet
for this, which has the size of an Integer and thus can be cast on it.
var
Bits: TIntegerSet;
IntVal: Integer;
begin
if Value then
Include(Bits, ItemNumber)
else
Exclude(Bits, ItemNumber);
if ItemNumber in Bits then
{ Bit ItemNumber is set }
else
{ Bit ItemNumber is not set }
{ cast to Integer as needed }
IntVal := Integer(Bits);
{ or from Integer }
Bits := TIntegerSet(IntVal);
end;
For 8 bits in a byte you can declare a TByteSet
in a similar way:
type
TByteSet = set of 0..7;
and cast it to or from a Byte variable.
CodePudding user response:
This type implements arbitrarily sized bit sets.
type
TBitSet = record
private
FBitCount: Integer;
FSets: array of set of 0..255;
class function SetCount(BitCount: Integer): Integer; static;
procedure MakeUnique;
procedure GetSetIndexAndBitIndex(Bit: Integer; out SetIndex, BitIndex: Integer);
function GetIsEmpty: Boolean;
procedure SetBitCount(Value: Integer);
function GetSize: Integer;
public
class operator In(const Bit: Integer; const BitSet: TBitSet): Boolean;
class operator Equal(const bs1, bs2: TBitSet): Boolean;
class operator NotEqual(const bs1, bs2: TBitSet): Boolean;
class function SizeOfNativeSet(BitCount: Integer): Integer; static;
property BitCount: Integer read FBitCount write SetBitCount;
property Size: Integer read GetSize;
property IsEmpty: Boolean read GetIsEmpty;
procedure Clear;
procedure IncludeAll;
procedure Include(const Bit: Integer);
procedure Exclude(const Bit: Integer);
end;
{ TBitSet }
procedure TBitSet.MakeUnique;
begin
// this is used to implement copy-on-write so that the type behaves like a value
SetLength(FSets, Length(FSets));
end;
procedure TBitSet.GetSetIndexAndBitIndex(Bit: Integer; out SetIndex, BitIndex: Integer);
begin
Assert(InRange(Bit, 0, FBitCount-1));
SetIndex := Bit shr 8; // shr 8 = div 256
BitIndex := Bit and 255; // and 255 = mod 256
end;
function TBitSet.GetIsEmpty: Boolean;
var
i: Integer;
begin
for i := 0 to High(FSets) do begin
if FSets[i]<>[] then begin
Result := False;
Exit;
end;
end;
Result := True;
end;
procedure TBitSet.SetBitCount(Value: Integer);
var
Bit, SetIndex, BitIndex: Integer;
begin
if (Value<>FBitCount) or not Assigned(FSets) then begin
Assert(Value>=0);
FBitCount := Value;
SetLength(FSets, SetCount(Value));
if Value>0 then begin
(* Ensure that unused bits are cleared, necessary give the CompareMem call in Equal. This also
means that state does not persist when we decrease and then increase BitCount. For instance,
consider this code:
var
bs: TBitSet;
...
bs.BitCount := 2;
bs.Include(1);
bs.BitCount := 1;
bs.BitCount := 2;
Assert(not (1 in bs)); *)
GetSetIndexAndBitIndex(Value - 1, SetIndex, BitIndex);
for Bit := BitIndex 1 to 255 do begin
System.Exclude(FSets[SetIndex], Bit);
end;
end;
end;
end;
function TBitSet.GetSize: Integer;
begin
Result := Length(FSets)*SizeOf(FSets[0]);
end;
class function TBitSet.SetCount(BitCount: Integer): Integer;
begin
Result := (BitCount 255) shr 8; // shr 8 = div 256
end;
class function TBitSet.SizeOfNativeSet(BitCount: Integer): Integer;
begin
Result := (BitCount 7) shr 3; // shr 3 = div 8
end;
class operator TBitSet.In(const Bit: Integer; const BitSet: TBitSet): Boolean;
var
SetIndex, BitIndex: Integer;
begin
BitSet.GetSetIndexAndBitIndex(Bit, SetIndex, BitIndex);
Result := BitIndex in BitSet.FSets[SetIndex];
end;
class operator TBitSet.Equal(const bs1, bs2: TBitSet): Boolean;
begin
Result := (bs1.FBitCount=bs2.FBitCount)
and CompareMem(Pointer(bs1.FSets), Pointer(bs2.FSets), bs1.Size);
end;
class operator TBitSet.NotEqual(const bs1, bs2: TBitSet): Boolean;
begin
Result := not (bs1=bs2);
end;
procedure TBitSet.Clear;
var
i: Integer;
begin
MakeUnique;
for i := 0 to High(FSets) do begin
FSets[i] := [];
end;
end;
procedure TBitSet.IncludeAll;
var
i: Integer;
begin
for i := 0 to BitCount-1 do begin
Include(i);
end;
end;
procedure TBitSet.Include(const Bit: Integer);
var
SetIndex, BitIndex: Integer;
begin
MakeUnique;
GetSetIndexAndBitIndex(Bit, SetIndex, BitIndex);
System.Include(FSets[SetIndex], BitIndex);
end;
procedure TBitSet.Exclude(const Bit: Integer);
var
SetIndex, BitIndex: Integer;
begin
MakeUnique;
GetSetIndexAndBitIndex(Bit, SetIndex, BitIndex);
System.Exclude(FSets[SetIndex], BitIndex);
end;
CodePudding user response:
It's a simple binary logic. You can store data in any numeric type, but i recommend use Unsigned types. Here is example for BYTE type, but you can do write same for any(UInt16, UInt32, UInt64) just change type of AStorage param:
//for byte Index can be from 0 to 7
function GetByteBool(const AStorage : byte; AIndex : byte) : boolean;
begin
Result := (AStorage and (1 shl AIndex)) = (1 shl AIndex);
end;
procedure SetByteBool(var AStorage : byte; const AIndex : byte; const AValue : boolean);
begin
if AValue then begin
AStorage := AStorage or (1 shl AIndex);
end else begin
AStorage := AStorage xor (1 shl AIndex);
end;
end;
procedure TForm2.Button1Click(Sender: TObject);
begin
var b : byte := 17;
SetByteBool(b, 4, false);
if GetByteBool(b, 4) then
showmessage('true')
else
showmessage('false')
end;
In this case you will use just 1 BIT per 1 boolean value.