Migrate to ReadOnlyBitStream

This commit is contained in:
Matt Nadareski
2024-04-16 21:57:31 -04:00
parent c04c7d438d
commit 7580d49830
5 changed files with 15 additions and 251 deletions

View File

@@ -1,239 +0,0 @@
using System;
using System.IO;
using SabreTools.IO;
// TODO: Add more complete implementation to SabreTools.IO
namespace SabreTools.Compression
{
/// <summary>
/// Wrapper to allow reading bits from a source stream
/// </summary>
public class BitStream
{
/// <inheritdoc cref="Stream.Position"/>
public long Position => _source.Position;
/// <inheritdoc cref="Stream.Length"/>
public long Length => _source.Length;
/// <summary>
/// Original stream source
/// </summary>
private readonly Stream _source;
/// <summary>
/// Last read byte value from the stream
/// </summary>
private byte? _bitBuffer;
/// <summary>
/// Index in the byte of the current bit
/// </summary>
private int _bitIndex;
/// <summary>
/// Create a new BitStream from a source Stream
/// </summary>
public BitStream(Stream? source)
{
if (source == null || !source.CanRead || !source.CanSeek)
throw new ArgumentException(nameof(source));
_source = source;
_bitBuffer = null;
_bitIndex = 0;
}
/// <summary>
/// Discard the current cached byte
/// </summary>
public void Discard()
{
_bitBuffer = null;
_bitIndex = 0;
}
/// <summary>
/// Read a single bit, if possible
/// </summary>
/// <returns>The next bit encoded in a byte, null on error or end of stream</returns>
public byte? ReadBit()
{
// If we reached the end of the stream
if (_source.Position >= _source.Length)
return null;
// If we don't have a value cached
if (_bitBuffer == null)
{
// Read the next byte, if possible
_bitBuffer = ReadSourceByte();
if (_bitBuffer == null)
return null;
// Reset the bit index
_bitIndex = 0;
}
// Get the value by bit-shifting
int value = _bitBuffer.Value & 0x01;
_bitBuffer = (byte?)(_bitBuffer >> 1);
_bitIndex++;
// Reset the byte if we're at the end
if (_bitIndex >= 8)
Discard();
return (byte)value;
}
/// <summary>
/// Read a multiple bits in LSB, if possible
/// </summary>
/// <returns>The next bits encoded in a UInt32, null on error or end of stream</returns>
public uint? ReadBitsLSB(int bits)
{
uint value = 0;
for (int i = 0; i < bits; i++)
{
// Read the next bit
byte? bitValue = ReadBit();
if (bitValue == null)
return null;
// Add the bit shifted by the current index
value += (uint)(bitValue.Value << i);
}
return value;
}
/// <summary>
/// Read a multiple bits in MSB, if possible
/// </summary>
/// <returns>The next bits encoded in a UInt32, null on error or end of stream</returns>
public uint? ReadBitsMSB(int bits)
{
uint value = 0;
for (int i = 0; i < bits; i++)
{
// Read the next bit
byte? bitValue = ReadBit();
if (bitValue == null)
return null;
// Add the bit shifted by the current index
value += (value << 1) + bitValue.Value;
}
return value;
}
/// <summary>
/// Read a byte, if possible
/// </summary>
/// <returns>The next byte, null on error or end of stream</returns>
/// <remarks>Assumes the stream is byte-aligned</remarks>
public byte? ReadByte()
{
try
{
Discard();
return _source.ReadByteValue();
}
catch
{
return null;
}
}
/// <summary>
/// Read a UInt16, if possible
/// </summary>
/// <returns>The next UInt16, null on error or end of stream</returns>
/// <remarks>Assumes the stream is byte-aligned</remarks>
public ushort? ReadUInt16()
{
try
{
Discard();
return _source.ReadUInt16();
}
catch
{
return null;
}
}
/// <summary>
/// Read a UInt32, if possible
/// </summary>
/// <returns>The next UInt32, null on error or end of stream</returns>
/// <remarks>Assumes the stream is byte-aligned</remarks>
public uint? ReadUInt32()
{
try
{
Discard();
return _source.ReadUInt32();
}
catch
{
return null;
}
}
/// <summary>
/// Read a UInt64, if possible
/// </summary>
/// <returns>The next UInt64, null on error or end of stream</returns>
/// <remarks>Assumes the stream is byte-aligned</remarks>
public ulong? ReadUInt64()
{
try
{
Discard();
return _source.ReadUInt64();
}
catch
{
return null;
}
}
/// <summary>
/// Read <paramref name="bytes"/> bytes, if possible
/// </summary>
/// <param name="bytes">Number of bytes to read</param>
/// <returns>The next <paramref name="bytes"/> bytes, null on error or end of stream</returns>
/// <remarks>Assumes the stream is byte-aligned</remarks>
public byte[]? ReadBytes(int bytes)
{
try
{
Discard();
return _source.ReadBytes(bytes);
}
catch
{
return null;
}
}
/// <summary>
/// Read a single byte from the underlying stream, if possible
/// </summary>
/// <returns>The next full byte from the stream, null on error or end of stream</returns>
private byte? ReadSourceByte()
{
try
{
return _source.ReadByteValue();
}
catch
{
return null;
}
}
}
}

View File

@@ -1,7 +1,7 @@
using System.IO;
using System.Linq;
using System.Text;
using SabreTools.IO;
using SabreTools.IO.Extensions;
using SabreTools.Models.Compression.LZ;
using static SabreTools.Models.Compression.LZ.Constants;

View File

@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using SabreTools.IO.Streams;
using SabreTools.Models.Compression.MSZIP;
using static SabreTools.Models.Compression.MSZIP.Constants;
@@ -13,7 +14,7 @@ namespace SabreTools.Compression.MSZIP
/// <summary>
/// Internal bitstream to use for decompression
/// </summary>
private readonly BitStream _bitStream;
private readonly ReadOnlyBitStream _bitStream;
/// <summary>
/// Create a new Decompressor from a byte array
@@ -28,8 +29,8 @@ namespace SabreTools.Compression.MSZIP
// Create a memory stream to wrap
var ms = new MemoryStream(input);
// Wrap the stream in a BitStream
_bitStream = new BitStream(ms);
// Wrap the stream in a ReadOnlyBitStream
_bitStream = new ReadOnlyBitStream(ms);
}
/// <summary>
@@ -42,8 +43,8 @@ namespace SabreTools.Compression.MSZIP
if (input == null || !input.CanRead || !input.CanSeek)
throw new ArgumentException(nameof(input));
// Wrap the stream in a BitStream
_bitStream = new BitStream(input);
// Wrap the stream in a ReadOnlyBitStream
_bitStream = new ReadOnlyBitStream(input);
}
/// <summary>

View File

@@ -1,6 +1,7 @@
using System;
using System.IO;
using System.Linq;
using SabreTools.IO.Streams;
namespace SabreTools.Compression.MSZIP
{
@@ -83,7 +84,7 @@ namespace SabreTools.Compression.MSZIP
/// </summary>
/// <param name="input">BitStream representing the input</param>
/// <returns>Value of the node described by the input</returns>
public int Decode(BitStream input)
public int Decode(ReadOnlyBitStream input)
{
// Start at the root of the tree
var node = _root;

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using SabreTools.IO.Streams;
using SabreTools.Models.Compression.Quantum;
using static SabreTools.Compression.Quantum.Constants;
@@ -12,7 +13,7 @@ namespace SabreTools.Compression.Quantum
/// <summary>
/// Internal bitstream to use for decompression
/// </summary>
private BitStream _bitStream;
private readonly ReadOnlyBitStream _bitStream;
#region Models
@@ -100,8 +101,8 @@ namespace SabreTools.Compression.Quantum
// Create a memory stream to wrap
var ms = new MemoryStream(input);
// Wrap the stream in a BitStream
_bitStream = new BitStream(ms);
// Wrap the stream in a ReadOnlyBitStream
_bitStream = new ReadOnlyBitStream(ms);
// Initialize literal models
this._model0 = CreateModel(0, 64);
@@ -140,8 +141,8 @@ namespace SabreTools.Compression.Quantum
if (windowBits < 10 || windowBits > 21)
throw new ArgumentOutOfRangeException(nameof(windowBits));
// Wrap the stream in a BitStream
_bitStream = new BitStream(input);
// Wrap the stream in a ReadOnlyBitStream
_bitStream = new ReadOnlyBitStream(input);
// Initialize literal models
this._model0 = CreateModel(0, 64);