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

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

interface

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

function ReadBits(Data: Pointer;  // pointer to current byte
                  Bits: PtrInt;   // bits available at first byte (in 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 ReadSBits(Data: Pointer;
                   Bits: PtrInt;
                   Need: PtrInt;
                   out NextData: Pointer;
                   out NextBits: PtrInt
                  ): PtrInt;

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

implementation

function ReadBit(Data: Pointer;
                 Bits: PtrInt;
                 out NextData: Pointer;
                 out NextBits: PtrInt
                ): PtrInt; inline;
begin
  if Bits <= 0 then begin
    Inc(Data);
    Bits := 7;
  end else
    Dec(Bits);
  Result := (PByte(Data)^ shr Bits) and 1;
  NextData := Data;
  NextBits := Bits;
end;

function _ReadBits(Data: Pointer;
                   Need: PtrInt;
                   Value: PtrInt;
                   Bits: PtrInt;
                   out NextData: Pointer;
                   out NextBits: PtrInt
                  ): PtrInt;
begin
  if Need >= 8 then begin
    if Bits > 0 then begin
      Dec(Need, Bits);
      Value := (PByte(Data)^ and ((1 shl Bits) - 1)) shl Need;
    end;
    Inc(Data);
    while Need >= 8 do begin
      Dec(Need, 8);
      Value := Value or (PByte(Data)^ shl Need);
      Inc(Data);
    end;
    Result := Value or (PByte(Data)^ shr (8-Need));
    NextData := Data;
    NextBits := 8 - Need;
  end else begin
    if Bits >= Need then begin
      Result := (PByte(Data)^ shr (Bits - Need)) and ((1 shl Need) - 1);
      NextData := Data;
      NextBits := Bits - Need;
    end else begin
      Need := Need - Bits; // to be read from second byte
      Result := ((PByte(Data)[0] and ((1 shl Bits) - 1)) shl Need) or
                (PByte(Data)[1] shr (8 - Need));
      NextData := Data + 1;
      NextBits := 8 - Need;
    end;
  end;
end;

function ReadBits(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(_ReadBits(Data, Need, 0, Bits, NextData, NextBits));
end;

function ReadSBits(Data: Pointer;
                   Bits: PtrInt;
                   Need: PtrInt;
                   out NextData: Pointer;
                   out NextBits: PtrInt
                  ): PtrInt;
var
  Temp: Cardinal;
begin
  Result := _ReadBits(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 AlignBits(Data: Pointer; Bits: PtrInt): Pointer; inline;
begin
  if Bits < 8 then begin
    Exit(Data + 1);
  end else
    Exit(Data);
end;

end.
