diff --git a/Aaru6.Compression/ADC.cs b/Aaru6.Compression/ADC.cs new file mode 100644 index 0000000..6b4a2c8 --- /dev/null +++ b/Aaru6.Compression/ADC.cs @@ -0,0 +1,164 @@ +// +// ADC.cs +// +// Author: +// Natalia Portillo +// +// Copyright (c) 2016 © Claunia.com +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Runtime.CompilerServices; + +namespace Aaru6.Compression +{ + /// Implements the Apple version of RLE + public class ADC + { + const int PLAIN = 1; + const int TWO_BYTE = 2; + const int THREE_BYTE = 3; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static int GetChunkType(byte byt) => (byt & 0x80) == 0x80 + ? PLAIN + : (byt & 0x40) == 0x40 + ? THREE_BYTE + : TWO_BYTE; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static int GetChunkSize(byte byt) => GetChunkType(byt) switch + { + PLAIN => (byt & 0x7F) + 1, + TWO_BYTE => ((byt & 0x3F) >> 2) + 3, + THREE_BYTE => (byt & 0x3F) + 4, + _ => -1 + }; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static int GetOffset(ReadOnlySpan chunk) => GetChunkType(chunk[0]) switch + { + PLAIN => 0, + TWO_BYTE => ((chunk[0] & 0x03) << 8) + chunk[1], + THREE_BYTE => (chunk[1] << 8) + chunk[2], + _ => -1 + }; + + /// Decompresses a byte buffer that's compressed with ADC + /// Compressed buffer + /// Buffer to hold decompressed data + /// How many bytes are stored on + [MethodImpl(MethodImplOptions.AggressiveOptimization)] + public static int DecodeBuffer(byte[] source, byte[] destination) + { + int inputPosition = 0; + int chunkSize; + int offset; + int chunkType; + int outPosition = 0; + Span temp = stackalloc byte[3]; + + while(inputPosition < source.Length) + { + byte readByte = source[inputPosition++]; + + chunkType = GetChunkType(readByte); + + switch(chunkType) + { + case PLAIN: + chunkSize = GetChunkSize(readByte); + + if(outPosition + chunkSize > destination.Length) + goto finished; + + Array.Copy(source, inputPosition, destination, outPosition, chunkSize); + outPosition += chunkSize; + inputPosition += chunkSize; + + break; + case TWO_BYTE: + chunkSize = GetChunkSize(readByte); + temp[0] = readByte; + temp[1] = source[inputPosition++]; + offset = GetOffset(temp); + + if(outPosition + chunkSize > destination.Length) + goto finished; + + if(offset == 0) + { + byte lastByte = destination[outPosition - 1]; + + for(int i = 0; i < chunkSize; i++) + { + destination[outPosition] = lastByte; + outPosition++; + } + } + else + { + for(int i = 0; i < chunkSize; i++) + { + destination[outPosition] = destination[outPosition - offset - 1]; + outPosition++; + } + } + + break; + case THREE_BYTE: + chunkSize = GetChunkSize(readByte); + temp[0] = readByte; + temp[1] = source[inputPosition++]; + temp[2] = source[inputPosition++]; + offset = GetOffset(temp); + + if(outPosition + chunkSize > destination.Length) + goto finished; + + if(offset == 0) + { + byte lastByte = destination[outPosition - 1]; + + for(int i = 0; i < chunkSize; i++) + { + destination[outPosition] = lastByte; + outPosition++; + } + } + else + { + for(int i = 0; i < chunkSize; i++) + { + destination[outPosition] = destination[outPosition - offset - 1]; + outPosition++; + } + } + + break; + } + } + + finished: + + return outPosition; + } + } +} \ No newline at end of file diff --git a/AaruBenchmark/AaruBenchmark.csproj b/AaruBenchmark/AaruBenchmark.csproj index d0cf382..d7bcd73 100644 --- a/AaruBenchmark/AaruBenchmark.csproj +++ b/AaruBenchmark/AaruBenchmark.csproj @@ -34,6 +34,10 @@ Always + + + Always + Always diff --git a/AaruBenchmark/Benchs.cs b/AaruBenchmark/Benchs.cs index b306c4e..73ba26f 100644 --- a/AaruBenchmark/Benchs.cs +++ b/AaruBenchmark/Benchs.cs @@ -28,6 +28,19 @@ namespace AaruBenchmark public void Aaru6() => Aaru6Compressions.TeleDiskLzh(); } + [SimpleJob(RuntimeMoniker.Net60)] + public class ADCBenchs + { + [Benchmark(Baseline = true)] + public void Aaru() => Compression.SharpCompress.ADC(); + + [Benchmark] + public void Aaru6() => Aaru6Compressions.ADC(); + + [Benchmark] + public void AaruNative() => Compression.AaruNative.ADC(); + } + [SimpleJob(RuntimeMoniker.Net60)] public class GzipBenchs { diff --git a/AaruBenchmark/Compression/Aaru6Compressions.cs b/AaruBenchmark/Compression/Aaru6Compressions.cs index d4c6f5e..9eee4b6 100644 --- a/AaruBenchmark/Compression/Aaru6Compressions.cs +++ b/AaruBenchmark/Compression/Aaru6Compressions.cs @@ -68,5 +68,29 @@ namespace AaruBenchmark.Compression if(crc != "22bd5d44") throw new InvalidDataException("Incorrect decompressed checksum"); } + + public static void ADC() + { + const int bufferSize = 262144; + byte[] input = new byte[34367]; + + var fs = new FileStream(Path.Combine(Program.Folder, "adc.bin"), FileMode.Open, FileAccess.Read); + + fs.Read(input, 0, input.Length); + fs.Close(); + fs.Dispose(); + + byte[] output = new byte[bufferSize]; + + int realSize = Aaru6.Compression.ADC.DecodeBuffer(input, output); + + if(realSize != 262144) + throw new InvalidDataException("Incorrect decompressed size"); + + string crc = Crc32Context.Data(output, (uint)realSize, out _); + + if(crc != "5a5a7388") + throw new InvalidDataException("Incorrect decompressed checksum"); + } } } \ No newline at end of file diff --git a/AaruBenchmark/Compression/AaruNative.cs b/AaruBenchmark/Compression/AaruNative.cs index e95a9c7..a6f09ab 100644 --- a/AaruBenchmark/Compression/AaruNative.cs +++ b/AaruBenchmark/Compression/AaruNative.cs @@ -9,6 +9,9 @@ namespace AaruBenchmark.Compression [DllImport("libAaru.Compression.Native", SetLastError = true)] static extern int apple_rle_decode_buffer(byte[] dst_buffer, int dst_size, byte[] src_buffer, int src_size); + [DllImport("libAaru.Compression.Native", SetLastError = true)] + static extern int adc_decode_buffer(byte[] dst_buffer, int dst_size, byte[] src_buffer, int src_size); + public static void AppleRle() { const int bufferSize = 32768; @@ -32,5 +35,29 @@ namespace AaruBenchmark.Compression if(crc != "3525ef06") throw new InvalidDataException("Incorrect decompressed checksum"); } + + public static void ADC() + { + const int bufferSize = 327680; + byte[] input = new byte[34367]; + + var fs = new FileStream(Path.Combine(Program.Folder, "adc.bin"), FileMode.Open, FileAccess.Read); + + fs.Read(input, 0, input.Length); + fs.Close(); + fs.Dispose(); + + byte[] output = new byte[bufferSize]; + + int realSize = adc_decode_buffer(output, output.Length, input, input.Length); + + if(realSize != 262144) + throw new InvalidDataException("Incorrect decompressed size"); + + string crc = Crc32Context.Data(output, (uint)realSize, out _); + + if(crc != "5a5a7388") + throw new InvalidDataException("Incorrect decompressed checksum"); + } } } \ No newline at end of file diff --git a/AaruBenchmark/Compression/SharpCompress.cs b/AaruBenchmark/Compression/SharpCompress.cs index aa460be..1752efb 100644 --- a/AaruBenchmark/Compression/SharpCompress.cs +++ b/AaruBenchmark/Compression/SharpCompress.cs @@ -1,5 +1,7 @@ using System.IO; +using Aaru6.Checksums; using SharpCompress.Compressors; +using SharpCompress.Compressors.ADC; using SharpCompress.Compressors.BZip2; using SharpCompress.Compressors.Deflate; @@ -64,5 +66,39 @@ namespace AaruBenchmark.Compression str.Close(); str.Dispose(); } + + public static void ADC() + { + var _dataStream = new FileStream(Path.Combine(Program.Folder, "adc.bin"), FileMode.Open, FileAccess.Read); + Stream str = new ADCStream(_dataStream); + byte[] compressed = new byte[262144]; + int pos = 0; + int left = 262144; + bool oneZero = false; + + while(left > 0) + { + int done = str.Read(compressed, pos, left); + + if(done == 0) + { + if(oneZero) + throw new IOException("Could not read the file!"); + + oneZero = true; + } + + left -= done; + pos += done; + } + + str.Close(); + str.Dispose(); + + string crc = Crc32Context.Data(compressed, 262144, out _); + + if(crc != "5a5a7388") + throw new InvalidDataException("Incorrect decompressed checksum"); + } } } \ No newline at end of file diff --git a/AaruBenchmark/Program.cs b/AaruBenchmark/Program.cs index a071108..b25dd24 100644 --- a/AaruBenchmark/Program.cs +++ b/AaruBenchmark/Program.cs @@ -13,6 +13,7 @@ namespace AaruBenchmark { var config = ManualConfig.Create(DefaultConfig.Instance); + BenchmarkRunner.Run(config); BenchmarkRunner.Run(config); BenchmarkRunner.Run(config); BenchmarkRunner.Run(config); diff --git a/AaruBenchmark/data/adc.bin b/AaruBenchmark/data/adc.bin new file mode 100644 index 0000000..c9074a2 Binary files /dev/null and b/AaruBenchmark/data/adc.bin differ diff --git a/Benchmarks.ods b/Benchmarks.ods index ce467b6..6af81aa 100644 Binary files a/Benchmarks.ods and b/Benchmarks.ods differ