unit dbitmem_le;
      // Reads memory bit-by-bit, bits order in a single byte is Little Endian.

{$MODE FPC}
{$MODESWITCH OUT}
{$MODESWITCH RESULT}

interface

function ReadBitLE(Data: Pointer; // pointer to current byte
                   Bits: PtrInt;  // bit pos in first byte (0..8)
                   out NextData: Pointer; // updated pointer
                   out NextBits: PtrInt   // updated number of available bits
                  ): PtrInt; // returns 0 or 1

function ReadBitsLE(Data: Pointer;  // pointer to current byte
                    Bits: PtrInt;   // bit pos in first byte (0..8)
                    Need: PtrInt;   // bits to be read, <=8*SizeOf(PtrInt)
                    out NextData: Pointer;  // updated pointer
                    out NextBits: PtrInt    // updated number of available bits
                   ): PtrInt;

function ReadSBitsLE(Data: Pointer;
                     Bits: PtrInt;
                     Need: PtrInt;
                     out NextData: Pointer;
                     out NextBits: PtrInt
                    ): PtrInt;
      // Like ReadBitsLE, but reads signed integer.

function AlignBitsLE(Data: Pointer; Bits: PtrInt): Pointer; inline;

implementation

function ReadBitLE(Data: Pointer;
                   Bits: PtrInt;
                   out NextData: Pointer;
                   out NextBits: PtrInt
                  ): PtrInt; inline;
begin
  Result := (PByte(Data)^ shr Bits) and 1;
  if Bits >= 7 then begin
    NextData := Data + 1;
    NextBits := 0;
  end else begin
    NextData := Data;
    NextBits := Bits + 1;
  end;
end;

function _ReadBitsLE(Data: Pointer;
                     Need: PtrInt;
                     Value: PtrInt;
                     Bits: PtrInt;
                     out NextData: Pointer;
                     out NextBits: PtrInt
                    ): PtrInt;
var
  Mask, Limit: Cardinal;
begin
  Mask := 1;
  Limit := 1 shl Need;
  while Mask < Limit do begin
    if ReadBitLE(Data, Bits, Data, Bits) <> 0 then
      Value := Value or Mask;
    Mask := Mask shl 1;
  end;
  NextData := Data;
  NextBits := Bits;
  Exit(Value);
end;

function ReadBitsLE(Data: Pointer;
                    Bits: PtrInt;
                    Need: PtrInt;
                    out NextData: Pointer;
                    out NextBits: PtrInt
                   ): PtrInt;
begin
  // Slightly change parameters order: first 3 arguments will be in registers
  Exit(_ReadBitsLE(Data, Need, 0, Bits, NextData, NextBits));
end;

function ReadSBitsLE(Data: Pointer;
                     Bits: PtrInt;
                     Need: PtrInt;
                     out NextData: Pointer;
                     out NextBits: PtrInt
                    ): PtrInt;
var
  Temp: Cardinal;
begin
  Result := _ReadBitsLE(Data, Need, 0, Bits, NextData, NextBits);
  if (Result and (1 shl (Need - 1))) <> 0 then begin
    Temp := Cardinal(Result) or ($ffffffff shl Need);
    Exit(Temp);
  end else
    Exit(Result);
end;

function AlignBitsLE(Data: Pointer; Bits: PtrInt): Pointer; inline;
begin
  if Bits > 0 then begin
    Exit(Data + 1);
  end else
    Exit(Data);
end;

end.
