Task: Bitwise IO



C#: using System;
using System.IO;

public class BitReader
{
    uint readData = 0;
    int startPosition = 0;
    int endPosition = 0;

    public int InBuffer
    {
        get { return endPosition - startPosition; }
    }

    private Stream stream;

    public Stream BaseStream
    {
        get { return stream; }
    }

    public BitReader(Stream stream)
    {
        this.stream = stream;
    }

    void EnsureData(int bitCount)
    {
        int readBits = bitCount - InBuffer;
        while (readBits > 0)
        {
            int b = BaseStream.ReadByte();

            if (b < 0) throw new InvalidOperationException("Unexpected end of stream");

            readData |= checked((uint)b << endPosition);
            endPosition += 8;
            readBits -= 8;
        }
    }

    public bool ReadBit()
    {
        return Read(1) > 0;
    }

    public int Read(int bitCount)
    {
        EnsureData(bitCount);

        int result = (int)(readData >> startPosition) & ((1 << bitCount) - 1);
        startPosition += bitCount;
        if (endPosition == startPosition)
        {
            endPosition = startPosition = 0;
            readData = 0;
        }
        else if (startPosition >= 8)
        {
            readData >>= startPosition;
            endPosition -= startPosition;
            startPosition = 0;
        }

        return result;
    }

    public void Align()
    {
        endPosition = startPosition = 0;
        readData = 0;
    }
}

public class BitWriter
{
    uint data = 0;
    int dataLength = 0;
    Stream stream;

    public Stream BaseStream
    {
        get { return stream; }
    }

    public int BitsToAligment
    {
        get { return (32 - dataLength) % 8; }
    }

    public BitWriter(Stream stream)
    {
        this.stream = stream;
    }

    public void WriteBit(bool value)
    {
        Write(value ? 1 : 0, 1);
    }

    public void Write(int value, int length)
    {
        uint currentData = data | checked((uint)value << dataLength);
        int currentLength = dataLength + length;
        while (currentLength >= 8)
        {
            BaseStream.WriteByte((byte)currentData);
            currentData >>= 8;
            currentLength -= 8;
        }
        data = currentData;
        dataLength = currentLength;
    }

    public void Align()
    {
        if (dataLength > 0)
        {
            BaseStream.WriteByte((byte)data);

            data = 0;
            dataLength = 0;
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        MemoryStream ms = new MemoryStream();
        BitWriter writer = new BitWriter(ms);
        writer.WriteBit(true);
        writer.Write(5, 3);
        writer.Write(0x0155, 11);
        writer.Align();

        ms.Position = 0;
        BitReader reader = new BitReader(ms);
        Console.WriteLine(reader.ReadBit());
        Console.WriteLine(reader.Read(3));
        Console.WriteLine(reader.Read(11).ToString("x4"));
        reader.Align();
    }
}



Java: module BitwiseIO
    {
    class BitReader
        {
        construct(Byte[] bytes)
            {
            this.bits = bytes.toBitArray();
            }

        private Bit[] bits;
        private Int   index;

        Int offset          
            {
            @Override
            Int get()
                {
                return index;
                }

            @Override
            void set(Int offset)
                {
                assert 0 <= offset < size;
                index = offset;
                }
            }

        Int size.get()      
            {
            return bits.size;
            }

        Boolean eof.get()   
            {
            return index >= size;
            }

        Bit readBit()
            {
            return eof ? assert:bounds : bits[index++];
            }

        Byte readByte()
            {
            assert:bounds index + 8 <= size as $"eof (offset={index}, size={size}";
            Int start = index;
            index += 8;
            return bits[start ..< index].toByte();
            }
        }

    class BitWriter
        {
        private Bit[] bits = new Bit[];

        BitWriter writeBit(Bit bit)
            {
            bits.add(bit);
            return this;
            }

        BitWriter writeByte(Byte byte)
            {
            bits.addAll(byte.toBitArray());
            return this;
            }

        Byte[] bytes.get()
            {
            
            
            
            bits += bits.size & 0x7 == 0 ? [] : Byte:0.toBitArray() [bits.size & 0x7 ..< 8];
            return bits.toByteArray();
            }
        }

    @Inject Console console;
    void run()
        {
        Bit[] orig = [0,1,0,1,0,1,1,1,0,1,0,1,0];   

        val out = new BitWriter();
        orig.forEach(bit -> out.writeBit(bit));

        val bytes = out.bytes;
        console.print($"bytes written={bytes}");  

        val in = new BitReader(bytes);
        val test = new Bit[orig.size]((Int i) -> in.readBit());
        assert test == orig;
        }
    }