Home > database >  Storing up to 8 boolean values in a byte or up to 32 boolean values in an Integer
Storing up to 8 boolean values in a byte or up to 32 boolean values in an Integer

Time:08-09

I have this need where I need to store either:

  1. up to 8 Boolean values in a Byte
  2. up to 32 Boolean values in a (U)Int32
  3. 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 Booleans? 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 Booleans, 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.

  • Related