From e3f23389dab70c84a64bf56288f44f878bc532dc Mon Sep 17 00:00:00 2001 From: Lani Aung Date: Sun, 6 Oct 2024 23:12:50 -0600 Subject: [PATCH] I dunno man --- fxl.codes.kisekae.data.test/BitStreamTests.cs | 2 +- fxl.codes.kisekae.data.test/LZWTests.cs | 29 ++---------- .../Archives/Algorithms/Huffman.cs | 47 +++++++++++++++++++ .../Archives/Algorithms/IAlgorithm.cs | 4 +- .../Archives/Algorithms/LZW.cs | 41 +++++++++++----- fxl.codes.kisekae.data/Archives/BitStream.cs | 20 ++++++-- 6 files changed, 97 insertions(+), 46 deletions(-) create mode 100644 fxl.codes.kisekae.data/Archives/Algorithms/Huffman.cs diff --git a/fxl.codes.kisekae.data.test/BitStreamTests.cs b/fxl.codes.kisekae.data.test/BitStreamTests.cs index 61d9d87..024ecdb 100644 --- a/fxl.codes.kisekae.data.test/BitStreamTests.cs +++ b/fxl.codes.kisekae.data.test/BitStreamTests.cs @@ -39,7 +39,7 @@ public class BitStreamTests } [Test] - public void TestReadLong() + public void TestReadInt() { var random = (uint)Random.Shared.Next(); var bytes = BitConverter.GetBytes(random); diff --git a/fxl.codes.kisekae.data.test/LZWTests.cs b/fxl.codes.kisekae.data.test/LZWTests.cs index 84f2811..206c6f6 100644 --- a/fxl.codes.kisekae.data.test/LZWTests.cs +++ b/fxl.codes.kisekae.data.test/LZWTests.cs @@ -1,4 +1,3 @@ -using fxl.codes.kisekae.data.Archives; using fxl.codes.kisekae.data.Archives.Algorithms; namespace fxl.codes.kisekae.data.test; @@ -20,30 +19,10 @@ public class LZWTests { Assert.Multiple(() => { - Assert.That(_lzw.Encode(_unencoded), Is.EqualTo(_encoded), "Encoding failed"); - Assert.That(_lzw.Decode(_encoded), Is.EqualTo(_unencoded), "Decoding failed"); + var streamUnencoded = new MemoryStream(_unencoded); + var streamEncoded = new MemoryStream(_encoded); + Assert.That(_lzw.Encode(streamUnencoded), Is.EqualTo(_encoded), "Encoding failed"); + Assert.That(_lzw.Decode(streamEncoded), Is.EqualTo(_unencoded), "Decoding failed"); }); } - - [Test] - public void TestDecompressLzh() - { - var assembly = typeof(LZWTests).Assembly; - foreach (var resource in assembly.GetManifestResourceNames()) - { - using var stream = assembly.GetManifestResourceStream(resource); - if (stream == null) continue; - - var files = LhCodecService.GetFiles(in stream); - foreach (var file in files) - Assert.Multiple(() => - { - Assert.That(file.CompressedFile, Is.Not.Null); - Assert.That(file.CompressedFile, Is.Not.Empty); - - var decompressed = _lzw.Decode(file.CompressedFile); - Assert.That(decompressed, Is.Not.Null); - }); - } - } } \ No newline at end of file diff --git a/fxl.codes.kisekae.data/Archives/Algorithms/Huffman.cs b/fxl.codes.kisekae.data/Archives/Algorithms/Huffman.cs new file mode 100644 index 0000000..84584b6 --- /dev/null +++ b/fxl.codes.kisekae.data/Archives/Algorithms/Huffman.cs @@ -0,0 +1,47 @@ +namespace fxl.codes.kisekae.data.Archives.Algorithms; + +public class Huffman +{ + private readonly List Nodes; + + public Huffman() + { + Nodes = new List(); + for (var i = char.MinValue; i < char.MaxValue; i++) Nodes.Add(new HuffmanNode(i, 1)); + } +} + +internal class HuffmanNode +{ + public readonly char Value; + public HuffmanNode Left; + public HuffmanNode Parent; + public HuffmanNode Right; + public HuffmanSide Side; + public int Weight; + + public HuffmanNode(char value, int weight = 0) + { + Value = value; + Weight = weight; + } + + public HuffmanNode(HuffmanNode left, HuffmanNode right) + { + Left = left; + Left.Side = HuffmanSide.Left; + Left.Parent = this; + + Right = right; + Right.Side = HuffmanSide.Right; + Right.Parent = this; + + Weight = left.Weight + right.Weight; + } +} + +internal enum HuffmanSide +{ + Left, + Right +} \ No newline at end of file diff --git a/fxl.codes.kisekae.data/Archives/Algorithms/IAlgorithm.cs b/fxl.codes.kisekae.data/Archives/Algorithms/IAlgorithm.cs index 76997e3..083bcdf 100644 --- a/fxl.codes.kisekae.data/Archives/Algorithms/IAlgorithm.cs +++ b/fxl.codes.kisekae.data/Archives/Algorithms/IAlgorithm.cs @@ -2,6 +2,6 @@ namespace fxl.codes.kisekae.data.Archives.Algorithms; public interface IAlgorithm { - byte[] Encode(ReadOnlySpan stream); - byte[] Decode(ReadOnlySpan stream); + byte[] Encode(Stream stream); + byte[] Decode(Stream stream, int bitLength); } \ No newline at end of file diff --git a/fxl.codes.kisekae.data/Archives/Algorithms/LZW.cs b/fxl.codes.kisekae.data/Archives/Algorithms/LZW.cs index a9d5046..906da17 100644 --- a/fxl.codes.kisekae.data/Archives/Algorithms/LZW.cs +++ b/fxl.codes.kisekae.data/Archives/Algorithms/LZW.cs @@ -4,24 +4,38 @@ namespace fxl.codes.kisekae.data.Archives.Algorithms; internal class LZW : IAlgorithm { + private static readonly Dictionary DecodeDictionary; + private static readonly Dictionary EncodeDictionary; private readonly int Max; + static LZW() + { + DecodeDictionary = new Dictionary(); + EncodeDictionary = new Dictionary(); + + for (uint i = 0; i <= byte.MaxValue; i++) + { + var value = $"{(char)i}"; + DecodeDictionary.Add(i, value); + EncodeDictionary.Add(value, i); + } + } + internal LZW(int max = short.MaxValue) { Max = max; } - public byte[] Decode(ReadOnlySpan stream) + public byte[] Decode(Stream stream, int bitLength = 32) { + var dictionary = DecodeDictionary.ToDictionary(x => x.Key, x => x.Value); uint next = byte.MaxValue + 1; - var dictionary = new Dictionary(); - for (uint i = 0; i <= byte.MaxValue; i++) dictionary.Add(i, $"{(char)i}"); - var buffer = ""; var decoded = new List(); - for (var i = 0; i < stream.Length; i += 4) + var bitStream = new BitStream(stream); + while (bitStream.Position < bitStream.Length) { - var code = BitConverter.ToUInt32(stream[i..(i + 4)]); + var code = bitStream.ReadBits(bitLength); if (!dictionary.ContainsKey(code)) dictionary[code] = buffer + buffer[0]; decoded.Add(dictionary[code]); @@ -33,17 +47,18 @@ internal class LZW : IAlgorithm return decoded.SelectMany(Encoding.UTF8.GetBytes).ToArray(); } - public byte[] Encode(ReadOnlySpan stream) + public byte[] Encode(Stream stream) { - var next = byte.MaxValue + 1; - var dictionary = new Dictionary(); - for (var i = 0; i <= byte.MaxValue; i++) dictionary.Add($"{(char)i}", i); + uint next = byte.MaxValue + 1; + var dictionary = EncodeDictionary.ToDictionary(x => x.Key, x => x.Value); var buffer = ""; - var encoded = new List(); - foreach (var part in stream) + var encoded = new List(); + while (true) { - var charValue = (char)part; + if (stream.Position >= stream.Length) break; + + var charValue = (char)stream.ReadByte(); buffer += charValue; if (dictionary.ContainsKey(buffer)) continue; if (next <= Max) dictionary.Add(buffer, next++); diff --git a/fxl.codes.kisekae.data/Archives/BitStream.cs b/fxl.codes.kisekae.data/Archives/BitStream.cs index 4c814ab..e300916 100644 --- a/fxl.codes.kisekae.data/Archives/BitStream.cs +++ b/fxl.codes.kisekae.data/Archives/BitStream.cs @@ -41,17 +41,27 @@ public class BitStream : Stream } var buffer = _bitBuffer; - buffer >>= 32 - bitLength; -#if DEBUG - Console.WriteLine($"BitStream buffer: {_bitBuffer:b}, extracted number: {buffer:b}"); -#endif - _bitBuffer <<= bitLength; + if (bitLength < 32) + { + buffer >>= 32 - bitLength; + _bitBuffer <<= bitLength; + } + else + { + _bitBuffer = 0; + } + Position += bitLength; _bitBufferCount -= bitLength; return buffer; } + public override int ReadByte() + { + return (int)ReadBits(8); + } + public override void Flush() { throw new NotImplementedException();