publicsealedclassBitStream : IDisposable { ///<summary> /// Base stream that the BitStream operates on. ///</summary> public Stream BaseStream { get; }
///<summary> /// Endianness used for reading and writing data for the <see cref="BitStream" />. ///</summary> ///<remarks>Endianness determines the byte order in which data is processed. This field is used /// internally to specify whether the bit stream operates in big-endian or little-endian mode.</remarks> ///<example> /// Read mode example: /// 0b10100001 /// In big-endian (MSB), the most significant bit 10100001 is processed first. /// ^ /// In little-endian (LSB), the least significant bit 10100001 is processed first. /// ^ /// Write mode example: /// If we keep writing bit 1 into the stream: /// In big-endian (MSB), we get: 0b10000000, 0b11000000, 0b11100000, 0b11110000, 0b11111000, 0b11111100, 0b11111110, 0b11111111 /// In little-endian (LSB), we get: 0b00000001, 0b00000011, 0b00000111, 0b00001111, 0b00011111, 0b00111111, 0b01111111, 0b11111111 ///</example> privatereadonly BitStreamEndianness endianness;
///<summary> /// Mode of operation for the <see cref="BitStream" />. ///</summary> ///<remarks>Specifies whether the <see cref="BitStream" /> operates in a read or write /// mode. It is intended for internal use to control the behavior of the stream.</remarks> privatereadonly BitStreamMode mode;
///<summary> /// Current byte being processed. ///</summary> privatebyte currentByte;
///<summary> /// Bit position in the current byte. ///</summary> privateint bitPosition;
///<summary> /// Original byte position in the stream when the BitStream was created. ///</summary> /// Used for resetting the stream. privatereadonlyint originalPos;
publicBitStream(Stream stream, BitStreamEndianness order = BitStreamEndianness.Msb, BitStreamMode mode = BitStreamMode.Read) { BaseStream = stream ?? thrownew ArgumentNullException(nameof(stream)); this.endianness = order; this.mode = mode; this.currentByte = 0; this.bitPosition = mode is BitStreamMode.Read ? 8 : 0; this.originalPos = (int)stream.Position; switch (mode) { case BitStreamMode.Write when !stream.CanWrite: thrownew InvalidOperationException("Stream must be writable in write mode."); case BitStreamMode.Read when !stream.CanRead: thrownew InvalidOperationException("Stream must be readable in read mode."); } }
publicintReadBits(int bitCount) { if (mode != BitStreamMode.Read) thrownew InvalidOperationException("Stream is not in Read mode."); if (bitCount < 1 || bitCount > 32) thrownew ArgumentOutOfRangeException(nameof(bitCount), "Bit count must be between 1 and 32.");
int result = 0;
for (int i = 0; i < bitCount; i++) { if (bitPosition == 8) { int readByte = BaseStream.ReadByte(); if (readByte == -1) thrownew EndOfStreamException();
currentByte = (byte)readByte; bitPosition = 0; }
int shift = endianness is BitStreamEndianness.Msb ? 7 - bitPosition : bitPosition; int bit = (currentByte >> shift) & 1;
result = (result << 1) | bit; bitPosition++; }
return result; }
publicintReadBit() { return ReadBits(1); }
publicvoidWriteBit(int bit) { if (mode != BitStreamMode.Write) thrownew InvalidOperationException("Stream is not in Write mode."); if (bit != 0 && bit != 1) thrownew ArgumentOutOfRangeException(nameof(bit), "Bit must be 0 or 1.");
publicvoidWriteBits(byte[] bits, int bitCount) { if (mode != BitStreamMode.Write) thrownew InvalidOperationException("Stream is not in Write mode."); if (bits == null || bits.Length == 0) return; if (bitCount < 1 || bitCount > bits.Length * 8) thrownew ArgumentOutOfRangeException(nameof(bitCount), "Bit count must be between 1 and the number of bits in the byte array.");
int bitCountWritten = 0; int currentByteIndex = 0; int currentBitIndex = 0;
while (bitCountWritten < bitCount) { int shift = endianness == BitStreamEndianness.Msb ? 7 - currentBitIndex : currentBitIndex; int bit = (bits[currentByteIndex] >> shift) & 1; WriteBit(bit); bitCountWritten++; currentBitIndex++;
publicvoidWriteBits(bool[] bits) { Debug.WriteLine("You are using bool[] as input. The BitStream will just traverse the array and write 1 for true and 0 for false regardless of the endianness."); if (bits == null || bits.Length == 0) return; foreach (bool bit in bits) { WriteBit(bit ? 1 : 0); } }
publicvoidReset() { if (mode != BitStreamMode.Read) thrownew InvalidOperationException("Stream not in Read mode cannot be reset.");
internalsealedclassHuffmanNode { ///<summary> /// Creates a new huffman leaf node. ///</summary> ///<param name="freq"> Frequency of this leaf node. </param> ///<param name="val"> Leaf node value. </param> publicHuffmanNode(int freq, byte val) { Frequency = freq; Value = val; IsLeaf = true; }
///<summary> /// Creates a new huffman node with two children. ///</summary> /// Used for building the huffman tree. ///<param name="leftChild"> Left side child node. </param> ///<param name="rightChild"> Right side child node. </param> publicHuffmanNode(HuffmanNode leftChild, HuffmanNode rightChild) { LeftChild = leftChild; RightChild = rightChild; leftChild.Bit = false; rightChild.Bit = true; Frequency = leftChild.Frequency + rightChild.Frequency; leftChild.Parent = rightChild.Parent = this; IsLeaf = false; }
///<summary> /// Parent node of this Huffman node. ///</summary> public HuffmanNode Parent { get; privateset; }
///<summary> /// Left child node of this Huffman node. ///</summary> public HuffmanNode LeftChild { get; }
///<summary> /// Right child node of this Huffman node. ///</summary> public HuffmanNode RightChild { get; }
///<summary> /// Represents whether this node is a leaf node. ///</summary> /// This property is set when the node is created. publicbool IsLeaf { get; }
///<summary> /// Represents whether this node is the root of the Huffman tree. ///</summary> publicbool IsRoot => Parent == null;
///<summary> /// Represents the value of the edge leading to this node. ///</summary> /// 0 for left child, 1 for right child. /// Part of the Huffman Code. publicbool Bit { get; privateset; }
///<summary> /// Leaf node value. Represents the byte value of the leaf node. ///</summary> publicbyte Value { get; }
public T Dequeue() { if (_heap.Count == 0) { thrownew InvalidOperationException("Queue is empty"); }
T firstItem = _heap[0]; int lastIndex = _heap.Count - 1; _heap[0] = _heap[lastIndex]; _heap.RemoveAt(lastIndex);
if (_heap.Count > 0) { HeapifyDown(0); } return firstItem; }
public T Peek() { if (_heap.Count == 0) { thrownew InvalidOperationException("Queue is empty"); } return _heap[0]; }
publicvoidClear() { _heap.Clear(); }
privatevoidHeapifyUp(int index) { while (index > 0) { int parentIndex = (index - 1) / 2; if (_comparer.Compare(_heap[index], _heap[parentIndex]) >= 0) { break; } Swap(index, parentIndex); index = parentIndex; } }
privatevoidHeapifyDown(int index) { int current = index; while (true) { int smallest = current; int leftChild = 2 * current + 1; int rightChild = 2 * current + 2;
foreach (byte b in data) { if (codeTable.TryGetValue(b, outbool[] code)) { input.WriteBits(code); } else { thrownew InvalidDataException($"No code found for byte {b}."); } }
public MemoryStream Decompress() { if (mode != CompressionMode.Decompress) { thrownew InvalidOperationException("Not in decompression mode."); }
ushort root = RebuildTree(); while (output.Length != decompressedLength) { ushort node = root; while (node >= 256) // Keep reading bits until we reach a leaf node { int bit = input.ReadBit(); if (bit != -1) { node = children[node, bit]; // Traverse the tree based on the bit read } } output.WriteByte((byte)node); } return output; }