The aim of this task is to write functions (or create a class if your
language is Object Oriented and you prefer) for reading and writing sequences of
bits, most significant bit first. While the output of a asciiprint "STRING" is the ASCII byte sequence
"S", "T", "R", "I", "N", "G", the output of a "print" of the bits sequence
0101011101010 (13 bits) must be 0101011101010; real I/O is performed always
quantized by byte (avoiding endianness issues and relying on underlying
buffering for performance), therefore you must obtain as output the bytes
0101 0111 0101 0000 (bold bits are padding bits), i.e. in hexadecimal 57 50.

As test, you can implement a rough (e.g. don't care about error handling or
other issues) compression/decompression program for ASCII sequences
of bytes, i.e. bytes for which the most significant bit is always unused, so that you can write
seven bits instead of eight (each 8 bytes of input, we write 7 bytes of output).

These bit oriented I/O functions can be used to implement compressors and
decompressors; e.g. Dynamic and Static Huffman encodings use variable length
bits sequences, while LZW (see LZW compression) use fixed or variable words
nine (or more) bits long.

 Limits in the maximum number of bits that can be written/read in a single read/write operation are allowed.
 Errors handling is not mandatory


 
program Bitwise_IO;
 
{$APPTYPE CONSOLE}
 
uses
  System.SysUtils,
  System.Classes;
 
type
  TBitReader = class
  private
    readData: Cardinal;
    startPosition: Integer;
    endPosition: Integer;
    Stream: TStream;
    procedure Align;
    function BaseStream: TStream;
    procedure EnsureData(bitCount: Integer);
    function GetInBuffer: Integer;
  public
    constructor Create(sm: TStream);
    function Read(bitCount: Integer): Integer;
    function ReadBit: Boolean;
  end;
 
  TBitWriter = class
  private
    data: Byte;
    dataLength: Integer;
    Stream: TStream;
    procedure Align;
    function BaseStream: TStream;
    function BitsToAligment: Integer;
  public
    constructor Create(sm: TStream);
    procedure Write(value, length: Integer);
    procedure WriteBit(value: Boolean);
  end;
 
function TBitReader.GetInBuffer: Integer;
begin
  result := endPosition - startPosition;
end;
 
function TBitReader.BaseStream: TStream;
begin
  result := stream;
end;
 
constructor TBitReader.Create(sm: TStream);
begin
  Stream := sm;
end;
 
procedure TBitReader.EnsureData(bitCount: Integer);
var
  readBits: Integer;
  b: Byte;
begin
  readBits := bitCount - GetInBuffer;
  while readBits > 0 do
  begin
    BaseStream.Read(b, 1);
    readData := readData or (b shl endPosition);
    endPosition := endPosition + 8;
    readBits := readBits - 8;
  end;
end;
 
function TBitReader.ReadBit: Boolean;
begin
  Exit(Read(1) > 0);
end;
 
function TBitReader.Read(bitCount: Integer): Integer;
begin
  EnsureData(bitCount);
  result := (readData shr startPosition) and ((1 shl bitCount) - 1);
  startPosition := startPosition + bitCount;
  if endPosition = startPosition then
  begin
    endPosition := ord(startPosition = 0);
    readData := 0;
  end
  else if (startPosition >= 8) then
  begin
    readData := readData shr startPosition;
    endPosition := endPosition - startPosition;
    startPosition := 0;
  end;
end;
 
procedure TBitReader.Align;
begin
  endPosition := ord(startPosition = 0);
  readData := 0;
end;
 
function TBitWriter.BaseStream: TStream;
begin
  Exit(stream);
end;
 
function TBitWriter.BitsToAligment: Integer;
begin
  Exit((32 - dataLength) mod 8);
end;
 
constructor TBitWriter.Create(sm: TStream);
begin
  Stream := sm;
end;
 
procedure TBitWriter.WriteBit(value: Boolean);
begin
  self.Write(ord(value), 1);
end;
 
procedure TBitWriter.Write(value, length: Integer);
var
  currentData: Cardinal;
  currentLength: Integer;
begin
  currentData := data or (value shl dataLength);
  currentLength := dataLength + length;
  while currentLength >= 8 do
  begin
 
    BaseStream.Write(currentData, 1);
    currentData := currentData shr 8;
    currentLength := currentLength - 8;
  end;
  data := currentData;
  dataLength := currentLength;
end;
 
procedure TBitWriter.Align;
begin
  if dataLength > 0 then
  begin
    BaseStream.Write(data, 1);
    data := 0;
    dataLength := 0;
  end;
end;
 
var
  ms: TMemoryStream;
  writer: TBitWriter;
  reader: TBitReader;
 
begin
  ms := TMemoryStream.create();
  writer := TBitWriter.create(ms);
  writer.WriteBit(true);
  writer.Write(5, 3);
  writer.Write($0155, 11);
  writer.Align();
  ms.Position := 0;
  reader := TBitReader.create(ms);
  writeln(reader.ReadBit());
  writeln(reader.Read(3));
  writeln(format('0x%.4x', [reader.Read(11)]));
  reader.Align();
  ms.Free;
  writer.Free;
  reader.Free;
  Readln;
end.