mirror of
https://github.com/SabreTools/SabreTools.IO.git
synced 2026-02-08 05:37:52 +00:00
Compare commits
137 Commits
1.6.1
...
split-test
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ef17819a3 | ||
|
|
b4d87e53a0 | ||
|
|
ae87869bb9 | ||
|
|
46ad576668 | ||
|
|
37b09b07a6 | ||
|
|
448e43dd05 | ||
|
|
e29b8ab4db | ||
|
|
6075aa25a2 | ||
|
|
c53fb33278 | ||
|
|
fb1fc5d85d | ||
|
|
2763d3915b | ||
|
|
16b238539b | ||
|
|
68f49eeb48 | ||
|
|
c5ecd41a8f | ||
|
|
9ab9dd4ff8 | ||
|
|
867c8d11da | ||
|
|
65dbb7a31a | ||
|
|
1eaf7954fe | ||
|
|
a602a07514 | ||
|
|
466b0e90e7 | ||
|
|
abdf50c9e0 | ||
|
|
12341ba6aa | ||
|
|
70b78f861c | ||
|
|
5b306ce9e8 | ||
|
|
2b6fc200e2 | ||
|
|
7c63f44c75 | ||
|
|
edd3e6eef2 | ||
|
|
244b7411d4 | ||
|
|
fb60f1fed5 | ||
|
|
8f06bf5859 | ||
|
|
2c5d7ad56b | ||
|
|
46996c10e5 | ||
|
|
7491821679 | ||
|
|
8fe404e732 | ||
|
|
793168fbe5 | ||
|
|
67b6118cc1 | ||
|
|
b12d122721 | ||
|
|
20f1679557 | ||
|
|
7ccedbeac5 | ||
|
|
72910cc1c0 | ||
|
|
8f4ea0da16 | ||
|
|
eb4975b261 | ||
|
|
995c19d903 | ||
|
|
f0fe9af467 | ||
|
|
d33b47d15a | ||
|
|
e4a0a08d13 | ||
|
|
24a69166f0 | ||
|
|
6c13cdcf31 | ||
|
|
4138c271e5 | ||
|
|
f80d31597b | ||
|
|
5054aeb077 | ||
|
|
d2e9b8d6e5 | ||
|
|
2c29aee834 | ||
|
|
576bafcb87 | ||
|
|
2b310ac528 | ||
|
|
4f6b6d7b59 | ||
|
|
17e55ee233 | ||
|
|
8b78906d1d | ||
|
|
cff2dcf4cc | ||
|
|
a56942cb73 | ||
|
|
5ed661b77c | ||
|
|
a0a0cd0386 | ||
|
|
bcc0fca4ad | ||
|
|
843e821e5f | ||
|
|
630b01283e | ||
|
|
22abb96013 | ||
|
|
314de12661 | ||
|
|
a0b24031b5 | ||
|
|
b4628485c3 | ||
|
|
4610ddc9b9 | ||
|
|
e392ddc8d7 | ||
|
|
1908d1b32e | ||
|
|
9d73195f86 | ||
|
|
335a486f17 | ||
|
|
d3e41ac187 | ||
|
|
8ddd9f3f78 | ||
|
|
54ad538c08 | ||
|
|
e6bc9ab3e3 | ||
|
|
94934b00a9 | ||
|
|
e49f56fccc | ||
|
|
79c64ddfa8 | ||
|
|
b22384d5f3 | ||
|
|
955c1b5641 | ||
|
|
535f9f928d | ||
|
|
f0cb15c2e4 | ||
|
|
ec99304c51 | ||
|
|
aefc931055 | ||
|
|
e7fe342379 | ||
|
|
f372999b1b | ||
|
|
2679975945 | ||
|
|
54dd7f2f8f | ||
|
|
aee5891c50 | ||
|
|
b81d3314ea | ||
|
|
4a3ffa5f90 | ||
|
|
a20c7529d6 | ||
|
|
baea5cb0d7 | ||
|
|
659674dd4a | ||
|
|
5c199a143b | ||
|
|
99ec814808 | ||
|
|
ea1f02798c | ||
|
|
e3d4cc5e45 | ||
|
|
c98eb5c42a | ||
|
|
d0392be2d8 | ||
|
|
8761629828 | ||
|
|
a3b258dfeb | ||
|
|
f7505effa1 | ||
|
|
40d6ccf804 | ||
|
|
31c41f8775 | ||
|
|
cf7daff040 | ||
|
|
8570b107da | ||
|
|
cf4f613a65 | ||
|
|
16248ef02f | ||
|
|
abeaa92112 | ||
|
|
4b9b4a22cc | ||
|
|
bd1ac0c01c | ||
|
|
670594d13f | ||
|
|
89441103b3 | ||
|
|
71021c61f7 | ||
|
|
d49c211882 | ||
|
|
f2bdc815b8 | ||
|
|
e89450c4c1 | ||
|
|
44d5227c14 | ||
|
|
a914b554d1 | ||
|
|
20382360ee | ||
|
|
ff26c0a4b9 | ||
|
|
6028032c89 | ||
|
|
8f3a9d0ede | ||
|
|
4400f0bb87 | ||
|
|
9f12ee642d | ||
|
|
b084b13ec3 | ||
|
|
93b3accbfd | ||
|
|
e96c3114d9 | ||
|
|
04f095018c | ||
|
|
ff18049832 | ||
|
|
5e7d2901c8 | ||
|
|
add7bc8ea5 | ||
|
|
f95853cc87 |
48
.github/workflows/build_and_test.yml
vendored
Normal file
48
.github/workflows/build_and_test.yml
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
name: Build and Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v5
|
||||
with:
|
||||
dotnet-version: |
|
||||
8.0.x
|
||||
9.0.x
|
||||
10.0.x
|
||||
|
||||
- name: Run tests
|
||||
run: dotnet test
|
||||
|
||||
- name: Run publish script
|
||||
run: ./publish-nix.sh
|
||||
|
||||
- name: Update rolling tag
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git tag -f rolling
|
||||
git push origin :refs/tags/rolling || true
|
||||
git push origin rolling --force
|
||||
|
||||
- name: Upload to rolling
|
||||
uses: ncipollo/release-action@v1.20.0
|
||||
with:
|
||||
allowUpdates: True
|
||||
artifacts: "*.nupkg,*.snupkg"
|
||||
body: "Last built commit: ${{ github.sha }}"
|
||||
name: "Rolling Release"
|
||||
prerelease: True
|
||||
replacesArtifacts: True
|
||||
tag: "rolling"
|
||||
updateOnlyUnreleased: True
|
||||
49
.github/workflows/build_nupkg.yml
vendored
49
.github/workflows/build_nupkg.yml
vendored
@@ -1,49 +0,0 @@
|
||||
name: Nuget Pack
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 9.0.x
|
||||
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
|
||||
- name: Build library
|
||||
run: dotnet build
|
||||
|
||||
- name: Run tests
|
||||
run: dotnet test
|
||||
|
||||
- name: Pack
|
||||
run: dotnet pack
|
||||
|
||||
- name: Upload build
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: 'Nuget Package'
|
||||
path: 'SabreTools.IO/bin/Release/*.nupkg'
|
||||
|
||||
- name: Upload to rolling
|
||||
uses: ncipollo/release-action@v1.14.0
|
||||
with:
|
||||
allowUpdates: True
|
||||
artifacts: 'SabreTools.IO/bin/Release/*.nupkg'
|
||||
body: 'Last built commit: ${{ github.sha }}'
|
||||
name: 'Rolling Release'
|
||||
prerelease: True
|
||||
replacesArtifacts: True
|
||||
tag: "rolling"
|
||||
updateOnlyUnreleased: True
|
||||
31
.github/workflows/check_pr.yml
vendored
31
.github/workflows/check_pr.yml
vendored
@@ -3,18 +3,21 @@ name: Build PR
|
||||
on: [pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 9.0.x
|
||||
|
||||
- name: Build
|
||||
run: dotnet build
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Run tests
|
||||
run: dotnet test
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v5
|
||||
with:
|
||||
dotnet-version: |
|
||||
8.0.x
|
||||
9.0.x
|
||||
10.0.x
|
||||
|
||||
- name: Build
|
||||
run: dotnet build
|
||||
|
||||
- name: Run tests
|
||||
run: dotnet test
|
||||
|
||||
7
LICENSE
Normal file
7
LICENSE
Normal file
@@ -0,0 +1,7 @@
|
||||
Copyright (c) 2018-2025 Matt Nadareski
|
||||
|
||||
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 (including the next paragraph) 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.
|
||||
12
README.MD
12
README.MD
@@ -1,11 +1,11 @@
|
||||
# SabreTools.IO
|
||||
|
||||
This library comprises I/O functionality for the following file types:
|
||||
[](https://github.com/SabreTools/SabreTools.IO/actions/workflows/build_and_test.yml)
|
||||
|
||||
- ClrMamePro-derived Metadata files
|
||||
- Standard and non-standard INI files
|
||||
- Separated-Value files (e.g. CSV, SSV, TSV)
|
||||
This set of libraries enable special I/O-focused functionality used by other SabreTools projects. See the README files in each project directory for more details.
|
||||
|
||||
There are also some extensions that are useful for wrapping common functionality required by SabreTools.
|
||||
## Releases
|
||||
|
||||
Find the link to the Nuget package [here](https://www.nuget.org/packages/SabreTools.IO).
|
||||
For the most recent stable build, download the latest release here: [Releases Page](https://github.com/SabreTools/SabreTools.IO/releases)
|
||||
|
||||
For the latest WIP build here: [Rolling Release](https://github.com/SabreTools/SabreTools.IO/releases/rolling)
|
||||
|
||||
41
SabreTools.IO.Test/Compression/BZip2Tests.cs
Normal file
41
SabreTools.IO.Test/Compression/BZip2Tests.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SabreTools.IO.Compression.BZip2;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.IO.Test.Compression
|
||||
{
|
||||
public class BZip2Tests
|
||||
{
|
||||
[Fact]
|
||||
public void BZip2InputStreamTest()
|
||||
{
|
||||
string path = Path.Combine(Environment.CurrentDirectory, "TestData", "test-archive.bz2");
|
||||
Stream input = File.OpenRead(path);
|
||||
byte[] output = new byte[1024];
|
||||
|
||||
var bzip = new BZip2InputStream(input);
|
||||
int actual = bzip.Read(output, 0, output.Length);
|
||||
bzip.Close();
|
||||
|
||||
Assert.Equal(125, actual);
|
||||
string str = Encoding.UTF8.GetString(output, 0, 125);
|
||||
Assert.Equal("This is just a file that has a known set of hashes to make sure that everything with hashing is still working as anticipated.", str);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BZip2OutputStreamTest()
|
||||
{
|
||||
string path = Path.Combine(Environment.CurrentDirectory, "TestData", "file-to-compress.bin");
|
||||
byte[] input = File.ReadAllBytes(path);
|
||||
var output = new MemoryStream();
|
||||
|
||||
var bzip = new BZip2OutputStream(output, leaveOpen: true);
|
||||
bzip.Write(input, 0, input.Length);
|
||||
bzip.Close();
|
||||
|
||||
Assert.Equal(122, output.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
27
SabreTools.IO.Test/Compression/BlastTests.cs
Normal file
27
SabreTools.IO.Test/Compression/BlastTests.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SabreTools.IO.Compression.Blast;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.IO.Test.Compression
|
||||
{
|
||||
public class BlastTests
|
||||
{
|
||||
[Fact]
|
||||
public void DecompressorTest()
|
||||
{
|
||||
string path = Path.Combine(Environment.CurrentDirectory, "TestData", "test-archive.pk");
|
||||
byte[] input = File.ReadAllBytes(path);
|
||||
MemoryStream output = new MemoryStream();
|
||||
|
||||
var decompressor = Decompressor.Create();
|
||||
decompressor.CopyTo(input, output);
|
||||
|
||||
Assert.Equal(13, output.Length);
|
||||
byte[] bytes = output.ToArray();
|
||||
string str = Encoding.ASCII.GetString(bytes);
|
||||
Assert.Equal("AIAIAIAIAIAIA", str);
|
||||
}
|
||||
}
|
||||
}
|
||||
31
SabreTools.IO.Test/Compression/MSZIPTests.cs
Normal file
31
SabreTools.IO.Test/Compression/MSZIPTests.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SabreTools.IO.Compression.MSZIP;
|
||||
using SabreTools.IO.Extensions;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.IO.Test.Compression
|
||||
{
|
||||
public class MSZIPTests
|
||||
{
|
||||
[Fact]
|
||||
public void DecompressorTest()
|
||||
{
|
||||
// Testing Note: This is a fake file that has multiple blocks
|
||||
// sequentially. In real cabinet files, these are embedded in
|
||||
// CFDATA blocks.
|
||||
string path = Path.Combine(Environment.CurrentDirectory, "TestData", "test-archive.msz");
|
||||
byte[] inputBytes = File.ReadAllBytes(path);
|
||||
var input = new MemoryStream(inputBytes);
|
||||
var output = new MemoryStream();
|
||||
|
||||
var decompressor = Decompressor.Create();
|
||||
input.SeekIfPossible(0x0000);
|
||||
decompressor.CopyTo(input, output);
|
||||
input.SeekIfPossible(0x3969);
|
||||
decompressor.CopyTo(input, output);
|
||||
|
||||
Assert.Equal(38470, output.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
29
SabreTools.IO.Test/Compression/QuantumTests.cs
Normal file
29
SabreTools.IO.Test/Compression/QuantumTests.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SabreTools.IO.Compression.Quantum;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.IO.Test.Compression
|
||||
{
|
||||
public class QuantumTests
|
||||
{
|
||||
// This test is disabled for the forseeable future. The current Quantum
|
||||
// processing code only handles standalone Quantum archives in theory.
|
||||
// There is additional work that needs to be done to support MS-CAB padding
|
||||
// before this test will pass properly.
|
||||
|
||||
//[Fact]
|
||||
public void DecompressorTest()
|
||||
{
|
||||
// Testing Note: This is a fake file that has been taken
|
||||
// from the CFDATA block of a cabinet file.
|
||||
string path = Path.Combine(Environment.CurrentDirectory, "TestData", "test-archive.qtm");
|
||||
byte[] inputBytes = File.ReadAllBytes(path);
|
||||
|
||||
var decompressor = Decompressor.Create(inputBytes, 19);
|
||||
byte[] output = decompressor.Process();
|
||||
|
||||
Assert.Equal(38470, output.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,7 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
#if NET7_0_OR_GREATER
|
||||
using System.Numerics;
|
||||
#endif
|
||||
using System.Text;
|
||||
using SabreTools.IO.Extensions;
|
||||
using Xunit;
|
||||
@@ -35,17 +33,29 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(1).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(1)];
|
||||
bw.Write((byte)0x00);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteByteBothEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = [.. _bytes.Take(2)];
|
||||
|
||||
int offset = 0;
|
||||
bw.WriteBothEndian(_bytes.ReadByteBothEndian(ref offset));
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteBytesTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(4)];
|
||||
bw.Write([0x00, 0x01, 0x02, 0x03]);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
@@ -55,7 +65,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(4)];
|
||||
bw.WriteBigEndian([0x03, 0x02, 0x01, 0x00]);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
@@ -65,17 +75,29 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(1).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(1)];
|
||||
bw.Write((sbyte)0x00);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteSByteBothEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = [.. _bytes.Take(2)];
|
||||
|
||||
int offset = 0;
|
||||
bw.WriteBothEndian(_bytes.ReadSByteBothEndian(ref offset));
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteCharTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(1).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(1)];
|
||||
bw.Write('\0');
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
@@ -95,7 +117,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(2).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(2)];
|
||||
bw.Write((short)0x0100);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
@@ -105,18 +127,30 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(2).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(2)];
|
||||
bool write = bw.WriteBigEndian((short)0x0001);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteInt16BothEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = [.. _bytes.Take(4)];
|
||||
|
||||
int offset = 0;
|
||||
bw.WriteBothEndian(_bytes.ReadInt16BothEndian(ref offset));
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt16Test()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(2).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(2)];
|
||||
bw.Write((ushort)0x0100);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
@@ -126,19 +160,30 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(2).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(2)];
|
||||
bool write = bw.WriteBigEndian((ushort)0x0001);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
[Fact]
|
||||
public void WriteUInt16BothEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = [.. _bytes.Take(4)];
|
||||
|
||||
int offset = 0;
|
||||
bw.WriteBothEndian(_bytes.ReadUInt16BothEndian(ref offset));
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteHalfTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(2).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(2)];
|
||||
bw.Write(BitConverter.Int16BitsToHalf(0x0100));
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
@@ -148,19 +193,18 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(2).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(2)];
|
||||
bool write = bw.WriteBigEndian(BitConverter.Int16BitsToHalf(0x0001));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
#endif
|
||||
|
||||
[Fact]
|
||||
public void WriteInt24Test()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(3).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(3)];
|
||||
bw.WriteAsInt24(0x020100);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
@@ -170,7 +214,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(3).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(3)];
|
||||
bool write = bw.WriteAsInt24BigEndian(0x000102);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
@@ -181,7 +225,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(3).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(3)];
|
||||
bw.WriteAsUInt24(0x020100);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
@@ -191,7 +235,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(3).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(3)];
|
||||
bool write = bw.WriteAsUInt24BigEndian(0x000102);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
@@ -202,7 +246,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(4)];
|
||||
bw.Write(0x03020100);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
@@ -212,18 +256,30 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(4)];
|
||||
bool write = bw.WriteBigEndian(0x00010203);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteInt32BothEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = [.. _bytes.Take(8)];
|
||||
|
||||
int offset = 0;
|
||||
bw.WriteBothEndian(_bytes.ReadInt32BothEndian(ref offset));
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt32Test()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(4)];
|
||||
bw.Write((uint)0x03020100);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
@@ -233,18 +289,30 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(4)];
|
||||
bool write = bw.WriteBigEndian((uint)0x00010203);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt32BothEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = [.. _bytes.Take(8)];
|
||||
|
||||
int offset = 0;
|
||||
bw.WriteBothEndian(_bytes.ReadUInt32BothEndian(ref offset));
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteSingleTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(4)];
|
||||
bw.Write(BitConverter.Int32BitsToSingle(0x03020100));
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
@@ -254,7 +322,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(4)];
|
||||
bool write = bw.WriteBigEndian(BitConverter.Int32BitsToSingle(0x00010203));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
@@ -265,7 +333,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(6).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(6)];
|
||||
bw.WriteAsInt48(0x050403020100);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
@@ -275,7 +343,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(6).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(6)];
|
||||
bool write = bw.WriteAsInt48BigEndian(0x000102030405);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
@@ -286,7 +354,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(6).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(6)];
|
||||
bw.WriteAsUInt48(0x050403020100);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
@@ -296,7 +364,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(6).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(6)];
|
||||
bool write = bw.WriteAsUInt48BigEndian(0x000102030405);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
@@ -307,7 +375,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(8).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(8)];
|
||||
bw.Write(0x0706050403020100);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
@@ -317,18 +385,30 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(8).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(8)];
|
||||
bool write = bw.WriteBigEndian(0x0001020304050607);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteInt64BothEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = [.. _bytes.Take(16)];
|
||||
|
||||
int offset = 0;
|
||||
bw.WriteBothEndian(_bytes.ReadInt64BothEndian(ref offset));
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt64Test()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(8).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(8)];
|
||||
bw.Write((ulong)0x0706050403020100);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
@@ -338,18 +418,30 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(8).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(8)];
|
||||
bool write = bw.WriteBigEndian((ulong)0x0001020304050607);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt64BothEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = [.. _bytes.Take(16)];
|
||||
|
||||
int offset = 0;
|
||||
bw.WriteBothEndian(_bytes.ReadUInt64BothEndian(ref offset));
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteDoubleTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(8).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(8)];
|
||||
bw.Write(BitConverter.Int64BitsToDouble(0x0706050403020100));
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
@@ -359,7 +451,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(8).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(8)];
|
||||
bool write = bw.WriteBigEndian(BitConverter.Int64BitsToDouble(0x0001020304050607));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
@@ -370,7 +462,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _decimalBytes.Take(16).ToArray();
|
||||
byte[] expected = [.. _decimalBytes.Take(16)];
|
||||
bw.Write(0.0123456789M);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
@@ -380,7 +472,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _decimalBytes.Take(16).Reverse().ToArray();
|
||||
byte[] expected = [.. _decimalBytes.Take(16).Reverse()];
|
||||
bool write = bw.WriteBigEndian(0.0123456789M);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
@@ -391,7 +483,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(16).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(16)];
|
||||
bool write = bw.Write(new Guid(_bytes));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
@@ -402,19 +494,18 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(16).ToArray();
|
||||
bool write = bw.WriteBigEndian(new Guid(_bytes.Reverse().ToArray()));
|
||||
byte[] expected = [.. _bytes.Take(16)];
|
||||
bool write = bw.WriteBigEndian(new Guid([.. Enumerable.Reverse(_bytes)]));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
#if NET7_0_OR_GREATER
|
||||
[Fact]
|
||||
public void WriteInt128Test()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(16).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(16)];
|
||||
bool write = bw.Write((Int128)new BigInteger(_bytes));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
@@ -425,8 +516,8 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(16).ToArray();
|
||||
bool write = bw.WriteBigEndian((Int128)new BigInteger(_bytes.Reverse().ToArray()));
|
||||
byte[] expected = [.. _bytes.Take(16)];
|
||||
bool write = bw.WriteBigEndian((Int128)new BigInteger(Enumerable.Reverse(_bytes).ToArray()));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
@@ -436,7 +527,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(16).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(16)];
|
||||
bool write = bw.Write((UInt128)new BigInteger(_bytes));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
@@ -447,12 +538,11 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(16).ToArray();
|
||||
bool write = bw.WriteBigEndian((UInt128)new BigInteger(_bytes.Reverse().ToArray()));
|
||||
byte[] expected = [.. _bytes.Take(16)];
|
||||
bool write = bw.WriteBigEndian((UInt128)new BigInteger(Enumerable.Reverse(_bytes).ToArray()));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
#endif
|
||||
|
||||
[Fact]
|
||||
public void WriteNullTerminatedAnsiStringTest()
|
||||
@@ -536,16 +626,13 @@ namespace SabreTools.IO.Test.Extensions
|
||||
Assert.True(actual);
|
||||
ValidateBytes(_bytes, stream.GetBuffer());
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
// Half
|
||||
stream = new MemoryStream(new byte[2], 0, 2, true, true);
|
||||
bw = new BinaryWriter(stream);
|
||||
actual = bw.WriteType<Half>(BitConverter.Int16BitsToHalf(0x0100));
|
||||
Assert.True(actual);
|
||||
ValidateBytes([.. _bytes.Take(2)], stream.GetBuffer());
|
||||
#endif
|
||||
|
||||
#if NET7_0_OR_GREATER
|
||||
// Int128
|
||||
stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
bw = new BinaryWriter(stream);
|
||||
@@ -559,7 +646,6 @@ namespace SabreTools.IO.Test.Extensions
|
||||
actual = bw.WriteType<UInt128>((UInt128)new BigInteger(_bytes));
|
||||
Assert.True(actual);
|
||||
ValidateBytes(_bytes, stream.GetBuffer());
|
||||
#endif
|
||||
|
||||
// Enum
|
||||
stream = new MemoryStream(new byte[4], 0, 4, true, true);
|
||||
@@ -586,7 +672,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
SecondValue = 0x07060504,
|
||||
FifthValue = "ABC",
|
||||
};
|
||||
byte[] expected = bytesWithString.Take(12).ToArray();
|
||||
byte[] expected = [.. bytesWithString.Take(12)];
|
||||
bool write = bw.WriteType(obj);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
@@ -611,7 +697,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
FourthValue = 0x0B0A,
|
||||
FifthValue = "ABC",
|
||||
};
|
||||
byte[] expected = bytesWithString.Take(16).ToArray();
|
||||
byte[] expected = [.. bytesWithString.Take(16)];
|
||||
bool write = bw.WriteType(obj);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
@@ -628,4 +714,4 @@ namespace SabreTools.IO.Test.Extensions
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using SabreTools.IO.Extensions;
|
||||
using Xunit;
|
||||
|
||||
@@ -7,7 +8,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
public class ByteArrayExtensionsTests
|
||||
{
|
||||
#region Is Null or Empty
|
||||
#region IsNullOrEmpty
|
||||
|
||||
[Fact]
|
||||
public void IsNullOrEmpty_Null_True()
|
||||
@@ -35,7 +36,511 @@ namespace SabreTools.IO.Test.Extensions
|
||||
|
||||
#endregion
|
||||
|
||||
#region To Hex String
|
||||
#region IsNumericArray
|
||||
|
||||
[Fact]
|
||||
public void IsNumericArray_Empty_False()
|
||||
{
|
||||
byte[] arr = [];
|
||||
bool actual = arr.IsNumericArray();
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsNumericArray_NonNumeric_False()
|
||||
{
|
||||
byte[] arr = Encoding.ASCII.GetBytes("ABCDEF");
|
||||
bool actual = arr.IsNumericArray();
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsNumericArray_MixedNumeric_False()
|
||||
{
|
||||
byte[] arr = Encoding.ASCII.GetBytes("ABC123");
|
||||
bool actual = arr.IsNumericArray();
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsNumericArray_Numeric_True()
|
||||
{
|
||||
byte[] arr = Encoding.ASCII.GetBytes("0123456789");
|
||||
bool actual = arr.IsNumericArray();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region FindAllPositions
|
||||
|
||||
[Fact]
|
||||
public void FindAllPositions_EmptyStack_NoMatches()
|
||||
{
|
||||
byte[] stack = [];
|
||||
var positions = stack.FindAllPositions([0x01]);
|
||||
Assert.Empty(positions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindAllPositions_EmptyNeedle_NoMatches()
|
||||
{
|
||||
byte[] stack = [0x01];
|
||||
var positions = stack.FindAllPositions(Array.Empty<byte>());
|
||||
Assert.Empty(positions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindAllPositions_LongerNeedle_NoMatches()
|
||||
{
|
||||
byte[] stack = [0x01];
|
||||
var positions = stack.FindAllPositions([0x01, 0x02]);
|
||||
Assert.Empty(positions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindAllPositions_InvalidStart_NoMatches()
|
||||
{
|
||||
byte[] stack = [0x01];
|
||||
var positions = stack.FindAllPositions([0x01, 0x02], start: -1);
|
||||
Assert.Empty(positions);
|
||||
|
||||
positions = stack.FindAllPositions([0x01, 0x02], start: 2);
|
||||
Assert.Empty(positions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindAllPositions_InvalidEnd_NoMatches()
|
||||
{
|
||||
byte[] stack = [0x01];
|
||||
var positions = stack.FindAllPositions([0x01, 0x02], end: -2);
|
||||
Assert.Empty(positions);
|
||||
|
||||
positions = stack.FindAllPositions([0x01, 0x02], end: 0);
|
||||
Assert.Empty(positions);
|
||||
|
||||
positions = stack.FindAllPositions([0x01, 0x02], end: 2);
|
||||
Assert.Empty(positions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindAllPositions_Matching_Matches()
|
||||
{
|
||||
byte[] stack = [0x01, 0x02];
|
||||
var positions = stack.FindAllPositions([0x01, 0x02]);
|
||||
int position = Assert.Single(positions);
|
||||
Assert.Equal(0, position);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindAllPositions_Mismatch_NoMatches()
|
||||
{
|
||||
byte[] stack = [0x01, 0x03];
|
||||
var positions = stack.FindAllPositions([0x01, 0x02]);
|
||||
Assert.Empty(positions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindAllPositions_Multiple_Matches()
|
||||
{
|
||||
byte[] stack = [0x01, 0x01];
|
||||
var positions = stack.FindAllPositions([0x01]);
|
||||
Assert.Equal(2, positions.Count);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region FirstPosition
|
||||
|
||||
[Fact]
|
||||
public void FirstPosition_EmptyStack_NoMatches()
|
||||
{
|
||||
byte[] stack = [];
|
||||
int position = stack.FirstPosition([0x01]);
|
||||
Assert.Equal(-1, position);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FirstPosition_EmptyNeedle_NoMatches()
|
||||
{
|
||||
byte[] stack = [0x01];
|
||||
int position = stack.FirstPosition(Array.Empty<byte>());
|
||||
Assert.Equal(-1, position);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FirstPosition_LongerNeedle_NoMatches()
|
||||
{
|
||||
byte[] stack = [0x01];
|
||||
int position = stack.FirstPosition([0x01, 0x02]);
|
||||
Assert.Equal(-1, position);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FirstPosition_InvalidStart_NoMatches()
|
||||
{
|
||||
byte[] stack = [0x01];
|
||||
int position = stack.FirstPosition([0x01, 0x02], start: -1);
|
||||
Assert.Equal(-1, position);
|
||||
|
||||
position = stack.FirstPosition([0x01, 0x02], start: 2);
|
||||
Assert.Equal(-1, position);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FirstPosition_InvalidEnd_NoMatches()
|
||||
{
|
||||
byte[] stack = [0x01];
|
||||
int position = stack.FirstPosition([0x01, 0x02], end: -2);
|
||||
Assert.Equal(-1, position);
|
||||
|
||||
position = stack.FirstPosition([0x01, 0x02], end: 0);
|
||||
Assert.Equal(-1, position);
|
||||
|
||||
position = stack.FirstPosition([0x01, 0x02], end: 2);
|
||||
Assert.Equal(-1, position);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FirstPosition_Matching_Matches()
|
||||
{
|
||||
byte[] stack = [0x01, 0x02];
|
||||
int position = stack.FirstPosition([0x01, 0x02]);
|
||||
Assert.Equal(0, position);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FirstPosition_Mismatch_NoMatches()
|
||||
{
|
||||
byte[] stack = [0x01, 0x03];
|
||||
int position = stack.FirstPosition([0x01, 0x02]);
|
||||
Assert.Equal(-1, position);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FirstPosition_Multiple_Matches()
|
||||
{
|
||||
byte[] stack = [0x01, 0x01];
|
||||
int position = stack.FirstPosition([0x01]);
|
||||
Assert.Equal(0, position);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region LastPosition
|
||||
|
||||
[Fact]
|
||||
public void LastPosition_EmptyStack_NoMatches()
|
||||
{
|
||||
byte[] stack = [];
|
||||
int position = stack.LastPosition([0x01]);
|
||||
Assert.Equal(-1, position);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LastPosition_EmptyNeedle_NoMatches()
|
||||
{
|
||||
byte[] stack = [0x01];
|
||||
int position = stack.LastPosition(Array.Empty<byte>());
|
||||
Assert.Equal(-1, position);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LastPosition_LongerNeedle_NoMatches()
|
||||
{
|
||||
byte[] stack = [0x01];
|
||||
int position = stack.LastPosition([0x01, 0x02]);
|
||||
Assert.Equal(-1, position);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LastPosition_InvalidStart_NoMatches()
|
||||
{
|
||||
byte[] stack = [0x01];
|
||||
int position = stack.LastPosition([0x01, 0x02], start: -1);
|
||||
Assert.Equal(-1, position);
|
||||
|
||||
position = stack.LastPosition([0x01, 0x02], start: 2);
|
||||
Assert.Equal(-1, position);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LastPosition_InvalidEnd_NoMatches()
|
||||
{
|
||||
byte[] stack = [0x01];
|
||||
int position = stack.LastPosition([0x01, 0x02], end: -2);
|
||||
Assert.Equal(-1, position);
|
||||
|
||||
position = stack.LastPosition([0x01, 0x02], end: 0);
|
||||
Assert.Equal(-1, position);
|
||||
|
||||
position = stack.LastPosition([0x01, 0x02], end: 2);
|
||||
Assert.Equal(-1, position);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LastPosition_Matching_Matches()
|
||||
{
|
||||
byte[] stack = [0x01, 0x02];
|
||||
int position = stack.LastPosition([0x01, 0x02]);
|
||||
Assert.Equal(0, position);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LastPosition_Mismatch_NoMatches()
|
||||
{
|
||||
byte[] stack = [0x01, 0x03];
|
||||
int position = stack.LastPosition([0x01, 0x02]);
|
||||
Assert.Equal(-1, position);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LastPosition_Multiple_Matches()
|
||||
{
|
||||
byte[] stack = [0x01, 0x01];
|
||||
int position = stack.LastPosition([0x01]);
|
||||
Assert.Equal(1, position);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region EqualsExactly
|
||||
|
||||
[Fact]
|
||||
public void EqualsExactly_EmptyStack_NoMatches()
|
||||
{
|
||||
byte[] stack = [];
|
||||
bool found = stack.EqualsExactly([0x01]);
|
||||
Assert.False(found);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EqualsExactly_EmptyNeedle_NoMatches()
|
||||
{
|
||||
byte[] stack = [0x01];
|
||||
bool found = stack.EqualsExactly(Array.Empty<byte>());
|
||||
Assert.False(found);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EqualsExactly_ShorterNeedle_NoMatches()
|
||||
{
|
||||
byte[] stack = [0x01, 0x02];
|
||||
bool found = stack.EqualsExactly([0x01]);
|
||||
Assert.False(found);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EqualsExactly_LongerNeedle_NoMatches()
|
||||
{
|
||||
byte[] stack = [0x01];
|
||||
bool found = stack.EqualsExactly([0x01, 0x02]);
|
||||
Assert.False(found);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EqualsExactly_Matching_Matches()
|
||||
{
|
||||
byte[] stack = [0x01, 0x02];
|
||||
bool found = stack.EqualsExactly([0x01, 0x02]);
|
||||
Assert.True(found);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EqualsExactly_Mismatch_NoMatches()
|
||||
{
|
||||
byte[] stack = [0x01, 0x03];
|
||||
bool found = stack.EqualsExactly([0x01, 0x02]);
|
||||
Assert.False(found);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region StartsWith
|
||||
|
||||
[Fact]
|
||||
public void StartsWith_EmptyStack_NoMatches()
|
||||
{
|
||||
byte[] stack = [];
|
||||
bool found = stack.StartsWith([0x01]);
|
||||
Assert.False(found);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StartsWith_EmptyNeedle_NoMatches()
|
||||
{
|
||||
byte[] stack = [0x01];
|
||||
bool found = stack.StartsWith(Array.Empty<byte>());
|
||||
Assert.False(found);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StartsWith_LongerNeedle_NoMatches()
|
||||
{
|
||||
byte[] stack = [0x01];
|
||||
bool found = stack.StartsWith([0x01, 0x02]);
|
||||
Assert.False(found);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StartsWith_Matching_Matches()
|
||||
{
|
||||
byte[] stack = [0x01, 0x02];
|
||||
bool found = stack.StartsWith([0x01, 0x02]);
|
||||
Assert.True(found);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StartsWith_Mismatch_NoMatches()
|
||||
{
|
||||
byte[] stack = [0x01, 0x03];
|
||||
bool found = stack.StartsWith([0x01, 0x02]);
|
||||
Assert.False(found);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StartsWith_Multiple_Matches()
|
||||
{
|
||||
byte[] stack = [0x01, 0x01];
|
||||
bool found = stack.StartsWith([0x01]);
|
||||
Assert.True(found);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region EndsWith
|
||||
|
||||
[Fact]
|
||||
public void EndsWith_EmptyStack_NoMatches()
|
||||
{
|
||||
byte[] stack = [];
|
||||
bool found = stack.EndsWith([0x01]);
|
||||
Assert.False(found);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EndsWith_EmptyNeedle_NoMatches()
|
||||
{
|
||||
byte[] stack = [0x01];
|
||||
bool found = stack.EndsWith(Array.Empty<byte>());
|
||||
Assert.False(found);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EndsWith_LongerNeedle_NoMatches()
|
||||
{
|
||||
byte[] stack = [0x01];
|
||||
bool found = stack.StartsWith([0x01, 0x02]);
|
||||
Assert.False(found);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EndsWith_Matching_Matches()
|
||||
{
|
||||
byte[] stack = [0x01, 0x02];
|
||||
bool found = stack.EndsWith([0x01, 0x02]);
|
||||
Assert.True(found);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EndsWith_Mismatch_NoMatches()
|
||||
{
|
||||
byte[] stack = [0x01, 0x03];
|
||||
bool found = stack.EndsWith([0x01, 0x02]);
|
||||
Assert.False(found);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EndsWith_Multiple_Matches()
|
||||
{
|
||||
byte[] stack = [0x01, 0x01];
|
||||
bool found = stack.EndsWith([0x01]);
|
||||
Assert.True(found);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Add
|
||||
|
||||
[Theory]
|
||||
[InlineData(new byte[0], 0, new byte[0])]
|
||||
[InlineData(new byte[0], 1234, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xD2 })]
|
||||
[InlineData(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xD2 }, 0, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xD2 })]
|
||||
[InlineData(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xD2 }, 1234, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0xA4 })]
|
||||
public void Add_NumericInput(byte[] self, uint add, byte[] expected)
|
||||
{
|
||||
byte[] actual = self.Add(add);
|
||||
|
||||
Assert.Equal(expected.Length, actual.Length);
|
||||
if (actual.Length > 0)
|
||||
Assert.True(actual.EqualsExactly(expected));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(new byte[0], new byte[0], new byte[0])]
|
||||
[InlineData(new byte[0], new byte[] { 0x04, 0xD2 }, new byte[] { 0x04, 0xD2 })]
|
||||
[InlineData(new byte[] { 0x04, 0xD2 }, new byte[0], new byte[] { 0x04, 0xD2 })]
|
||||
[InlineData(new byte[] { 0x04, 0xD2 }, new byte[] { 0x00, 0x00 }, new byte[] { 0x04, 0xD2 })]
|
||||
[InlineData(new byte[] { 0x00, 0x00 }, new byte[] { 0x04, 0xD2 }, new byte[] { 0x04, 0xD2 })]
|
||||
[InlineData(new byte[] { 0x04, 0xD2 }, new byte[] { 0x04, 0xD2 }, new byte[] { 0x09, 0xA4 })]
|
||||
[InlineData(new byte[] { 0xAB, 0x04, 0xD2 }, new byte[] { 0x04, 0xD2 }, new byte[] { 0xAB, 0x09, 0xA4 })]
|
||||
[InlineData(new byte[] { 0x04, 0xD2 }, new byte[] { 0xAB, 0x04, 0xD2 }, new byte[] { 0xAB, 0x09, 0xA4 })]
|
||||
public void Add_ArrayInput(byte[] self, byte[] add, byte[] expected)
|
||||
{
|
||||
byte[] actual = self.Add(add);
|
||||
|
||||
Assert.Equal(expected.Length, actual.Length);
|
||||
if (actual.Length > 0)
|
||||
Assert.True(actual.EqualsExactly(expected));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region RotateLeft
|
||||
|
||||
[Theory]
|
||||
[InlineData(new byte[0], 0, new byte[0])]
|
||||
[InlineData(new byte[] { 0x01 }, 0, new byte[] { 0x01 })]
|
||||
[InlineData(new byte[] { 0x01 }, 1, new byte[] { 0x02 })]
|
||||
[InlineData(new byte[] { 0x80 }, 1, new byte[] { 0x01 })]
|
||||
[InlineData(new byte[] { 0x00, 0x01 }, 0, new byte[] { 0x00, 0x01 })]
|
||||
[InlineData(new byte[] { 0x00, 0x01 }, 1, new byte[] { 0x00, 0x02 })]
|
||||
[InlineData(new byte[] { 0x00, 0x80 }, 1, new byte[] { 0x01, 0x00 })]
|
||||
[InlineData(new byte[] { 0x80, 0x00 }, 1, new byte[] { 0x00, 0x01 })]
|
||||
public void RotateLeftTest(byte[] self, int numBits, byte[] expected)
|
||||
{
|
||||
byte[] actual = self.RotateLeft(numBits);
|
||||
|
||||
Assert.Equal(expected.Length, actual.Length);
|
||||
if (actual.Length > 0)
|
||||
Assert.True(actual.EqualsExactly(expected));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Xor
|
||||
|
||||
[Theory]
|
||||
[InlineData(new byte[0], new byte[0], new byte[0])]
|
||||
[InlineData(new byte[0], new byte[] { 0x04, 0xD2 }, new byte[] { 0x04, 0xD2 })]
|
||||
[InlineData(new byte[] { 0x04, 0xD2 }, new byte[0], new byte[] { 0x04, 0xD2 })]
|
||||
[InlineData(new byte[] { 0x04, 0xD2 }, new byte[] { 0x00, 0x00 }, new byte[] { 0x04, 0xD2 })]
|
||||
[InlineData(new byte[] { 0x00, 0x00 }, new byte[] { 0x04, 0xD2 }, new byte[] { 0x04, 0xD2 })]
|
||||
[InlineData(new byte[] { 0x04, 0xD2 }, new byte[] { 0x04, 0xD2 }, new byte[] { 0x00, 0x00 })]
|
||||
[InlineData(new byte[] { 0xAB, 0x04, 0xD2 }, new byte[] { 0x04, 0xD2 }, new byte[] { 0xAB, 0x00, 0x00 })]
|
||||
[InlineData(new byte[] { 0x04, 0xD2 }, new byte[] { 0xAB, 0x04, 0xD2 }, new byte[] { 0xAB, 0x00, 0x00 })]
|
||||
public void XorTest(byte[] self, byte[] add, byte[] expected)
|
||||
{
|
||||
byte[] actual = self.Xor(add);
|
||||
|
||||
Assert.Equal(expected.Length, actual.Length);
|
||||
if (actual.Length > 0)
|
||||
Assert.True(actual.EqualsExactly(expected));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ToHexString
|
||||
|
||||
[Fact]
|
||||
public void ToHexString_Null()
|
||||
@@ -58,7 +563,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
|
||||
#endregion
|
||||
|
||||
#region From Hex String
|
||||
#region FromHexString
|
||||
|
||||
[Fact]
|
||||
public void FromHexString_Null()
|
||||
@@ -88,5 +593,385 @@ namespace SabreTools.IO.Test.Extensions
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ReadStringsFrom
|
||||
|
||||
[Fact]
|
||||
public void ReadStringsFrom_Null_Null()
|
||||
{
|
||||
byte[]? arr = null;
|
||||
var actual = arr.ReadStringsFrom(3);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadStringsFrom_Empty_Null()
|
||||
{
|
||||
byte[]? arr = [];
|
||||
var actual = arr.ReadStringsFrom(3);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(-1)]
|
||||
[InlineData(0)]
|
||||
[InlineData(2048)]
|
||||
public void ReadStringsFrom_InvalidLimit_Empty(int charLimit)
|
||||
{
|
||||
byte[]? arr = new byte[1024];
|
||||
var actual = arr.ReadStringsFrom(charLimit);
|
||||
Assert.NotNull(actual);
|
||||
Assert.Empty(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadStringsFrom_NoValidStrings_Empty()
|
||||
{
|
||||
byte[]? arr = new byte[1024];
|
||||
var actual = arr.ReadStringsFrom(4);
|
||||
Assert.NotNull(actual);
|
||||
Assert.Empty(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadStringsFrom_AsciiStrings_Filled()
|
||||
{
|
||||
byte[]? arr =
|
||||
[
|
||||
.. Encoding.ASCII.GetBytes("TEST"),
|
||||
.. new byte[] { 0x00 },
|
||||
.. Encoding.ASCII.GetBytes("TWO"),
|
||||
.. new byte[] { 0x00 },
|
||||
.. Encoding.ASCII.GetBytes("DATA"),
|
||||
.. new byte[] { 0x00 },
|
||||
];
|
||||
var actual = arr.ReadStringsFrom(4);
|
||||
Assert.NotNull(actual);
|
||||
Assert.Equal(2, actual.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadStringsFrom_Latin1Strings_Filled()
|
||||
{
|
||||
byte[]? arr =
|
||||
[
|
||||
.. Encoding.Latin1.GetBytes("TEST"),
|
||||
.. new byte[] { 0x00 },
|
||||
.. Encoding.Latin1.GetBytes("TWO"),
|
||||
.. new byte[] { 0x00 },
|
||||
.. Encoding.Latin1.GetBytes("DATA"),
|
||||
.. new byte[] { 0x00 },
|
||||
];
|
||||
var actual = arr.ReadStringsFrom(4);
|
||||
Assert.NotNull(actual);
|
||||
Assert.Equal(2, actual.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadStringsFrom_UTF16_Filled()
|
||||
{
|
||||
byte[]? arr =
|
||||
[
|
||||
.. Encoding.Unicode.GetBytes("TEST"),
|
||||
.. new byte[] { 0x00 },
|
||||
.. Encoding.Unicode.GetBytes("TWO"),
|
||||
.. new byte[] { 0x00 },
|
||||
.. Encoding.Unicode.GetBytes("DATA"),
|
||||
.. new byte[] { 0x00 },
|
||||
];
|
||||
var actual = arr.ReadStringsFrom(4);
|
||||
Assert.NotNull(actual);
|
||||
Assert.Equal(2, actual.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadStringsFrom_Mixed_Filled()
|
||||
{
|
||||
byte[]? arr =
|
||||
[
|
||||
.. Encoding.ASCII.GetBytes("TEST1"),
|
||||
.. new byte[] { 0x00 },
|
||||
.. Encoding.ASCII.GetBytes("TWO1"),
|
||||
.. new byte[] { 0x00 },
|
||||
.. Encoding.ASCII.GetBytes("DATA1"),
|
||||
.. new byte[] { 0x00 },
|
||||
.. Encoding.Latin1.GetBytes("TEST2"),
|
||||
.. new byte[] { 0x00 },
|
||||
.. Encoding.Latin1.GetBytes("TWO2"),
|
||||
.. new byte[] { 0x00 },
|
||||
.. Encoding.Latin1.GetBytes("DATA2"),
|
||||
.. new byte[] { 0x00 },
|
||||
.. Encoding.Unicode.GetBytes("TEST3"),
|
||||
.. new byte[] { 0x00 },
|
||||
.. Encoding.Unicode.GetBytes("TWO3"),
|
||||
.. new byte[] { 0x00 },
|
||||
.. Encoding.Unicode.GetBytes("DATA3"),
|
||||
.. new byte[] { 0x00 },
|
||||
];
|
||||
var actual = arr.ReadStringsFrom(5);
|
||||
Assert.NotNull(actual);
|
||||
Assert.Equal(6, actual.Count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This test is here mainly for performance testing
|
||||
/// and should not be enabled unless there are changes
|
||||
/// to the core reading methods that need comparison.
|
||||
/// </summary>
|
||||
// [Fact]
|
||||
// public void ReadStringsFrom_Mixed_MASSIVE()
|
||||
// {
|
||||
// byte[]? arr =
|
||||
// [
|
||||
// .. Encoding.ASCII.GetBytes("TEST1"),
|
||||
// .. new byte[] { 0x00 },
|
||||
// .. Encoding.ASCII.GetBytes("TWO1"),
|
||||
// .. new byte[] { 0x00 },
|
||||
// .. Encoding.ASCII.GetBytes("DATA1"),
|
||||
// .. new byte[] { 0x00 },
|
||||
// .. Encoding.UTF8.GetBytes("TEST2"),
|
||||
// .. new byte[] { 0x00 },
|
||||
// .. Encoding.UTF8.GetBytes("TWO2"),
|
||||
// .. new byte[] { 0x00 },
|
||||
// .. Encoding.UTF8.GetBytes("DATA2"),
|
||||
// .. new byte[] { 0x00 },
|
||||
// .. Encoding.Unicode.GetBytes("TEST3"),
|
||||
// .. new byte[] { 0x00 },
|
||||
// .. Encoding.Unicode.GetBytes("TWO3"),
|
||||
// .. new byte[] { 0x00 },
|
||||
// .. Encoding.Unicode.GetBytes("DATA3"),
|
||||
// .. new byte[] { 0x00 },
|
||||
// ];
|
||||
// arr = [.. arr, .. arr, .. arr, .. arr];
|
||||
// arr = [.. arr, .. arr, .. arr, .. arr];
|
||||
// arr = [.. arr, .. arr, .. arr, .. arr];
|
||||
// arr = [.. arr, .. arr, .. arr, .. arr];
|
||||
// arr = [.. arr, .. arr, .. arr, .. arr];
|
||||
// arr = [.. arr, .. arr, .. arr, .. arr];
|
||||
// arr = [.. arr, .. arr, .. arr, .. arr];
|
||||
// arr = [.. arr, .. arr, .. arr, .. arr];
|
||||
// arr = [.. arr, .. arr, .. arr, .. arr];
|
||||
// arr = [.. arr, .. arr, .. arr, .. arr];
|
||||
// // arr = [.. arr, .. arr, .. arr, .. arr];
|
||||
// // arr = [.. arr, .. arr, .. arr, .. arr];
|
||||
|
||||
// var actual = arr.ReadStringsFrom(5);
|
||||
// Assert.NotNull(actual);
|
||||
// Assert.NotEmpty(actual);
|
||||
// }
|
||||
|
||||
#endregion
|
||||
|
||||
#region ReadStringsWithEncoding
|
||||
|
||||
[Fact]
|
||||
public void ReadStringsWithEncoding_Null_Empty()
|
||||
{
|
||||
byte[]? bytes = null;
|
||||
var actual = bytes.ReadStringsWithEncoding(1, Encoding.ASCII);
|
||||
Assert.Empty(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadStringsWithEncoding_Empty_Empty()
|
||||
{
|
||||
byte[]? bytes = [];
|
||||
var actual = bytes.ReadStringsWithEncoding(1, Encoding.ASCII);
|
||||
Assert.Empty(actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(-1)]
|
||||
[InlineData(0)]
|
||||
[InlineData(2048)]
|
||||
public void ReadStringsWithEncoding_InvalidLimit_Empty(int charLimit)
|
||||
{
|
||||
byte[]? bytes = new byte[1024];
|
||||
var actual = bytes.ReadStringsWithEncoding(charLimit, Encoding.ASCII);
|
||||
Assert.Empty(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadStringsWithEncoding_NoValidStrings_Empty()
|
||||
{
|
||||
byte[]? bytes = new byte[1024];
|
||||
var actual = bytes.ReadStringsWithEncoding(5, Encoding.ASCII);
|
||||
Assert.Empty(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadStringsWithEncoding_AsciiStrings_Filled()
|
||||
{
|
||||
byte[]? bytes =
|
||||
[
|
||||
.. Encoding.ASCII.GetBytes("TEST"),
|
||||
.. new byte[] { 0x00 },
|
||||
.. Encoding.ASCII.GetBytes("ONE"),
|
||||
.. new byte[] { 0x00 },
|
||||
.. Encoding.ASCII.GetBytes("TWO"),
|
||||
.. new byte[] { 0x00 },
|
||||
.. Encoding.ASCII.GetBytes("DATA"),
|
||||
.. new byte[] { 0x00 },
|
||||
];
|
||||
var actual = bytes.ReadStringsWithEncoding(4, Encoding.ASCII);
|
||||
Assert.Equal(2, actual.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadStringsWithEncoding_InvalidAsciiChars_Empty()
|
||||
{
|
||||
byte[]? arr =
|
||||
[
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
|
||||
.. Enumerable.Range(0x80, 0x80).Select(i => (byte)i),
|
||||
];
|
||||
var actual = arr.ReadStringsWithEncoding(1, Encoding.ASCII);
|
||||
Assert.NotNull(actual);
|
||||
Assert.Empty(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadStringsWithEncoding_Latin1_Filled()
|
||||
{
|
||||
byte[]? bytes =
|
||||
[
|
||||
.. Encoding.Latin1.GetBytes("TEST"),
|
||||
.. new byte[] { 0x00 },
|
||||
.. Encoding.Latin1.GetBytes("ONE"),
|
||||
.. new byte[] { 0x00 },
|
||||
.. Encoding.Latin1.GetBytes("TWO"),
|
||||
.. new byte[] { 0x00 },
|
||||
.. Encoding.Latin1.GetBytes("DATA"),
|
||||
.. new byte[] { 0x00 },
|
||||
];
|
||||
var actual = bytes.ReadStringsWithEncoding(4, Encoding.Latin1);
|
||||
Assert.Equal(2, actual.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadStringsWithEncoding_InvalidLatin1Chars_Empty()
|
||||
{
|
||||
byte[]? arr =
|
||||
[
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
|
||||
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
|
||||
0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
|
||||
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
|
||||
0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
|
||||
];
|
||||
var actual = arr.ReadStringsWithEncoding(1, Encoding.Latin1);
|
||||
Assert.NotNull(actual);
|
||||
Assert.Empty(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadStringsWithEncoding_UTF8_Filled()
|
||||
{
|
||||
byte[]? bytes =
|
||||
[
|
||||
.. Encoding.UTF8.GetBytes("TEST"),
|
||||
.. new byte[] { 0x00 },
|
||||
.. Encoding.UTF8.GetBytes("ONE"),
|
||||
.. new byte[] { 0x00 },
|
||||
.. Encoding.UTF8.GetBytes("TWO"),
|
||||
.. new byte[] { 0x00 },
|
||||
.. Encoding.UTF8.GetBytes("DATA"),
|
||||
.. new byte[] { 0x00 },
|
||||
];
|
||||
var actual = bytes.ReadStringsWithEncoding(4, Encoding.UTF8);
|
||||
Assert.Equal(2, actual.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadStringsWithEncoding_InvalidUTF8Chars_Empty()
|
||||
{
|
||||
byte[]? arr =
|
||||
[
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
|
||||
.. Enumerable.Range(0x80, 0x42).Select(i => (byte)i),
|
||||
0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC,
|
||||
0xFD, 0xFE, 0xFF,
|
||||
];
|
||||
var actual = arr.ReadStringsWithEncoding(1, Encoding.UTF8);
|
||||
Assert.NotNull(actual);
|
||||
Assert.Empty(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadStringsWithEncoding_UTF16_Filled()
|
||||
{
|
||||
byte[]? bytes =
|
||||
[
|
||||
.. Encoding.Unicode.GetBytes("TEST"),
|
||||
.. new byte[] { 0x00 },
|
||||
.. Encoding.Unicode.GetBytes("ONE"),
|
||||
.. new byte[] { 0x00 },
|
||||
.. Encoding.Unicode.GetBytes("TWO"),
|
||||
.. new byte[] { 0x00 },
|
||||
.. Encoding.Unicode.GetBytes("DATA"),
|
||||
.. new byte[] { 0x00 },
|
||||
];
|
||||
var actual = bytes.ReadStringsWithEncoding(4, Encoding.Unicode);
|
||||
Assert.Equal(2, actual.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadStringsWithEncoding_InvalidUTF16Chars_Empty()
|
||||
{
|
||||
byte[]? arr =
|
||||
[
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
|
||||
];
|
||||
var actual = arr.ReadStringsWithEncoding(1, Encoding.Unicode);
|
||||
Assert.NotNull(actual);
|
||||
Assert.Empty(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadStringsWithEncoding_UTF32_Filled()
|
||||
{
|
||||
byte[]? bytes =
|
||||
[
|
||||
.. Encoding.UTF32.GetBytes("TEST"),
|
||||
.. new byte[] { 0x00 },
|
||||
.. Encoding.UTF32.GetBytes("ONE"),
|
||||
.. new byte[] { 0x00 },
|
||||
.. Encoding.UTF32.GetBytes("TWO"),
|
||||
.. new byte[] { 0x00 },
|
||||
.. Encoding.UTF32.GetBytes("DATA"),
|
||||
.. new byte[] { 0x00 },
|
||||
];
|
||||
var actual = bytes.ReadStringsWithEncoding(4, Encoding.UTF32);
|
||||
Assert.Equal(2, actual.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadStringsWithEncoding_InvalidUTF32Chars_Empty()
|
||||
{
|
||||
byte[]? arr =
|
||||
[
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
|
||||
];
|
||||
var actual = arr.ReadStringsWithEncoding(1, Encoding.UTF32);
|
||||
Assert.NotNull(actual);
|
||||
Assert.Empty(actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,6 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
#if NET7_0_OR_GREATER
|
||||
using System.Numerics;
|
||||
#endif
|
||||
using System.Text;
|
||||
using SabreTools.IO.Extensions;
|
||||
using Xunit;
|
||||
@@ -34,18 +32,30 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(1).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(1)];
|
||||
bool write = buffer.Write(ref offset, (byte)0x00);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteByteBothEndianTest()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = [.. _bytes.Take(2)];
|
||||
|
||||
int readOffset = 0;
|
||||
buffer.WriteBothEndian(ref offset, _bytes.ReadByteBothEndian(ref readOffset));
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteBytesTest()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(4)];
|
||||
bool write = buffer.Write(ref offset, [0x00, 0x01, 0x02, 0x03]);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
@@ -56,7 +66,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(4)];
|
||||
bool write = buffer.WriteBigEndian(ref offset, [0x03, 0x02, 0x01, 0x00]);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
@@ -67,18 +77,30 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(1).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(1)];
|
||||
bool write = buffer.Write(ref offset, (sbyte)0x00);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteSByteBothEndianTest()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = [.. _bytes.Take(2)];
|
||||
|
||||
int readOffset = 0;
|
||||
buffer.WriteBothEndian(ref offset, _bytes.ReadSByteBothEndian(ref readOffset));
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteCharTest()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(1).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(1)];
|
||||
bool write = buffer.Write(ref offset, '\0');
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
@@ -100,7 +122,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(2).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(2)];
|
||||
bool write = buffer.Write(ref offset, (short)0x0100);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
@@ -111,18 +133,30 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(2).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(2)];
|
||||
bool write = buffer.WriteBigEndian(ref offset, (short)0x0001);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteInt16BothEndianTest()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = [.. _bytes.Take(4)];
|
||||
|
||||
int readOffset = 0;
|
||||
buffer.WriteBothEndian(ref offset, _bytes.ReadInt16BothEndian(ref readOffset));
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt16Test()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(2).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(2)];
|
||||
bool write = buffer.Write(ref offset, (ushort)0x0100);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
@@ -133,19 +167,30 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(2).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(2)];
|
||||
bool write = buffer.WriteBigEndian(ref offset, (ushort)0x0001);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
[Fact]
|
||||
public void WriteUInt16BothEndianTest()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = [.. _bytes.Take(4)];
|
||||
|
||||
int readOffset = 0;
|
||||
buffer.WriteBothEndian(ref offset, _bytes.ReadUInt16BothEndian(ref readOffset));
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteHalfTest()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(2).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(2)];
|
||||
bool write = buffer.Write(ref offset, BitConverter.Int16BitsToHalf(0x0100));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
@@ -156,19 +201,18 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(2).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(2)];
|
||||
bool write = buffer.WriteBigEndian(ref offset, BitConverter.Int16BitsToHalf(0x0001));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
[Fact]
|
||||
public void WriteInt24Test()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(3).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(3)];
|
||||
bool write = buffer.WriteAsInt24(ref offset, 0x020100);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
@@ -179,7 +223,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(3).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(3)];
|
||||
bool write = buffer.WriteAsInt24BigEndian(ref offset, 0x000102);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
@@ -190,7 +234,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(3).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(3)];
|
||||
bool write = buffer.WriteAsUInt24(ref offset, 0x020100);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
@@ -201,7 +245,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(3).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(3)];
|
||||
bool write = buffer.WriteAsUInt24BigEndian(ref offset, 0x000102);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
@@ -212,7 +256,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(4)];
|
||||
bool write = buffer.Write(ref offset, 0x03020100);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
@@ -223,18 +267,30 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(4)];
|
||||
bool write = buffer.WriteBigEndian(ref offset, 0x00010203);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteInt32BothEndianTest()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = [.. _bytes.Take(8)];
|
||||
|
||||
int readOffset = 0;
|
||||
buffer.WriteBothEndian(ref offset, _bytes.ReadInt32BothEndian(ref readOffset));
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt32Test()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(4)];
|
||||
bool write = buffer.Write(ref offset, (uint)0x03020100);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
@@ -245,18 +301,30 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(4)];
|
||||
bool write = buffer.WriteBigEndian(ref offset, (uint)0x00010203);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt32BothEndianTest()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = [.. _bytes.Take(8)];
|
||||
|
||||
int readOffset = 0;
|
||||
buffer.WriteBothEndian(ref offset, _bytes.ReadUInt32BothEndian(ref readOffset));
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteSingleTest()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(4)];
|
||||
bool write = buffer.Write(ref offset, BitConverter.Int32BitsToSingle(0x03020100));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
@@ -267,7 +335,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(4)];
|
||||
bool write = buffer.WriteBigEndian(ref offset, BitConverter.Int32BitsToSingle(0x00010203));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
@@ -278,7 +346,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(6).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(6)];
|
||||
bool write = buffer.WriteAsInt48(ref offset, 0x050403020100);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
@@ -289,7 +357,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(6).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(6)];
|
||||
bool write = buffer.WriteAsInt48BigEndian(ref offset, 0x000102030405);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
@@ -300,7 +368,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(6).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(6)];
|
||||
bool write = buffer.WriteAsUInt48(ref offset, 0x050403020100);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
@@ -311,7 +379,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(6).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(6)];
|
||||
bool write = buffer.WriteAsUInt48BigEndian(ref offset, 0x000102030405);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
@@ -322,7 +390,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(8).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(8)];
|
||||
bool write = buffer.Write(ref offset, 0x0706050403020100);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
@@ -333,18 +401,30 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(8).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(8)];
|
||||
bool write = buffer.WriteBigEndian(ref offset, 0x0001020304050607);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteInt64BothEndianTest()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = [.. _bytes.Take(16)];
|
||||
|
||||
int readOffset = 0;
|
||||
buffer.WriteBothEndian(ref offset, _bytes.ReadInt64BothEndian(ref readOffset));
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt64Test()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(8).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(8)];
|
||||
bool write = buffer.Write(ref offset, (ulong)0x0706050403020100);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
@@ -355,18 +435,30 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(8).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(8)];
|
||||
bool write = buffer.WriteBigEndian(ref offset, (ulong)0x0001020304050607);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt64BothEndianTest()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = [.. _bytes.Take(16)];
|
||||
|
||||
int readOffset = 0;
|
||||
buffer.WriteBothEndian(ref offset, _bytes.ReadUInt64BothEndian(ref readOffset));
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteDoubleTest()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(8).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(8)];
|
||||
bool write = buffer.Write(ref offset, BitConverter.Int64BitsToDouble(0x0706050403020100));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
@@ -377,7 +469,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(8).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(8)];
|
||||
bool write = buffer.WriteBigEndian(ref offset, BitConverter.Int64BitsToDouble(0x0001020304050607));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
@@ -388,7 +480,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _decimalBytes.Take(16).ToArray();
|
||||
byte[] expected = [.. _decimalBytes.Take(16)];
|
||||
bool write = buffer.Write(ref offset, 0.0123456789M);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
@@ -399,7 +491,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _decimalBytes.Take(16).Reverse().ToArray();
|
||||
byte[] expected = [.. _decimalBytes.Take(16).Reverse()];
|
||||
bool write = buffer.WriteBigEndian(ref offset, 0.0123456789M);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
@@ -410,7 +502,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(16).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(16)];
|
||||
bool write = buffer.Write(ref offset, new Guid(_bytes));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
@@ -421,19 +513,18 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(16).ToArray();
|
||||
bool write = buffer.WriteBigEndian(ref offset, new Guid(_bytes.Reverse().ToArray()));
|
||||
byte[] expected = [.. _bytes.Take(16)];
|
||||
bool write = buffer.WriteBigEndian(ref offset, new Guid([.. Enumerable.Reverse(_bytes)]));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
#if NET7_0_OR_GREATER
|
||||
[Fact]
|
||||
public void WriteInt128Test()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(16).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(16)];
|
||||
bool write = buffer.Write(ref offset, (Int128)new BigInteger(_bytes));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
@@ -444,8 +535,8 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(16).ToArray();
|
||||
bool write = buffer.WriteBigEndian(ref offset, (Int128)new BigInteger(_bytes.Reverse().ToArray()));
|
||||
byte[] expected = [.. _bytes.Take(16)];
|
||||
bool write = buffer.WriteBigEndian(ref offset, (Int128)new BigInteger(Enumerable.Reverse(_bytes).ToArray()));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
@@ -455,7 +546,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(16).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(16)];
|
||||
bool write = buffer.Write(ref offset, (UInt128)new BigInteger(_bytes));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
@@ -466,12 +557,11 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(16).ToArray();
|
||||
bool write = buffer.WriteBigEndian(ref offset, (UInt128)new BigInteger(_bytes.Reverse().ToArray()));
|
||||
byte[] expected = [.. _bytes.Take(16)];
|
||||
bool write = buffer.WriteBigEndian(ref offset, (UInt128)new BigInteger(Enumerable.Reverse(_bytes).ToArray()));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
[Fact]
|
||||
public void WriteNullTerminatedAnsiStringTest()
|
||||
@@ -555,16 +645,13 @@ namespace SabreTools.IO.Test.Extensions
|
||||
Assert.True(actual);
|
||||
ValidateBytes(_bytes, buffer);
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
// Half
|
||||
offset = 0;
|
||||
buffer = new byte[2];
|
||||
actual = buffer.WriteType<Half>(ref offset, BitConverter.Int16BitsToHalf(0x0100));
|
||||
Assert.True(actual);
|
||||
ValidateBytes([.. _bytes.Take(2)], buffer);
|
||||
#endif
|
||||
|
||||
#if NET7_0_OR_GREATER
|
||||
// Int128
|
||||
offset = 0;
|
||||
buffer = new byte[16];
|
||||
@@ -578,7 +665,6 @@ namespace SabreTools.IO.Test.Extensions
|
||||
actual = buffer.WriteType<UInt128>(ref offset, (UInt128)new BigInteger(_bytes));
|
||||
Assert.True(actual);
|
||||
ValidateBytes(_bytes, buffer);
|
||||
#endif
|
||||
|
||||
// Enum
|
||||
offset = 0;
|
||||
@@ -605,7 +691,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
SecondValue = 0x07060504,
|
||||
FifthValue = "ABC",
|
||||
};
|
||||
byte[] expected = bytesWithString.Take(12).ToArray();
|
||||
byte[] expected = [.. bytesWithString.Take(12)];
|
||||
bool write = buffer.WriteType(ref offset, obj);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
@@ -630,7 +716,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
FourthValue = 0x0B0A,
|
||||
FifthValue = "ABC",
|
||||
};
|
||||
byte[] expected = bytesWithString.Take(16).ToArray();
|
||||
byte[] expected = [.. bytesWithString.Take(16)];
|
||||
bool write = buffer.WriteType(ref offset, obj);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
@@ -647,4 +733,4 @@ namespace SabreTools.IO.Test.Extensions
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
86
SabreTools.IO.Test/Extensions/DictionaryExtensionsTests.cs
Normal file
86
SabreTools.IO.Test/Extensions/DictionaryExtensionsTests.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
using System.Collections.Generic;
|
||||
using SabreTools.IO.Extensions;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
public class DictionaryExtensionsTests
|
||||
{
|
||||
#region MergeWith
|
||||
|
||||
[Fact]
|
||||
public void MergeWith_EmptySource_EmptyOther_Empty()
|
||||
{
|
||||
Dictionary<string, List<string>> source = [];
|
||||
Dictionary<string, List<string>> other = [];
|
||||
|
||||
source.MergeWith(other);
|
||||
Assert.Empty(source);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MergeWith_EmptySource_EmptyKeyOther_Empty()
|
||||
{
|
||||
Dictionary<string, List<string>> source = [];
|
||||
Dictionary<string, List<string>> other = [];
|
||||
other.Add("key", []);
|
||||
|
||||
source.MergeWith(other);
|
||||
Assert.Empty(source);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MergeWith_EmptySource_FilledOther_Filled()
|
||||
{
|
||||
Dictionary<string, List<string>> source = [];
|
||||
Dictionary<string, List<string>> other = [];
|
||||
other.Add("key", ["value"]);
|
||||
|
||||
source.MergeWith(other);
|
||||
string key = Assert.Single(source.Keys);
|
||||
Assert.Equal("key", key);
|
||||
List<string> actual = source[key];
|
||||
string value = Assert.Single(actual);
|
||||
Assert.Equal("value", value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MergeWith_FilledSource_EmptyOther_Filled()
|
||||
{
|
||||
Dictionary<string, List<string>> source = [];
|
||||
source.Add("key", ["value"]);
|
||||
Dictionary<string, List<string>> other = [];
|
||||
|
||||
source.MergeWith(other);
|
||||
string key = Assert.Single(source.Keys);
|
||||
Assert.Equal("key", key);
|
||||
List<string> actual = source[key];
|
||||
string value = Assert.Single(actual);
|
||||
Assert.Equal("value", value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MergeWith_FilledSource_FilledOther_Filled()
|
||||
{
|
||||
Dictionary<string, List<string>> source = [];
|
||||
source.Add("key1", ["value1"]);
|
||||
Dictionary<string, List<string>> other = [];
|
||||
other.Add("key2", ["value2"]);
|
||||
|
||||
source.MergeWith(other);
|
||||
Assert.Equal(2, source.Keys.Count);
|
||||
|
||||
Assert.Contains("key1", source.Keys);
|
||||
List<string> actualKey1 = source["key1"];
|
||||
string value1 = Assert.Single(actualKey1);
|
||||
Assert.Equal("value1", value1);
|
||||
|
||||
Assert.Contains("key2", source.Keys);
|
||||
List<string> actualKey2 = source["key2"];
|
||||
string value2 = Assert.Single(actualKey2);
|
||||
Assert.Equal("value2", value2);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using SabreTools.IO.Extensions;
|
||||
using Xunit;
|
||||
|
||||
@@ -9,6 +10,22 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
public class EnumerableExtensionsTests
|
||||
{
|
||||
#region IterateWithAction
|
||||
|
||||
[Fact]
|
||||
public void IterateWithActionTest()
|
||||
{
|
||||
List<int> source = [1, 2, 3, 4];
|
||||
int actual = 0;
|
||||
|
||||
source.IterateWithAction(i => Interlocked.Add(ref actual, i));
|
||||
Assert.Equal(10, actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region SafeEnumerate
|
||||
|
||||
[Fact]
|
||||
public void SafeEnumerate_Empty()
|
||||
{
|
||||
@@ -60,8 +77,10 @@ namespace SabreTools.IO.Test.Extensions
|
||||
Assert.Equal(2, list.Count);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Fake enumerable that uses <see cref="ErrorEnumerator"/>
|
||||
/// Fake enumerable that uses <see cref="ErrorEnumerator"/>
|
||||
/// </summary>
|
||||
private class ErrorEnumerable : IEnumerable<string>
|
||||
{
|
||||
@@ -160,4 +179,4 @@ namespace SabreTools.IO.Test.Extensions
|
||||
IEnumerator IEnumerable.GetEnumerator() => throw new Exception();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,7 +186,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
var dirs = dir.SafeGetDirectories("*", SearchOption.AllDirectories);
|
||||
Assert.Single(dirs);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void SafeGetFiles_ValidDirectory()
|
||||
{
|
||||
@@ -258,7 +258,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
var dirs = dir.SafeEnumerateDirectories("*", SearchOption.AllDirectories);
|
||||
Assert.Single(dirs);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void SafeEnumerateFiles_ValidDirectory()
|
||||
{
|
||||
@@ -309,4 +309,4 @@ namespace SabreTools.IO.Test.Extensions
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SabreTools.IO.Extensions;
|
||||
using Xunit;
|
||||
|
||||
@@ -7,7 +8,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
public class StreamExtensionsTests
|
||||
{
|
||||
#region Align to Boundary
|
||||
#region AlignToBoundary
|
||||
|
||||
[Fact]
|
||||
public void AlignToBoundary_Null_False()
|
||||
@@ -62,7 +63,215 @@ namespace SabreTools.IO.Test.Extensions
|
||||
|
||||
#endregion
|
||||
|
||||
#region Seek If Possible
|
||||
#region ReadFrom
|
||||
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public void ReadFrom_Null_Null(bool retainPosition)
|
||||
{
|
||||
Stream? stream = null;
|
||||
byte[]? actual = stream.ReadFrom(0, 1, retainPosition);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public void ReadFrom_NonSeekable_Null(bool retainPosition)
|
||||
{
|
||||
Stream? stream = new NonSeekableStream();
|
||||
byte[]? actual = stream.ReadFrom(0, 1, retainPosition);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public void ReadFrom_Empty_Null(bool retainPosition)
|
||||
{
|
||||
Stream? stream = new MemoryStream([]);
|
||||
byte[]? actual = stream.ReadFrom(0, 1, retainPosition);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(-1, true)]
|
||||
[InlineData(2048, true)]
|
||||
[InlineData(-1, false)]
|
||||
[InlineData(2048, false)]
|
||||
public void ReadFrom_InvalidOffset_Null(long offset, bool retainPosition)
|
||||
{
|
||||
Stream? stream = new MemoryStream(new byte[1024]);
|
||||
byte[]? actual = stream.ReadFrom(offset, 1, retainPosition);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(-1, true)]
|
||||
[InlineData(2048, true)]
|
||||
[InlineData(-1, false)]
|
||||
[InlineData(2048, false)]
|
||||
public void ReadFrom_InvalidLength_Null(int length, bool retainPosition)
|
||||
{
|
||||
Stream? stream = new MemoryStream(new byte[1024]);
|
||||
byte[]? actual = stream.ReadFrom(0, length, retainPosition);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public void ReadFrom_Valid_Filled(bool retainPosition)
|
||||
{
|
||||
Stream? stream = new MemoryStream(new byte[1024]);
|
||||
byte[]? actual = stream.ReadFrom(0, 512, retainPosition);
|
||||
|
||||
Assert.NotNull(actual);
|
||||
Assert.Equal(512, actual.Length);
|
||||
|
||||
if (retainPosition)
|
||||
Assert.Equal(0, stream.Position);
|
||||
else
|
||||
Assert.Equal(512, stream.Position);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ReadStringsFrom
|
||||
|
||||
[Fact]
|
||||
public void ReadStringsFrom_Null_Null()
|
||||
{
|
||||
Stream? stream = null;
|
||||
var actual = stream.ReadStringsFrom(0, 1, 3);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadStringsFrom_NonSeekable_Null()
|
||||
{
|
||||
Stream? stream = new NonSeekableStream();
|
||||
var actual = stream.ReadStringsFrom(0, 1, 3);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadStringsFrom_Empty_Null()
|
||||
{
|
||||
Stream? stream = new MemoryStream([]);
|
||||
var actual = stream.ReadStringsFrom(0, 1, 3);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(-1)]
|
||||
[InlineData(0)]
|
||||
[InlineData(2048)]
|
||||
public void ReadStringsFrom_InvalidLimit_Empty(int charLimit)
|
||||
{
|
||||
Stream? stream = new MemoryStream(new byte[1024]);
|
||||
var actual = stream.ReadStringsFrom(0, 1024, charLimit);
|
||||
Assert.NotNull(actual);
|
||||
Assert.Empty(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadStringsFrom_NoValidStrings_Empty()
|
||||
{
|
||||
Stream? stream = new MemoryStream(new byte[1024]);
|
||||
var actual = stream.ReadStringsFrom(0, 1024, 4);
|
||||
Assert.NotNull(actual);
|
||||
Assert.Empty(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadStringsFrom_AsciiStrings_Filled()
|
||||
{
|
||||
byte[]? bytes =
|
||||
[
|
||||
.. Encoding.ASCII.GetBytes("TEST"),
|
||||
.. new byte[] { 0x00 },
|
||||
.. Encoding.ASCII.GetBytes("TWO"),
|
||||
.. new byte[] { 0x00 },
|
||||
.. Encoding.ASCII.GetBytes("DATA"),
|
||||
.. new byte[] { 0x00 },
|
||||
];
|
||||
Stream? stream = new MemoryStream(bytes);
|
||||
var actual = stream.ReadStringsFrom(0, bytes.Length, 4);
|
||||
Assert.NotNull(actual);
|
||||
Assert.Equal(2, actual.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadStringsFrom_Latin1Strings_Filled()
|
||||
{
|
||||
byte[]? bytes =
|
||||
[
|
||||
.. Encoding.Latin1.GetBytes("TEST"),
|
||||
.. new byte[] { 0x00 },
|
||||
.. Encoding.Latin1.GetBytes("TWO"),
|
||||
.. new byte[] { 0x00 },
|
||||
.. Encoding.Latin1.GetBytes("DATA"),
|
||||
.. new byte[] { 0x00 },
|
||||
];
|
||||
Stream? stream = new MemoryStream(bytes);
|
||||
var actual = stream.ReadStringsFrom(0, bytes.Length, 4);
|
||||
Assert.NotNull(actual);
|
||||
Assert.Equal(2, actual.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadStringsFrom_UTF16_Filled()
|
||||
{
|
||||
byte[]? bytes =
|
||||
[
|
||||
.. Encoding.Unicode.GetBytes("TEST"),
|
||||
.. new byte[] { 0x00 },
|
||||
.. Encoding.Unicode.GetBytes("TWO"),
|
||||
.. new byte[] { 0x00 },
|
||||
.. Encoding.Unicode.GetBytes("DATA"),
|
||||
.. new byte[] { 0x00 },
|
||||
];
|
||||
Stream? stream = new MemoryStream(bytes);
|
||||
var actual = stream.ReadStringsFrom(0, bytes.Length, 4);
|
||||
Assert.NotNull(actual);
|
||||
Assert.Equal(2, actual.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadStringsFrom_Mixed_Filled()
|
||||
{
|
||||
byte[]? bytes =
|
||||
[
|
||||
.. Encoding.ASCII.GetBytes("TEST1"),
|
||||
.. new byte[] { 0x00 },
|
||||
.. Encoding.ASCII.GetBytes("TWO1"),
|
||||
.. new byte[] { 0x00 },
|
||||
.. Encoding.ASCII.GetBytes("DATA1"),
|
||||
.. new byte[] { 0x00 },
|
||||
.. Encoding.Latin1.GetBytes("TEST2"),
|
||||
.. new byte[] { 0x00 },
|
||||
.. Encoding.Latin1.GetBytes("TWO2"),
|
||||
.. new byte[] { 0x00 },
|
||||
.. Encoding.Latin1.GetBytes("DATA2"),
|
||||
.. new byte[] { 0x00 },
|
||||
.. Encoding.Unicode.GetBytes("TEST3"),
|
||||
.. new byte[] { 0x00 },
|
||||
.. Encoding.Unicode.GetBytes("TWO3"),
|
||||
.. new byte[] { 0x00 },
|
||||
.. Encoding.Unicode.GetBytes("DATA3"),
|
||||
.. new byte[] { 0x00 },
|
||||
];
|
||||
Stream? stream = new MemoryStream(bytes);
|
||||
var actual = stream.ReadStringsFrom(0, bytes.Length, 5);
|
||||
Assert.NotNull(actual);
|
||||
Assert.Equal(6, actual.Count);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region SeekIfPossible
|
||||
|
||||
[Fact]
|
||||
public void SeekIfPossible_NonSeekable_CurrentPosition()
|
||||
@@ -104,6 +313,92 @@ namespace SabreTools.IO.Test.Extensions
|
||||
Assert.Equal(13, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(SeekOrigin.Begin)]
|
||||
[InlineData(SeekOrigin.Current)]
|
||||
[InlineData(SeekOrigin.End)]
|
||||
public void SeekIfPossible_NonSeekable_OriginTest(SeekOrigin origin)
|
||||
{
|
||||
var stream = new NonSeekableStream();
|
||||
long actual = stream.SeekIfPossible(0, origin);
|
||||
Assert.Equal(8, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(SeekOrigin.Begin)]
|
||||
[InlineData(SeekOrigin.Current)]
|
||||
[InlineData(SeekOrigin.End)]
|
||||
public void SeekIfPossible_NonPositionable_OriginTest(SeekOrigin origin)
|
||||
{
|
||||
var stream = new NonPositionableStream();
|
||||
long actual = stream.SeekIfPossible(0, origin);
|
||||
Assert.Equal(-1, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(SeekOrigin.Begin)]
|
||||
[InlineData(SeekOrigin.Current)]
|
||||
[InlineData(SeekOrigin.End)]
|
||||
public void SeekIfPossible_HiddenNonSeekable_OriginTest(SeekOrigin origin)
|
||||
{
|
||||
var stream = new HiddenNonSeekableStream();
|
||||
long actual = stream.SeekIfPossible(0, origin);
|
||||
Assert.Equal(-1, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(SeekOrigin.Begin, 5, 5)]
|
||||
[InlineData(SeekOrigin.Current, 5, 7)]
|
||||
[InlineData(SeekOrigin.End, -5, 11)]
|
||||
public void SeekIfPossible_Seekable_OriginTest(SeekOrigin origin, long offset, long expected)
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, false, true);
|
||||
stream.Position = 2;
|
||||
|
||||
long actual = stream.SeekIfPossible(offset, origin);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region SegmentValid
|
||||
|
||||
[Fact]
|
||||
public void SegmentValid_Null_False()
|
||||
{
|
||||
Stream? stream = null;
|
||||
bool actual = stream.SegmentValid(0, 1);
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(-1)]
|
||||
[InlineData(2048)]
|
||||
public void SegmentValid_InvalidOffset_False(long offset)
|
||||
{
|
||||
Stream? stream = new MemoryStream(new byte[1024]);
|
||||
bool actual = stream.SegmentValid(offset, 1);
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(-1)]
|
||||
[InlineData(2048)]
|
||||
public void SegmentValid_InvalidLength_False(int length)
|
||||
{
|
||||
Stream? stream = new MemoryStream(new byte[1024]);
|
||||
bool actual = stream.SegmentValid(0, length);
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SegmentValid_ValidSegment_True()
|
||||
{
|
||||
Stream? stream = new MemoryStream(new byte[1024]);
|
||||
bool actual = stream.SegmentValid(0, 1024);
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
@@ -229,4 +524,4 @@ namespace SabreTools.IO.Test.Extensions
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,7 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
#if NET7_0_OR_GREATER
|
||||
using System.Numerics;
|
||||
#endif
|
||||
using System.Text;
|
||||
using SabreTools.IO.Extensions;
|
||||
using Xunit;
|
||||
@@ -34,17 +32,28 @@ namespace SabreTools.IO.Test.Extensions
|
||||
public void WriteByteValueTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(1).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(1)];
|
||||
bool write = stream.Write((byte)0x00);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteByteBothEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = [.. _bytes.Take(2)];
|
||||
|
||||
int offset = 0;
|
||||
stream.WriteBothEndian(_bytes.ReadByteBothEndian(ref offset));
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteBytesTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(4)];
|
||||
bool write = StreamWriterExtensions.Write(stream, [0x00, 0x01, 0x02, 0x03]);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
@@ -54,7 +63,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
public void WriteBytesBigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(4)];
|
||||
stream.WriteBigEndian([0x03, 0x02, 0x01, 0x00]);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
@@ -63,17 +72,28 @@ namespace SabreTools.IO.Test.Extensions
|
||||
public void WriteSByteTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(1).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(1)];
|
||||
bool write = stream.Write((sbyte)0x00);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteSByteBothEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = [.. _bytes.Take(2)];
|
||||
|
||||
int offset = 0;
|
||||
stream.WriteBothEndian(_bytes.ReadSByteBothEndian(ref offset));
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteCharTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(1).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(1)];
|
||||
bool write = stream.Write('\0');
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
@@ -92,7 +112,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
public void WriteInt16Test()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(2).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(2)];
|
||||
bool write = stream.Write((short)0x0100);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
@@ -102,17 +122,28 @@ namespace SabreTools.IO.Test.Extensions
|
||||
public void WriteInt16BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(2).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(2)];
|
||||
bool write = stream.WriteBigEndian((short)0x0001);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteInt16BothEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = [.. _bytes.Take(4)];
|
||||
|
||||
int offset = 0;
|
||||
stream.WriteBothEndian(_bytes.ReadInt16BothEndian(ref offset));
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt16Test()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(2).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(2)];
|
||||
bool write = stream.Write((ushort)0x0100);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
@@ -122,18 +153,28 @@ namespace SabreTools.IO.Test.Extensions
|
||||
public void WriteUInt16BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(2).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(2)];
|
||||
bool write = stream.WriteBigEndian((ushort)0x0001);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
[Fact]
|
||||
public void WriteUInt16BothEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = [.. _bytes.Take(4)];
|
||||
|
||||
int offset = 0;
|
||||
stream.WriteBothEndian(_bytes.ReadUInt16BothEndian(ref offset));
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteHalfTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(2).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(2)];
|
||||
bool write = stream.Write(BitConverter.Int16BitsToHalf(0x0100));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
@@ -143,18 +184,17 @@ namespace SabreTools.IO.Test.Extensions
|
||||
public void WriteHalfBigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(2).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(2)];
|
||||
bool write = stream.WriteBigEndian(BitConverter.Int16BitsToHalf(0x0001));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
#endif
|
||||
|
||||
[Fact]
|
||||
public void WriteInt24Test()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(3).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(3)];
|
||||
bool write = stream.WriteAsInt24(0x020100);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
@@ -164,7 +204,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
public void WriteInt24BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(3).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(3)];
|
||||
bool write = stream.WriteAsInt24BigEndian(0x000102);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
@@ -174,7 +214,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
public void WriteUInt24Test()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(3).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(3)];
|
||||
bool write = stream.WriteAsUInt24(0x020100);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
@@ -184,7 +224,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
public void WriteUInt24BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(3).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(3)];
|
||||
bool write = stream.WriteAsUInt24BigEndian(0x000102);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
@@ -194,7 +234,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
public void WriteInt32Test()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(4)];
|
||||
bool write = stream.Write(0x03020100);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
@@ -204,17 +244,28 @@ namespace SabreTools.IO.Test.Extensions
|
||||
public void WriteInt32BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(4)];
|
||||
bool write = stream.WriteBigEndian(0x00010203);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteInt32BothEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = [.. _bytes.Take(8)];
|
||||
|
||||
int offset = 0;
|
||||
stream.WriteBothEndian(_bytes.ReadInt32BothEndian(ref offset));
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt32Test()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(4)];
|
||||
bool write = stream.Write((uint)0x03020100);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
@@ -224,17 +275,28 @@ namespace SabreTools.IO.Test.Extensions
|
||||
public void WriteUInt32BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(4)];
|
||||
bool write = stream.WriteBigEndian((uint)0x00010203);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt32BothEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = [.. _bytes.Take(8)];
|
||||
|
||||
int offset = 0;
|
||||
stream.WriteBothEndian(_bytes.ReadUInt32BothEndian(ref offset));
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteSingleTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(4)];
|
||||
bool write = stream.Write(BitConverter.Int32BitsToSingle(0x03020100));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
@@ -244,7 +306,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
public void WriteSingleBigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(4)];
|
||||
bool write = stream.WriteBigEndian(BitConverter.Int32BitsToSingle(0x00010203));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
@@ -254,7 +316,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
public void WriteInt48Test()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(6).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(6)];
|
||||
bool write = stream.WriteAsInt48(0x050403020100);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
@@ -264,7 +326,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
public void WriteInt48BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(6).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(6)];
|
||||
bool write = stream.WriteAsInt48BigEndian(0x000102030405);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
@@ -274,7 +336,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
public void WriteUInt48Test()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(6).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(6)];
|
||||
bool write = stream.WriteAsUInt48(0x050403020100);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
@@ -284,7 +346,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
public void WriteUInt48BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(6).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(6)];
|
||||
bool write = stream.WriteAsUInt48BigEndian(0x000102030405);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
@@ -294,7 +356,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
public void WriteInt64Test()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(8).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(8)];
|
||||
bool write = stream.Write(0x0706050403020100);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
@@ -304,17 +366,28 @@ namespace SabreTools.IO.Test.Extensions
|
||||
public void WriteInt64BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(8).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(8)];
|
||||
bool write = stream.WriteBigEndian(0x0001020304050607);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteInt64BothEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = [.. _bytes.Take(16)];
|
||||
|
||||
int offset = 0;
|
||||
stream.WriteBothEndian(_bytes.ReadInt64BothEndian(ref offset));
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt64Test()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(8).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(8)];
|
||||
bool write = stream.Write((ulong)0x0706050403020100);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
@@ -324,17 +397,28 @@ namespace SabreTools.IO.Test.Extensions
|
||||
public void WriteUInt64BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(8).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(8)];
|
||||
bool write = stream.WriteBigEndian((ulong)0x0001020304050607);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt64BothEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = [.. _bytes.Take(16)];
|
||||
|
||||
int offset = 0;
|
||||
stream.WriteBothEndian(_bytes.ReadUInt64BothEndian(ref offset));
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteDoubleTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(8).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(8)];
|
||||
bool write = stream.Write(BitConverter.Int64BitsToDouble(0x0706050403020100));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
@@ -344,7 +428,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
public void WriteDoubleBigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(8).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(8)];
|
||||
bool write = stream.WriteBigEndian(BitConverter.Int64BitsToDouble(0x0001020304050607));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
@@ -354,7 +438,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
public void WriteDecimalTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _decimalBytes.Take(16).ToArray();
|
||||
byte[] expected = [.. _decimalBytes.Take(16)];
|
||||
bool write = stream.Write(0.0123456789M);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
@@ -364,7 +448,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
public void WriteDecimalBigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _decimalBytes.Take(16).Reverse().ToArray();
|
||||
byte[] expected = [.. _decimalBytes.Take(16).Reverse()];
|
||||
bool write = stream.WriteBigEndian(0.0123456789M);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
@@ -374,7 +458,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
public void WriteGuidTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(16).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(16)];
|
||||
bool write = stream.Write(new Guid(_bytes));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
@@ -384,18 +468,17 @@ namespace SabreTools.IO.Test.Extensions
|
||||
public void WriteGuidBigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(16).ToArray();
|
||||
bool write = stream.WriteBigEndian(new Guid(_bytes.Reverse().ToArray()));
|
||||
byte[] expected = [.. _bytes.Take(16)];
|
||||
bool write = stream.WriteBigEndian(new Guid([.. Enumerable.Reverse(_bytes)]));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
#if NET7_0_OR_GREATER
|
||||
[Fact]
|
||||
public void WriteInt128Test()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(16).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(16)];
|
||||
bool write = stream.Write((Int128)new BigInteger(_bytes));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
@@ -405,8 +488,8 @@ namespace SabreTools.IO.Test.Extensions
|
||||
public void WriteInt128BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(16).ToArray();
|
||||
bool write = stream.WriteBigEndian((Int128)new BigInteger(_bytes.Reverse().ToArray()));
|
||||
byte[] expected = [.. _bytes.Take(16)];
|
||||
bool write = stream.WriteBigEndian((Int128)new BigInteger(Enumerable.Reverse(_bytes).ToArray()));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
@@ -415,7 +498,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
public void WriteUInt128Test()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(16).ToArray();
|
||||
byte[] expected = [.. _bytes.Take(16)];
|
||||
bool write = stream.Write((UInt128)new BigInteger(_bytes));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
@@ -425,12 +508,11 @@ namespace SabreTools.IO.Test.Extensions
|
||||
public void WriteUInt128BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(16).ToArray();
|
||||
bool write = stream.WriteBigEndian((UInt128)new BigInteger(_bytes.Reverse().ToArray()));
|
||||
byte[] expected = [.. _bytes.Take(16)];
|
||||
bool write = stream.WriteBigEndian((UInt128)new BigInteger(Enumerable.Reverse(_bytes).ToArray()));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
#endif
|
||||
|
||||
[Fact]
|
||||
public void WriteNullTerminatedAnsiStringTest()
|
||||
@@ -507,15 +589,12 @@ namespace SabreTools.IO.Test.Extensions
|
||||
Assert.True(actual);
|
||||
ValidateBytes(_bytes, stream.GetBuffer());
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
// Half
|
||||
stream = new MemoryStream(new byte[2], 0, 2, true, true);
|
||||
actual = stream.WriteType<Half>(BitConverter.Int16BitsToHalf(0x0100));
|
||||
Assert.True(actual);
|
||||
ValidateBytes([.. _bytes.Take(2)], stream.GetBuffer());
|
||||
#endif
|
||||
|
||||
#if NET7_0_OR_GREATER
|
||||
// Int128
|
||||
stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
actual = stream.WriteType<Int128>((Int128)new BigInteger(_bytes));
|
||||
@@ -527,7 +606,6 @@ namespace SabreTools.IO.Test.Extensions
|
||||
actual = stream.WriteType<UInt128>((UInt128)new BigInteger(_bytes));
|
||||
Assert.True(actual);
|
||||
ValidateBytes(_bytes, stream.GetBuffer());
|
||||
#endif
|
||||
|
||||
// Enum
|
||||
stream = new MemoryStream(new byte[4], 0, 4, true, true);
|
||||
@@ -552,7 +630,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
SecondValue = 0x07060504,
|
||||
FifthValue = "ABC",
|
||||
};
|
||||
byte[] expected = bytesWithString.Take(12).ToArray();
|
||||
byte[] expected = [.. bytesWithString.Take(12)];
|
||||
bool write = stream.WriteType(obj);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
@@ -576,7 +654,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
FourthValue = 0x0B0A,
|
||||
FifthValue = "ABC",
|
||||
};
|
||||
byte[] expected = bytesWithString.Take(16).ToArray();
|
||||
byte[] expected = [.. bytesWithString.Take(16)];
|
||||
bool write = stream.WriteType(obj);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
@@ -593,4 +671,4 @@ namespace SabreTools.IO.Test.Extensions
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
76
SabreTools.IO.Test/Extensions/StringExtensionsTests.cs
Normal file
76
SabreTools.IO.Test/Extensions/StringExtensionsTests.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
using SabreTools.IO.Extensions;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
public class StringExtensionsTests
|
||||
{
|
||||
#region OptionalContains
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, "ANY", false)]
|
||||
[InlineData("", "ANY", false)]
|
||||
[InlineData("ANY", "ANY", true)]
|
||||
[InlineData("ANYTHING", "ANY", true)]
|
||||
[InlineData("THING", "ANY", false)]
|
||||
[InlineData("THINGANY", "ANY", true)]
|
||||
public void OptionalContainsTest(string? haystack, string needle, bool expected)
|
||||
{
|
||||
bool actual = haystack.OptionalContains(needle);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region OptionalEndsWith
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, "ANY", false)]
|
||||
[InlineData("", "ANY", false)]
|
||||
[InlineData("ANY", "ANY", true)]
|
||||
[InlineData("ANYTHING", "ANY", false)]
|
||||
[InlineData("THING", "ANY", false)]
|
||||
[InlineData("THINGANY", "ANY", true)]
|
||||
public void OptionalEndsWithTest(string? haystack, string needle, bool expected)
|
||||
{
|
||||
bool actual = haystack.OptionalEndsWith(needle);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region OptionalEquals
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, "ANY", false)]
|
||||
[InlineData("", "ANY", false)]
|
||||
[InlineData("ANY", "ANY", true)]
|
||||
[InlineData("ANYTHING", "ANY", false)]
|
||||
[InlineData("THING", "ANY", false)]
|
||||
[InlineData("THINGANY", "ANY", false)]
|
||||
public void OptionalEqualsTest(string? haystack, string needle, bool expected)
|
||||
{
|
||||
bool actual = haystack.OptionalEquals(needle);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region OptionalStartsWith
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, "ANY", false)]
|
||||
[InlineData("", "ANY", false)]
|
||||
[InlineData("ANY", "ANY", true)]
|
||||
[InlineData("ANYTHING", "ANY", true)]
|
||||
[InlineData("THING", "ANY", false)]
|
||||
[InlineData("THINGANY", "ANY", false)]
|
||||
public void OptionalStartsWithTest(string? haystack, string needle, bool expected)
|
||||
{
|
||||
bool actual = haystack.OptionalStartsWith(needle);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -5,5 +5,5 @@ namespace SabreTools.IO.Test.Extensions
|
||||
None = 0x00000000,
|
||||
RecognizedTestValue = 0x03020100,
|
||||
UpperBoundaryValue = 0xFFFFFFFF,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
public TestStructPoint[]? StructArray;
|
||||
|
||||
/// <summary>
|
||||
/// Length of <see cref="LPByteArray"/>
|
||||
/// Length of <see cref="LPByteArray"/>
|
||||
/// </summary>
|
||||
public ushort LPByteArrayLength;
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 4)]
|
||||
public byte[]? LPByteArray;
|
||||
|
||||
|
||||
// /// <summary>
|
||||
// /// 4 entry nested byte array
|
||||
// /// </summary>
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
|
||||
public uint FieldB;
|
||||
}
|
||||
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
|
||||
internal class TestStructInheritanceChild2 : TestStructInheritanceParent
|
||||
{
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
public int SecondValue;
|
||||
|
||||
public ushort ThirdValue;
|
||||
|
||||
|
||||
public short FourthValue;
|
||||
|
||||
[MarshalAs(UnmanagedType.LPStr)]
|
||||
|
||||
@@ -124,7 +124,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
string actual = Encoding.UTF8.GetString(stream.ToArray(), 3, (int)stream.Length - 3);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void WriteOptionalAttributeString_NullInput_NoWrite()
|
||||
{
|
||||
@@ -184,7 +184,7 @@ namespace SabreTools.IO.Test.Extensions
|
||||
string actual = Encoding.UTF8.GetString(stream.ToArray(), 3, (int)stream.Length - 3);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void WriteOptionalElementString_NullInput_NoWrite()
|
||||
{
|
||||
@@ -239,4 +239,4 @@ namespace SabreTools.IO.Test.Extensions
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,4 +64,4 @@ namespace SabreTools.IO.Test
|
||||
Assert.False(write);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,4 +35,4 @@ namespace SabreTools.IO.Test.Logging
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,4 +37,4 @@ namespace SabreTools.IO.Test.Logging
|
||||
stopwatch.Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,4 +51,4 @@ namespace SabreTools.IO.Test.Logging
|
||||
LoggerImpl.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
324
SabreTools.IO.Test/MatchUtilTests.cs
Normal file
324
SabreTools.IO.Test/MatchUtilTests.cs
Normal file
@@ -0,0 +1,324 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SabreTools.IO.Matching;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.IO.Test
|
||||
{
|
||||
public class MatchUtilTests
|
||||
{
|
||||
#region Array
|
||||
|
||||
[Fact]
|
||||
public void ArrayGetAllMatches_NullStack_NoMatches()
|
||||
{
|
||||
byte[]? stack = null;
|
||||
List<ContentMatchSet> matchSets = [new ContentMatchSet(new byte[1], "name")];
|
||||
var matches = MatchUtil.GetAllMatches("file", stack, matchSets);
|
||||
Assert.Empty(matches);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ArrayGetAllMatches_EmptyStack_NoMatches()
|
||||
{
|
||||
byte[] stack = [];
|
||||
List<ContentMatchSet> matchSets = [new ContentMatchSet(new byte[1], "name")];
|
||||
var matches = MatchUtil.GetAllMatches("file", stack, matchSets);
|
||||
Assert.Empty(matches);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ArrayGetAllMatches_EmptyMatchSets_NoMatches()
|
||||
{
|
||||
byte[] stack = [0x01];
|
||||
List<ContentMatchSet> matchSets = [];
|
||||
var matches = MatchUtil.GetAllMatches("file", stack, matchSets);
|
||||
Assert.Empty(matches);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ArrayGetAllMatches_Matching_Matches()
|
||||
{
|
||||
byte[] stack = [0x01];
|
||||
List<ContentMatchSet> matchSets = [new ContentMatchSet(new byte[] { 0x01 }, "name")];
|
||||
var matches = MatchUtil.GetAllMatches("file", stack, matchSets);
|
||||
string setName = Assert.Single(matches);
|
||||
Assert.Equal("name", setName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ArrayGetAllMatches_PartialMatchingAny_Matches()
|
||||
{
|
||||
byte[] stack = [0x01];
|
||||
List<ContentMatchSet> matchSets =
|
||||
[
|
||||
new ContentMatchSet([
|
||||
new byte[] { 0x00 },
|
||||
new ContentMatch([0x01]),
|
||||
], "name")
|
||||
];
|
||||
var matches = MatchUtil.GetAllMatches("file", stack, matchSets, any: true);
|
||||
string setName = Assert.Single(matches);
|
||||
Assert.Equal("name", setName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ArrayGetAllMatches_PartialMatchingAll_NoMatches()
|
||||
{
|
||||
byte[] stack = [0x01];
|
||||
List<ContentMatchSet> matchSets =
|
||||
[
|
||||
new ContentMatchSet([
|
||||
new byte[] { 0x00 },
|
||||
new ContentMatch([0x01]),
|
||||
], "name")
|
||||
];
|
||||
var matches = MatchUtil.GetAllMatches("file", stack, matchSets, any: false);
|
||||
Assert.Empty(matches);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ArrayGetFirstMatch_NullStack_NoMatches()
|
||||
{
|
||||
byte[]? stack = null;
|
||||
List<ContentMatchSet> matchSets = [new ContentMatchSet(new byte[1], "name")];
|
||||
string? match = MatchUtil.GetFirstMatch("file", stack, matchSets);
|
||||
Assert.Null(match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ArrayGetFirstMatch_EmptyStack_NoMatches()
|
||||
{
|
||||
byte[] stack = [];
|
||||
List<ContentMatchSet> matchSets = [new ContentMatchSet(new byte[1], "name")];
|
||||
string? match = MatchUtil.GetFirstMatch("file", stack, matchSets);
|
||||
Assert.Null(match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ArrayGetFirstMatch_EmptyMatchSets_NoMatches()
|
||||
{
|
||||
byte[] stack = [0x01];
|
||||
List<ContentMatchSet> matchSets = [];
|
||||
string? match = MatchUtil.GetFirstMatch("file", stack, matchSets);
|
||||
Assert.Null(match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ArrayGetFirstMatch_Matching_Matches()
|
||||
{
|
||||
byte[] stack = [0x01];
|
||||
List<ContentMatchSet> matchSets = [new ContentMatchSet(new byte[] { 0x01 }, "name")];
|
||||
string? setName = MatchUtil.GetFirstMatch("file", stack, matchSets);
|
||||
Assert.Equal("name", setName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ArrayGetFirstMatch_PartialMatchingAny_Matches()
|
||||
{
|
||||
byte[] stack = [0x01];
|
||||
List<ContentMatchSet> matchSets =
|
||||
[
|
||||
new ContentMatchSet([
|
||||
new byte[] { 0x00 },
|
||||
new ContentMatch([0x01]),
|
||||
], "name")
|
||||
];
|
||||
string? setName = MatchUtil.GetFirstMatch("file", stack, matchSets, any: true);
|
||||
Assert.Equal("name", setName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ArrayGetFirstMatch_PartialMatchingAll_NoMatches()
|
||||
{
|
||||
byte[] stack = [0x01];
|
||||
List<ContentMatchSet> matchSets =
|
||||
[
|
||||
new ContentMatchSet([
|
||||
new byte[] { 0x00 },
|
||||
new ContentMatch([0x01]),
|
||||
], "name")
|
||||
];
|
||||
string? setName = MatchUtil.GetFirstMatch("file", stack, matchSets, any: false);
|
||||
Assert.Null(setName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExactSizeArrayMatch()
|
||||
{
|
||||
byte[] source = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07];
|
||||
byte?[] check = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07];
|
||||
string expected = "match";
|
||||
|
||||
var matchers = new List<ContentMatchSet>
|
||||
{
|
||||
new(check, expected),
|
||||
};
|
||||
|
||||
string? actual = MatchUtil.GetFirstMatch("testfile", source, matchers, any: false);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Stream
|
||||
|
||||
[Fact]
|
||||
public void StreamGetAllMatches_NullStack_NoMatches()
|
||||
{
|
||||
Stream? stack = null;
|
||||
List<ContentMatchSet> matchSets = [new ContentMatchSet(new byte[1], "name")];
|
||||
var matches = MatchUtil.GetAllMatches("file", stack, matchSets);
|
||||
Assert.Empty(matches);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StreamGetAllMatches_EmptyStack_NoMatches()
|
||||
{
|
||||
Stream stack = new MemoryStream();
|
||||
List<ContentMatchSet> matchSets = [new ContentMatchSet(new byte[1], "name")];
|
||||
var matches = MatchUtil.GetAllMatches("file", stack, matchSets);
|
||||
Assert.Empty(matches);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StreamGetAllMatches_EmptyMatchSets_NoMatches()
|
||||
{
|
||||
Stream stack = new MemoryStream([0x01]);
|
||||
List<ContentMatchSet> matchSets = [];
|
||||
var matches = MatchUtil.GetAllMatches("file", stack, matchSets);
|
||||
Assert.Empty(matches);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StreamGetAllMatches_Matching_Matches()
|
||||
{
|
||||
Stream stack = new MemoryStream([0x01]);
|
||||
List<ContentMatchSet> matchSets = [new ContentMatchSet(new byte[] { 0x01 }, "name")];
|
||||
var matches = MatchUtil.GetAllMatches("file", stack, matchSets);
|
||||
string setName = Assert.Single(matches);
|
||||
Assert.Equal("name", setName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StreamGetAllMatches_PartialMatchingAny_Matches()
|
||||
{
|
||||
Stream stack = new MemoryStream([0x01]);
|
||||
List<ContentMatchSet> matchSets =
|
||||
[
|
||||
new ContentMatchSet([
|
||||
new byte[] { 0x00 },
|
||||
new ContentMatch([0x01]),
|
||||
], "name")
|
||||
];
|
||||
var matches = MatchUtil.GetAllMatches("file", stack, matchSets, any: true);
|
||||
string setName = Assert.Single(matches);
|
||||
Assert.Equal("name", setName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StreamGetAllMatches_PartialMatchingAll_NoMatches()
|
||||
{
|
||||
Stream stack = new MemoryStream([0x01]);
|
||||
List<ContentMatchSet> matchSets =
|
||||
[
|
||||
new ContentMatchSet([
|
||||
new byte[] { 0x00 },
|
||||
new ContentMatch([0x01]),
|
||||
], "name")
|
||||
];
|
||||
var matches = MatchUtil.GetAllMatches("file", stack, matchSets, any: false);
|
||||
Assert.Empty(matches);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StreamGetFirstMatch_NullStack_NoMatches()
|
||||
{
|
||||
Stream? stack = null;
|
||||
List<ContentMatchSet> matchSets = [new ContentMatchSet(new byte[1], "name")];
|
||||
string? match = MatchUtil.GetFirstMatch("file", stack, matchSets);
|
||||
Assert.Null(match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StreamGetFirstMatch_EmptyStack_NoMatches()
|
||||
{
|
||||
Stream stack = new MemoryStream();
|
||||
List<ContentMatchSet> matchSets = [new ContentMatchSet(new byte[1], "name")];
|
||||
string? match = MatchUtil.GetFirstMatch("file", stack, matchSets);
|
||||
Assert.Null(match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StreamGetFirstMatch_EmptyMatchSets_NoMatches()
|
||||
{
|
||||
Stream stack = new MemoryStream([0x01]);
|
||||
List<ContentMatchSet> matchSets = [];
|
||||
string? match = MatchUtil.GetFirstMatch("file", stack, matchSets);
|
||||
Assert.Null(match);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StreamGetFirstMatch_Matching_Matches()
|
||||
{
|
||||
Stream stack = new MemoryStream([0x01]);
|
||||
List<ContentMatchSet> matchSets = [new ContentMatchSet(new byte[] { 0x01 }, "name")];
|
||||
string? setName = MatchUtil.GetFirstMatch("file", stack, matchSets);
|
||||
Assert.Equal("name", setName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StreamGetFirstMatch_PartialMatchingAny_Matches()
|
||||
{
|
||||
Stream stack = new MemoryStream([0x01]);
|
||||
List<ContentMatchSet> matchSets =
|
||||
[
|
||||
new ContentMatchSet([
|
||||
new byte[] { 0x00 },
|
||||
new ContentMatch([0x01]),
|
||||
], "name")
|
||||
];
|
||||
string? setName = MatchUtil.GetFirstMatch("file", stack, matchSets, any: true);
|
||||
Assert.Equal("name", setName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StreamGetFirstMatch_PartialMatchingAll_NoMatches()
|
||||
{
|
||||
Stream stack = new MemoryStream([0x01]);
|
||||
List<ContentMatchSet> matchSets =
|
||||
[
|
||||
new ContentMatchSet([
|
||||
new byte[] { 0x00 },
|
||||
new ContentMatch([0x01]),
|
||||
], "name")
|
||||
];
|
||||
string? setName = MatchUtil.GetFirstMatch("file", stack, matchSets, any: false);
|
||||
Assert.Null(setName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExactSizeStreamMatch()
|
||||
{
|
||||
byte[] source = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07];
|
||||
var stream = new MemoryStream(source);
|
||||
|
||||
byte?[] check = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07];
|
||||
string expected = "match";
|
||||
|
||||
var matchers = new List<ContentMatchSet>
|
||||
{
|
||||
new(check, expected),
|
||||
};
|
||||
|
||||
string? actual = MatchUtil.GetFirstMatch("testfile", stream, matchers, any: false);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Path
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
202
SabreTools.IO.Test/Matching/ContentMatchSetTests.cs
Normal file
202
SabreTools.IO.Test/Matching/ContentMatchSetTests.cs
Normal file
@@ -0,0 +1,202 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SabreTools.IO.Matching;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.IO.Test.Matching
|
||||
{
|
||||
public class ContentMatchSetTests
|
||||
{
|
||||
[Fact]
|
||||
public void InvalidNeedle_ThrowsException()
|
||||
{
|
||||
Assert.Throws<InvalidDataException>(() => new ContentMatchSet(Array.Empty<byte>(), "name"));
|
||||
Assert.Throws<InvalidDataException>(() => new ContentMatchSet(Array.Empty<byte>(), ArrayVersionMock, "name"));
|
||||
Assert.Throws<InvalidDataException>(() => new ContentMatchSet(Array.Empty<byte>(), StreamVersionMock, "name"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvalidNeedles_ThrowsException()
|
||||
{
|
||||
Assert.Throws<InvalidDataException>(() => new ContentMatchSet([], "name"));
|
||||
Assert.Throws<InvalidDataException>(() => new ContentMatchSet([], ArrayVersionMock, "name"));
|
||||
Assert.Throws<InvalidDataException>(() => new ContentMatchSet([], StreamVersionMock, "name"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GenericConstructor_NoDelegates()
|
||||
{
|
||||
var needles = new List<ContentMatch> { new byte[] { 0x01, 0x02, 0x03, 0x04 } };
|
||||
var cms = new ContentMatchSet(needles, "name");
|
||||
Assert.Null(cms.GetArrayVersion);
|
||||
Assert.Null(cms.GetStreamVersion);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ArrayConstructor_SingleDelegate()
|
||||
{
|
||||
var needles = new List<ContentMatch> { new byte[] { 0x01, 0x02, 0x03, 0x04 } };
|
||||
var cms = new ContentMatchSet(needles, ArrayVersionMock, "name");
|
||||
Assert.NotNull(cms.GetArrayVersion);
|
||||
Assert.Null(cms.GetStreamVersion);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StreamConstructor_SingleDelegate()
|
||||
{
|
||||
var needles = new List<ContentMatch> { new byte[] { 0x01, 0x02, 0x03, 0x04 } };
|
||||
var cms = new ContentMatchSet(needles, StreamVersionMock, "name");
|
||||
Assert.Null(cms.GetArrayVersion);
|
||||
Assert.NotNull(cms.GetStreamVersion);
|
||||
}
|
||||
|
||||
#region Array
|
||||
|
||||
[Fact]
|
||||
public void MatchesAll_NullArray_NoMatches()
|
||||
{
|
||||
var cms = new ContentMatchSet(new byte[] { 0x01, 0x02, 0x03, 0x04 }, "name");
|
||||
var actual = cms.MatchesAll((byte[]?)null);
|
||||
Assert.Empty(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MatchesAll_EmptyArray_NoMatches()
|
||||
{
|
||||
var cms = new ContentMatchSet(new byte[] { 0x01, 0x02, 0x03, 0x04 }, "name");
|
||||
var actual = cms.MatchesAll([]);
|
||||
Assert.Empty(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MatchesAll_MatchingArray_Matches()
|
||||
{
|
||||
var cms = new ContentMatchSet(new byte[] { 0x01, 0x02, 0x03, 0x04 }, "name");
|
||||
var actual = cms.MatchesAll([0x01, 0x02, 0x03, 0x04]);
|
||||
int position = Assert.Single(actual);
|
||||
Assert.Equal(0, position);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MatchesAll_MismatchedArray_NoMatches()
|
||||
{
|
||||
var cms = new ContentMatchSet(new byte[] { 0x01, 0x02, 0x03, 0x04 }, "name");
|
||||
var actual = cms.MatchesAll([0x01, 0x03]);
|
||||
Assert.Empty(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MatchesAny_NullArray_NoMatches()
|
||||
{
|
||||
var cms = new ContentMatchSet(new byte[] { 0x01, 0x02, 0x03, 0x04 }, "name");
|
||||
int actual = cms.MatchesAny((byte[]?)null);
|
||||
Assert.Equal(-1, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MatchesAny_EmptyArray_NoMatches()
|
||||
{
|
||||
var cms = new ContentMatchSet(new byte[] { 0x01, 0x02, 0x03, 0x04 }, "name");
|
||||
int actual = cms.MatchesAny([]);
|
||||
Assert.Equal(-1, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MatchesAny_MatchingArray_Matches()
|
||||
{
|
||||
var cms = new ContentMatchSet(new byte[] { 0x01, 0x02, 0x03, 0x04 }, "name");
|
||||
int actual = cms.MatchesAny([0x01, 0x02, 0x03, 0x04]);
|
||||
Assert.Equal(0, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MatchesAny_MismatchedArray_NoMatches()
|
||||
{
|
||||
var cms = new ContentMatchSet(new byte[] { 0x01, 0x02, 0x03, 0x04 }, "name");
|
||||
int actual = cms.MatchesAny([0x01, 0x03]);
|
||||
Assert.Equal(-1, actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Stream
|
||||
|
||||
[Fact]
|
||||
public void MatchesAll_NullStream_NoMatches()
|
||||
{
|
||||
var cms = new ContentMatchSet(new byte[] { 0x01, 0x02, 0x03, 0x04 }, "name");
|
||||
var actual = cms.MatchesAll((Stream?)null);
|
||||
Assert.Empty(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MatchesAll_EmptyStream_NoMatches()
|
||||
{
|
||||
var cms = new ContentMatchSet(new byte[] { 0x01, 0x02, 0x03, 0x04 }, "name");
|
||||
var actual = cms.MatchesAll(new MemoryStream());
|
||||
Assert.Empty(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MatchesAll_MatchingStream_Matches()
|
||||
{
|
||||
var cms = new ContentMatchSet(new byte[] { 0x01, 0x02, 0x03, 0x04 }, "name");
|
||||
var actual = cms.MatchesAll(new MemoryStream([0x01, 0x02, 0x03, 0x04]));
|
||||
int position = Assert.Single(actual);
|
||||
Assert.Equal(0, position);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MatchesAll_MismatchedStream_NoMatches()
|
||||
{
|
||||
var cms = new ContentMatchSet(new byte[] { 0x01, 0x02, 0x03, 0x04 }, "name");
|
||||
var actual = cms.MatchesAll([0x01, 0x03]);
|
||||
Assert.Empty(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MatchesAny_NullStream_NoMatches()
|
||||
{
|
||||
var cms = new ContentMatchSet(new byte[] { 0x01, 0x02, 0x03, 0x04 }, "name");
|
||||
int actual = cms.MatchesAny((Stream?)null);
|
||||
Assert.Equal(-1, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MatchesAny_EmptyStream_NoMatches()
|
||||
{
|
||||
var cms = new ContentMatchSet(new byte[] { 0x01, 0x02, 0x03, 0x04 }, "name");
|
||||
int actual = cms.MatchesAny(new MemoryStream());
|
||||
Assert.Equal(-1, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MatchesAny_MatchingStream_Matches()
|
||||
{
|
||||
var cms = new ContentMatchSet(new byte[] { 0x01, 0x02, 0x03, 0x04 }, "name");
|
||||
int actual = cms.MatchesAny(new MemoryStream([0x01, 0x02, 0x03, 0x04]));
|
||||
Assert.Equal(0, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MatchesAny_MismatchedStream_NoMatches()
|
||||
{
|
||||
var cms = new ContentMatchSet(new byte[] { 0x01, 0x02, 0x03, 0x04 }, "name");
|
||||
int actual = cms.MatchesAny([0x01, 0x03]);
|
||||
Assert.Equal(-1, actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Mock Delegates
|
||||
|
||||
/// <inheritdoc cref="GetArrayVersion"/>
|
||||
private static string? ArrayVersionMock(string path, byte[]? content, List<int> positions) => null;
|
||||
|
||||
/// <inheritdoc cref="GetStreamVersion"/>
|
||||
private static string? StreamVersionMock(string path, Stream? content, List<int> positions) => null;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
271
SabreTools.IO.Test/Matching/ContentMatchTests.cs
Normal file
271
SabreTools.IO.Test/Matching/ContentMatchTests.cs
Normal file
@@ -0,0 +1,271 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SabreTools.IO.Matching;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.IO.Test.Matching
|
||||
{
|
||||
public class ContentMatchTests
|
||||
{
|
||||
[Fact]
|
||||
public void InvalidNeedle_ThrowsException()
|
||||
{
|
||||
Assert.Throws<InvalidDataException>(() => new ContentMatch(Array.Empty<byte>()));
|
||||
Assert.Throws<InvalidDataException>(() => new ContentMatch(Array.Empty<byte?>()));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvalidStart_ThrowsException()
|
||||
{
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => new ContentMatch(new byte[1], start: -1));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => new ContentMatch(new byte?[1], start: -1));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvalidEnd_ThrowsException()
|
||||
{
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => new ContentMatch(new byte[1], end: -2));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => new ContentMatch(new byte?[1], end: -2));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ImplicitOperatorArray_Success()
|
||||
{
|
||||
byte[] needle = [0x01, 0x02, 0x03, 0x04];
|
||||
var cm = (ContentMatch)needle;
|
||||
Assert.NotNull(cm);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ImplicitOperatorNullableArray_Success()
|
||||
{
|
||||
byte?[] needle = [0x01, 0x02, 0x03, 0x04];
|
||||
var cm = (ContentMatch)needle;
|
||||
Assert.NotNull(cm);
|
||||
}
|
||||
|
||||
#region Byte Array
|
||||
|
||||
[Fact]
|
||||
public void NullArray_NoMatch()
|
||||
{
|
||||
var cm = new ContentMatch(new byte?[1]);
|
||||
int actual = cm.Match((byte[]?)null);
|
||||
Assert.Equal(-1, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EmptyArray_NoMatch()
|
||||
{
|
||||
var cm = new ContentMatch(new byte?[1]);
|
||||
int actual = cm.Match([]);
|
||||
Assert.Equal(-1, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LargerNeedleArray_NoMatch()
|
||||
{
|
||||
var cm = new ContentMatch(new byte?[2]);
|
||||
int actual = cm.Match(new byte[1]);
|
||||
Assert.Equal(-1, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EqualLengthMatchingArray_Match()
|
||||
{
|
||||
byte[] needle = [0x01, 0x02, 0x03, 0x04];
|
||||
var cm = new ContentMatch(needle);
|
||||
|
||||
int actual = cm.Match(needle);
|
||||
Assert.Equal(0, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EqualLengthMatchingArrayReverse_Match()
|
||||
{
|
||||
byte[] needle = [0x01, 0x02, 0x03, 0x04];
|
||||
var cm = new ContentMatch(needle);
|
||||
|
||||
int actual = cm.Match(needle, reverse: true);
|
||||
Assert.Equal(0, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EqualLengthMismatchedArray_NoMatch()
|
||||
{
|
||||
byte[] needle = [0x01, 0x02, 0x03, 0x04];
|
||||
var cm = new ContentMatch(needle);
|
||||
|
||||
int actual = cm.Match(new byte[4]);
|
||||
Assert.Equal(-1, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EqualLengthMismatchedArrayReverse_NoMatch()
|
||||
{
|
||||
byte[] needle = [0x01, 0x02, 0x03, 0x04];
|
||||
var cm = new ContentMatch(needle);
|
||||
|
||||
int actual = cm.Match(new byte[4], reverse: true);
|
||||
Assert.Equal(-1, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InequalLengthMatchingArray_Match()
|
||||
{
|
||||
byte[] stack = [0x01, 0x02, 0x03, 0x04];
|
||||
byte[] needle = [0x02, 0x03];
|
||||
var cm = new ContentMatch(needle);
|
||||
|
||||
int actual = cm.Match(stack);
|
||||
Assert.Equal(1, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InequalLengthMatchingArrayReverse_Match()
|
||||
{
|
||||
byte[] stack = [0x01, 0x02, 0x03, 0x04];
|
||||
byte[] needle = [0x02, 0x03];
|
||||
var cm = new ContentMatch(needle);
|
||||
|
||||
int actual = cm.Match(stack, reverse: true);
|
||||
Assert.Equal(1, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InequalLengthMismatchedArray_NoMatch()
|
||||
{
|
||||
byte[] stack = [0x01, 0x02, 0x03, 0x04];
|
||||
byte[] needle = [0x02, 0x04];
|
||||
var cm = new ContentMatch(needle);
|
||||
|
||||
int actual = cm.Match(stack);
|
||||
Assert.Equal(-1, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InequalLengthMismatchedArrayReverse_NoMatch()
|
||||
{
|
||||
byte[] stack = [0x01, 0x02, 0x03, 0x04];
|
||||
byte[] needle = [0x02, 0x04];
|
||||
var cm = new ContentMatch(needle);
|
||||
|
||||
int actual = cm.Match(stack, reverse: true);
|
||||
Assert.Equal(-1, actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Stream
|
||||
|
||||
[Fact]
|
||||
public void NullStream_NoMatch()
|
||||
{
|
||||
var cm = new ContentMatch(new byte?[1]);
|
||||
int actual = cm.Match((Stream?)null);
|
||||
Assert.Equal(-1, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EmptyStream_NoMatch()
|
||||
{
|
||||
var cm = new ContentMatch(new byte?[1]);
|
||||
int actual = cm.Match(new MemoryStream());
|
||||
Assert.Equal(-1, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LargerNeedleStream_NoMatch()
|
||||
{
|
||||
var cm = new ContentMatch(new byte?[2]);
|
||||
int actual = cm.Match(new MemoryStream(new byte[1]));
|
||||
Assert.Equal(-1, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EqualLengthMatchingStream_Match()
|
||||
{
|
||||
byte[] needle = [0x01, 0x02, 0x03, 0x04];
|
||||
var cm = new ContentMatch(needle);
|
||||
|
||||
int actual = cm.Match(new MemoryStream(needle));
|
||||
Assert.Equal(0, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EqualLengthMatchingStreamReverse_Match()
|
||||
{
|
||||
byte[] needle = [0x01, 0x02, 0x03, 0x04];
|
||||
var cm = new ContentMatch(needle);
|
||||
|
||||
int actual = cm.Match(new MemoryStream(needle), reverse: true);
|
||||
Assert.Equal(0, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EqualLengthMismatchedStream_NoMatch()
|
||||
{
|
||||
byte[] needle = [0x01, 0x02, 0x03, 0x04];
|
||||
var cm = new ContentMatch(needle);
|
||||
|
||||
int actual = cm.Match(new MemoryStream(new byte[4]));
|
||||
Assert.Equal(-1, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EqualLengthMismatchedStreamReverse_NoMatch()
|
||||
{
|
||||
byte[] needle = [0x01, 0x02, 0x03, 0x04];
|
||||
var cm = new ContentMatch(needle);
|
||||
|
||||
int actual = cm.Match(new MemoryStream(new byte[4]), reverse: true);
|
||||
Assert.Equal(-1, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InequalLengthMatchingStream_Match()
|
||||
{
|
||||
Stream stack = new MemoryStream([0x01, 0x02, 0x03, 0x04]);
|
||||
byte[] needle = [0x02, 0x03];
|
||||
var cm = new ContentMatch(needle);
|
||||
|
||||
int actual = cm.Match(stack);
|
||||
Assert.Equal(1, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InequalLengthMatchingStreamReverse_Match()
|
||||
{
|
||||
Stream stack = new MemoryStream([0x01, 0x02, 0x03, 0x04]);
|
||||
byte[] needle = [0x02, 0x03];
|
||||
var cm = new ContentMatch(needle);
|
||||
|
||||
int actual = cm.Match(stack, reverse: true);
|
||||
Assert.Equal(1, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InequalLengthMismatchedStream_NoMatch()
|
||||
{
|
||||
Stream stack = new MemoryStream([0x01, 0x02, 0x03, 0x04]);
|
||||
byte[] needle = [0x02, 0x04];
|
||||
var cm = new ContentMatch(needle);
|
||||
|
||||
int actual = cm.Match(stack);
|
||||
Assert.Equal(-1, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InequalLengthMismatchedStreamReverse_NoMatch()
|
||||
{
|
||||
Stream stack = new MemoryStream([0x01, 0x02, 0x03, 0x04]);
|
||||
byte[] needle = [0x02, 0x04];
|
||||
var cm = new ContentMatch(needle);
|
||||
|
||||
int actual = cm.Match(stack, reverse: true);
|
||||
Assert.Equal(-1, actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
22
SabreTools.IO.Test/Matching/FilePathMatchTests.cs
Normal file
22
SabreTools.IO.Test/Matching/FilePathMatchTests.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using System.IO;
|
||||
using SabreTools.IO.Matching;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.IO.Test.Matching
|
||||
{
|
||||
/// <remarks>
|
||||
/// All other test cases are covered by <see cref="PathMatchTests"/>
|
||||
/// </remarks>
|
||||
public class FilePathMatchTests
|
||||
{
|
||||
[Fact]
|
||||
public void ConstructorFormatsNeedle()
|
||||
{
|
||||
string needle = "test";
|
||||
string expected = $"{Path.DirectorySeparatorChar}{needle}";
|
||||
|
||||
var fpm = new FilePathMatch(needle);
|
||||
Assert.Equal(expected, fpm.Needle);
|
||||
}
|
||||
}
|
||||
}
|
||||
186
SabreTools.IO.Test/Matching/PathMatchSetTests.cs
Normal file
186
SabreTools.IO.Test/Matching/PathMatchSetTests.cs
Normal file
@@ -0,0 +1,186 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SabreTools.IO.Matching;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.IO.Test.Matching
|
||||
{
|
||||
public class PathMatchSetTests
|
||||
{
|
||||
[Fact]
|
||||
public void InvalidNeedle_ThrowsException()
|
||||
{
|
||||
Assert.Throws<InvalidDataException>(() => new PathMatchSet(string.Empty, "name"));
|
||||
Assert.Throws<InvalidDataException>(() => new PathMatchSet(string.Empty, PathVersionMock, "name"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvalidNeedles_ThrowsException()
|
||||
{
|
||||
Assert.Throws<InvalidDataException>(() => new PathMatchSet([], "name"));
|
||||
Assert.Throws<InvalidDataException>(() => new PathMatchSet([], PathVersionMock, "name"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GenericConstructor_NoDelegates()
|
||||
{
|
||||
var needles = new List<PathMatch> { "test" };
|
||||
var cms = new PathMatchSet(needles, "name");
|
||||
Assert.Null(cms.GetVersion);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VersionConstructor_SingleDelegate()
|
||||
{
|
||||
var needles = new List<PathMatch> { "test" };
|
||||
var cms = new PathMatchSet(needles, PathVersionMock, "name");
|
||||
Assert.NotNull(cms.GetVersion);
|
||||
}
|
||||
|
||||
#region Array
|
||||
|
||||
[Fact]
|
||||
public void MatchesAll_NullArray_NoMatches()
|
||||
{
|
||||
var cms = new PathMatchSet("test", "name");
|
||||
var actual = cms.MatchesAll((string[]?)null);
|
||||
Assert.Empty(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MatchesAll_EmptyArray_NoMatches()
|
||||
{
|
||||
var cms = new PathMatchSet("test", "name");
|
||||
var actual = cms.MatchesAll(Array.Empty<string>());
|
||||
Assert.Empty(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MatchesAll_MatchingArray_Matches()
|
||||
{
|
||||
var cms = new PathMatchSet("test", "name");
|
||||
var actual = cms.MatchesAll(new string[] { "test" });
|
||||
string path = Assert.Single(actual);
|
||||
Assert.Equal("test", path);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MatchesAll_MismatchedArray_NoMatches()
|
||||
{
|
||||
var cms = new PathMatchSet("test", "name");
|
||||
var actual = cms.MatchesAll(new string[] { "not" });
|
||||
Assert.Empty(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MatchesAny_NullArray_NoMatches()
|
||||
{
|
||||
var cms = new PathMatchSet("test", "name");
|
||||
string? actual = cms.MatchesAny((string[]?)null);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MatchesAny_EmptyArray_NoMatches()
|
||||
{
|
||||
var cms = new PathMatchSet("test", "name");
|
||||
string? actual = cms.MatchesAny(Array.Empty<string>());
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MatchesAny_MatchingArray_Matches()
|
||||
{
|
||||
var cms = new PathMatchSet("test", "name");
|
||||
string? actual = cms.MatchesAny(new string[] { "test" });
|
||||
Assert.Equal("test", actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MatchesAny_MismatchedArray_NoMatches()
|
||||
{
|
||||
var cms = new PathMatchSet("test", "name");
|
||||
string? actual = cms.MatchesAny(new string[] { "not" });
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region List
|
||||
|
||||
[Fact]
|
||||
public void MatchesAll_NullList_NoMatches()
|
||||
{
|
||||
var cms = new PathMatchSet("test", "name");
|
||||
var actual = cms.MatchesAll((List<string>?)null);
|
||||
Assert.Empty(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MatchesAll_EmptyList_NoMatches()
|
||||
{
|
||||
var cms = new PathMatchSet("test", "name");
|
||||
var actual = cms.MatchesAll(new List<string>());
|
||||
Assert.Empty(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MatchesAll_MatchingList_Matches()
|
||||
{
|
||||
var cms = new PathMatchSet("test", "name");
|
||||
var actual = cms.MatchesAll(new List<string> { "test" });
|
||||
string path = Assert.Single(actual);
|
||||
Assert.Equal("test", path);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MatchesAll_MismatchedList_NoMatches()
|
||||
{
|
||||
var cms = new PathMatchSet("test", "name");
|
||||
var actual = cms.MatchesAll(new List<string> { "not" });
|
||||
Assert.Empty(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MatchesAny_NullList_NoMatches()
|
||||
{
|
||||
var cms = new PathMatchSet("test", "name");
|
||||
string? actual = cms.MatchesAny((List<string>?)null);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MatchesAny_EmptyList_NoMatches()
|
||||
{
|
||||
var cms = new PathMatchSet("test", "name");
|
||||
string? actual = cms.MatchesAny(new List<string>());
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MatchesAny_MatchingList_Matches()
|
||||
{
|
||||
var cms = new PathMatchSet("test", "name");
|
||||
string? actual = cms.MatchesAny(new List<string> { "test" });
|
||||
Assert.Equal("test", actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MatchesAny_MismatchedList_NoMatches()
|
||||
{
|
||||
var cms = new PathMatchSet("test", "name");
|
||||
string? actual = cms.MatchesAny(new List<string> { "not" });
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Mock Delegates
|
||||
|
||||
/// <inheritdoc cref="GetPathVersion"/>
|
||||
private static string? PathVersionMock(string path, List<string>? files) => null;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
293
SabreTools.IO.Test/Matching/PathMatchTests.cs
Normal file
293
SabreTools.IO.Test/Matching/PathMatchTests.cs
Normal file
@@ -0,0 +1,293 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SabreTools.IO.Matching;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.IO.Test.Matching
|
||||
{
|
||||
public class PathMatchTests
|
||||
{
|
||||
[Fact]
|
||||
public void InvalidNeedle_ThrowsException()
|
||||
{
|
||||
Assert.Throws<InvalidDataException>(() => new PathMatch(string.Empty));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ImplicitOperatorArray_Success()
|
||||
{
|
||||
string needle = "test";
|
||||
var pm = (PathMatch)needle;
|
||||
Assert.NotNull(pm);
|
||||
}
|
||||
|
||||
#region Array
|
||||
|
||||
[Fact]
|
||||
public void NullArray_NoMatch()
|
||||
{
|
||||
var pm = new PathMatch("test");
|
||||
string? actual = pm.Match((string[]?)null);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EmptyArray_NoMatch()
|
||||
{
|
||||
var pm = new PathMatch("test");
|
||||
string? actual = pm.Match(Array.Empty<string>());
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SingleItemArrayMatching_Match()
|
||||
{
|
||||
string needle = "test";
|
||||
string[] stack = [needle];
|
||||
var pm = new PathMatch(needle);
|
||||
|
||||
string? actual = pm.Match(stack);
|
||||
Assert.Equal(needle, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SingleItemArrayMismatched_NoMatch()
|
||||
{
|
||||
string needle = "test";
|
||||
string[] stack = ["not"];
|
||||
var pm = new PathMatch(needle);
|
||||
|
||||
string? actual = pm.Match(stack);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MultiItemArrayMatching_Match()
|
||||
{
|
||||
string needle = "test";
|
||||
string[] stack = ["not", needle, "far"];
|
||||
var pm = new PathMatch(needle);
|
||||
|
||||
string? actual = pm.Match(stack);
|
||||
Assert.Equal(needle, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MultiItemArrayMismatched_NoMatch()
|
||||
{
|
||||
string needle = "test";
|
||||
string[] stack = ["not", "too", "far"];
|
||||
var pm = new PathMatch(needle);
|
||||
|
||||
string? actual = pm.Match(stack);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region List
|
||||
|
||||
[Fact]
|
||||
public void NullList_NoMatch()
|
||||
{
|
||||
var pm = new PathMatch("test");
|
||||
string? actual = pm.Match((List<string>?)null);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EmptyList_NoMatch()
|
||||
{
|
||||
var pm = new PathMatch("test");
|
||||
string? actual = pm.Match(new List<string>());
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SingleItemListMatching_Match()
|
||||
{
|
||||
string needle = "test";
|
||||
List<string> stack = [needle];
|
||||
var pm = new PathMatch(needle);
|
||||
|
||||
string? actual = pm.Match(stack);
|
||||
Assert.Equal(needle, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SingleItemListMismatched_NoMatch()
|
||||
{
|
||||
string needle = "test";
|
||||
List<string> stack = ["not"];
|
||||
var pm = new PathMatch(needle);
|
||||
|
||||
string? actual = pm.Match(stack);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MultiItemListMatching_Match()
|
||||
{
|
||||
string needle = "test";
|
||||
List<string> stack = ["not", needle, "far"];
|
||||
var pm = new PathMatch(needle);
|
||||
|
||||
string? actual = pm.Match(stack);
|
||||
Assert.Equal(needle, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MultiItemListMismatched_NoMatch()
|
||||
{
|
||||
string needle = "test";
|
||||
List<string> stack = ["not", "too", "far"];
|
||||
var pm = new PathMatch(needle);
|
||||
|
||||
string? actual = pm.Match(stack);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Match Case
|
||||
|
||||
[Fact]
|
||||
public void MatchCaseEqual_Match()
|
||||
{
|
||||
string needle = "test";
|
||||
List<string> stack = [needle];
|
||||
var pm = new PathMatch(needle, matchCase: true);
|
||||
|
||||
string? actual = pm.Match(stack);
|
||||
Assert.Equal(needle, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NoMatchCaseEqual_Match()
|
||||
{
|
||||
string needle = "test";
|
||||
List<string> stack = [needle];
|
||||
var pm = new PathMatch(needle, matchCase: false);
|
||||
|
||||
string? actual = pm.Match(stack);
|
||||
Assert.Equal(needle, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MatchCaseInequal_NoMatch()
|
||||
{
|
||||
string needle = "test";
|
||||
List<string> stack = [needle.ToUpperInvariant()];
|
||||
var pm = new PathMatch(needle, matchCase: true);
|
||||
|
||||
string? actual = pm.Match(stack);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NoMatchCaseInequal_Match()
|
||||
{
|
||||
string needle = "test";
|
||||
List<string> stack = [needle.ToUpperInvariant()];
|
||||
var pm = new PathMatch(needle, matchCase: false);
|
||||
|
||||
string? actual = pm.Match(stack);
|
||||
Assert.Equal(needle.ToUpperInvariant(), actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MatchCaseContains_Match()
|
||||
{
|
||||
string needle = "test";
|
||||
List<string> stack = [$"prefix_{needle}_postfix"];
|
||||
var pm = new PathMatch(needle, matchCase: true);
|
||||
|
||||
string? actual = pm.Match(stack);
|
||||
Assert.Equal($"prefix_{needle}_postfix", actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NoMatchCaseContains_Match()
|
||||
{
|
||||
string needle = "test";
|
||||
List<string> stack = [$"prefix_{needle}_postfix"];
|
||||
var pm = new PathMatch(needle, matchCase: false);
|
||||
|
||||
string? actual = pm.Match(stack);
|
||||
Assert.Equal($"prefix_{needle}_postfix", actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Use Ends With
|
||||
|
||||
[Fact]
|
||||
public void EndsWithEqual_Match()
|
||||
{
|
||||
string needle = "test";
|
||||
List<string> stack = [needle];
|
||||
var pm = new PathMatch(needle, useEndsWith: true);
|
||||
|
||||
string? actual = pm.Match(stack);
|
||||
Assert.Equal(needle, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NoEndsWithEqual_Match()
|
||||
{
|
||||
string needle = "test";
|
||||
List<string> stack = [needle];
|
||||
var pm = new PathMatch(needle, useEndsWith: false);
|
||||
|
||||
string? actual = pm.Match(stack);
|
||||
Assert.Equal(needle, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EndsWithInequal_Match()
|
||||
{
|
||||
string needle = "test";
|
||||
List<string> stack = [needle.ToUpperInvariant()];
|
||||
var pm = new PathMatch(needle, useEndsWith: true);
|
||||
|
||||
string? actual = pm.Match(stack);
|
||||
Assert.Equal(needle.ToUpperInvariant(), actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NoEndsWithInequal_Match()
|
||||
{
|
||||
string needle = "test";
|
||||
List<string> stack = [needle.ToUpperInvariant()];
|
||||
var pm = new PathMatch(needle, useEndsWith: false);
|
||||
|
||||
string? actual = pm.Match(stack);
|
||||
Assert.Equal(needle.ToUpperInvariant(), actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EndsWithContains_NoMatch()
|
||||
{
|
||||
string needle = "test";
|
||||
List<string> stack = [$"prefix_{needle}_postfix"];
|
||||
var pm = new PathMatch(needle, useEndsWith: true);
|
||||
|
||||
string? actual = pm.Match(stack);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NoEndsWithContains_Match()
|
||||
{
|
||||
string needle = "test";
|
||||
List<string> stack = [$"prefix_{needle}_postfix"];
|
||||
var pm = new PathMatch(needle, useEndsWith: false);
|
||||
|
||||
string? actual = pm.Match(stack);
|
||||
Assert.Equal($"prefix_{needle}_postfix", actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -84,4 +84,4 @@ namespace SabreTools.IO.Test
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,5 +88,34 @@ namespace SabreTools.IO.Test
|
||||
Assert.Equal(expectedCurrent, first.CurrentPath);
|
||||
Assert.Equal(string.Empty, first.ParentPath);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, false, "")]
|
||||
[InlineData(null, true, "")]
|
||||
[InlineData("", false, "")]
|
||||
[InlineData("", true, "")]
|
||||
[InlineData("filename.bin", false, "filename.bin")]
|
||||
[InlineData("filename.bin", true, "filename.bin")]
|
||||
[InlineData("\"filename.bin\"", false, "filename.bin")]
|
||||
[InlineData("\"filename.bin\"", true, "filename.bin")]
|
||||
[InlineData("<filename.bin>", false, "filename.bin")]
|
||||
[InlineData("<filename.bin>", true, "filename.bin")]
|
||||
[InlineData("1.2.3.4..bin", false, "1.2.3.4..bin")]
|
||||
[InlineData("1.2.3.4..bin", true, "1.2.3.4..bin")]
|
||||
[InlineData("dir/filename.bin", false, "dir/filename.bin")]
|
||||
[InlineData("dir/filename.bin", true, "dir/filename.bin")]
|
||||
[InlineData(" dir / filename.bin", false, "dir/filename.bin")]
|
||||
[InlineData(" dir / filename.bin", true, "dir/filename.bin")]
|
||||
[InlineData("\0dir/\0filename.bin", false, "_dir/_filename.bin")]
|
||||
[InlineData("\0dir/\0filename.bin", true, "_dir/_filename.bin")]
|
||||
public void NormalizeOutputPathsTest(string? path, bool getFullPath, string expected)
|
||||
{
|
||||
// Modify expected to account for test data if necessary
|
||||
if (getFullPath && !string.IsNullOrEmpty(expected))
|
||||
expected = Path.GetFullPath(expected);
|
||||
|
||||
string actual = PathTool.NormalizeFilePath(path, getFullPath);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,4 +57,4 @@ namespace SabreTools.IO.Test.ReadersWriters
|
||||
reader.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,4 +47,4 @@ namespace SabreTools.IO.Test.ReadersWriters
|
||||
reader.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,4 +57,4 @@ namespace SabreTools.IO.Test.ReadersWriters
|
||||
reader.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0;net8.0;net9.0</TargetFrameworks>
|
||||
<TargetFrameworks>net8.0;net9.0;net10.0</TargetFrameworks>
|
||||
<IsPackable>false</IsPackable>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
@@ -20,13 +20,13 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.2">
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.4">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
|
||||
<PackageReference Include="xunit" Version="2.9.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
|
||||
<PackageReference Include="xunit" Version="2.9.3" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
|
||||
40
SabreTools.IO.Test/Streams/BufferedStreamTests.cs
Normal file
40
SabreTools.IO.Test/Streams/BufferedStreamTests.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using System.IO;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.IO.Test.Streams
|
||||
{
|
||||
public class BufferedStreamTests
|
||||
{
|
||||
#region ReadNextByte
|
||||
|
||||
[Fact]
|
||||
public void ReadNextByte_Empty_Null()
|
||||
{
|
||||
var source = new MemoryStream();
|
||||
var stream = new IO.Streams.BufferedStream(source);
|
||||
byte? actual = stream.ReadNextByte();
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadNextByte_Filled_ValidPosition_Byte()
|
||||
{
|
||||
var source = new MemoryStream(new byte[1024]);
|
||||
var stream = new IO.Streams.BufferedStream(source);
|
||||
byte? actual = stream.ReadNextByte();
|
||||
Assert.Equal((byte)0x00, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadNextByte_Filled_InvalidPosition_Null()
|
||||
{
|
||||
var source = new MemoryStream(new byte[1024]);
|
||||
source.Seek(0, SeekOrigin.End);
|
||||
var stream = new IO.Streams.BufferedStream(source);
|
||||
byte? actual = stream.ReadNextByte();
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -116,4 +116,4 @@ namespace SabreTools.IO.Test.Streams
|
||||
Assert.True(data.SequenceEqual(actual));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SabreTools.IO.Streams;
|
||||
@@ -7,8 +8,10 @@ namespace SabreTools.IO.Test.Streams
|
||||
{
|
||||
public class ReadOnlyCompositeStreamTests
|
||||
{
|
||||
#region Constructor
|
||||
|
||||
[Fact]
|
||||
public void DefaultConstructorTest()
|
||||
public void Constructor_Default()
|
||||
{
|
||||
var stream = new ReadOnlyCompositeStream();
|
||||
Assert.Equal(0, stream.Length);
|
||||
@@ -16,7 +19,7 @@ namespace SabreTools.IO.Test.Streams
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EmptyArrayConstructorTest()
|
||||
public void Constructor_EmptyArray()
|
||||
{
|
||||
Stream[] arr = [new MemoryStream()];
|
||||
var stream = new ReadOnlyCompositeStream(arr);
|
||||
@@ -25,9 +28,8 @@ namespace SabreTools.IO.Test.Streams
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EmptyEnumerableConstructorTest()
|
||||
public void Constructor_EmptyEnumerable()
|
||||
{
|
||||
// Empty enumerable constructor
|
||||
List<Stream> list = [new MemoryStream()];
|
||||
var stream = new ReadOnlyCompositeStream(list);
|
||||
Assert.Equal(0, stream.Length);
|
||||
@@ -35,7 +37,7 @@ namespace SabreTools.IO.Test.Streams
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SingleStreamConstructorTest()
|
||||
public void Constructor_SingleStream()
|
||||
{
|
||||
var stream = new ReadOnlyCompositeStream(new MemoryStream(new byte[1024]));
|
||||
Assert.Equal(1024, stream.Length);
|
||||
@@ -43,7 +45,7 @@ namespace SabreTools.IO.Test.Streams
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FilledArrayConstructorTest()
|
||||
public void Constructor_FilledArray()
|
||||
{
|
||||
Stream[] arr = [new MemoryStream(new byte[1024]), new MemoryStream(new byte[1024])];
|
||||
var stream = new ReadOnlyCompositeStream(arr);
|
||||
@@ -52,14 +54,18 @@ namespace SabreTools.IO.Test.Streams
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FilledEnumerableConstructorTest()
|
||||
public void Constructor_FilledEnumerable()
|
||||
{
|
||||
List<Stream> list = [new MemoryStream(new byte[1024]), new MemoryStream(new byte[1024])];
|
||||
var stream = new ReadOnlyCompositeStream(list);
|
||||
Assert.Equal(2048, stream.Length);
|
||||
Assert.Equal(0, stream.Position);
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region AddStream
|
||||
|
||||
[Fact]
|
||||
public void AddStreamTest()
|
||||
{
|
||||
@@ -70,10 +76,18 @@ namespace SabreTools.IO.Test.Streams
|
||||
stream.AddStream(new MemoryStream(new byte[1024]));
|
||||
Assert.Equal(1024, stream.Length);
|
||||
Assert.Equal(0, stream.Position);
|
||||
|
||||
stream.AddStream(new MemoryStream([]));
|
||||
Assert.Equal(1024, stream.Length);
|
||||
Assert.Equal(0, stream.Position);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Read
|
||||
|
||||
[Fact]
|
||||
public void EmptyStreamReadTest()
|
||||
public void Read_EmptyStream()
|
||||
{
|
||||
var stream = new ReadOnlyCompositeStream();
|
||||
|
||||
@@ -84,7 +98,7 @@ namespace SabreTools.IO.Test.Streams
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SingleStreamReadTest()
|
||||
public void Read_SingleStream()
|
||||
{
|
||||
Stream[] arr = [new MemoryStream(new byte[1024])];
|
||||
var stream = new ReadOnlyCompositeStream(arr);
|
||||
@@ -96,7 +110,7 @@ namespace SabreTools.IO.Test.Streams
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MultipleStreamSingleContainedReadTest()
|
||||
public void Read_MultipleStream_SingleContained()
|
||||
{
|
||||
Stream[] arr = [new MemoryStream(new byte[1024]), new MemoryStream(new byte[1024])];
|
||||
var stream = new ReadOnlyCompositeStream(arr);
|
||||
@@ -108,7 +122,7 @@ namespace SabreTools.IO.Test.Streams
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MultipleStreamMultipleContainedReadTest()
|
||||
public void Read_MultipleStream_MultipleContained()
|
||||
{
|
||||
Stream[] arr = [new MemoryStream(new byte[256]), new MemoryStream(new byte[256])];
|
||||
var stream = new ReadOnlyCompositeStream(arr);
|
||||
@@ -120,7 +134,7 @@ namespace SabreTools.IO.Test.Streams
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SingleStreamExtraReadTest()
|
||||
public void Read_SingleStream_Extra()
|
||||
{
|
||||
Stream[] arr = [new MemoryStream(new byte[256])];
|
||||
var stream = new ReadOnlyCompositeStream(arr);
|
||||
@@ -132,7 +146,7 @@ namespace SabreTools.IO.Test.Streams
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MultipleStreamExtraReadTest()
|
||||
public void Read_MultipleStream_Extra()
|
||||
{
|
||||
Stream[] arr = [new MemoryStream(new byte[128]), new MemoryStream(new byte[128])];
|
||||
var stream = new ReadOnlyCompositeStream(arr);
|
||||
@@ -142,5 +156,32 @@ namespace SabreTools.IO.Test.Streams
|
||||
|
||||
Assert.Equal(256, read);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unimplemented
|
||||
|
||||
[Fact]
|
||||
public void Flush_Throws()
|
||||
{
|
||||
var stream = new ReadOnlyCompositeStream();
|
||||
Assert.Throws<NotImplementedException>(() => stream.Flush());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SetLength_Throws()
|
||||
{
|
||||
var stream = new ReadOnlyCompositeStream();
|
||||
Assert.Throws<NotImplementedException>(() => stream.SetLength(0));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Write_Throws()
|
||||
{
|
||||
var stream = new ReadOnlyCompositeStream();
|
||||
Assert.Throws<NotImplementedException>(() => stream.Write([], 0, 0));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
418
SabreTools.IO.Test/Streams/ViewStreamTests.cs
Normal file
418
SabreTools.IO.Test/Streams/ViewStreamTests.cs
Normal file
@@ -0,0 +1,418 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SabreTools.IO.Streams;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.IO.Test.Streams
|
||||
{
|
||||
public class ViewStreamTests
|
||||
{
|
||||
#region Constructor
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, 0, 0)]
|
||||
[InlineData(1024, 0, 1024)]
|
||||
[InlineData(1024, 256, 768)]
|
||||
public void Constructor_Array(int size, long offset, long expectedLength)
|
||||
{
|
||||
byte[] data = new byte[size];
|
||||
var stream = new ViewStream(data, offset);
|
||||
Assert.Equal(expectedLength, stream.Length);
|
||||
Assert.Equal(0, stream.Position);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, 0, 0, 0)]
|
||||
[InlineData(1024, 0, 1024, 1024)]
|
||||
[InlineData(1024, 256, 512, 512)]
|
||||
public void Constructor_Array_Length(int size, long offset, long length, long expectedLength)
|
||||
{
|
||||
byte[] data = new byte[size];
|
||||
var stream = new ViewStream(data, offset, length);
|
||||
Assert.Equal(expectedLength, stream.Length);
|
||||
Assert.Equal(0, stream.Position);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, -1, 0)]
|
||||
[InlineData(0, 2048, 0)]
|
||||
[InlineData(1024, -1, 1024)]
|
||||
[InlineData(1024, 2048, 1024)]
|
||||
[InlineData(1024, -1, 512)]
|
||||
[InlineData(1024, 2048, 512)]
|
||||
public void Constructor_Array_InvalidOffset(int size, long offset, long length)
|
||||
{
|
||||
byte[] data = new byte[size];
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => _ = new ViewStream(data, offset, length));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, 0, -1)]
|
||||
[InlineData(0, 0, 2048)]
|
||||
[InlineData(1024, 0, -1)]
|
||||
[InlineData(1024, 0, 2048)]
|
||||
[InlineData(1024, 256, -1)]
|
||||
[InlineData(1024, 256, 2048)]
|
||||
public void Constructor_Array_InvalidLength(int size, long offset, long length)
|
||||
{
|
||||
byte[] data = new byte[size];
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => _ = new ViewStream(data, offset, length));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, 0, 0)]
|
||||
[InlineData(1024, 0, 1024)]
|
||||
[InlineData(1024, 256, 768)]
|
||||
public void Constructor_Stream(int size, long offset, long expectedLength)
|
||||
{
|
||||
Stream data = new MemoryStream(new byte[size]);
|
||||
var stream = new ViewStream(data, offset);
|
||||
Assert.Equal(expectedLength, stream.Length);
|
||||
Assert.Equal(0, stream.Position);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, 0, 0, 0)]
|
||||
[InlineData(1024, 0, 1024, 1024)]
|
||||
[InlineData(1024, 256, 512, 512)]
|
||||
public void Constructor_Stream_Length(int size, long offset, long length, long expectedLength)
|
||||
{
|
||||
Stream data = new MemoryStream(new byte[size]);
|
||||
var stream = new ViewStream(data, offset, length);
|
||||
Assert.Equal(expectedLength, stream.Length);
|
||||
Assert.Equal(0, stream.Position);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, -1, 0)]
|
||||
[InlineData(0, 2048, 0)]
|
||||
[InlineData(1024, -1, 1024)]
|
||||
[InlineData(1024, 2048, 1024)]
|
||||
[InlineData(1024, -1, 512)]
|
||||
[InlineData(1024, 2048, 512)]
|
||||
public void Constructor_Stream_InvalidOffset(int size, long offset, long length)
|
||||
{
|
||||
Stream data = new MemoryStream(new byte[size]);
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => _ = new ViewStream(data, offset, length));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, 0, -1)]
|
||||
[InlineData(0, 0, 2048)]
|
||||
[InlineData(1024, 0, -1)]
|
||||
[InlineData(1024, 0, 2048)]
|
||||
[InlineData(1024, 256, -1)]
|
||||
[InlineData(1024, 256, 2048)]
|
||||
public void Constructor_Stream_InvalidLength(int size, long offset, long length)
|
||||
{
|
||||
Stream data = new MemoryStream(new byte[size]);
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => _ = new ViewStream(data, offset, length));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Position
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, 0, 0, -1, 0)]
|
||||
[InlineData(0, 0, 0, 0, 0)]
|
||||
[InlineData(0, 0, 0, 256, 0)]
|
||||
[InlineData(0, 0, 0, 2048, 0)]
|
||||
[InlineData(1024, 0, 1024, -1, 0)]
|
||||
[InlineData(1024, 0, 1024, 0, 0)]
|
||||
[InlineData(1024, 0, 1024, 256, 256)]
|
||||
[InlineData(1024, 0, 1024, 2048, 1023)]
|
||||
[InlineData(1024, 256, 512, -1, 0)]
|
||||
[InlineData(1024, 256, 512, 0, 0)]
|
||||
[InlineData(1024, 256, 512, 256, 256)]
|
||||
[InlineData(1024, 256, 512, 2048, 511)]
|
||||
public void Position_Array(int size, long offset, long length, long position, long expectedPosition)
|
||||
{
|
||||
byte[] data = new byte[size];
|
||||
var stream = new ViewStream(data, offset, length);
|
||||
stream.Position = position;
|
||||
Assert.Equal(expectedPosition, stream.Position);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, 0, 0, -1, 0)]
|
||||
[InlineData(0, 0, 0, 0, 0)]
|
||||
[InlineData(0, 0, 0, 256, 0)]
|
||||
[InlineData(0, 0, 0, 2048, 0)]
|
||||
[InlineData(1024, 0, 1024, -1, 0)]
|
||||
[InlineData(1024, 0, 1024, 0, 0)]
|
||||
[InlineData(1024, 0, 1024, 256, 256)]
|
||||
[InlineData(1024, 0, 1024, 2048, 1023)]
|
||||
[InlineData(1024, 256, 512, -1, 0)]
|
||||
[InlineData(1024, 256, 512, 0, 0)]
|
||||
[InlineData(1024, 256, 512, 256, 256)]
|
||||
[InlineData(1024, 256, 512, 2048, 511)]
|
||||
public void Position_Stream(int size, long offset, long length, long position, long expectedPosition)
|
||||
{
|
||||
Stream data = new MemoryStream(new byte[size]);
|
||||
var stream = new ViewStream(data, offset, length);
|
||||
stream.Position = position;
|
||||
Assert.Equal(expectedPosition, stream.Position);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region SegmentValid
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, 0, 0, -1, 0, false)]
|
||||
[InlineData(0, 0, 0, 2048, 0, false)]
|
||||
[InlineData(0, 0, 0, 0, 0, true)]
|
||||
[InlineData(0, 0, 0, 0, -1, false)]
|
||||
[InlineData(0, 0, 0, 0, 2048, false)]
|
||||
[InlineData(1024, 0, 1024, -1, 0, false)]
|
||||
[InlineData(1024, 0, 1024, 2048, 0, false)]
|
||||
[InlineData(1024, 0, 1024, 0, 0, true)]
|
||||
[InlineData(1024, 0, 1024, 256, 0, true)]
|
||||
[InlineData(1024, 0, 1024, 256, 256, true)]
|
||||
[InlineData(1024, 0, 1024, 0, -1, false)]
|
||||
[InlineData(1024, 0, 1024, 0, 2048, false)]
|
||||
[InlineData(1024, 256, 512, -1, 0, false)]
|
||||
[InlineData(1024, 256, 512, 2048, 0, false)]
|
||||
[InlineData(1024, 256, 512, 0, 0, true)]
|
||||
[InlineData(1024, 256, 512, 256, 0, true)]
|
||||
[InlineData(1024, 256, 512, 256, 256, true)]
|
||||
[InlineData(1024, 256, 512, 0, -1, false)]
|
||||
[InlineData(1024, 256, 512, 0, 2048, false)]
|
||||
public void SegmentValid_Array(int size, long offset, long length, int segmentStart, int segmentLength, bool expected)
|
||||
{
|
||||
byte[] data = new byte[size];
|
||||
var stream = new ViewStream(data, offset, length);
|
||||
bool actual = stream.SegmentValid(segmentStart, segmentLength);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, 0, 0, -1, 0, false)]
|
||||
[InlineData(0, 0, 0, 2048, 0, false)]
|
||||
[InlineData(0, 0, 0, 0, 0, true)]
|
||||
[InlineData(0, 0, 0, 0, -1, false)]
|
||||
[InlineData(0, 0, 0, 0, 2048, false)]
|
||||
[InlineData(1024, 0, 1024, -1, 0, false)]
|
||||
[InlineData(1024, 0, 1024, 2048, 0, false)]
|
||||
[InlineData(1024, 0, 1024, 0, 0, true)]
|
||||
[InlineData(1024, 0, 1024, 256, 0, true)]
|
||||
[InlineData(1024, 0, 1024, 256, 256, true)]
|
||||
[InlineData(1024, 0, 1024, 0, -1, false)]
|
||||
[InlineData(1024, 0, 1024, 0, 2048, false)]
|
||||
[InlineData(1024, 256, 512, -1, 0, false)]
|
||||
[InlineData(1024, 256, 512, 2048, 0, false)]
|
||||
[InlineData(1024, 256, 512, 0, 0, true)]
|
||||
[InlineData(1024, 256, 512, 256, 0, true)]
|
||||
[InlineData(1024, 256, 512, 256, 256, true)]
|
||||
[InlineData(1024, 256, 512, 0, -1, false)]
|
||||
[InlineData(1024, 256, 512, 0, 2048, false)]
|
||||
public void SegmentValid_Stream(int size, long offset, long length, int segmentStart, int segmentLength, bool expected)
|
||||
{
|
||||
Stream data = new MemoryStream(new byte[size]);
|
||||
var stream = new ViewStream(data, offset, length);
|
||||
bool actual = stream.SegmentValid(segmentStart, segmentLength);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Read
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, 0, 0, -1, 0)]
|
||||
[InlineData(0, 0, 0, 0, 0)]
|
||||
[InlineData(0, 0, 0, 2048, 0)]
|
||||
[InlineData(1024, 0, 1024, -1, 0)]
|
||||
[InlineData(1024, 0, 1024, 0, 0)]
|
||||
[InlineData(1024, 0, 1024, 256, 256)]
|
||||
[InlineData(1024, 0, 1024, 1024, 1024)]
|
||||
[InlineData(1024, 0, 1024, 2048, 0)]
|
||||
[InlineData(1024, 256, 512, -1, 0)]
|
||||
[InlineData(1024, 256, 512, 0, 0)]
|
||||
[InlineData(1024, 256, 512, 256, 256)]
|
||||
[InlineData(1024, 256, 512, 512, 512)]
|
||||
[InlineData(1024, 256, 512, 2048, 0)]
|
||||
public void Read_Array(int size, long offset, long length, int count, int expectedRead)
|
||||
{
|
||||
byte[] data = new byte[size];
|
||||
var stream = new ViewStream(data, offset, length);
|
||||
|
||||
byte[] buffer = new byte[1024];
|
||||
int actual = stream.Read(buffer, 0, count);
|
||||
Assert.Equal(expectedRead, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, 0, 0, -1, 0)]
|
||||
[InlineData(0, 0, 0, 0, 0)]
|
||||
[InlineData(0, 0, 0, 2048, 0)]
|
||||
[InlineData(1024, 0, 1024, -1, 0)]
|
||||
[InlineData(1024, 0, 1024, 0, 0)]
|
||||
[InlineData(1024, 0, 1024, 256, 256)]
|
||||
[InlineData(1024, 0, 1024, 1024, 1024)]
|
||||
[InlineData(1024, 0, 1024, 2048, 0)]
|
||||
[InlineData(1024, 256, 512, -1, 0)]
|
||||
[InlineData(1024, 256, 512, 0, 0)]
|
||||
[InlineData(1024, 256, 512, 256, 256)]
|
||||
[InlineData(1024, 256, 512, 512, 512)]
|
||||
[InlineData(1024, 256, 512, 2048, 0)]
|
||||
public void Read_Stream(int size, long offset, long length, int count, int expectedRead)
|
||||
{
|
||||
Stream data = new MemoryStream(new byte[size]);
|
||||
var stream = new ViewStream(data, offset, length);
|
||||
|
||||
byte[] buffer = new byte[1024];
|
||||
int actual = stream.Read(buffer, 0, count);
|
||||
Assert.Equal(expectedRead, actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Seek
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, 0, 0, -1, SeekOrigin.Begin, 0)]
|
||||
[InlineData(0, 0, 0, -1, SeekOrigin.End, 0)]
|
||||
[InlineData(0, 0, 0, -1, SeekOrigin.Current, 0)]
|
||||
[InlineData(0, 0, 0, 0, SeekOrigin.Begin, 0)]
|
||||
[InlineData(0, 0, 0, 0, SeekOrigin.End, 0)]
|
||||
[InlineData(0, 0, 0, 0, SeekOrigin.Current, 0)]
|
||||
[InlineData(0, 0, 0, 256, SeekOrigin.Begin, 0)]
|
||||
[InlineData(0, 0, 0, 256, SeekOrigin.End, 0)]
|
||||
[InlineData(0, 0, 0, 256, SeekOrigin.Current, 0)]
|
||||
[InlineData(0, 0, 0, 2048, SeekOrigin.Begin, 0)]
|
||||
[InlineData(0, 0, 0, 2048, SeekOrigin.End, 0)]
|
||||
[InlineData(0, 0, 0, 2048, SeekOrigin.Current, 0)]
|
||||
[InlineData(1024, 0, 1024, -1, SeekOrigin.Begin, 0)]
|
||||
[InlineData(1024, 0, 1024, -1, SeekOrigin.End, 1022)]
|
||||
[InlineData(1024, 0, 1024, -1, SeekOrigin.Current, 0)]
|
||||
[InlineData(1024, 0, 1024, 0, SeekOrigin.Begin, 0)]
|
||||
[InlineData(1024, 0, 1024, 0, SeekOrigin.End, 1023)]
|
||||
[InlineData(1024, 0, 1024, 0, SeekOrigin.Current, 0)]
|
||||
[InlineData(1024, 0, 1024, 256, SeekOrigin.Begin, 256)]
|
||||
[InlineData(1024, 0, 1024, 256, SeekOrigin.End, 1023)]
|
||||
[InlineData(1024, 0, 1024, 256, SeekOrigin.Current, 256)]
|
||||
[InlineData(1024, 0, 1024, 2048, SeekOrigin.Begin, 1023)]
|
||||
[InlineData(1024, 0, 1024, 2048, SeekOrigin.End, 1023)]
|
||||
[InlineData(1024, 0, 1024, 2048, SeekOrigin.Current, 1023)]
|
||||
[InlineData(1024, 256, 512, -1, SeekOrigin.Begin, 0)]
|
||||
[InlineData(1024, 256, 512, -1, SeekOrigin.End, 510)]
|
||||
[InlineData(1024, 256, 512, -1, SeekOrigin.Current, 0)]
|
||||
[InlineData(1024, 256, 512, 0, SeekOrigin.Begin, 0)]
|
||||
[InlineData(1024, 256, 512, 0, SeekOrigin.End, 511)]
|
||||
[InlineData(1024, 256, 512, 0, SeekOrigin.Current, 0)]
|
||||
[InlineData(1024, 256, 512, 256, SeekOrigin.Begin, 256)]
|
||||
[InlineData(1024, 256, 512, 256, SeekOrigin.End, 511)]
|
||||
[InlineData(1024, 256, 512, 256, SeekOrigin.Current, 256)]
|
||||
[InlineData(1024, 256, 512, 2048, SeekOrigin.Begin, 511)]
|
||||
[InlineData(1024, 256, 512, 2048, SeekOrigin.End, 511)]
|
||||
[InlineData(1024, 256, 512, 2048, SeekOrigin.Current, 511)]
|
||||
public void Seek_Array(int size, long offset, long length, long position, SeekOrigin seekOrigin, long expectedPosition)
|
||||
{
|
||||
byte[] data = new byte[size];
|
||||
var stream = new ViewStream(data, offset, length);
|
||||
stream.Seek(position, seekOrigin);
|
||||
Assert.Equal(expectedPosition, stream.Position);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, 0, 0, -1, SeekOrigin.Begin, 0)]
|
||||
[InlineData(0, 0, 0, -1, SeekOrigin.End, 0)]
|
||||
[InlineData(0, 0, 0, -1, SeekOrigin.Current, 0)]
|
||||
[InlineData(0, 0, 0, 0, SeekOrigin.Begin, 0)]
|
||||
[InlineData(0, 0, 0, 0, SeekOrigin.End, 0)]
|
||||
[InlineData(0, 0, 0, 0, SeekOrigin.Current, 0)]
|
||||
[InlineData(0, 0, 0, 256, SeekOrigin.Begin, 0)]
|
||||
[InlineData(0, 0, 0, 256, SeekOrigin.End, 0)]
|
||||
[InlineData(0, 0, 0, 256, SeekOrigin.Current, 0)]
|
||||
[InlineData(0, 0, 0, 2048, SeekOrigin.Begin, 0)]
|
||||
[InlineData(0, 0, 0, 2048, SeekOrigin.End, 0)]
|
||||
[InlineData(0, 0, 0, 2048, SeekOrigin.Current, 0)]
|
||||
[InlineData(1024, 0, 1024, -1, SeekOrigin.Begin, 0)]
|
||||
[InlineData(1024, 0, 1024, -1, SeekOrigin.End, 1022)]
|
||||
[InlineData(1024, 0, 1024, -1, SeekOrigin.Current, 0)]
|
||||
[InlineData(1024, 0, 1024, 0, SeekOrigin.Begin, 0)]
|
||||
[InlineData(1024, 0, 1024, 0, SeekOrigin.End, 1023)]
|
||||
[InlineData(1024, 0, 1024, 0, SeekOrigin.Current, 0)]
|
||||
[InlineData(1024, 0, 1024, 256, SeekOrigin.Begin, 256)]
|
||||
[InlineData(1024, 0, 1024, 256, SeekOrigin.End, 1023)]
|
||||
[InlineData(1024, 0, 1024, 256, SeekOrigin.Current, 256)]
|
||||
[InlineData(1024, 0, 1024, 2048, SeekOrigin.Begin, 1023)]
|
||||
[InlineData(1024, 0, 1024, 2048, SeekOrigin.End, 1023)]
|
||||
[InlineData(1024, 0, 1024, 2048, SeekOrigin.Current, 1023)]
|
||||
[InlineData(1024, 256, 512, -1, SeekOrigin.Begin, 0)]
|
||||
[InlineData(1024, 256, 512, -1, SeekOrigin.End, 510)]
|
||||
[InlineData(1024, 256, 512, -1, SeekOrigin.Current, 0)]
|
||||
[InlineData(1024, 256, 512, 0, SeekOrigin.Begin, 0)]
|
||||
[InlineData(1024, 256, 512, 0, SeekOrigin.End, 511)]
|
||||
[InlineData(1024, 256, 512, 0, SeekOrigin.Current, 0)]
|
||||
[InlineData(1024, 256, 512, 256, SeekOrigin.Begin, 256)]
|
||||
[InlineData(1024, 256, 512, 256, SeekOrigin.End, 511)]
|
||||
[InlineData(1024, 256, 512, 256, SeekOrigin.Current, 256)]
|
||||
[InlineData(1024, 256, 512, 2048, SeekOrigin.Begin, 511)]
|
||||
[InlineData(1024, 256, 512, 2048, SeekOrigin.End, 511)]
|
||||
[InlineData(1024, 256, 512, 2048, SeekOrigin.Current, 511)]
|
||||
public void Seek_Stream(int size, long offset, long length, long position, SeekOrigin seekOrigin, long expectedPosition)
|
||||
{
|
||||
Stream data = new MemoryStream(new byte[size]);
|
||||
var stream = new ViewStream(data, offset, length);
|
||||
stream.Seek(position, seekOrigin);
|
||||
Assert.Equal(expectedPosition, stream.Position);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unimplemented
|
||||
|
||||
[Fact]
|
||||
public void Flush_Array_Throws()
|
||||
{
|
||||
byte[] data = new byte[1024];
|
||||
var stream = new ViewStream(data, 0, 1024);
|
||||
Assert.Throws<NotImplementedException>(() => stream.Flush());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Flush_Stream_Throws()
|
||||
{
|
||||
Stream data = new MemoryStream(new byte[1024]);
|
||||
var stream = new ViewStream(data, 0, 1024);
|
||||
Assert.Throws<NotImplementedException>(() => stream.Flush());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SetLength_Array_Throws()
|
||||
{
|
||||
byte[] data = new byte[1024];
|
||||
var stream = new ViewStream(data, 0, 1024);
|
||||
Assert.Throws<NotImplementedException>(() => stream.SetLength(0));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SetLength_Stream_Throws()
|
||||
{
|
||||
Stream data = new MemoryStream(new byte[1024]);
|
||||
var stream = new ViewStream(data, 0, 1024);
|
||||
Assert.Throws<NotImplementedException>(() => stream.SetLength(0));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Write_Array_Throws()
|
||||
{
|
||||
byte[] data = new byte[1024];
|
||||
var stream = new ViewStream(data, 0, 1024);
|
||||
Assert.Throws<NotImplementedException>(() => stream.Write([], 0, 0));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Write_Stream_Throws()
|
||||
{
|
||||
Stream data = new MemoryStream(new byte[1024]);
|
||||
var stream = new ViewStream(data, 0, 1024);
|
||||
Assert.Throws<NotImplementedException>(() => stream.Write([], 0, 0));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
1
SabreTools.IO.Test/TestData/file-to-compress.bin
Normal file
1
SabreTools.IO.Test/TestData/file-to-compress.bin
Normal file
@@ -0,0 +1 @@
|
||||
This is just a file that has a known set of hashes to make sure that everything with hashing is still working as anticipated.
|
||||
BIN
SabreTools.IO.Test/TestData/test-archive.bz2
Normal file
BIN
SabreTools.IO.Test/TestData/test-archive.bz2
Normal file
Binary file not shown.
BIN
SabreTools.IO.Test/TestData/test-archive.msz
Normal file
BIN
SabreTools.IO.Test/TestData/test-archive.msz
Normal file
Binary file not shown.
BIN
SabreTools.IO.Test/TestData/test-archive.pk
Normal file
BIN
SabreTools.IO.Test/TestData/test-archive.pk
Normal file
Binary file not shown.
BIN
SabreTools.IO.Test/TestData/test-archive.qtm
Normal file
BIN
SabreTools.IO.Test/TestData/test-archive.qtm
Normal file
Binary file not shown.
124
SabreTools.IO.Test/Transform/CombineTests.cs
Normal file
124
SabreTools.IO.Test/Transform/CombineTests.cs
Normal file
@@ -0,0 +1,124 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SabreTools.IO.Transform;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.IO.Test.Transform
|
||||
{
|
||||
public class CombineTests
|
||||
{
|
||||
#region Concatenate
|
||||
|
||||
[Fact]
|
||||
public void Concatenate_EmptyList_False()
|
||||
{
|
||||
List<string> paths = [];
|
||||
string output = string.Empty;
|
||||
bool actual = Combine.Concatenate(paths, output);
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Concatenate_InvalidOutput_False()
|
||||
{
|
||||
List<string> paths = ["a"];
|
||||
string output = string.Empty;
|
||||
bool actual = Combine.Concatenate(paths, output);
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Concatenate_FilledList_True()
|
||||
{
|
||||
List<string> paths = [
|
||||
Path.Combine(Environment.CurrentDirectory, "TestData", "ascii.txt"),
|
||||
Path.Combine(Environment.CurrentDirectory, "TestData", "file-to-compress.bin"),
|
||||
];
|
||||
string output = Guid.NewGuid().ToString();
|
||||
bool actual = Combine.Concatenate(paths, output);
|
||||
Assert.True(actual);
|
||||
|
||||
string text = File.ReadAllText(output);
|
||||
Assert.Equal("This doesn't match anythingThis is just a file that has a known set of hashes to make sure that everything with hashing is still working as anticipated.", text);
|
||||
|
||||
File.Delete(output);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Interleave
|
||||
|
||||
[Fact]
|
||||
public void Interleave_EvenNotExists_False()
|
||||
{
|
||||
string even = "NOT A REAL PATH";
|
||||
string odd = Path.Combine(Environment.CurrentDirectory, "TestData", "ascii.txt");
|
||||
string output = Guid.NewGuid().ToString();
|
||||
|
||||
bool actual = Combine.Interleave(even, odd, output, BlockSize.Byte);
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Interleave_OddNotExists_False()
|
||||
{
|
||||
string even = Path.Combine(Environment.CurrentDirectory, "TestData", "ascii.txt");
|
||||
string odd = "NOT A REAL PATH";
|
||||
string output = Guid.NewGuid().ToString();
|
||||
|
||||
bool actual = Combine.Interleave(even, odd, output, BlockSize.Byte);
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Interleave_InvalidType_False()
|
||||
{
|
||||
string even = Path.Combine(Environment.CurrentDirectory, "TestData", "ascii.txt");
|
||||
string odd = Path.Combine(Environment.CurrentDirectory, "TestData", "ascii.txt");
|
||||
string output = Guid.NewGuid().ToString();
|
||||
|
||||
bool actual = Combine.Interleave(even, odd, output, (BlockSize)int.MaxValue);
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(BlockSize.Byte, "TThhiiss ddooeessnn''tt mmaattcchh aannyytthhiinngg")]
|
||||
[InlineData(BlockSize.Word, "ThThisis d doeoesnsn't't m matatchch a anynyththiningg")]
|
||||
[InlineData(BlockSize.Dword, "ThisThis doe doesn'tsn't mat match ach anythnythinging")]
|
||||
[InlineData(BlockSize.Qword, "This doeThis doesn't matsn't match anythch anythinging")]
|
||||
public void Interleave_SameLength_True(BlockSize type, string expected)
|
||||
{
|
||||
string even = Path.Combine(Environment.CurrentDirectory, "TestData", "ascii.txt");
|
||||
string odd = Path.Combine(Environment.CurrentDirectory, "TestData", "ascii.txt");
|
||||
string output = Guid.NewGuid().ToString();
|
||||
|
||||
bool actual = Combine.Interleave(even, odd, output, type);
|
||||
Assert.True(actual);
|
||||
|
||||
string text = File.ReadAllText(output);
|
||||
Assert.Equal(expected, text);
|
||||
|
||||
File.Delete(output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Interleave_DifferentLength_True()
|
||||
{
|
||||
string even = Path.Combine(Environment.CurrentDirectory, "TestData", "ascii.txt");
|
||||
string odd = Path.Combine(Environment.CurrentDirectory, "TestData", "file-to-compress.bin");
|
||||
|
||||
string output = Guid.NewGuid().ToString();
|
||||
|
||||
bool actual = Combine.Interleave(even, odd, output, BlockSize.Byte);
|
||||
Assert.True(actual);
|
||||
|
||||
string text = File.ReadAllText(output);
|
||||
Assert.Equal("TThhiiss diose sjnu'stt maa tfcihl ea ntyhtahti nhgas a known set of hashes to make sure that everything with hashing is still working as anticipated.", text);
|
||||
|
||||
File.Delete(output);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
124
SabreTools.IO.Test/Transform/SplitTests.cs
Normal file
124
SabreTools.IO.Test/Transform/SplitTests.cs
Normal file
@@ -0,0 +1,124 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SabreTools.IO.Transform;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.IO.Test.Transform
|
||||
{
|
||||
public class SplitTests
|
||||
{
|
||||
#region BlockSplit
|
||||
|
||||
[Fact]
|
||||
public void BlockSplit_EmptyFileName_False()
|
||||
{
|
||||
string input = string.Empty;
|
||||
string outputDir = string.Empty;
|
||||
bool actual = Split.BlockSplit(input, outputDir, BlockSize.Byte);
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BlockSplit_InvalidFile_False()
|
||||
{
|
||||
string input = "INVALID";
|
||||
string outputDir = string.Empty;
|
||||
bool actual = Split.BlockSplit(input, outputDir, BlockSize.Byte);
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BlockSplit_InvalidType_False()
|
||||
{
|
||||
string input = Path.Combine(Environment.CurrentDirectory, "TestData", "ascii.txt");
|
||||
string outputDir = Guid.NewGuid().ToString();
|
||||
|
||||
bool actual = Split.BlockSplit(input, outputDir, (BlockSize)int.MaxValue);
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(BlockSize.Byte, "Ti os' ac ntig", "hsdentmthayhn")]
|
||||
[InlineData(BlockSize.Word, "Th dsn mchnyin", "isoe'tat athg")]
|
||||
[InlineData(BlockSize.Dword, "Thissn'tch aing", " doe matnyth")]
|
||||
[InlineData(BlockSize.Qword, "This doech anyth", "sn't mating")]
|
||||
public void BlockSplit_ValidFile_True(BlockSize type, string expectedEven, string expectedOdd)
|
||||
{
|
||||
string input = Path.Combine(Environment.CurrentDirectory, "TestData", "ascii.txt");
|
||||
string outputDir = Guid.NewGuid().ToString();
|
||||
|
||||
bool actual = Split.BlockSplit(input, outputDir, type);
|
||||
Assert.True(actual);
|
||||
|
||||
string baseFilename = Path.GetFileName(input);
|
||||
string text = File.ReadAllText(Path.Combine(outputDir, $"{baseFilename}.even"));
|
||||
Assert.Equal(expectedEven, text);
|
||||
text = File.ReadAllText(Path.Combine(outputDir, $"{baseFilename}.odd"));
|
||||
Assert.Equal(expectedOdd, text);
|
||||
|
||||
File.Delete($"{baseFilename}.even");
|
||||
File.Delete($"{baseFilename}.odd");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region SizeSplit
|
||||
|
||||
[Fact]
|
||||
public void SizeSplit_EmptyFileName_False()
|
||||
{
|
||||
string input = string.Empty;
|
||||
string outputDir = string.Empty;
|
||||
int size = 1;
|
||||
|
||||
bool actual = Split.SizeSplit(input, outputDir, size);
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SizeSplit_InvalidFile_False()
|
||||
{
|
||||
string input = "INVALID";
|
||||
string outputDir = string.Empty;
|
||||
int size = 1;
|
||||
|
||||
bool actual = Split.SizeSplit(input, outputDir, size);
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SizeSplit_InvalidSize_False()
|
||||
{
|
||||
string input = Path.Combine(Environment.CurrentDirectory, "TestData", "ascii.txt");
|
||||
string outputDir = string.Empty;
|
||||
int size = 0;
|
||||
|
||||
bool actual = Split.SizeSplit(input, outputDir, size);
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SizeSplit_Valid_True()
|
||||
{
|
||||
string input = Path.Combine(Environment.CurrentDirectory, "TestData", "ascii.txt");
|
||||
string outputDir = Guid.NewGuid().ToString();
|
||||
int size = 16;
|
||||
|
||||
bool actual = Split.SizeSplit(input, outputDir, size);
|
||||
Assert.True(actual);
|
||||
|
||||
Assert.Equal(2, Directory.GetFiles(outputDir).Length);
|
||||
|
||||
string baseFilename = Path.GetFileName(input);
|
||||
string text = File.ReadAllText(Path.Combine(outputDir, $"{baseFilename}.0"));
|
||||
Assert.Equal("This doesn't mat", text);
|
||||
text = File.ReadAllText(Path.Combine(outputDir, $"{baseFilename}.1"));
|
||||
Assert.Equal("ch anything", text);
|
||||
|
||||
File.Delete($"{baseFilename}.0");
|
||||
File.Delete($"{baseFilename}.1");
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
76
SabreTools.IO.Test/Transform/SwapTests.cs
Normal file
76
SabreTools.IO.Test/Transform/SwapTests.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.IO.Transform;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.IO.Test.Transform
|
||||
{
|
||||
public class SwapTests
|
||||
{
|
||||
#region Process
|
||||
|
||||
[Fact]
|
||||
public void Process_EmptyFileName_False()
|
||||
{
|
||||
string input = string.Empty;
|
||||
string output = string.Empty;
|
||||
bool actual = Swap.Process(input, output, Operation.Byteswap);
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Process_InvalidFile_False()
|
||||
{
|
||||
string input = "INVALID";
|
||||
string output = string.Empty;
|
||||
bool actual = Swap.Process(input, output, Operation.Byteswap);
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Process_InvalidType_False()
|
||||
{
|
||||
string input = Path.Combine(Environment.CurrentDirectory, "TestData", "ascii.txt");
|
||||
string output = Guid.NewGuid().ToString();
|
||||
|
||||
bool actual = Swap.Process(input, output, (Operation)int.MaxValue);
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Process_Valid_True()
|
||||
{
|
||||
string input = Path.Combine(Environment.CurrentDirectory, "TestData", "ascii.txt");
|
||||
string output = Guid.NewGuid().ToString();
|
||||
|
||||
// Bitswap
|
||||
bool actual = Swap.Process(input, output, Operation.Bitswap);
|
||||
Assert.True(actual);
|
||||
byte[] actualBytes = File.ReadAllBytes(output);
|
||||
Assert.True(new byte[] { 0x2A, 0x16, 0x96, 0xCE, 0x04, 0x26, 0xF6, 0xA6, 0xCE, 0x76, 0xE4, 0x2E, 0x04, 0xB6, 0x86, 0x2E, 0xC6, 0x16, 0x04, 0x86, 0x76, 0x9E, 0x2E, 0x16, 0x96, 0x76, 0xE6 }.EqualsExactly(actualBytes));
|
||||
|
||||
// Byteswap
|
||||
actual = Swap.Process(input, output, Operation.Byteswap);
|
||||
Assert.True(actual);
|
||||
actualBytes = File.ReadAllBytes(output);
|
||||
Assert.True(new byte[] { 0x68, 0x54, 0x73, 0x69, 0x64, 0x20, 0x65, 0x6F, 0x6E, 0x73, 0x74, 0x27, 0x6D, 0x20, 0x74, 0x61, 0x68, 0x63, 0x61, 0x20, 0x79, 0x6E, 0x68, 0x74, 0x6E, 0x69, 0x67 }.EqualsExactly(actualBytes));
|
||||
|
||||
// Wordswap
|
||||
actual = Swap.Process(input, output, Operation.Wordswap);
|
||||
Assert.True(actual);
|
||||
actualBytes = File.ReadAllBytes(output);
|
||||
Assert.True(new byte[] { 0x69, 0x73, 0x54, 0x68, 0x6F, 0x65, 0x20, 0x64, 0x27, 0x74, 0x73, 0x6E, 0x61, 0x74, 0x20, 0x6D, 0x20, 0x61, 0x63, 0x68, 0x74, 0x68, 0x6E, 0x79, 0x69, 0x6E, 0x67 }.EqualsExactly(actualBytes));
|
||||
|
||||
// WordByteswap
|
||||
actual = Swap.Process(input, output, Operation.WordByteswap);
|
||||
Assert.True(actual);
|
||||
actualBytes = File.ReadAllBytes(output);
|
||||
Assert.True(new byte[] { 0x73, 0x69, 0x68, 0x54, 0x65, 0x6F, 0x64, 0x20, 0x74, 0x27, 0x6E, 0x73, 0x74, 0x61, 0x6D, 0x20, 0x61, 0x20, 0x68, 0x63, 0x68, 0x74, 0x79, 0x6E, 0x69, 0x6E, 0x67 }.EqualsExactly(actualBytes));
|
||||
|
||||
File.Delete(output);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -7,22 +7,98 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SabreTools.IO", "SabreTools
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SabreTools.IO.Test", "SabreTools.IO.Test\SabreTools.IO.Test.csproj", "{A9767735-5042-48A1-849C-96035DB1DD53}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SabreTools.Text", "SabreTools.Text\SabreTools.Text.csproj", "{EC8FD8AD-579F-4B8B-A65E-119E5294ADC1}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SabreTools.Numerics", "SabreTools.Numerics\SabreTools.Numerics.csproj", "{565EAC34-B533-4C5C-9B81-77AE4B33A027}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SabreTools.Numerics.Test", "SabreTools.Numerics.Test\SabreTools.Numerics.Test.csproj", "{35F068EF-A652-4435-8890-1E6239EEBEFB}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SabreTools.Text.Test", "SabreTools.Text.Test\SabreTools.Text.Test.csproj", "{71513020-B588-4A66-8122-E1639A39B8A8}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{87CE4411-80D9-49FF-894C-761F1C20D9A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{87CE4411-80D9-49FF-894C-761F1C20D9A5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{87CE4411-80D9-49FF-894C-761F1C20D9A5}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{87CE4411-80D9-49FF-894C-761F1C20D9A5}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{87CE4411-80D9-49FF-894C-761F1C20D9A5}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{87CE4411-80D9-49FF-894C-761F1C20D9A5}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{87CE4411-80D9-49FF-894C-761F1C20D9A5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{87CE4411-80D9-49FF-894C-761F1C20D9A5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{87CE4411-80D9-49FF-894C-761F1C20D9A5}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{87CE4411-80D9-49FF-894C-761F1C20D9A5}.Release|x64.Build.0 = Release|Any CPU
|
||||
{87CE4411-80D9-49FF-894C-761F1C20D9A5}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{87CE4411-80D9-49FF-894C-761F1C20D9A5}.Release|x86.Build.0 = Release|Any CPU
|
||||
{A9767735-5042-48A1-849C-96035DB1DD53}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A9767735-5042-48A1-849C-96035DB1DD53}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A9767735-5042-48A1-849C-96035DB1DD53}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{A9767735-5042-48A1-849C-96035DB1DD53}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{A9767735-5042-48A1-849C-96035DB1DD53}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{A9767735-5042-48A1-849C-96035DB1DD53}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{A9767735-5042-48A1-849C-96035DB1DD53}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A9767735-5042-48A1-849C-96035DB1DD53}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A9767735-5042-48A1-849C-96035DB1DD53}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{A9767735-5042-48A1-849C-96035DB1DD53}.Release|x64.Build.0 = Release|Any CPU
|
||||
{A9767735-5042-48A1-849C-96035DB1DD53}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{A9767735-5042-48A1-849C-96035DB1DD53}.Release|x86.Build.0 = Release|Any CPU
|
||||
{EC8FD8AD-579F-4B8B-A65E-119E5294ADC1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{EC8FD8AD-579F-4B8B-A65E-119E5294ADC1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{EC8FD8AD-579F-4B8B-A65E-119E5294ADC1}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{EC8FD8AD-579F-4B8B-A65E-119E5294ADC1}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{EC8FD8AD-579F-4B8B-A65E-119E5294ADC1}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{EC8FD8AD-579F-4B8B-A65E-119E5294ADC1}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{EC8FD8AD-579F-4B8B-A65E-119E5294ADC1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{EC8FD8AD-579F-4B8B-A65E-119E5294ADC1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{EC8FD8AD-579F-4B8B-A65E-119E5294ADC1}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{EC8FD8AD-579F-4B8B-A65E-119E5294ADC1}.Release|x64.Build.0 = Release|Any CPU
|
||||
{EC8FD8AD-579F-4B8B-A65E-119E5294ADC1}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{EC8FD8AD-579F-4B8B-A65E-119E5294ADC1}.Release|x86.Build.0 = Release|Any CPU
|
||||
{565EAC34-B533-4C5C-9B81-77AE4B33A027}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{565EAC34-B533-4C5C-9B81-77AE4B33A027}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{565EAC34-B533-4C5C-9B81-77AE4B33A027}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{565EAC34-B533-4C5C-9B81-77AE4B33A027}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{565EAC34-B533-4C5C-9B81-77AE4B33A027}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{565EAC34-B533-4C5C-9B81-77AE4B33A027}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{565EAC34-B533-4C5C-9B81-77AE4B33A027}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{565EAC34-B533-4C5C-9B81-77AE4B33A027}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{565EAC34-B533-4C5C-9B81-77AE4B33A027}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{565EAC34-B533-4C5C-9B81-77AE4B33A027}.Release|x64.Build.0 = Release|Any CPU
|
||||
{565EAC34-B533-4C5C-9B81-77AE4B33A027}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{565EAC34-B533-4C5C-9B81-77AE4B33A027}.Release|x86.Build.0 = Release|Any CPU
|
||||
{35F068EF-A652-4435-8890-1E6239EEBEFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{35F068EF-A652-4435-8890-1E6239EEBEFB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{35F068EF-A652-4435-8890-1E6239EEBEFB}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{35F068EF-A652-4435-8890-1E6239EEBEFB}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{35F068EF-A652-4435-8890-1E6239EEBEFB}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{35F068EF-A652-4435-8890-1E6239EEBEFB}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{35F068EF-A652-4435-8890-1E6239EEBEFB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{35F068EF-A652-4435-8890-1E6239EEBEFB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{35F068EF-A652-4435-8890-1E6239EEBEFB}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{35F068EF-A652-4435-8890-1E6239EEBEFB}.Release|x64.Build.0 = Release|Any CPU
|
||||
{35F068EF-A652-4435-8890-1E6239EEBEFB}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{35F068EF-A652-4435-8890-1E6239EEBEFB}.Release|x86.Build.0 = Release|Any CPU
|
||||
{71513020-B588-4A66-8122-E1639A39B8A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{71513020-B588-4A66-8122-E1639A39B8A8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{71513020-B588-4A66-8122-E1639A39B8A8}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{71513020-B588-4A66-8122-E1639A39B8A8}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{71513020-B588-4A66-8122-E1639A39B8A8}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{71513020-B588-4A66-8122-E1639A39B8A8}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{71513020-B588-4A66-8122-E1639A39B8A8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{71513020-B588-4A66-8122-E1639A39B8A8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{71513020-B588-4A66-8122-E1639A39B8A8}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{71513020-B588-4A66-8122-E1639A39B8A8}.Release|x64.Build.0 = Release|Any CPU
|
||||
{71513020-B588-4A66-8122-E1639A39B8A8}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{71513020-B588-4A66-8122-E1639A39B8A8}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
114
SabreTools.IO/Compression/BZip2/BZip2.cs
Normal file
114
SabreTools.IO/Compression/BZip2/BZip2.cs
Normal file
@@ -0,0 +1,114 @@
|
||||
// BZip2InputStream.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2011 Dino Chiesa.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Last Saved: <2011-July-31 11:57:32>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines the BZip2InputStream class, which is a decompressing
|
||||
// stream that handles BZIP2. This code is derived from Apache commons source code.
|
||||
// The license below applies to the original Apache code.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This package is based on the work done by Keiron Liddle, Aftex Software
|
||||
* <keiron@aftexsw.com> to whom the Ant project is very grateful for his
|
||||
* great code.
|
||||
*/
|
||||
|
||||
// compile: msbuild
|
||||
// not: csc.exe /t:library /debug+ /out:SabreTools.IO.Compression.BZip2.dll BZip2InputStream.cs BCRC32.cs Rand.cs
|
||||
|
||||
namespace SabreTools.IO.Compression.BZip2
|
||||
{
|
||||
// /**
|
||||
// * Checks if the signature matches what is expected for a bzip2 file.
|
||||
// *
|
||||
// * @param signature
|
||||
// * the bytes to check
|
||||
// * @param length
|
||||
// * the number of bytes to check
|
||||
// * @return true, if this stream is a bzip2 compressed stream, false otherwise
|
||||
// *
|
||||
// * @since Apache Commons Compress 1.1
|
||||
// */
|
||||
// public static boolean MatchesSig(byte[] signature)
|
||||
// {
|
||||
// if ((signature.Length < 3) ||
|
||||
// (signature[0] != 'B') ||
|
||||
// (signature[1] != 'Z') ||
|
||||
// (signature[2] != 'h'))
|
||||
// return false;
|
||||
//
|
||||
// return true;
|
||||
// }
|
||||
|
||||
internal static class BZip2
|
||||
{
|
||||
internal static T[][] InitRectangularArray<T>(int d1, int d2)
|
||||
{
|
||||
var x = new T[d1][];
|
||||
for (int i = 0; i < d1; i++)
|
||||
{
|
||||
x[i] = new T[d2];
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
public static readonly int BlockSizeMultiple = 100000;
|
||||
public static readonly int MinBlockSize = 1;
|
||||
public static readonly int MaxBlockSize = 9;
|
||||
public static readonly int MaxAlphaSize = 258;
|
||||
public static readonly int MaxCodeLength = 23;
|
||||
public static readonly char RUNA = (char)0;
|
||||
public static readonly char RUNB = (char)1;
|
||||
public static readonly int NGroups = 6;
|
||||
public static readonly int G_SIZE = 50;
|
||||
public static readonly int N_ITERS = 4;
|
||||
public static readonly int MaxSelectors = (2 + (900000 / G_SIZE));
|
||||
public static readonly int NUM_OVERSHOOT_BYTES = 20;
|
||||
/*
|
||||
* <p> If you are ever unlucky/improbable enough to get a stack
|
||||
* overflow whilst sorting, increase the following constant and
|
||||
* try again. In practice I have never seen the stack go above 27
|
||||
* elems, so the following limit seems very generous. </p>
|
||||
*/
|
||||
internal static readonly int QSORT_STACK_SIZE = 1000;
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
1950
SabreTools.IO/Compression/BZip2/BZip2Compressor.cs
Normal file
1950
SabreTools.IO/Compression/BZip2/BZip2Compressor.cs
Normal file
File diff suppressed because it is too large
Load Diff
1385
SabreTools.IO/Compression/BZip2/BZip2InputStream.cs
Normal file
1385
SabreTools.IO/Compression/BZip2/BZip2InputStream.cs
Normal file
File diff suppressed because it is too large
Load Diff
530
SabreTools.IO/Compression/BZip2/BZip2OutputStream.cs
Normal file
530
SabreTools.IO/Compression/BZip2/BZip2OutputStream.cs
Normal file
@@ -0,0 +1,530 @@
|
||||
//#define Trace
|
||||
|
||||
// BZip2OutputStream.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2011 Dino Chiesa.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Last Saved: <2011-August-02 16:44:11>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines the BZip2OutputStream class, which is a
|
||||
// compressing stream that handles BZIP2. This code may have been
|
||||
// derived in part from Apache commons source code. The license below
|
||||
// applies to the original Apache code.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
// flymake: csc.exe /t:module BZip2InputStream.cs BZip2Compressor.cs Rand.cs BCRC32.cs @@FILE@@
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
|
||||
// Design Notes:
|
||||
//
|
||||
// This class follows the classic Decorator pattern: it is a Stream that
|
||||
// wraps itself around a Stream, and in doing so provides bzip2
|
||||
// compression as callers Write into it.
|
||||
//
|
||||
// BZip2 is a straightforward data format: there are 4 magic bytes at
|
||||
// the top of the file, followed by 1 or more compressed blocks. There
|
||||
// is a small "magic byte" trailer after all compressed blocks. This
|
||||
// class emits the magic bytes for the header and trailer, and relies on
|
||||
// a BZip2Compressor to generate each of the compressed data blocks.
|
||||
//
|
||||
// BZip2 does byte-shredding - it uses partial fractions of bytes to
|
||||
// represent independent pieces of information. This class relies on the
|
||||
// BitWriter to adapt the bit-oriented BZip2 output to the byte-oriented
|
||||
// model of the .NET Stream class.
|
||||
//
|
||||
// ----
|
||||
//
|
||||
// Regarding the Apache code base: Most of the code in this particular
|
||||
// class is related to stream operations, and is my own code. It largely
|
||||
// does not rely on any code obtained from Apache commons. If you
|
||||
// compare this code with the Apache commons BZip2OutputStream, you will
|
||||
// see very little code that is common, except for the
|
||||
// nearly-boilerplate structure that is common to all subtypes of
|
||||
// System.IO.Stream. There may be some small remnants of code in this
|
||||
// module derived from the Apache stuff, which is why I left the license
|
||||
// in here. Most of the Apache commons compressor magic has been ported
|
||||
// into the BZip2Compressor class.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
#nullable disable
|
||||
namespace SabreTools.IO.Compression.BZip2
|
||||
{
|
||||
/// <summary>
|
||||
/// A write-only decorator stream that compresses data as it is
|
||||
/// written using the BZip2 algorithm.
|
||||
/// </summary>
|
||||
public class BZip2OutputStream : System.IO.Stream
|
||||
{
|
||||
int totalBytesWrittenIn;
|
||||
readonly bool leaveOpen;
|
||||
BZip2Compressor compressor;
|
||||
uint combinedCRC;
|
||||
Stream output;
|
||||
BitWriter bw;
|
||||
readonly int blockSize100k; // 0...9
|
||||
|
||||
private readonly TraceBits desiredTrace = TraceBits.Crc | TraceBits.Write;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new <c>BZip2OutputStream</c>, that sends its
|
||||
/// compressed output to the given output stream.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name='output'>
|
||||
/// The destination stream, to which compressed output will be sent.
|
||||
/// </param>
|
||||
///
|
||||
/// <example>
|
||||
///
|
||||
/// This example reads a file, then compresses it with bzip2 file,
|
||||
/// and writes the compressed data into a newly created file.
|
||||
///
|
||||
/// <code>
|
||||
/// var fname = "logfile.log";
|
||||
/// using (var fs = File.OpenRead(fname))
|
||||
/// {
|
||||
/// var outFname = fname + ".bz2";
|
||||
/// using (var output = File.Create(outFname))
|
||||
/// {
|
||||
/// using (var compressor = new SabreTools.IO.Compression.BZip2.BZip2OutputStream(output))
|
||||
/// {
|
||||
/// byte[] buffer = new byte[2048];
|
||||
/// int n;
|
||||
/// while ((n = fs.Read(buffer, 0, buffer.Length)) > 0)
|
||||
/// {
|
||||
/// compressor.Write(buffer, 0, n);
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public BZip2OutputStream(Stream output)
|
||||
: this(output, BZip2.MaxBlockSize, false)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new <c>BZip2OutputStream</c> with specified blocksize.
|
||||
/// </summary>
|
||||
/// <param name = "output">the destination stream.</param>
|
||||
/// <param name = "blockSize">
|
||||
/// The blockSize in units of 100000 bytes.
|
||||
/// The valid range is 1..9.
|
||||
/// </param>
|
||||
public BZip2OutputStream(Stream output, int blockSize)
|
||||
: this(output, blockSize, false)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new <c>BZip2OutputStream</c>.
|
||||
/// </summary>
|
||||
/// <param name = "output">the destination stream.</param>
|
||||
/// <param name = "leaveOpen">
|
||||
/// whether to leave the captive stream open upon closing this stream.
|
||||
/// </param>
|
||||
public BZip2OutputStream(Stream output, bool leaveOpen)
|
||||
: this(output, BZip2.MaxBlockSize, leaveOpen)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new <c>BZip2OutputStream</c> with specified blocksize,
|
||||
/// and explicitly specifies whether to leave the wrapped stream open.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name = "output">the destination stream.</param>
|
||||
/// <param name = "blockSize">
|
||||
/// The blockSize in units of 100000 bytes.
|
||||
/// The valid range is 1..9.
|
||||
/// </param>
|
||||
/// <param name = "leaveOpen">
|
||||
/// whether to leave the captive stream open upon closing this stream.
|
||||
/// </param>
|
||||
public BZip2OutputStream(Stream output, int blockSize, bool leaveOpen)
|
||||
{
|
||||
if (blockSize < BZip2.MinBlockSize ||
|
||||
blockSize > BZip2.MaxBlockSize)
|
||||
{
|
||||
var msg = String.Format("blockSize={0} is out of range; must be between {1} and {2}",
|
||||
blockSize,
|
||||
BZip2.MinBlockSize, BZip2.MaxBlockSize);
|
||||
throw new ArgumentException(msg, "blockSize");
|
||||
}
|
||||
|
||||
this.output = output;
|
||||
if (!this.output.CanWrite)
|
||||
throw new ArgumentException("The stream is not writable.", "output");
|
||||
|
||||
this.bw = new BitWriter(this.output);
|
||||
this.blockSize100k = blockSize;
|
||||
this.compressor = new BZip2Compressor(this.bw, blockSize);
|
||||
this.leaveOpen = leaveOpen;
|
||||
this.combinedCRC = 0;
|
||||
EmitHeader();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Close the stream.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This may or may not close the underlying stream. Check the
|
||||
/// constructors that accept a bool value.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public override void Close()
|
||||
{
|
||||
if (output != null)
|
||||
{
|
||||
Stream o = this.output;
|
||||
Finish();
|
||||
if (!leaveOpen)
|
||||
o.Close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Flush the stream.
|
||||
/// </summary>
|
||||
public override void Flush()
|
||||
{
|
||||
if (this.output != null)
|
||||
{
|
||||
this.bw.Flush();
|
||||
this.output.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitHeader()
|
||||
{
|
||||
var magic = new byte[] {
|
||||
(byte) 'B',
|
||||
(byte) 'Z',
|
||||
(byte) 'h',
|
||||
(byte) ('0' + this.blockSize100k)
|
||||
};
|
||||
|
||||
// not necessary to shred the initial magic bytes
|
||||
this.output.Write(magic, 0, magic.Length);
|
||||
}
|
||||
|
||||
private void EmitTrailer()
|
||||
{
|
||||
// A magic 48-bit number, 0x177245385090, to indicate the end
|
||||
// of the last block. (sqrt(pi), if you want to know)
|
||||
|
||||
TraceOutput(TraceBits.Write, "total written out: {0} (0x{0:X})",
|
||||
this.bw.TotalBytesWrittenOut);
|
||||
|
||||
// must shred
|
||||
this.bw.WriteByte(0x17);
|
||||
this.bw.WriteByte(0x72);
|
||||
this.bw.WriteByte(0x45);
|
||||
this.bw.WriteByte(0x38);
|
||||
this.bw.WriteByte(0x50);
|
||||
this.bw.WriteByte(0x90);
|
||||
|
||||
this.bw.WriteInt(this.combinedCRC);
|
||||
|
||||
this.bw.FinishAndPad();
|
||||
|
||||
TraceOutput(TraceBits.Write, "final total: {0} (0x{0:X})",
|
||||
this.bw.TotalBytesWrittenOut);
|
||||
}
|
||||
|
||||
void Finish()
|
||||
{
|
||||
// Console.WriteLine("BZip2:Finish");
|
||||
|
||||
try
|
||||
{
|
||||
var totalBefore = this.bw.TotalBytesWrittenOut;
|
||||
this.compressor.CompressAndWrite();
|
||||
TraceOutput(TraceBits.Write, "out block length (bytes): {0} (0x{0:X})",
|
||||
this.bw.TotalBytesWrittenOut - totalBefore);
|
||||
|
||||
TraceOutput(TraceBits.Crc, " combined CRC (before): {0:X8}",
|
||||
this.combinedCRC);
|
||||
this.combinedCRC = (this.combinedCRC << 1) | (this.combinedCRC >> 31);
|
||||
this.combinedCRC ^= (uint)compressor.Crc32;
|
||||
TraceOutput(TraceBits.Crc, " block CRC : {0:X8}",
|
||||
this.compressor.Crc32);
|
||||
TraceOutput(TraceBits.Crc, " combined CRC (final) : {0:X8}",
|
||||
this.combinedCRC);
|
||||
|
||||
EmitTrailer();
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.output = null;
|
||||
this.compressor = null;
|
||||
this.bw = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The blocksize parameter specified at construction time.
|
||||
/// </summary>
|
||||
public int BlockSize
|
||||
{
|
||||
get { return this.blockSize100k; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Write data to the stream.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// Use the <c>BZip2OutputStream</c> to compress data while writing:
|
||||
/// create a <c>BZip2OutputStream</c> with a writable output stream.
|
||||
/// Then call <c>Write()</c> on that <c>BZip2OutputStream</c>, providing
|
||||
/// uncompressed data as input. The data sent to the output stream will
|
||||
/// be the compressed form of the input data.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// A <c>BZip2OutputStream</c> can be used only for <c>Write()</c> not for <c>Read()</c>.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <param name="buffer">The buffer holding data to write to the stream.</param>
|
||||
/// <param name="offset">the offset within that data array to find the first byte to write.</param>
|
||||
/// <param name="count">the number of bytes to write.</param>
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (offset < 0)
|
||||
throw new IndexOutOfRangeException(String.Format("offset ({0}) must be > 0", offset));
|
||||
if (count < 0)
|
||||
throw new IndexOutOfRangeException(String.Format("count ({0}) must be > 0", count));
|
||||
if (offset + count > buffer.Length)
|
||||
throw new IndexOutOfRangeException(String.Format("offset({0}) count({1}) bLength({2})",
|
||||
offset, count, buffer.Length));
|
||||
if (this.output == null)
|
||||
throw new IOException("the stream is not open");
|
||||
|
||||
if (count == 0) return; // nothing to do
|
||||
|
||||
int bytesWritten = 0;
|
||||
int bytesRemaining = count;
|
||||
|
||||
do
|
||||
{
|
||||
int n = compressor.Fill(buffer, offset, bytesRemaining);
|
||||
if (n != bytesRemaining)
|
||||
{
|
||||
// The compressor data block is full. Compress and
|
||||
// write out the compressed data, then reset the
|
||||
// compressor and continue.
|
||||
|
||||
var totalBefore = this.bw.TotalBytesWrittenOut;
|
||||
this.compressor.CompressAndWrite();
|
||||
TraceOutput(TraceBits.Write, "out block length (bytes): {0} (0x{0:X})",
|
||||
this.bw.TotalBytesWrittenOut - totalBefore);
|
||||
|
||||
// and now any remaining bits
|
||||
TraceOutput(TraceBits.Write,
|
||||
" remaining: {0} 0x{1:X}",
|
||||
this.bw.NumRemainingBits,
|
||||
this.bw.RemainingBits);
|
||||
|
||||
TraceOutput(TraceBits.Crc, " combined CRC (before): {0:X8}",
|
||||
this.combinedCRC);
|
||||
this.combinedCRC = (this.combinedCRC << 1) | (this.combinedCRC >> 31);
|
||||
this.combinedCRC ^= (uint)compressor.Crc32;
|
||||
TraceOutput(TraceBits.Crc, " block CRC : {0:X8}",
|
||||
compressor.Crc32);
|
||||
TraceOutput(TraceBits.Crc, " combined CRC (after) : {0:X8}",
|
||||
this.combinedCRC);
|
||||
offset += n;
|
||||
}
|
||||
bytesRemaining -= n;
|
||||
bytesWritten += n;
|
||||
} while (bytesRemaining > 0);
|
||||
|
||||
totalBytesWrittenIn += bytesWritten;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the stream can be read.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The return value is always false.
|
||||
/// </remarks>
|
||||
public override bool CanRead
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the stream supports Seek operations.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Always returns false.
|
||||
/// </remarks>
|
||||
public override bool CanSeek
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the stream can be written.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The return value should always be true, unless and until the
|
||||
/// object is disposed and closed.
|
||||
/// </remarks>
|
||||
public override bool CanWrite
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.output == null) throw new ObjectDisposedException("BZip2Stream");
|
||||
return this.output.CanWrite;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reading this property always throws a <see cref="NotImplementedException"/>.
|
||||
/// </summary>
|
||||
public override long Length
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The position of the stream pointer.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// Setting this property always throws a <see
|
||||
/// cref="NotImplementedException"/>. Reading will return the
|
||||
/// total number of uncompressed bytes written through.
|
||||
/// </remarks>
|
||||
public override long Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.totalBytesWrittenIn;
|
||||
}
|
||||
set { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calling this method always throws a <see cref="NotImplementedException"/>.
|
||||
/// </summary>
|
||||
/// <param name="offset">this is irrelevant, since it will always throw!</param>
|
||||
/// <param name="origin">this is irrelevant, since it will always throw!</param>
|
||||
/// <returns>irrelevant!</returns>
|
||||
public override long Seek(long offset, System.IO.SeekOrigin origin)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calling this method always throws a <see cref="NotImplementedException"/>.
|
||||
/// </summary>
|
||||
/// <param name="value">this is irrelevant, since it will always throw!</param>
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calling this method always throws a <see cref="NotImplementedException"/>.
|
||||
/// </summary>
|
||||
/// <param name='buffer'>this parameter is never used</param>
|
||||
/// <param name='offset'>this parameter is never used</param>
|
||||
/// <param name='count'>this parameter is never used</param>
|
||||
/// <returns>never returns anything; always throws</returns>
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
||||
// used only when Trace is defined
|
||||
[Flags]
|
||||
enum TraceBits : uint
|
||||
{
|
||||
None = 0,
|
||||
Crc = 1,
|
||||
Write = 2,
|
||||
All = 0xffffffff,
|
||||
}
|
||||
|
||||
|
||||
[System.Diagnostics.ConditionalAttribute("Trace")]
|
||||
private void TraceOutput(TraceBits bits, string format, params object[] varParams)
|
||||
{
|
||||
if ((bits & this.desiredTrace) != 0)
|
||||
{
|
||||
//lock(outputLock)
|
||||
{
|
||||
int tid = System.Threading.Thread.CurrentThread.GetHashCode();
|
||||
#if !SILVERLIGHT && !NETCF
|
||||
Console.ForegroundColor = (ConsoleColor)(tid % 8 + 10);
|
||||
#endif
|
||||
Console.Write("{0:000} PBOS ", tid);
|
||||
Console.WriteLine(format, varParams);
|
||||
#if !SILVERLIGHT && !NETCF
|
||||
Console.ResetColor();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
245
SabreTools.IO/Compression/BZip2/BitWriter.cs
Normal file
245
SabreTools.IO/Compression/BZip2/BitWriter.cs
Normal file
@@ -0,0 +1,245 @@
|
||||
// BitWriter.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2011 Dino Chiesa.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Last Saved: <2011-July-25 18:57:31>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines the BitWriter class, which writes bits at a time
|
||||
// to an output stream. It's used by the BZip2Compressor class, and by
|
||||
// the BZip2OutputStream class and its parallel variant,
|
||||
// ParallelBZip2OutputStream.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
//
|
||||
// Design notes:
|
||||
//
|
||||
// BZip2 employs byte-shredding in its data format - rather than
|
||||
// aligning all data items in a compressed .bz2 file on byte barriers,
|
||||
// the BZip2 format uses portions of bytes to represent independent
|
||||
// pieces of information. This "shredding" starts with the first
|
||||
// "randomised" bit - just 12 bytes or so into a bz2 file or stream. But
|
||||
// the approach is used extensively in bzip2 files - sometimes 5 bits
|
||||
// are used, sometimes 24 or 3 bits, sometimes just 1 bit, and so on.
|
||||
// It's not possible to send this information directly to a stream in
|
||||
// this form; Streams in .NET accept byte-oriented input. Therefore,
|
||||
// when actually writing a bz2 file, the output data must be organized
|
||||
// into a byte-aligned format before being written to the output stream.
|
||||
//
|
||||
// This BitWriter class provides the byte-shredding necessary for BZip2
|
||||
// output. Think of this class as an Adapter that enables Bit-oriented
|
||||
// output to a standard byte-oriented .NET stream. This class writes
|
||||
// data out to the captive output stream only after the data bits have
|
||||
// been accumulated and aligned. For example, suppose that during
|
||||
// operation, the BZip2 compressor emits 5 bits, then 24 bits, then 32
|
||||
// bits. When the first 5 bits are sent to the BitWriter, nothing is
|
||||
// written to the output stream; instead these 5 bits are simply stored
|
||||
// in the internal accumulator. When the next 24 bits are written, the
|
||||
// first 3 bits are gathered with the accumulated bits. The resulting
|
||||
// 5+3 constitutes an entire byte; the BitWriter then actually writes
|
||||
// that byte to the output stream. This leaves 21 bits. BitWriter writes
|
||||
// 2 more whole bytes (16 more bits), in 8-bit chunks, leaving 5 in the
|
||||
// accumulator. BitWriter then follows the same procedure with the 32
|
||||
// new bits. And so on.
|
||||
//
|
||||
// A quick tour of the implementation:
|
||||
//
|
||||
// The accumulator is a uint - so it can accumulate at most 4 bytes of
|
||||
// information. In practice because of the design of this class, it
|
||||
// never accumulates more than 3 bytes.
|
||||
//
|
||||
// The Flush() method emits all whole bytes available. After calling
|
||||
// Flush(), there may be between 0-7 bits yet to be emitted into the
|
||||
// output stream.
|
||||
//
|
||||
// FinishAndPad() emits all data, including the last partial byte and
|
||||
// any necessary padding. In effect, it establishes a byte-alignment
|
||||
// barrier. To support bzip2, FinishAndPad() should be called only once
|
||||
// for a bz2 file, after the last bit of data has been written through
|
||||
// this adapter. Other binary file formats may use byte-alignment at
|
||||
// various points within the file, and FinishAndPad() would support that
|
||||
// scenario.
|
||||
//
|
||||
// The internal fn Reset() is used to reset the state of the adapter;
|
||||
// this class is used by BZip2Compressor, instances of which get re-used
|
||||
// by multiple distinct threads, for different blocks of data.
|
||||
//
|
||||
|
||||
using System.IO;
|
||||
|
||||
namespace SabreTools.IO.Compression.BZip2
|
||||
{
|
||||
internal class BitWriter
|
||||
{
|
||||
uint accumulator;
|
||||
int nAccumulatedBits;
|
||||
readonly Stream output;
|
||||
int totalBytesWrittenOut;
|
||||
|
||||
public BitWriter(Stream s)
|
||||
{
|
||||
this.output = s;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delivers the remaining bits, left-aligned, in a byte.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This is valid only if NumRemainingBits is less than 8;
|
||||
/// in other words it is valid only after a call to Flush().
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public byte RemainingBits
|
||||
{
|
||||
get
|
||||
{
|
||||
return (byte)(this.accumulator >> (32 - this.nAccumulatedBits) & 0xff);
|
||||
}
|
||||
}
|
||||
|
||||
public int NumRemainingBits
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.nAccumulatedBits;
|
||||
}
|
||||
}
|
||||
|
||||
public int TotalBytesWrittenOut
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.totalBytesWrittenOut;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset the BitWriter.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This is useful when the BitWriter writes into a MemoryStream, and
|
||||
/// is used by a BZip2Compressor, which itself is re-used for multiple
|
||||
/// distinct data blocks.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public void Reset()
|
||||
{
|
||||
this.accumulator = 0;
|
||||
this.nAccumulatedBits = 0;
|
||||
this.totalBytesWrittenOut = 0;
|
||||
this.output.Seek(0, SeekOrigin.Begin);
|
||||
this.output.SetLength(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write some number of bits from the given value, into the output.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The nbits value should be a max of 25, for safety. For performance
|
||||
/// reasons, this method does not check!
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public void WriteBits(int nbits, uint value)
|
||||
{
|
||||
int nAccumulated = this.nAccumulatedBits;
|
||||
uint u = this.accumulator;
|
||||
|
||||
while (nAccumulated >= 8)
|
||||
{
|
||||
this.output.WriteByte((byte)(u >> 24 & 0xff));
|
||||
this.totalBytesWrittenOut++;
|
||||
u <<= 8;
|
||||
nAccumulated -= 8;
|
||||
}
|
||||
|
||||
this.accumulator = u | (value << (32 - nAccumulated - nbits));
|
||||
this.nAccumulatedBits = nAccumulated + nbits;
|
||||
|
||||
// Console.WriteLine("WriteBits({0}, 0x{1:X2}) => {2:X8} n({3})",
|
||||
// nbits, value, accumulator, nAccumulatedBits);
|
||||
// Console.ReadLine();
|
||||
|
||||
// At this point the accumulator may contain up to 31 bits waiting for
|
||||
// output.
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Write a full 8-bit byte into the output.
|
||||
/// </summary>
|
||||
public void WriteByte(byte b)
|
||||
{
|
||||
WriteBits(8, b);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write four 8-bit bytes into the output.
|
||||
/// </summary>
|
||||
public void WriteInt(uint u)
|
||||
{
|
||||
WriteBits(8, (u >> 24) & 0xff);
|
||||
WriteBits(8, (u >> 16) & 0xff);
|
||||
WriteBits(8, (u >> 8) & 0xff);
|
||||
WriteBits(8, u & 0xff);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write all available byte-aligned bytes.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This method writes no new output, but flushes any accumulated
|
||||
/// bits. At completion, the accumulator may contain up to 7
|
||||
/// bits.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This is necessary when re-assembling output from N independent
|
||||
/// compressors, one for each of N blocks. The output of any
|
||||
/// particular compressor will in general have some fragment of a byte
|
||||
/// remaining. This fragment needs to be accumulated into the
|
||||
/// parent BZip2OutputStream.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public void Flush()
|
||||
{
|
||||
WriteBits(0, 0);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Writes all available bytes, and emits padding for the final byte as
|
||||
/// necessary. This must be the last method invoked on an instance of
|
||||
/// BitWriter.
|
||||
/// </summary>
|
||||
public void FinishAndPad()
|
||||
{
|
||||
Flush();
|
||||
|
||||
if (this.NumRemainingBits > 0)
|
||||
{
|
||||
byte b = (byte)((this.accumulator >> 24) & 0xff);
|
||||
this.output.WriteByte(b);
|
||||
this.totalBytesWrittenOut++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
815
SabreTools.IO/Compression/BZip2/CRC32.cs
Normal file
815
SabreTools.IO/Compression/BZip2/CRC32.cs
Normal file
@@ -0,0 +1,815 @@
|
||||
// CRC32.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2011 Dino Chiesa.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Last Saved: <2011-August-02 18:25:54>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines the CRC32 class, which can do the CRC32 algorithm, using
|
||||
// arbitrary starting polynomials, and bit reversal. The bit reversal is what
|
||||
// distinguishes this CRC-32 used in BZip2 from the CRC-32 that is used in PKZIP
|
||||
// files, or GZIP files. This class does both.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using Interop = System.Runtime.InteropServices;
|
||||
|
||||
#nullable disable
|
||||
namespace SabreTools.IO.Compression.BZip2
|
||||
{
|
||||
/// <summary>
|
||||
/// Computes a CRC-32. The CRC-32 algorithm is parameterized - you
|
||||
/// can set the polynomial and enable or disable bit
|
||||
/// reversal. This can be used for GZIP, BZip2, or ZIP.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This type is used internally by DotNetZip; it is generally not used
|
||||
/// directly by applications wishing to create, read, or manipulate zip
|
||||
/// archive files.
|
||||
/// </remarks>
|
||||
|
||||
[Interop.GuidAttribute("ebc25cf6-9120-4283-b972-0e5520d0000C")]
|
||||
[Interop.ComVisible(true)]
|
||||
#if !NETCF
|
||||
[Interop.ClassInterface(Interop.ClassInterfaceType.AutoDispatch)]
|
||||
#endif
|
||||
public class CRC32
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates the total number of bytes applied to the CRC.
|
||||
/// </summary>
|
||||
public Int64 TotalBytesRead
|
||||
{
|
||||
get
|
||||
{
|
||||
return _TotalBytesRead;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the current CRC for all blocks slurped in.
|
||||
/// </summary>
|
||||
public Int32 Crc32Result
|
||||
{
|
||||
get
|
||||
{
|
||||
return unchecked((Int32)(~_register));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the CRC32 for the specified stream.
|
||||
/// </summary>
|
||||
/// <param name="input">The stream over which to calculate the CRC32</param>
|
||||
/// <returns>the CRC32 calculation</returns>
|
||||
public Int32 GetCrc32(System.IO.Stream input)
|
||||
{
|
||||
return GetCrc32AndCopy(input, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the CRC32 for the specified stream, and writes the input into the
|
||||
/// output stream.
|
||||
/// </summary>
|
||||
/// <param name="input">The stream over which to calculate the CRC32</param>
|
||||
/// <param name="output">The stream into which to deflate the input</param>
|
||||
/// <returns>the CRC32 calculation</returns>
|
||||
public Int32 GetCrc32AndCopy(System.IO.Stream input, System.IO.Stream output)
|
||||
{
|
||||
if (input == null)
|
||||
throw new Exception("The input stream must not be null.");
|
||||
|
||||
unchecked
|
||||
{
|
||||
byte[] buffer = new byte[BUFFER_SIZE];
|
||||
int readSize = BUFFER_SIZE;
|
||||
|
||||
_TotalBytesRead = 0;
|
||||
int count = input.Read(buffer, 0, readSize);
|
||||
output?.Write(buffer, 0, count);
|
||||
_TotalBytesRead += count;
|
||||
while (count > 0)
|
||||
{
|
||||
SlurpBlock(buffer, 0, count);
|
||||
count = input.Read(buffer, 0, readSize);
|
||||
output?.Write(buffer, 0, count);
|
||||
_TotalBytesRead += count;
|
||||
}
|
||||
|
||||
return (Int32)(~_register);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get the CRC32 for the given (word,byte) combo. This is a
|
||||
/// computation defined by PKzip for PKZIP 2.0 (weak) encryption.
|
||||
/// </summary>
|
||||
/// <param name="W">The word to start with.</param>
|
||||
/// <param name="B">The byte to combine it with.</param>
|
||||
/// <returns>The CRC-ized result.</returns>
|
||||
public Int32 ComputeCrc32(Int32 W, byte B)
|
||||
{
|
||||
return _InternalComputeCrc32((UInt32)W, B);
|
||||
}
|
||||
|
||||
internal Int32 _InternalComputeCrc32(UInt32 W, byte B)
|
||||
{
|
||||
return (Int32)(crc32Table[(W ^ B) & 0xFF] ^ (W >> 8));
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Update the value for the running CRC32 using the given block of bytes.
|
||||
/// This is useful when using the CRC32() class in a Stream.
|
||||
/// </summary>
|
||||
/// <param name="block">block of bytes to slurp</param>
|
||||
/// <param name="offset">starting point in the block</param>
|
||||
/// <param name="count">how many bytes within the block to slurp</param>
|
||||
public void SlurpBlock(byte[] block, int offset, int count)
|
||||
{
|
||||
if (block == null)
|
||||
throw new Exception("The data buffer must not be null.");
|
||||
|
||||
// bzip algorithm
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
int x = offset + i;
|
||||
byte b = block[x];
|
||||
if (this.reverseBits)
|
||||
{
|
||||
UInt32 temp = (_register >> 24) ^ b;
|
||||
_register = (_register << 8) ^ crc32Table[temp];
|
||||
}
|
||||
else
|
||||
{
|
||||
UInt32 temp = (_register & 0x000000FF) ^ b;
|
||||
_register = (_register >> 8) ^ crc32Table[temp];
|
||||
}
|
||||
}
|
||||
_TotalBytesRead += count;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Process one byte in the CRC.
|
||||
/// </summary>
|
||||
/// <param name = "b">the byte to include into the CRC . </param>
|
||||
public void UpdateCRC(byte b)
|
||||
{
|
||||
if (this.reverseBits)
|
||||
{
|
||||
UInt32 temp = (_register >> 24) ^ b;
|
||||
_register = (_register << 8) ^ crc32Table[temp];
|
||||
}
|
||||
else
|
||||
{
|
||||
UInt32 temp = (_register & 0x000000FF) ^ b;
|
||||
_register = (_register >> 8) ^ crc32Table[temp];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process a run of N identical bytes into the CRC.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This method serves as an optimization for updating the CRC when a
|
||||
/// run of identical bytes is found. Rather than passing in a buffer of
|
||||
/// length n, containing all identical bytes b, this method accepts the
|
||||
/// byte value and the length of the (virtual) buffer - the length of
|
||||
/// the run.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name = "b">the byte to include into the CRC. </param>
|
||||
/// <param name = "n">the number of times that byte should be repeated. </param>
|
||||
public void UpdateCRC(byte b, int n)
|
||||
{
|
||||
while (n-- > 0)
|
||||
{
|
||||
if (this.reverseBits)
|
||||
{
|
||||
uint temp = (_register >> 24) ^ b;
|
||||
_register = (_register << 8) ^ crc32Table[(temp >= 0)
|
||||
? temp
|
||||
: (temp + 256)];
|
||||
}
|
||||
else
|
||||
{
|
||||
UInt32 temp = (_register & 0x000000FF) ^ b;
|
||||
_register = (_register >> 8) ^ crc32Table[(temp >= 0)
|
||||
? temp
|
||||
: (temp + 256)];
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static uint ReverseBits(uint data)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
uint ret = data;
|
||||
ret = (ret & 0x55555555) << 1 | (ret >> 1) & 0x55555555;
|
||||
ret = (ret & 0x33333333) << 2 | (ret >> 2) & 0x33333333;
|
||||
ret = (ret & 0x0F0F0F0F) << 4 | (ret >> 4) & 0x0F0F0F0F;
|
||||
ret = (ret << 24) | ((ret & 0xFF00) << 8) | ((ret >> 8) & 0xFF00) | (ret >> 24);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
private static byte ReverseBits(byte data)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
uint u = (uint)data * 0x00020202;
|
||||
uint m = 0x01044010;
|
||||
uint s = u & m;
|
||||
uint t = (u << 2) & (m << 1);
|
||||
return (byte)((0x01001001 * (s + t)) >> 24);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void GenerateLookupTable()
|
||||
{
|
||||
crc32Table = new UInt32[256];
|
||||
unchecked
|
||||
{
|
||||
UInt32 dwCrc;
|
||||
byte i = 0;
|
||||
do
|
||||
{
|
||||
dwCrc = i;
|
||||
for (byte j = 8; j > 0; j--)
|
||||
{
|
||||
if ((dwCrc & 1) == 1)
|
||||
{
|
||||
dwCrc = (dwCrc >> 1) ^ dwPolynomial;
|
||||
}
|
||||
else
|
||||
{
|
||||
dwCrc >>= 1;
|
||||
}
|
||||
}
|
||||
if (reverseBits)
|
||||
{
|
||||
crc32Table[ReverseBits(i)] = ReverseBits(dwCrc);
|
||||
}
|
||||
else
|
||||
{
|
||||
crc32Table[i] = dwCrc;
|
||||
}
|
||||
i++;
|
||||
} while (i != 0);
|
||||
}
|
||||
|
||||
#if VERBOSE
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("private static readonly UInt32[] crc32Table = {");
|
||||
for (int i = 0; i < crc32Table.Length; i+=4)
|
||||
{
|
||||
Console.Write(" ");
|
||||
for (int j=0; j < 4; j++)
|
||||
{
|
||||
Console.Write(" 0x{0:X8}U,", crc32Table[i+j]);
|
||||
}
|
||||
Console.WriteLine();
|
||||
}
|
||||
Console.WriteLine("};");
|
||||
Console.WriteLine();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
private uint gf2_matrix_times(uint[] matrix, uint vec)
|
||||
{
|
||||
uint sum = 0;
|
||||
int i = 0;
|
||||
while (vec != 0)
|
||||
{
|
||||
if ((vec & 0x01) == 0x01)
|
||||
sum ^= matrix[i];
|
||||
vec >>= 1;
|
||||
i++;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
private void gf2_matrix_square(uint[] square, uint[] mat)
|
||||
{
|
||||
for (int i = 0; i < 32; i++)
|
||||
square[i] = gf2_matrix_times(mat, mat[i]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Combines the given CRC32 value with the current running total.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is useful when using a divide-and-conquer approach to
|
||||
/// calculating a CRC. Multiple threads can each calculate a
|
||||
/// CRC32 on a segment of the data, and then combine the
|
||||
/// individual CRC32 values at the end.
|
||||
/// </remarks>
|
||||
/// <param name="crc">the crc value to be combined with this one</param>
|
||||
/// <param name="length">the length of data the CRC value was calculated on</param>
|
||||
public void Combine(int crc, int length)
|
||||
{
|
||||
uint[] even = new uint[32]; // even-power-of-two zeros operator
|
||||
uint[] odd = new uint[32]; // odd-power-of-two zeros operator
|
||||
|
||||
if (length == 0)
|
||||
return;
|
||||
|
||||
uint crc1 = ~_register;
|
||||
uint crc2 = (uint)crc;
|
||||
|
||||
// put operator for one zero bit in odd
|
||||
odd[0] = this.dwPolynomial; // the CRC-32 polynomial
|
||||
uint row = 1;
|
||||
for (int i = 1; i < 32; i++)
|
||||
{
|
||||
odd[i] = row;
|
||||
row <<= 1;
|
||||
}
|
||||
|
||||
// put operator for two zero bits in even
|
||||
gf2_matrix_square(even, odd);
|
||||
|
||||
// put operator for four zero bits in odd
|
||||
gf2_matrix_square(odd, even);
|
||||
|
||||
uint len2 = (uint)length;
|
||||
|
||||
// apply len2 zeros to crc1 (first square will put the operator for one
|
||||
// zero byte, eight zero bits, in even)
|
||||
do
|
||||
{
|
||||
// apply zeros operator for this bit of len2
|
||||
gf2_matrix_square(even, odd);
|
||||
|
||||
if ((len2 & 1) == 1)
|
||||
crc1 = gf2_matrix_times(even, crc1);
|
||||
len2 >>= 1;
|
||||
|
||||
if (len2 == 0)
|
||||
break;
|
||||
|
||||
// another iteration of the loop with odd and even swapped
|
||||
gf2_matrix_square(odd, even);
|
||||
if ((len2 & 1) == 1)
|
||||
crc1 = gf2_matrix_times(odd, crc1);
|
||||
len2 >>= 1;
|
||||
|
||||
|
||||
} while (len2 != 0);
|
||||
|
||||
crc1 ^= crc2;
|
||||
|
||||
_register = ~crc1;
|
||||
|
||||
//return (int) crc1;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance of the CRC32 class using the default settings: no
|
||||
/// bit reversal, and a polynomial of 0xEDB88320.
|
||||
/// </summary>
|
||||
public CRC32() : this(false)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance of the CRC32 class, specifying whether to reverse
|
||||
/// data bits or not.
|
||||
/// </summary>
|
||||
/// <param name='reverseBits'>
|
||||
/// specify true if the instance should reverse data bits.
|
||||
/// </param>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// In the CRC-32 used by BZip2, the bits are reversed. Therefore if you
|
||||
/// want a CRC32 with compatibility with BZip2, you should pass true
|
||||
/// here. In the CRC-32 used by GZIP and PKZIP, the bits are not
|
||||
/// reversed; Therefore if you want a CRC32 with compatibility with
|
||||
/// those, you should pass false.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public CRC32(bool reverseBits) :
|
||||
this(unchecked((int)0xEDB88320), reverseBits)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance of the CRC32 class, specifying the polynomial and
|
||||
/// whether to reverse data bits or not.
|
||||
/// </summary>
|
||||
/// <param name='polynomial'>
|
||||
/// The polynomial to use for the CRC, expressed in the reversed (LSB)
|
||||
/// format: the highest ordered bit in the polynomial value is the
|
||||
/// coefficient of the 0th power; the second-highest order bit is the
|
||||
/// coefficient of the 1 power, and so on. Expressed this way, the
|
||||
/// polynomial for the CRC-32C used in IEEE 802.3, is 0xEDB88320.
|
||||
/// </param>
|
||||
/// <param name='reverseBits'>
|
||||
/// specify true if the instance should reverse data bits.
|
||||
/// </param>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// In the CRC-32 used by BZip2, the bits are reversed. Therefore if you
|
||||
/// want a CRC32 with compatibility with BZip2, you should pass true
|
||||
/// here for the <c>reverseBits</c> parameter. In the CRC-32 used by
|
||||
/// GZIP and PKZIP, the bits are not reversed; Therefore if you want a
|
||||
/// CRC32 with compatibility with those, you should pass false for the
|
||||
/// <c>reverseBits</c> parameter.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public CRC32(int polynomial, bool reverseBits)
|
||||
{
|
||||
this.reverseBits = reverseBits;
|
||||
this.dwPolynomial = (uint)polynomial;
|
||||
this.GenerateLookupTable();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset the CRC-32 class - clear the CRC "remainder register."
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Use this when employing a single instance of this class to compute
|
||||
/// multiple, distinct CRCs on multiple, distinct data blocks.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public void Reset()
|
||||
{
|
||||
_register = 0xFFFFFFFFU;
|
||||
}
|
||||
|
||||
// private member vars
|
||||
private readonly UInt32 dwPolynomial;
|
||||
private Int64 _TotalBytesRead;
|
||||
private readonly bool reverseBits;
|
||||
private UInt32[] crc32Table;
|
||||
private const int BUFFER_SIZE = 8192;
|
||||
private UInt32 _register = 0xFFFFFFFFU;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A Stream that calculates a CRC32 (a checksum) on all bytes read,
|
||||
/// or on all bytes written.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This class can be used to verify the CRC of a ZipEntry when
|
||||
/// reading from a stream, or to calculate a CRC when writing to a
|
||||
/// stream. The stream should be used to either read, or write, but
|
||||
/// not both. If you intermix reads and writes, the results are not
|
||||
/// defined.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// This class is intended primarily for use internally by the
|
||||
/// DotNetZip library.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public class CrcCalculatorStream : System.IO.Stream, System.IDisposable
|
||||
{
|
||||
private static readonly Int64 UnsetLengthLimit = -99;
|
||||
|
||||
internal System.IO.Stream _innerStream;
|
||||
private readonly CRC32 _Crc32;
|
||||
private readonly Int64 _lengthLimit = -99;
|
||||
private bool _leaveOpen;
|
||||
|
||||
/// <summary>
|
||||
/// The default constructor.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Instances returned from this constructor will leave the underlying
|
||||
/// stream open upon Close(). The stream uses the default CRC32
|
||||
/// algorithm, which implies a polynomial of 0xEDB88320.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name="stream">The underlying stream</param>
|
||||
public CrcCalculatorStream(System.IO.Stream stream)
|
||||
: this(true, CrcCalculatorStream.UnsetLengthLimit, stream, null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The constructor allows the caller to specify how to handle the
|
||||
/// underlying stream at close.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The stream uses the default CRC32 algorithm, which implies a
|
||||
/// polynomial of 0xEDB88320.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name="stream">The underlying stream</param>
|
||||
/// <param name="leaveOpen">true to leave the underlying stream
|
||||
/// open upon close of the <c>CrcCalculatorStream</c>; false otherwise.</param>
|
||||
public CrcCalculatorStream(System.IO.Stream stream, bool leaveOpen)
|
||||
: this(leaveOpen, CrcCalculatorStream.UnsetLengthLimit, stream, null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A constructor allowing the specification of the length of the stream
|
||||
/// to read.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The stream uses the default CRC32 algorithm, which implies a
|
||||
/// polynomial of 0xEDB88320.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Instances returned from this constructor will leave the underlying
|
||||
/// stream open upon Close().
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name="stream">The underlying stream</param>
|
||||
/// <param name="length">The length of the stream to slurp</param>
|
||||
public CrcCalculatorStream(System.IO.Stream stream, Int64 length)
|
||||
: this(true, length, stream, null)
|
||||
{
|
||||
if (length < 0)
|
||||
throw new ArgumentException("length");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A constructor allowing the specification of the length of the stream
|
||||
/// to read, as well as whether to keep the underlying stream open upon
|
||||
/// Close().
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The stream uses the default CRC32 algorithm, which implies a
|
||||
/// polynomial of 0xEDB88320.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name="stream">The underlying stream</param>
|
||||
/// <param name="length">The length of the stream to slurp</param>
|
||||
/// <param name="leaveOpen">true to leave the underlying stream
|
||||
/// open upon close of the <c>CrcCalculatorStream</c>; false otherwise.</param>
|
||||
public CrcCalculatorStream(System.IO.Stream stream, Int64 length, bool leaveOpen)
|
||||
: this(leaveOpen, length, stream, null)
|
||||
{
|
||||
if (length < 0)
|
||||
throw new ArgumentException("length");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A constructor allowing the specification of the length of the stream
|
||||
/// to read, as well as whether to keep the underlying stream open upon
|
||||
/// Close(), and the CRC32 instance to use.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The stream uses the specified CRC32 instance, which allows the
|
||||
/// application to specify how the CRC gets calculated.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name="stream">The underlying stream</param>
|
||||
/// <param name="length">The length of the stream to slurp</param>
|
||||
/// <param name="leaveOpen">true to leave the underlying stream
|
||||
/// open upon close of the <c>CrcCalculatorStream</c>; false otherwise.</param>
|
||||
/// <param name="crc32">the CRC32 instance to use to calculate the CRC32</param>
|
||||
public CrcCalculatorStream(System.IO.Stream stream, Int64 length, bool leaveOpen,
|
||||
CRC32 crc32)
|
||||
: this(leaveOpen, length, stream, crc32)
|
||||
{
|
||||
if (length < 0)
|
||||
throw new ArgumentException("length");
|
||||
}
|
||||
|
||||
|
||||
// This ctor is private - no validation is done here. This is to allow the use
|
||||
// of a (specific) negative value for the _lengthLimit, to indicate that there
|
||||
// is no length set. So we validate the length limit in those ctors that use an
|
||||
// explicit param, otherwise we don't validate, because it could be our special
|
||||
// value.
|
||||
private CrcCalculatorStream
|
||||
(bool leaveOpen, Int64 length, System.IO.Stream stream, CRC32 crc32)
|
||||
: base()
|
||||
{
|
||||
_innerStream = stream;
|
||||
_Crc32 = crc32 ?? new CRC32();
|
||||
_lengthLimit = length;
|
||||
_leaveOpen = leaveOpen;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total number of bytes run through the CRC32 calculator.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// This is either the total number of bytes read, or the total number of
|
||||
/// bytes written, depending on the direction of this stream.
|
||||
/// </remarks>
|
||||
public Int64 TotalBytesSlurped
|
||||
{
|
||||
get { return _Crc32.TotalBytesRead; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides the current CRC for all blocks slurped in.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The running total of the CRC is kept as data is written or read
|
||||
/// through the stream. read this property after all reads or writes to
|
||||
/// get an accurate CRC for the entire stream.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public Int32 Crc
|
||||
{
|
||||
get { return _Crc32.Crc32Result; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the underlying stream will be left open when the
|
||||
/// <c>CrcCalculatorStream</c> is Closed.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Set this at any point before calling <see cref="Close()"/>.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public bool LeaveOpen
|
||||
{
|
||||
get { return _leaveOpen; }
|
||||
set { _leaveOpen = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read from the stream
|
||||
/// </summary>
|
||||
/// <param name="buffer">the buffer to read</param>
|
||||
/// <param name="offset">the offset at which to start</param>
|
||||
/// <param name="count">the number of bytes to read</param>
|
||||
/// <returns>the number of bytes actually read</returns>
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
int bytesToRead = count;
|
||||
|
||||
// Need to limit the # of bytes returned, if the stream is intended to have
|
||||
// a definite length. This is especially useful when returning a stream for
|
||||
// the uncompressed data directly to the application. The app won't
|
||||
// necessarily read only the UncompressedSize number of bytes. For example
|
||||
// wrapping the stream returned from OpenReader() into a StreadReader() and
|
||||
// calling ReadToEnd() on it, We can "over-read" the zip data and get a
|
||||
// corrupt string. The length limits that, prevents that problem.
|
||||
|
||||
if (_lengthLimit != CrcCalculatorStream.UnsetLengthLimit)
|
||||
{
|
||||
if (_Crc32.TotalBytesRead >= _lengthLimit) return 0; // EOF
|
||||
Int64 bytesRemaining = _lengthLimit - _Crc32.TotalBytesRead;
|
||||
if (bytesRemaining < count) bytesToRead = (int)bytesRemaining;
|
||||
}
|
||||
int n = _innerStream.Read(buffer, offset, bytesToRead);
|
||||
if (n > 0) _Crc32.SlurpBlock(buffer, offset, n);
|
||||
return n;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write to the stream.
|
||||
/// </summary>
|
||||
/// <param name="buffer">the buffer from which to write</param>
|
||||
/// <param name="offset">the offset at which to start writing</param>
|
||||
/// <param name="count">the number of bytes to write</param>
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (count > 0) _Crc32.SlurpBlock(buffer, offset, count);
|
||||
_innerStream.Write(buffer, offset, count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the stream supports reading.
|
||||
/// </summary>
|
||||
public override bool CanRead
|
||||
{
|
||||
get { return _innerStream.CanRead; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the stream supports seeking.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Always returns false.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public override bool CanSeek
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the stream supports writing.
|
||||
/// </summary>
|
||||
public override bool CanWrite
|
||||
{
|
||||
get { return _innerStream.CanWrite; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flush the stream.
|
||||
/// </summary>
|
||||
public override void Flush()
|
||||
{
|
||||
_innerStream.Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the length of the underlying stream.
|
||||
/// </summary>
|
||||
public override long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_lengthLimit == CrcCalculatorStream.UnsetLengthLimit)
|
||||
return _innerStream.Length;
|
||||
else return _lengthLimit;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The getter for this property returns the total bytes read.
|
||||
/// If you use the setter, it will throw
|
||||
/// <see cref="NotSupportedException"/>.
|
||||
/// </summary>
|
||||
public override long Position
|
||||
{
|
||||
get { return _Crc32.TotalBytesRead; }
|
||||
set { throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Seeking is not supported on this stream. This method always throws
|
||||
/// <see cref="NotSupportedException"/>
|
||||
/// </summary>
|
||||
/// <param name="offset">N/A</param>
|
||||
/// <param name="origin">N/A</param>
|
||||
/// <returns>N/A</returns>
|
||||
public override long Seek(long offset, System.IO.SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method always throws
|
||||
/// <see cref="NotSupportedException"/>
|
||||
/// </summary>
|
||||
/// <param name="value">N/A</param>
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
|
||||
void IDisposable.Dispose()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes the stream.
|
||||
/// </summary>
|
||||
public override void Close()
|
||||
{
|
||||
base.Close();
|
||||
if (!_leaveOpen)
|
||||
_innerStream.Close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
99
SabreTools.IO/Compression/BZip2/Rand.cs
Normal file
99
SabreTools.IO/Compression/BZip2/Rand.cs
Normal file
@@ -0,0 +1,99 @@
|
||||
// Rand.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2011 Dino Chiesa.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Last Saved: <2011-July-31 15:09:16>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines a helper class for the BZip2 classes. This code
|
||||
// is derived from the original BZip2 source code.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
|
||||
namespace SabreTools.IO.Compression.BZip2
|
||||
{
|
||||
internal static class Rand
|
||||
{
|
||||
private static readonly int[] RNUMS =
|
||||
[
|
||||
619, 720, 127, 481, 931, 816, 813, 233, 566, 247,
|
||||
985, 724, 205, 454, 863, 491, 741, 242, 949, 214,
|
||||
733, 859, 335, 708, 621, 574, 73, 654, 730, 472,
|
||||
419, 436, 278, 496, 867, 210, 399, 680, 480, 51,
|
||||
878, 465, 811, 169, 869, 675, 611, 697, 867, 561,
|
||||
862, 687, 507, 283, 482, 129, 807, 591, 733, 623,
|
||||
150, 238, 59, 379, 684, 877, 625, 169, 643, 105,
|
||||
170, 607, 520, 932, 727, 476, 693, 425, 174, 647,
|
||||
73, 122, 335, 530, 442, 853, 695, 249, 445, 515,
|
||||
909, 545, 703, 919, 874, 474, 882, 500, 594, 612,
|
||||
641, 801, 220, 162, 819, 984, 589, 513, 495, 799,
|
||||
161, 604, 958, 533, 221, 400, 386, 867, 600, 782,
|
||||
382, 596, 414, 171, 516, 375, 682, 485, 911, 276,
|
||||
98, 553, 163, 354, 666, 933, 424, 341, 533, 870,
|
||||
227, 730, 475, 186, 263, 647, 537, 686, 600, 224,
|
||||
469, 68, 770, 919, 190, 373, 294, 822, 808, 206,
|
||||
184, 943, 795, 384, 383, 461, 404, 758, 839, 887,
|
||||
715, 67, 618, 276, 204, 918, 873, 777, 604, 560,
|
||||
951, 160, 578, 722, 79, 804, 96, 409, 713, 940,
|
||||
652, 934, 970, 447, 318, 353, 859, 672, 112, 785,
|
||||
645, 863, 803, 350, 139, 93, 354, 99, 820, 908,
|
||||
609, 772, 154, 274, 580, 184, 79, 626, 630, 742,
|
||||
653, 282, 762, 623, 680, 81, 927, 626, 789, 125,
|
||||
411, 521, 938, 300, 821, 78, 343, 175, 128, 250,
|
||||
170, 774, 972, 275, 999, 639, 495, 78, 352, 126,
|
||||
857, 956, 358, 619, 580, 124, 737, 594, 701, 612,
|
||||
669, 112, 134, 694, 363, 992, 809, 743, 168, 974,
|
||||
944, 375, 748, 52, 600, 747, 642, 182, 862, 81,
|
||||
344, 805, 988, 739, 511, 655, 814, 334, 249, 515,
|
||||
897, 955, 664, 981, 649, 113, 974, 459, 893, 228,
|
||||
433, 837, 553, 268, 926, 240, 102, 654, 459, 51,
|
||||
686, 754, 806, 760, 493, 403, 415, 394, 687, 700,
|
||||
946, 670, 656, 610, 738, 392, 760, 799, 887, 653,
|
||||
978, 321, 576, 617, 626, 502, 894, 679, 243, 440,
|
||||
680, 879, 194, 572, 640, 724, 926, 56, 204, 700,
|
||||
707, 151, 457, 449, 797, 195, 791, 558, 945, 679,
|
||||
297, 59, 87, 824, 713, 663, 412, 693, 342, 606,
|
||||
134, 108, 571, 364, 631, 212, 174, 643, 304, 329,
|
||||
343, 97, 430, 751, 497, 314, 983, 374, 822, 928,
|
||||
140, 206, 73, 263, 980, 736, 876, 478, 430, 305,
|
||||
170, 514, 364, 692, 829, 82, 855, 953, 676, 246,
|
||||
369, 970, 294, 750, 807, 827, 150, 790, 288, 923,
|
||||
804, 378, 215, 828, 592, 281, 565, 555, 710, 82,
|
||||
896, 831, 547, 261, 524, 462, 293, 465, 502, 56,
|
||||
661, 821, 976, 991, 658, 869, 905, 758, 745, 193,
|
||||
768, 550, 608, 933, 378, 286, 215, 979, 792, 961,
|
||||
61, 688, 793, 644, 986, 403, 106, 366, 905, 644,
|
||||
372, 567, 466, 434, 645, 210, 389, 550, 919, 135,
|
||||
780, 773, 635, 389, 707, 100, 626, 958, 165, 504,
|
||||
920, 176, 193, 713, 857, 265, 203, 50, 668, 108,
|
||||
645, 990, 626, 197, 510, 357, 358, 850, 858, 364,
|
||||
936, 638
|
||||
];
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the "random" number at a specific index.
|
||||
/// </summary>
|
||||
/// <param name='i'>the index</param>
|
||||
/// <returns>the random number</returns>
|
||||
internal static int Rnums(int i)
|
||||
{
|
||||
return RNUMS[i];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
15
SabreTools.IO/Compression/Blast/Constants.cs
Normal file
15
SabreTools.IO/Compression/Blast/Constants.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
namespace SabreTools.IO.Compression.Blast
|
||||
{
|
||||
public static class Constants
|
||||
{
|
||||
/// <summary>
|
||||
/// Maximum code length
|
||||
/// </summary>
|
||||
public const int MAXBITS = 13;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum window size
|
||||
/// </summary>
|
||||
public const int MAXWIN = 4096;
|
||||
}
|
||||
}
|
||||
287
SabreTools.IO/Compression/Blast/Decompressor.cs
Normal file
287
SabreTools.IO/Compression/Blast/Decompressor.cs
Normal file
@@ -0,0 +1,287 @@
|
||||
/* blast.c
|
||||
* Copyright (C) 2003, 2012, 2013 Mark Adler
|
||||
* For conditions of distribution and use, see copyright notice in blast.h
|
||||
* version 1.3, 24 Aug 2013
|
||||
*
|
||||
* blast.c decompresses data compressed by the PKWare Compression Library.
|
||||
* This function provides functionality similar to the explode() function of
|
||||
* the PKWare library, hence the name "blast".
|
||||
*
|
||||
* This decompressor is based on the excellent format description provided by
|
||||
* Ben Rudiak-Gould in comp.compression on August 13, 2001. Interestingly, the
|
||||
* example Ben provided in the post is incorrect. The distance 110001 should
|
||||
* instead be 111000. When corrected, the example byte stream becomes:
|
||||
*
|
||||
* 00 04 82 24 25 8f 80 7f
|
||||
*
|
||||
* which decompresses to "AIAIAIAIAIAIA" (without the quotes).
|
||||
*/
|
||||
|
||||
/*
|
||||
* Change history:
|
||||
*
|
||||
* 1.0 12 Feb 2003 - First version
|
||||
* 1.1 16 Feb 2003 - Fixed distance check for > 4 GB uncompressed data
|
||||
* 1.2 24 Oct 2012 - Add note about using binary mode in stdio
|
||||
* - Fix comparisons of differently signed integers
|
||||
* 1.3 24 Aug 2013 - Return unused input from blast()
|
||||
* - Fix test code to correctly report unused input
|
||||
* - Enable the provision of initial input to blast()
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using static SabreTools.IO.Compression.Blast.Constants;
|
||||
|
||||
namespace SabreTools.IO.Compression.Blast
|
||||
{
|
||||
/// <summary>
|
||||
/// blast() decompresses the PKWare Data Compression Library (DCL) compressed
|
||||
/// format. It provides the same functionality as the explode() function in
|
||||
/// that library. (Note: PKWare overused the "implode" verb, and the format
|
||||
/// used by their library implode() function is completely different and
|
||||
/// incompatible with the implode compression method supported by PKZIP.)
|
||||
///
|
||||
/// The binary mode for stdio functions should be used to assure that the
|
||||
/// compressed data is not corrupted when read or written. For example:
|
||||
/// fopen(..., "rb") and fopen(..., "wb").
|
||||
/// </summary>
|
||||
public unsafe class Decompressor
|
||||
{
|
||||
#region Huffman Encoding
|
||||
|
||||
/// <summary>
|
||||
/// Literal code
|
||||
/// </summary>
|
||||
private readonly Huffman litcode = new(MAXBITS + 1, 256);
|
||||
|
||||
/// <summary>
|
||||
/// Length code
|
||||
/// </summary>
|
||||
private readonly Huffman lencode = new(MAXBITS + 1, 16);
|
||||
|
||||
/// <summary>
|
||||
/// Distance code
|
||||
/// </summary>
|
||||
private readonly Huffman distcode = new(MAXBITS + 1, 64);
|
||||
|
||||
/// <summary>
|
||||
/// Base for length codes
|
||||
/// </summary>
|
||||
private static readonly short[] baseLength =
|
||||
[
|
||||
3, 2, 4, 5, 6, 7, 8, 9, 10, 12, 16, 24, 40, 72, 136, 264
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Extra bits for length codes
|
||||
/// </summary>
|
||||
private static readonly byte[] extra =
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8
|
||||
];
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Create a Blast decompressor
|
||||
/// </summary>
|
||||
private Decompressor()
|
||||
{
|
||||
// Repeated code lengths of literal codes
|
||||
byte[] litlen =
|
||||
[
|
||||
11, 124, 8, 7, 28, 7, 188, 13, 76, 4, 10, 8, 12, 10, 12, 10, 8, 23, 8,
|
||||
9, 7, 6, 7, 8, 7, 6, 55, 8, 23, 24, 12, 11, 7, 9, 11, 12, 6, 7, 22, 5,
|
||||
7, 24, 6, 11, 9, 6, 7, 22, 7, 11, 38, 7, 9, 8, 25, 11, 8, 11, 9, 12,
|
||||
8, 12, 5, 38, 5, 38, 5, 11, 7, 5, 6, 21, 6, 10, 53, 8, 7, 24, 10, 27,
|
||||
44, 253, 253, 253, 252, 252, 252, 13, 12, 45, 12, 45, 12, 61, 12, 45,
|
||||
44, 173
|
||||
];
|
||||
litcode.Initialize(litlen);
|
||||
|
||||
// Repeated code lengths of length codes 0..15
|
||||
byte[] lenlen =
|
||||
[
|
||||
2, 35, 36, 53, 38, 23
|
||||
];
|
||||
lencode.Initialize(lenlen);
|
||||
|
||||
// Repeated code lengths of distance codes 0..63
|
||||
byte[] distlen =
|
||||
[
|
||||
2, 20, 53, 230, 247, 151, 248
|
||||
];
|
||||
distcode.Initialize(distlen);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a Blast decompressor
|
||||
/// </summary>
|
||||
public static Decompressor Create() => new();
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Decompress source data to an output stream
|
||||
/// </summary>
|
||||
public bool CopyTo(byte[] source, Stream dest)
|
||||
=> CopyTo(new MemoryStream(source), dest);
|
||||
|
||||
/// <summary>
|
||||
/// Decompress source data to an output stream
|
||||
/// </summary>
|
||||
public bool CopyTo(Stream source, Stream dest)
|
||||
{
|
||||
// Ignore unwritable streams
|
||||
if (!dest.CanWrite)
|
||||
return false;
|
||||
|
||||
// Input/output state
|
||||
var state = new State(source, dest);
|
||||
|
||||
// Attempt to decompress using the above state
|
||||
int err;
|
||||
try
|
||||
{
|
||||
err = Decompress(state);
|
||||
}
|
||||
catch (IndexOutOfRangeException)
|
||||
{
|
||||
// This was originally a jump, which is bad form for C#
|
||||
err = 2;
|
||||
}
|
||||
|
||||
// Write any leftover output and update the error code if needed
|
||||
if (err != 1 && state.Next != 0 && !state.ProcessOutput() && err == 0)
|
||||
err = 1;
|
||||
|
||||
return err == 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decode PKWare Compression Library stream.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// First byte is 0 if literals are uncoded or 1 if they are coded. Second
|
||||
/// byte is 4, 5, or 6 for the number of extra bits in the distance code.
|
||||
/// This is the base-2 logarithm of the dictionary size minus six.
|
||||
///
|
||||
/// Compressed data is a combination of literals and length/distance pairs
|
||||
/// terminated by an end code. Literals are either Huffman coded or
|
||||
/// uncoded bytes. A length/distance pair is a coded length followed by a
|
||||
/// coded distance to represent a string that occurs earlier in the
|
||||
/// uncompressed data that occurs again at the current location.
|
||||
///
|
||||
/// A bit preceding a literal or length/distance pair indicates which comes
|
||||
/// next, 0 for literals, 1 for length/distance.
|
||||
///
|
||||
/// If literals are uncoded, then the next eight bits are the literal, in the
|
||||
/// normal bit order in the stream, i.e. no bit-reversal is needed. Similarly,
|
||||
/// no bit reversal is needed for either the length extra bits or the distance
|
||||
/// extra bits.
|
||||
///
|
||||
/// Literal bytes are simply written to the output. A length/distance pair is
|
||||
/// an instruction to copy previously uncompressed bytes to the output. The
|
||||
/// copy is from distance bytes back in the output stream, copying for length
|
||||
/// bytes.
|
||||
///
|
||||
/// Distances pointing before the beginning of the output data are not
|
||||
/// permitted.
|
||||
///
|
||||
/// Overlapped copies, where the length is greater than the distance, are
|
||||
/// allowed and common. For example, a distance of one and a length of 518
|
||||
/// simply copies the last byte 518 times. A distance of four and a length of
|
||||
/// twelve copies the last four bytes three times. A simple forward copy
|
||||
/// ignoring whether the length is greater than the distance or not implements
|
||||
/// this correctly.
|
||||
/// </remarks>
|
||||
private int Decompress(State state)
|
||||
{
|
||||
int symbol; // decoded symbol, extra bits for distance
|
||||
int len; // length for copy
|
||||
uint dist; // distance for copy
|
||||
int copy; // copy counter
|
||||
int from, to; // copy pointers
|
||||
|
||||
// Read header
|
||||
int lit = state.ReadBits(8); // true if literals are coded
|
||||
if (lit > 1)
|
||||
return -1;
|
||||
|
||||
int dict = state.ReadBits(8); // log2(dictionary size) - 6
|
||||
if (dict < 4 || dict > 6)
|
||||
return -2;
|
||||
|
||||
// Decode literals and length/distance pairs
|
||||
while (true)
|
||||
{
|
||||
if (state.ReadBits(1) != 0)
|
||||
{
|
||||
// Get length
|
||||
symbol = lencode.Decode(state);
|
||||
len = baseLength[symbol] + state.ReadBits(extra[symbol]);
|
||||
if (len == 519)
|
||||
break; // end code
|
||||
|
||||
// Get distance
|
||||
symbol = len == 2 ? 2 : dict;
|
||||
dist = (uint)(distcode.Decode(state) << symbol);
|
||||
dist += (uint)state.ReadBits(symbol);
|
||||
dist++;
|
||||
if (state.First && dist > state.Next)
|
||||
return -3; //distance too far back
|
||||
|
||||
// Copy length bytes from distance bytes back
|
||||
do
|
||||
{
|
||||
to = (int)state.Next;
|
||||
from = (int)(to - dist);
|
||||
copy = MAXWIN;
|
||||
if (state.Next < dist)
|
||||
{
|
||||
from += copy;
|
||||
copy = (int)dist;
|
||||
}
|
||||
|
||||
copy -= (int)state.Next;
|
||||
if (copy > len)
|
||||
copy = len;
|
||||
|
||||
len -= copy;
|
||||
state.Next += (uint)copy;
|
||||
state.CopyOutputBytes(to, from, copy);
|
||||
|
||||
if (state.Next == MAXWIN)
|
||||
{
|
||||
if (!state.ProcessOutput())
|
||||
return 1;
|
||||
|
||||
state.Next = 0;
|
||||
state.First = false;
|
||||
}
|
||||
}
|
||||
while (len != 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get literal and write it
|
||||
symbol = lit != 0 ? litcode.Decode(state) : state.ReadBits(8);
|
||||
state.WriteToOutput((byte)symbol);
|
||||
if (state.Next == MAXWIN)
|
||||
{
|
||||
if (!state.ProcessOutput())
|
||||
return 1;
|
||||
|
||||
state.Next = 0;
|
||||
state.First = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
198
SabreTools.IO/Compression/Blast/Huffman.cs
Normal file
198
SabreTools.IO/Compression/Blast/Huffman.cs
Normal file
@@ -0,0 +1,198 @@
|
||||
using static SabreTools.IO.Compression.Blast.Constants;
|
||||
|
||||
namespace SabreTools.IO.Compression.Blast
|
||||
{
|
||||
/// <summary>
|
||||
/// Huffman code decoding tables. count[1..MAXBITS] is the number of symbols of
|
||||
/// each length, which for a canonical code are stepped through in order.
|
||||
/// symbol[] are the symbol values in canonical order, where the number of
|
||||
/// entries is the sum of the counts in count[]. The decoding process can be
|
||||
/// seen in the function decode() below.
|
||||
/// </summary>
|
||||
public class Huffman
|
||||
{
|
||||
/// <summary>
|
||||
/// Number of symbols of each length
|
||||
/// </summary>
|
||||
public short[] Count { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Pointer to number of symbols of each length
|
||||
/// </summary>
|
||||
public int CountPtr { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Canonically ordered symbols
|
||||
/// </summary>
|
||||
public short[] Symbol { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="countLength">Length of the Count array</param>
|
||||
/// <param name="symbolLength">Length of the Symbol array</param>
|
||||
public Huffman(int countLength, int symbolLength)
|
||||
{
|
||||
Count = new short[countLength];
|
||||
Symbol = new short[symbolLength];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a list of repeated code lengths rep[0..n-1], where each byte is a
|
||||
/// count (high four bits + 1) and a code length (low four bits), generate the
|
||||
/// list of code lengths. This compaction reduces the size of the object code.
|
||||
/// Then given the list of code lengths length[0..n-1] representing a canonical
|
||||
/// Huffman code for n symbols, construct the tables required to decode those
|
||||
/// codes. Those tables are the number of codes of each length, and the symbols
|
||||
/// sorted by length, retaining their original order within each length. The
|
||||
/// return value is zero for a complete code set, negative for an over-
|
||||
/// subscribed code set, and positive for an incomplete code set. The tables
|
||||
/// can be used if the return value is zero or positive, but they cannot be used
|
||||
/// if the return value is negative. If the return value is zero, it is not
|
||||
/// possible for decode() using that table to return an error--any stream of
|
||||
/// enough bits will resolve to a symbol. If the return value is positive, then
|
||||
/// it is possible for decode() using that table to return an error for received
|
||||
/// codes past the end of the incomplete lengths.
|
||||
/// </summary>
|
||||
/// <param name="rep">Repeated code length array</param>
|
||||
public int Initialize(byte[] rep)
|
||||
{
|
||||
int n = rep.Length; // Length of the bit length array
|
||||
short symbol = 0; // Current symbol when stepping through length[]
|
||||
short len; // Current length when stepping through h.Count[]
|
||||
int left; // Number of possible codes left of current length
|
||||
short[] offs = new short[MAXBITS + 1]; // offsets in symbol table for each length
|
||||
short[] length = new short[256]; // Code lengths
|
||||
|
||||
// Convert compact repeat counts into symbol bit length list
|
||||
int repPtr = 0;
|
||||
do
|
||||
{
|
||||
len = rep[repPtr++];
|
||||
left = (len >> 4) + 1;
|
||||
len &= 15;
|
||||
do
|
||||
{
|
||||
length[symbol++] = len;
|
||||
}
|
||||
while (--left != 0);
|
||||
}
|
||||
while (--n != 0);
|
||||
|
||||
n = symbol;
|
||||
|
||||
// Count number of codes of each length
|
||||
for (len = 0; len <= MAXBITS; len++)
|
||||
{
|
||||
Count[len] = 0;
|
||||
}
|
||||
|
||||
// Assumes lengths are within bounds
|
||||
for (symbol = 0; symbol < n; symbol++)
|
||||
{
|
||||
(Count[length[symbol]])++;
|
||||
}
|
||||
|
||||
// No codes! Complete, but decode() will fail
|
||||
if (Count[0] == n)
|
||||
return 0;
|
||||
|
||||
// Check for an over-subscribed or incomplete set of lengths
|
||||
left = 1; // One possible code of zero length
|
||||
for (len = 1; len <= MAXBITS; len++)
|
||||
{
|
||||
left <<= 1; // One more bit, double codes left
|
||||
left -= Count[len]; // Deduct count from possible codes
|
||||
if (left < 0)
|
||||
return left; // over-subscribed--return negative
|
||||
}
|
||||
|
||||
// Generate offsets into symbol table for each length for sorting
|
||||
offs[1] = 0;
|
||||
for (len = 1; len < MAXBITS; len++)
|
||||
{
|
||||
offs[len + 1] = (short)(offs[len] + Count[len]);
|
||||
}
|
||||
|
||||
// Put symbols in table sorted by length, by symbol order within each length
|
||||
for (symbol = 0; symbol < n; symbol++)
|
||||
{
|
||||
if (length[symbol] != 0)
|
||||
Symbol[offs[length[symbol]]++] = symbol;
|
||||
}
|
||||
|
||||
// Return zero for complete set, positive for incomplete set
|
||||
return left;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decode a code from the stream s using huffman table h. Return the symbol or
|
||||
/// a negative value if there is an error. If all of the lengths are zero, i.e.
|
||||
/// an empty code, or if the code is incomplete and an invalid code is received,
|
||||
/// then -9 is returned after reading MAXBITS bits.
|
||||
/// </summary>
|
||||
/// <param name="state">Current input/output state to process</param>
|
||||
/// <remarks>
|
||||
/// The codes as stored in the compressed data are bit-reversed relative to
|
||||
/// a simple integer ordering of codes of the same lengths. Hence below the
|
||||
/// bits are pulled from the compressed data one at a time and used to
|
||||
/// build the code value reversed from what is in the stream in order to
|
||||
/// permit simple integer comparisons for decoding.
|
||||
///
|
||||
/// The first code for the shortest length is all ones. Subsequent codes of
|
||||
/// the same length are simply integer decrements of the previous code. When
|
||||
/// moving up a length, a one bit is appended to the code. For a complete
|
||||
/// code, the last code of the longest length will be all zeros. To support
|
||||
/// this ordering, the bits pulled during decoding are inverted to apply the
|
||||
/// more "natural" ordering starting with all zeros and incrementing.
|
||||
/// </remarks>
|
||||
public int Decode(State state)
|
||||
{
|
||||
int len = 1; // Current number of bits in code
|
||||
int code = 0; // len bits being decoded
|
||||
int first = 0; // First code of length len
|
||||
int count; // Number of codes of length len
|
||||
int index = 0; // Index of first code of length len in symbol table
|
||||
int bitbuf = state.BitBuf; // Bits from stream
|
||||
int left = state.BitCnt; // Bits left in next or left to process
|
||||
int nextPtr = CountPtr + 1; // Next number of codes
|
||||
|
||||
while (true)
|
||||
{
|
||||
while (left-- != 0)
|
||||
{
|
||||
// Invert code
|
||||
code |= (bitbuf & 1) ^ 1;
|
||||
bitbuf >>= 1;
|
||||
count = Count[nextPtr++];
|
||||
|
||||
// If length len, return symbol
|
||||
if (code < first + count)
|
||||
{
|
||||
state.BitBuf = bitbuf;
|
||||
state.BitCnt = (state.BitCnt - len) & 7;
|
||||
return Symbol[index + (code - first)];
|
||||
}
|
||||
|
||||
// Else update for next length
|
||||
index += count;
|
||||
first += count;
|
||||
first <<= 1;
|
||||
code <<= 1;
|
||||
len++;
|
||||
}
|
||||
|
||||
left = (MAXBITS + 1) - len;
|
||||
if (left == 0)
|
||||
break;
|
||||
|
||||
bitbuf = state.ReadNextByte();
|
||||
if (left > 8)
|
||||
left = 8;
|
||||
}
|
||||
|
||||
// Ran out of codes
|
||||
return -9;
|
||||
}
|
||||
};
|
||||
}
|
||||
183
SabreTools.IO/Compression/Blast/State.cs
Normal file
183
SabreTools.IO/Compression/Blast/State.cs
Normal file
@@ -0,0 +1,183 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using static SabreTools.IO.Compression.Blast.Constants;
|
||||
|
||||
namespace SabreTools.IO.Compression.Blast
|
||||
{
|
||||
/// <summary>
|
||||
/// Input and output state
|
||||
/// </summary>
|
||||
public class State
|
||||
{
|
||||
#region Input State
|
||||
|
||||
/// <summary>
|
||||
/// Opaque information passed to InputFunction()
|
||||
/// </summary>
|
||||
private readonly Stream _source;
|
||||
|
||||
/// <summary>
|
||||
/// Next input location
|
||||
/// </summary>
|
||||
private readonly byte[] _input = new byte[MAXWIN];
|
||||
|
||||
/// <summary>
|
||||
/// Pointer to the next input location
|
||||
/// </summary>
|
||||
private int _inputPtr;
|
||||
|
||||
/// <summary>
|
||||
/// Available input at in
|
||||
/// </summary>
|
||||
private uint _available;
|
||||
|
||||
/// <summary>
|
||||
/// Bit buffer
|
||||
/// </summary>
|
||||
public int BitBuf { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of bits in bit buffer
|
||||
/// </summary>
|
||||
public int BitCnt { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Output State
|
||||
|
||||
/// <summary>
|
||||
/// Opaque information passed to OutputFunction()
|
||||
/// </summary>
|
||||
private readonly Stream _dest;
|
||||
|
||||
/// <summary>
|
||||
/// Index of next write location in out[]
|
||||
/// </summary>
|
||||
public uint Next { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// True to check distances (for first 4K)
|
||||
/// </summary>
|
||||
public bool First { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Output buffer and sliding window
|
||||
/// </summary>
|
||||
private readonly byte[] _output = new byte[MAXWIN];
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public State(Stream source, Stream dest)
|
||||
{
|
||||
_source = source;
|
||||
_inputPtr = 0;
|
||||
_available = 0;
|
||||
BitBuf = 0;
|
||||
BitCnt = 0;
|
||||
|
||||
_dest = dest;
|
||||
Next = 0;
|
||||
First = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy bytes in the output buffer between locations
|
||||
/// </summary>
|
||||
public void CopyOutputBytes(int to, int from, int len)
|
||||
{
|
||||
do
|
||||
{
|
||||
_output[to++] = _output[from++];
|
||||
}
|
||||
while (--len > 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return need bits from the input stream. This always leaves less than
|
||||
/// eight bits in the buffer. bits() works properly for need == 0.
|
||||
/// </summary>
|
||||
/// <param name="need">Number of bits to read</param>
|
||||
/// <remarks>
|
||||
/// Bits are stored in bytes from the least significant bit to the most
|
||||
/// significant bit. Therefore bits are dropped from the bottom of the bit
|
||||
/// buffer, using shift right, and new bytes are appended to the top of the
|
||||
/// bit buffer, using shift left.
|
||||
/// </remarks>
|
||||
public int ReadBits(int need)
|
||||
{
|
||||
// Load at least need bits into val
|
||||
int val = BitBuf;
|
||||
while (BitCnt < need)
|
||||
{
|
||||
// Load eight bits
|
||||
EnsureAvailable();
|
||||
val |= _input[_inputPtr++] << BitCnt;
|
||||
BitCnt += 8;
|
||||
}
|
||||
|
||||
// Drop need bits and update buffer, always zero to seven bits left
|
||||
BitBuf = val >> need;
|
||||
BitCnt -= need;
|
||||
|
||||
// Return need bits, zeroing the bits above that
|
||||
return val & ((1 << need) - 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process output for the current state
|
||||
/// </summary>
|
||||
/// <returns>True if the output could be added, false otherwise</returns>
|
||||
public bool ProcessOutput()
|
||||
{
|
||||
try
|
||||
{
|
||||
_dest.Write(_output, 0, (int)Next);
|
||||
_dest.Flush();
|
||||
|
||||
Next = 0;
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read the next byte from the input buffer
|
||||
/// </summary>
|
||||
public byte ReadNextByte()
|
||||
{
|
||||
EnsureAvailable();
|
||||
return _input[_inputPtr++];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a byte value to the output buffer
|
||||
/// </summary>
|
||||
public void WriteToOutput(byte value)
|
||||
=> _output[Next++] = value;
|
||||
|
||||
/// <summary>
|
||||
/// Ensure there are bytes available, if possible
|
||||
/// </summary>
|
||||
/// <exception cref="IndexOutOfRangeException"></exception>
|
||||
private void EnsureAvailable()
|
||||
{
|
||||
// If there are bytes
|
||||
if (_available != 0 && _inputPtr < _available)
|
||||
return;
|
||||
|
||||
// Read the next block
|
||||
_available = (uint)_source.Read(_input, 0, MAXWIN);
|
||||
if (_available == 0)
|
||||
throw new IndexOutOfRangeException();
|
||||
|
||||
// Reset the pointer
|
||||
_inputPtr = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
174
SabreTools.IO/Compression/Deflate/Adler.cs
Normal file
174
SabreTools.IO/Compression/Deflate/Adler.cs
Normal file
@@ -0,0 +1,174 @@
|
||||
// Zlib.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009-2011 Dino Chiesa and Microsoft Corporation.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Last Saved: <2011-August-03 19:52:28>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines classes for ZLIB compression and
|
||||
// decompression. This code is derived from the jzlib implementation of
|
||||
// zlib, but significantly modified. The object model is not the same,
|
||||
// and many of the behaviors are new or different. Nonetheless, in
|
||||
// keeping with the license for jzlib, the copyright to that code is
|
||||
// included below.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// The following notice applies to jzlib:
|
||||
//
|
||||
// Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in
|
||||
// the documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. The names of the authors may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
// INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
// jzlib is based on zlib-1.1.3.
|
||||
//
|
||||
// The following notice applies to zlib:
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (C) 1995-2004 Jean-loup Gailly and Mark Adler
|
||||
//
|
||||
// The ZLIB software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
// Jean-loup Gailly jloup@gzip.org
|
||||
// Mark Adler madler@alumni.caltech.edu
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace SabreTools.IO.Compression.Deflate
|
||||
{
|
||||
/// <summary>
|
||||
/// Computes an Adler-32 checksum.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The Adler checksum is similar to a CRC checksum, but faster to compute, though less
|
||||
/// reliable. It is used in producing RFC1950 compressed streams. The Adler checksum
|
||||
/// is a required part of the "ZLIB" standard. Applications will almost never need to
|
||||
/// use this class directly.
|
||||
/// </remarks>
|
||||
///
|
||||
/// <exclude/>
|
||||
public sealed class Adler
|
||||
{
|
||||
// largest prime smaller than 65536
|
||||
private static readonly uint BASE = 65521;
|
||||
// NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1
|
||||
private static readonly int NMAX = 5552;
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the Adler32 checksum.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This is used within ZLIB. You probably don't need to use this directly.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// To compute an Adler32 checksum on a byte array:
|
||||
/// <code>
|
||||
/// var adler = Adler.Adler32(0, null, 0, 0);
|
||||
/// adler = Adler.Adler32(adler, buffer, index, length);
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static uint Adler32(uint adler, byte[] buf, int index, int len)
|
||||
{
|
||||
if (buf == null)
|
||||
return 1;
|
||||
|
||||
uint s1 = (uint)(adler & 0xffff);
|
||||
uint s2 = (uint)((adler >> 16) & 0xffff);
|
||||
|
||||
while (len > 0)
|
||||
{
|
||||
int k = len < NMAX ? len : NMAX;
|
||||
len -= k;
|
||||
while (k >= 16)
|
||||
{
|
||||
//s1 += (buf[index++] & 0xff); s2 += s1;
|
||||
s1 += buf[index++]; s2 += s1;
|
||||
s1 += buf[index++]; s2 += s1;
|
||||
s1 += buf[index++]; s2 += s1;
|
||||
s1 += buf[index++]; s2 += s1;
|
||||
s1 += buf[index++]; s2 += s1;
|
||||
s1 += buf[index++]; s2 += s1;
|
||||
s1 += buf[index++]; s2 += s1;
|
||||
s1 += buf[index++]; s2 += s1;
|
||||
s1 += buf[index++]; s2 += s1;
|
||||
s1 += buf[index++]; s2 += s1;
|
||||
s1 += buf[index++]; s2 += s1;
|
||||
s1 += buf[index++]; s2 += s1;
|
||||
s1 += buf[index++]; s2 += s1;
|
||||
s1 += buf[index++]; s2 += s1;
|
||||
s1 += buf[index++]; s2 += s1;
|
||||
s1 += buf[index++]; s2 += s1;
|
||||
k -= 16;
|
||||
}
|
||||
if (k != 0)
|
||||
{
|
||||
do
|
||||
{
|
||||
s1 += buf[index++];
|
||||
s2 += s1;
|
||||
}
|
||||
while (--k != 0);
|
||||
}
|
||||
s1 %= BASE;
|
||||
s2 %= BASE;
|
||||
}
|
||||
return (uint)((s2 << 16) | s1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
78
SabreTools.IO/Compression/Deflate/BlockState.cs
Normal file
78
SabreTools.IO/Compression/Deflate/BlockState.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
// Deflate.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009 Dino Chiesa and Microsoft Corporation.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2011-August-03 19:52:15>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines logic for handling the Deflate or compression.
|
||||
//
|
||||
// This code is based on multiple sources:
|
||||
// - the original zlib v1.2.3 source, which is Copyright (C) 1995-2005 Jean-loup Gailly.
|
||||
// - the original jzlib, which is Copyright (c) 2000-2003 ymnk, JCraft,Inc.
|
||||
//
|
||||
// However, this code is significantly different from both.
|
||||
// The object model is not the same, and many of the behaviors are different.
|
||||
//
|
||||
// In keeping with the license for these other works, the copyrights for
|
||||
// jzlib and zlib are here.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in
|
||||
// the documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. The names of the authors may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
// INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
// This program is based on zlib-1.1.3; credit to authors
|
||||
// Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu)
|
||||
// and contributors of zlib.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace SabreTools.IO.Compression.Deflate
|
||||
{
|
||||
internal enum BlockState
|
||||
{
|
||||
NeedMore = 0, // block not completed, need more input or more output
|
||||
BlockDone, // block flush performed
|
||||
FinishStarted, // finish started, need only more output at next deflate
|
||||
FinishDone // finish done, accept no more input or output
|
||||
}
|
||||
}
|
||||
814
SabreTools.IO/Compression/Deflate/CRC32.cs
Normal file
814
SabreTools.IO/Compression/Deflate/CRC32.cs
Normal file
@@ -0,0 +1,814 @@
|
||||
// CRC32.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2011 Dino Chiesa.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Last Saved: <2011-August-02 18:25:54>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines the CRC32 class, which can do the CRC32 algorithm, using
|
||||
// arbitrary starting polynomials, and bit reversal. The bit reversal is what
|
||||
// distinguishes this CRC-32 used in BZip2 from the CRC-32 that is used in PKZIP
|
||||
// files, or GZIP files. This class does both.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using Interop = System.Runtime.InteropServices;
|
||||
|
||||
#nullable disable
|
||||
namespace SabreTools.IO.Compression.Deflate
|
||||
{
|
||||
/// <summary>
|
||||
/// Computes a CRC-32. The CRC-32 algorithm is parameterized - you
|
||||
/// can set the polynomial and enable or disable bit
|
||||
/// reversal. This can be used for GZIP, BZip2, or ZIP.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This type is used internally by DotNetZip; it is generally not used
|
||||
/// directly by applications wishing to create, read, or manipulate zip
|
||||
/// archive files.
|
||||
/// </remarks>
|
||||
|
||||
[Interop.GuidAttribute("ebc25cf6-9120-4283-b972-0e5520d0000C")]
|
||||
[Interop.ComVisible(true)]
|
||||
#if !NETCF
|
||||
[Interop.ClassInterface(Interop.ClassInterfaceType.AutoDispatch)]
|
||||
#endif
|
||||
public class CRC32
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates the total number of bytes applied to the CRC.
|
||||
/// </summary>
|
||||
public Int64 TotalBytesRead
|
||||
{
|
||||
get
|
||||
{
|
||||
return _TotalBytesRead;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the current CRC for all blocks slurped in.
|
||||
/// </summary>
|
||||
public Int32 Crc32Result
|
||||
{
|
||||
get
|
||||
{
|
||||
return unchecked((Int32)(~_register));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the CRC32 for the specified stream.
|
||||
/// </summary>
|
||||
/// <param name="input">The stream over which to calculate the CRC32</param>
|
||||
/// <returns>the CRC32 calculation</returns>
|
||||
public Int32 GetCrc32(System.IO.Stream input)
|
||||
{
|
||||
return GetCrc32AndCopy(input, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the CRC32 for the specified stream, and writes the input into the
|
||||
/// output stream.
|
||||
/// </summary>
|
||||
/// <param name="input">The stream over which to calculate the CRC32</param>
|
||||
/// <param name="output">The stream into which to deflate the input</param>
|
||||
/// <returns>the CRC32 calculation</returns>
|
||||
public Int32 GetCrc32AndCopy(System.IO.Stream input, System.IO.Stream output)
|
||||
{
|
||||
if (input == null)
|
||||
throw new Exception("The input stream must not be null.");
|
||||
|
||||
unchecked
|
||||
{
|
||||
byte[] buffer = new byte[BUFFER_SIZE];
|
||||
int readSize = BUFFER_SIZE;
|
||||
|
||||
_TotalBytesRead = 0;
|
||||
int count = input.Read(buffer, 0, readSize);
|
||||
output?.Write(buffer, 0, count);
|
||||
_TotalBytesRead += count;
|
||||
while (count > 0)
|
||||
{
|
||||
SlurpBlock(buffer, 0, count);
|
||||
count = input.Read(buffer, 0, readSize);
|
||||
output?.Write(buffer, 0, count);
|
||||
_TotalBytesRead += count;
|
||||
}
|
||||
|
||||
return (Int32)(~_register);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get the CRC32 for the given (word,byte) combo. This is a
|
||||
/// computation defined by PKzip for PKZIP 2.0 (weak) encryption.
|
||||
/// </summary>
|
||||
/// <param name="W">The word to start with.</param>
|
||||
/// <param name="B">The byte to combine it with.</param>
|
||||
/// <returns>The CRC-ized result.</returns>
|
||||
public Int32 ComputeCrc32(Int32 W, byte B)
|
||||
{
|
||||
return _InternalComputeCrc32((UInt32)W, B);
|
||||
}
|
||||
|
||||
internal Int32 _InternalComputeCrc32(UInt32 W, byte B)
|
||||
{
|
||||
return (Int32)(crc32Table[(W ^ B) & 0xFF] ^ (W >> 8));
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Update the value for the running CRC32 using the given block of bytes.
|
||||
/// This is useful when using the CRC32() class in a Stream.
|
||||
/// </summary>
|
||||
/// <param name="block">block of bytes to slurp</param>
|
||||
/// <param name="offset">starting point in the block</param>
|
||||
/// <param name="count">how many bytes within the block to slurp</param>
|
||||
public void SlurpBlock(byte[] block, int offset, int count)
|
||||
{
|
||||
if (block == null)
|
||||
throw new Exception("The data buffer must not be null.");
|
||||
|
||||
// bzip algorithm
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
int x = offset + i;
|
||||
byte b = block[x];
|
||||
if (this.reverseBits)
|
||||
{
|
||||
UInt32 temp = (_register >> 24) ^ b;
|
||||
_register = (_register << 8) ^ crc32Table[temp];
|
||||
}
|
||||
else
|
||||
{
|
||||
UInt32 temp = (_register & 0x000000FF) ^ b;
|
||||
_register = (_register >> 8) ^ crc32Table[temp];
|
||||
}
|
||||
}
|
||||
_TotalBytesRead += count;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Process one byte in the CRC.
|
||||
/// </summary>
|
||||
/// <param name = "b">the byte to include into the CRC . </param>
|
||||
public void UpdateCRC(byte b)
|
||||
{
|
||||
if (this.reverseBits)
|
||||
{
|
||||
UInt32 temp = (_register >> 24) ^ b;
|
||||
_register = (_register << 8) ^ crc32Table[temp];
|
||||
}
|
||||
else
|
||||
{
|
||||
UInt32 temp = (_register & 0x000000FF) ^ b;
|
||||
_register = (_register >> 8) ^ crc32Table[temp];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process a run of N identical bytes into the CRC.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This method serves as an optimization for updating the CRC when a
|
||||
/// run of identical bytes is found. Rather than passing in a buffer of
|
||||
/// length n, containing all identical bytes b, this method accepts the
|
||||
/// byte value and the length of the (virtual) buffer - the length of
|
||||
/// the run.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name = "b">the byte to include into the CRC. </param>
|
||||
/// <param name = "n">the number of times that byte should be repeated. </param>
|
||||
public void UpdateCRC(byte b, int n)
|
||||
{
|
||||
while (n-- > 0)
|
||||
{
|
||||
if (this.reverseBits)
|
||||
{
|
||||
uint temp = (_register >> 24) ^ b;
|
||||
_register = (_register << 8) ^ crc32Table[(temp >= 0)
|
||||
? temp
|
||||
: (temp + 256)];
|
||||
}
|
||||
else
|
||||
{
|
||||
UInt32 temp = (_register & 0x000000FF) ^ b;
|
||||
_register = (_register >> 8) ^ crc32Table[(temp >= 0)
|
||||
? temp
|
||||
: (temp + 256)];
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static uint ReverseBits(uint data)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
uint ret = data;
|
||||
ret = (ret & 0x55555555) << 1 | (ret >> 1) & 0x55555555;
|
||||
ret = (ret & 0x33333333) << 2 | (ret >> 2) & 0x33333333;
|
||||
ret = (ret & 0x0F0F0F0F) << 4 | (ret >> 4) & 0x0F0F0F0F;
|
||||
ret = (ret << 24) | ((ret & 0xFF00) << 8) | ((ret >> 8) & 0xFF00) | (ret >> 24);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
private static byte ReverseBits(byte data)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
uint u = (uint)data * 0x00020202;
|
||||
uint m = 0x01044010;
|
||||
uint s = u & m;
|
||||
uint t = (u << 2) & (m << 1);
|
||||
return (byte)((0x01001001 * (s + t)) >> 24);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void GenerateLookupTable()
|
||||
{
|
||||
crc32Table = new UInt32[256];
|
||||
unchecked
|
||||
{
|
||||
UInt32 dwCrc;
|
||||
byte i = 0;
|
||||
do
|
||||
{
|
||||
dwCrc = i;
|
||||
for (byte j = 8; j > 0; j--)
|
||||
{
|
||||
if ((dwCrc & 1) == 1)
|
||||
{
|
||||
dwCrc = (dwCrc >> 1) ^ dwPolynomial;
|
||||
}
|
||||
else
|
||||
{
|
||||
dwCrc >>= 1;
|
||||
}
|
||||
}
|
||||
if (reverseBits)
|
||||
{
|
||||
crc32Table[ReverseBits(i)] = ReverseBits(dwCrc);
|
||||
}
|
||||
else
|
||||
{
|
||||
crc32Table[i] = dwCrc;
|
||||
}
|
||||
i++;
|
||||
} while (i!=0);
|
||||
}
|
||||
|
||||
#if VERBOSE
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("private static readonly UInt32[] crc32Table = {");
|
||||
for (int i = 0; i < crc32Table.Length; i+=4)
|
||||
{
|
||||
Console.Write(" ");
|
||||
for (int j=0; j < 4; j++)
|
||||
{
|
||||
Console.Write(" 0x{0:X8}U,", crc32Table[i+j]);
|
||||
}
|
||||
Console.WriteLine();
|
||||
}
|
||||
Console.WriteLine("};");
|
||||
Console.WriteLine();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
private uint gf2_matrix_times(uint[] matrix, uint vec)
|
||||
{
|
||||
uint sum = 0;
|
||||
int i=0;
|
||||
while (vec != 0)
|
||||
{
|
||||
if ((vec & 0x01)== 0x01)
|
||||
sum ^= matrix[i];
|
||||
vec >>= 1;
|
||||
i++;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
private void gf2_matrix_square(uint[] square, uint[] mat)
|
||||
{
|
||||
for (int i = 0; i < 32; i++)
|
||||
square[i] = gf2_matrix_times(mat, mat[i]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Combines the given CRC32 value with the current running total.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is useful when using a divide-and-conquer approach to
|
||||
/// calculating a CRC. Multiple threads can each calculate a
|
||||
/// CRC32 on a segment of the data, and then combine the
|
||||
/// individual CRC32 values at the end.
|
||||
/// </remarks>
|
||||
/// <param name="crc">the crc value to be combined with this one</param>
|
||||
/// <param name="length">the length of data the CRC value was calculated on</param>
|
||||
public void Combine(int crc, int length)
|
||||
{
|
||||
uint[] even = new uint[32]; // even-power-of-two zeros operator
|
||||
uint[] odd = new uint[32]; // odd-power-of-two zeros operator
|
||||
|
||||
if (length == 0)
|
||||
return;
|
||||
|
||||
uint crc1= ~_register;
|
||||
uint crc2= (uint) crc;
|
||||
|
||||
// put operator for one zero bit in odd
|
||||
odd[0] = this.dwPolynomial; // the CRC-32 polynomial
|
||||
uint row = 1;
|
||||
for (int i = 1; i < 32; i++)
|
||||
{
|
||||
odd[i] = row;
|
||||
row <<= 1;
|
||||
}
|
||||
|
||||
// put operator for two zero bits in even
|
||||
gf2_matrix_square(even, odd);
|
||||
|
||||
// put operator for four zero bits in odd
|
||||
gf2_matrix_square(odd, even);
|
||||
|
||||
uint len2 = (uint) length;
|
||||
|
||||
// apply len2 zeros to crc1 (first square will put the operator for one
|
||||
// zero byte, eight zero bits, in even)
|
||||
do {
|
||||
// apply zeros operator for this bit of len2
|
||||
gf2_matrix_square(even, odd);
|
||||
|
||||
if ((len2 & 1)== 1)
|
||||
crc1 = gf2_matrix_times(even, crc1);
|
||||
len2 >>= 1;
|
||||
|
||||
if (len2 == 0)
|
||||
break;
|
||||
|
||||
// another iteration of the loop with odd and even swapped
|
||||
gf2_matrix_square(odd, even);
|
||||
if ((len2 & 1)==1)
|
||||
crc1 = gf2_matrix_times(odd, crc1);
|
||||
len2 >>= 1;
|
||||
|
||||
|
||||
} while (len2 != 0);
|
||||
|
||||
crc1 ^= crc2;
|
||||
|
||||
_register= ~crc1;
|
||||
|
||||
//return (int) crc1;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance of the CRC32 class using the default settings: no
|
||||
/// bit reversal, and a polynomial of 0xEDB88320.
|
||||
/// </summary>
|
||||
public CRC32() : this(false)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance of the CRC32 class, specifying whether to reverse
|
||||
/// data bits or not.
|
||||
/// </summary>
|
||||
/// <param name='reverseBits'>
|
||||
/// specify true if the instance should reverse data bits.
|
||||
/// </param>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// In the CRC-32 used by BZip2, the bits are reversed. Therefore if you
|
||||
/// want a CRC32 with compatibility with BZip2, you should pass true
|
||||
/// here. In the CRC-32 used by GZIP and PKZIP, the bits are not
|
||||
/// reversed; Therefore if you want a CRC32 with compatibility with
|
||||
/// those, you should pass false.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public CRC32(bool reverseBits) :
|
||||
this( unchecked((int)0xEDB88320), reverseBits)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance of the CRC32 class, specifying the polynomial and
|
||||
/// whether to reverse data bits or not.
|
||||
/// </summary>
|
||||
/// <param name='polynomial'>
|
||||
/// The polynomial to use for the CRC, expressed in the reversed (LSB)
|
||||
/// format: the highest ordered bit in the polynomial value is the
|
||||
/// coefficient of the 0th power; the second-highest order bit is the
|
||||
/// coefficient of the 1 power, and so on. Expressed this way, the
|
||||
/// polynomial for the CRC-32C used in IEEE 802.3, is 0xEDB88320.
|
||||
/// </param>
|
||||
/// <param name='reverseBits'>
|
||||
/// specify true if the instance should reverse data bits.
|
||||
/// </param>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// In the CRC-32 used by BZip2, the bits are reversed. Therefore if you
|
||||
/// want a CRC32 with compatibility with BZip2, you should pass true
|
||||
/// here for the <c>reverseBits</c> parameter. In the CRC-32 used by
|
||||
/// GZIP and PKZIP, the bits are not reversed; Therefore if you want a
|
||||
/// CRC32 with compatibility with those, you should pass false for the
|
||||
/// <c>reverseBits</c> parameter.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public CRC32(int polynomial, bool reverseBits)
|
||||
{
|
||||
this.reverseBits = reverseBits;
|
||||
this.dwPolynomial = (uint) polynomial;
|
||||
this.GenerateLookupTable();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset the CRC-32 class - clear the CRC "remainder register."
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Use this when employing a single instance of this class to compute
|
||||
/// multiple, distinct CRCs on multiple, distinct data blocks.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public void Reset()
|
||||
{
|
||||
_register = 0xFFFFFFFFU;
|
||||
}
|
||||
|
||||
// private member vars
|
||||
private readonly UInt32 dwPolynomial;
|
||||
private Int64 _TotalBytesRead;
|
||||
private readonly bool reverseBits;
|
||||
private UInt32[] crc32Table;
|
||||
private const int BUFFER_SIZE = 8192;
|
||||
private UInt32 _register = 0xFFFFFFFFU;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A Stream that calculates a CRC32 (a checksum) on all bytes read,
|
||||
/// or on all bytes written.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This class can be used to verify the CRC of a ZipEntry when
|
||||
/// reading from a stream, or to calculate a CRC when writing to a
|
||||
/// stream. The stream should be used to either read, or write, but
|
||||
/// not both. If you intermix reads and writes, the results are not
|
||||
/// defined.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// This class is intended primarily for use internally by the
|
||||
/// DotNetZip library.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public class CrcCalculatorStream : System.IO.Stream, System.IDisposable
|
||||
{
|
||||
private static readonly Int64 UnsetLengthLimit = -99;
|
||||
|
||||
internal System.IO.Stream _innerStream;
|
||||
private readonly CRC32 _Crc32;
|
||||
private readonly Int64 _lengthLimit = -99;
|
||||
private bool _leaveOpen;
|
||||
|
||||
/// <summary>
|
||||
/// The default constructor.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Instances returned from this constructor will leave the underlying
|
||||
/// stream open upon Close(). The stream uses the default CRC32
|
||||
/// algorithm, which implies a polynomial of 0xEDB88320.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name="stream">The underlying stream</param>
|
||||
public CrcCalculatorStream(System.IO.Stream stream)
|
||||
: this(true, CrcCalculatorStream.UnsetLengthLimit, stream, null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The constructor allows the caller to specify how to handle the
|
||||
/// underlying stream at close.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The stream uses the default CRC32 algorithm, which implies a
|
||||
/// polynomial of 0xEDB88320.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name="stream">The underlying stream</param>
|
||||
/// <param name="leaveOpen">true to leave the underlying stream
|
||||
/// open upon close of the <c>CrcCalculatorStream</c>; false otherwise.</param>
|
||||
public CrcCalculatorStream(System.IO.Stream stream, bool leaveOpen)
|
||||
: this(leaveOpen, CrcCalculatorStream.UnsetLengthLimit, stream, null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A constructor allowing the specification of the length of the stream
|
||||
/// to read.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The stream uses the default CRC32 algorithm, which implies a
|
||||
/// polynomial of 0xEDB88320.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Instances returned from this constructor will leave the underlying
|
||||
/// stream open upon Close().
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name="stream">The underlying stream</param>
|
||||
/// <param name="length">The length of the stream to slurp</param>
|
||||
public CrcCalculatorStream(System.IO.Stream stream, Int64 length)
|
||||
: this(true, length, stream, null)
|
||||
{
|
||||
if (length < 0)
|
||||
throw new ArgumentException("length");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A constructor allowing the specification of the length of the stream
|
||||
/// to read, as well as whether to keep the underlying stream open upon
|
||||
/// Close().
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The stream uses the default CRC32 algorithm, which implies a
|
||||
/// polynomial of 0xEDB88320.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name="stream">The underlying stream</param>
|
||||
/// <param name="length">The length of the stream to slurp</param>
|
||||
/// <param name="leaveOpen">true to leave the underlying stream
|
||||
/// open upon close of the <c>CrcCalculatorStream</c>; false otherwise.</param>
|
||||
public CrcCalculatorStream(System.IO.Stream stream, Int64 length, bool leaveOpen)
|
||||
: this(leaveOpen, length, stream, null)
|
||||
{
|
||||
if (length < 0)
|
||||
throw new ArgumentException("length");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A constructor allowing the specification of the length of the stream
|
||||
/// to read, as well as whether to keep the underlying stream open upon
|
||||
/// Close(), and the CRC32 instance to use.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The stream uses the specified CRC32 instance, which allows the
|
||||
/// application to specify how the CRC gets calculated.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name="stream">The underlying stream</param>
|
||||
/// <param name="length">The length of the stream to slurp</param>
|
||||
/// <param name="leaveOpen">true to leave the underlying stream
|
||||
/// open upon close of the <c>CrcCalculatorStream</c>; false otherwise.</param>
|
||||
/// <param name="crc32">the CRC32 instance to use to calculate the CRC32</param>
|
||||
public CrcCalculatorStream(System.IO.Stream stream, Int64 length, bool leaveOpen,
|
||||
CRC32 crc32)
|
||||
: this(leaveOpen, length, stream, crc32)
|
||||
{
|
||||
if (length < 0)
|
||||
throw new ArgumentException("length");
|
||||
}
|
||||
|
||||
|
||||
// This ctor is private - no validation is done here. This is to allow the use
|
||||
// of a (specific) negative value for the _lengthLimit, to indicate that there
|
||||
// is no length set. So we validate the length limit in those ctors that use an
|
||||
// explicit param, otherwise we don't validate, because it could be our special
|
||||
// value.
|
||||
private CrcCalculatorStream
|
||||
(bool leaveOpen, Int64 length, System.IO.Stream stream, CRC32 crc32)
|
||||
: base()
|
||||
{
|
||||
_innerStream = stream;
|
||||
_Crc32 = crc32 ?? new CRC32();
|
||||
_lengthLimit = length;
|
||||
_leaveOpen = leaveOpen;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total number of bytes run through the CRC32 calculator.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// This is either the total number of bytes read, or the total number of
|
||||
/// bytes written, depending on the direction of this stream.
|
||||
/// </remarks>
|
||||
public Int64 TotalBytesSlurped
|
||||
{
|
||||
get { return _Crc32.TotalBytesRead; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides the current CRC for all blocks slurped in.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The running total of the CRC is kept as data is written or read
|
||||
/// through the stream. read this property after all reads or writes to
|
||||
/// get an accurate CRC for the entire stream.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public Int32 Crc
|
||||
{
|
||||
get { return _Crc32.Crc32Result; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the underlying stream will be left open when the
|
||||
/// <c>CrcCalculatorStream</c> is Closed.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Set this at any point before calling <see cref="Close()"/>.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public bool LeaveOpen
|
||||
{
|
||||
get { return _leaveOpen; }
|
||||
set { _leaveOpen = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read from the stream
|
||||
/// </summary>
|
||||
/// <param name="buffer">the buffer to read</param>
|
||||
/// <param name="offset">the offset at which to start</param>
|
||||
/// <param name="count">the number of bytes to read</param>
|
||||
/// <returns>the number of bytes actually read</returns>
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
int bytesToRead = count;
|
||||
|
||||
// Need to limit the # of bytes returned, if the stream is intended to have
|
||||
// a definite length. This is especially useful when returning a stream for
|
||||
// the uncompressed data directly to the application. The app won't
|
||||
// necessarily read only the UncompressedSize number of bytes. For example
|
||||
// wrapping the stream returned from OpenReader() into a StreadReader() and
|
||||
// calling ReadToEnd() on it, We can "over-read" the zip data and get a
|
||||
// corrupt string. The length limits that, prevents that problem.
|
||||
|
||||
if (_lengthLimit != CrcCalculatorStream.UnsetLengthLimit)
|
||||
{
|
||||
if (_Crc32.TotalBytesRead >= _lengthLimit) return 0; // EOF
|
||||
Int64 bytesRemaining = _lengthLimit - _Crc32.TotalBytesRead;
|
||||
if (bytesRemaining < count) bytesToRead = (int)bytesRemaining;
|
||||
}
|
||||
int n = _innerStream.Read(buffer, offset, bytesToRead);
|
||||
if (n > 0) _Crc32.SlurpBlock(buffer, offset, n);
|
||||
return n;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write to the stream.
|
||||
/// </summary>
|
||||
/// <param name="buffer">the buffer from which to write</param>
|
||||
/// <param name="offset">the offset at which to start writing</param>
|
||||
/// <param name="count">the number of bytes to write</param>
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (count > 0) _Crc32.SlurpBlock(buffer, offset, count);
|
||||
_innerStream.Write(buffer, offset, count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the stream supports reading.
|
||||
/// </summary>
|
||||
public override bool CanRead
|
||||
{
|
||||
get { return _innerStream.CanRead; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the stream supports seeking.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Always returns false.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public override bool CanSeek
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the stream supports writing.
|
||||
/// </summary>
|
||||
public override bool CanWrite
|
||||
{
|
||||
get { return _innerStream.CanWrite; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flush the stream.
|
||||
/// </summary>
|
||||
public override void Flush()
|
||||
{
|
||||
_innerStream.Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the length of the underlying stream.
|
||||
/// </summary>
|
||||
public override long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_lengthLimit == CrcCalculatorStream.UnsetLengthLimit)
|
||||
return _innerStream.Length;
|
||||
else return _lengthLimit;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The getter for this property returns the total bytes read.
|
||||
/// If you use the setter, it will throw
|
||||
/// <see cref="NotSupportedException"/>.
|
||||
/// </summary>
|
||||
public override long Position
|
||||
{
|
||||
get { return _Crc32.TotalBytesRead; }
|
||||
set { throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Seeking is not supported on this stream. This method always throws
|
||||
/// <see cref="NotSupportedException"/>
|
||||
/// </summary>
|
||||
/// <param name="offset">N/A</param>
|
||||
/// <param name="origin">N/A</param>
|
||||
/// <returns>N/A</returns>
|
||||
public override long Seek(long offset, System.IO.SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method always throws
|
||||
/// <see cref="NotSupportedException"/>
|
||||
/// </summary>
|
||||
/// <param name="value">N/A</param>
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
|
||||
void IDisposable.Dispose()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes the stream.
|
||||
/// </summary>
|
||||
public override void Close()
|
||||
{
|
||||
base.Close();
|
||||
if (!_leaveOpen)
|
||||
_innerStream.Close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
168
SabreTools.IO/Compression/Deflate/CompressionLevel.cs
Normal file
168
SabreTools.IO/Compression/Deflate/CompressionLevel.cs
Normal file
@@ -0,0 +1,168 @@
|
||||
// Zlib.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009-2011 Dino Chiesa and Microsoft Corporation.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Last Saved: <2011-August-03 19:52:28>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines classes for ZLIB compression and
|
||||
// decompression. This code is derived from the jzlib implementation of
|
||||
// zlib, but significantly modified. The object model is not the same,
|
||||
// and many of the behaviors are new or different. Nonetheless, in
|
||||
// keeping with the license for jzlib, the copyright to that code is
|
||||
// included below.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// The following notice applies to jzlib:
|
||||
//
|
||||
// Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in
|
||||
// the documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. The names of the authors may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
// INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
// jzlib is based on zlib-1.1.3.
|
||||
//
|
||||
// The following notice applies to zlib:
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (C) 1995-2004 Jean-loup Gailly and Mark Adler
|
||||
//
|
||||
// The ZLIB software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
// Jean-loup Gailly jloup@gzip.org
|
||||
// Mark Adler madler@alumni.caltech.edu
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace SabreTools.IO.Compression.Deflate
|
||||
{
|
||||
/// <summary>
|
||||
/// The compression level to be used when using a DeflateStream or ZlibStream with CompressionMode.Compress.
|
||||
/// </summary>
|
||||
public enum CompressionLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// None means that the data will be simply stored, with no change at all.
|
||||
/// If you are producing ZIPs for use on Mac OSX, be aware that archives produced with CompressionLevel.None
|
||||
/// cannot be opened with the default zip reader. Use a different CompressionLevel.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
/// <summary>
|
||||
/// Same as None.
|
||||
/// </summary>
|
||||
Level0 = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The fastest but least effective compression.
|
||||
/// </summary>
|
||||
BestSpeed = 1,
|
||||
|
||||
/// <summary>
|
||||
/// A synonym for BestSpeed.
|
||||
/// </summary>
|
||||
Level1 = 1,
|
||||
|
||||
/// <summary>
|
||||
/// A little slower, but better, than level 1.
|
||||
/// </summary>
|
||||
Level2 = 2,
|
||||
|
||||
/// <summary>
|
||||
/// A little slower, but better, than level 2.
|
||||
/// </summary>
|
||||
Level3 = 3,
|
||||
|
||||
/// <summary>
|
||||
/// A little slower, but better, than level 3.
|
||||
/// </summary>
|
||||
Level4 = 4,
|
||||
|
||||
/// <summary>
|
||||
/// A little slower than level 4, but with better compression.
|
||||
/// </summary>
|
||||
Level5 = 5,
|
||||
|
||||
/// <summary>
|
||||
/// The default compression level, with a good balance of speed and compression efficiency.
|
||||
/// </summary>
|
||||
Default = 6,
|
||||
/// <summary>
|
||||
/// A synonym for Default.
|
||||
/// </summary>
|
||||
Level6 = 6,
|
||||
|
||||
/// <summary>
|
||||
/// Pretty good compression!
|
||||
/// </summary>
|
||||
Level7 = 7,
|
||||
|
||||
/// <summary>
|
||||
/// Better compression than Level7!
|
||||
/// </summary>
|
||||
Level8 = 8,
|
||||
|
||||
/// <summary>
|
||||
/// The "best" compression, where best means greatest reduction in size of the input data stream.
|
||||
/// This is also the slowest compression.
|
||||
/// </summary>
|
||||
BestCompression = 9,
|
||||
|
||||
/// <summary>
|
||||
/// A synonym for BestCompression.
|
||||
/// </summary>
|
||||
Level9 = 9,
|
||||
}
|
||||
|
||||
}
|
||||
106
SabreTools.IO/Compression/Deflate/CompressionMode.cs
Normal file
106
SabreTools.IO/Compression/Deflate/CompressionMode.cs
Normal file
@@ -0,0 +1,106 @@
|
||||
// Zlib.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009-2011 Dino Chiesa and Microsoft Corporation.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Last Saved: <2011-August-03 19:52:28>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines classes for ZLIB compression and
|
||||
// decompression. This code is derived from the jzlib implementation of
|
||||
// zlib, but significantly modified. The object model is not the same,
|
||||
// and many of the behaviors are new or different. Nonetheless, in
|
||||
// keeping with the license for jzlib, the copyright to that code is
|
||||
// included below.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// The following notice applies to jzlib:
|
||||
//
|
||||
// Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in
|
||||
// the documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. The names of the authors may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
// INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
// jzlib is based on zlib-1.1.3.
|
||||
//
|
||||
// The following notice applies to zlib:
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (C) 1995-2004 Jean-loup Gailly and Mark Adler
|
||||
//
|
||||
// The ZLIB software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
// Jean-loup Gailly jloup@gzip.org
|
||||
// Mark Adler madler@alumni.caltech.edu
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace SabreTools.IO.Compression.Deflate
|
||||
{
|
||||
/// <summary>
|
||||
/// An enum to specify the direction of transcoding - whether to compress or decompress.
|
||||
/// </summary>
|
||||
public enum CompressionMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to specify that the stream should compress the data.
|
||||
/// </summary>
|
||||
Compress = 0,
|
||||
/// <summary>
|
||||
/// Used to specify that the stream should decompress the data.
|
||||
/// </summary>
|
||||
Decompress = 1,
|
||||
}
|
||||
|
||||
}
|
||||
119
SabreTools.IO/Compression/Deflate/CompressionStrategy.cs
Normal file
119
SabreTools.IO/Compression/Deflate/CompressionStrategy.cs
Normal file
@@ -0,0 +1,119 @@
|
||||
// Zlib.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009-2011 Dino Chiesa and Microsoft Corporation.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Last Saved: <2011-August-03 19:52:28>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines classes for ZLIB compression and
|
||||
// decompression. This code is derived from the jzlib implementation of
|
||||
// zlib, but significantly modified. The object model is not the same,
|
||||
// and many of the behaviors are new or different. Nonetheless, in
|
||||
// keeping with the license for jzlib, the copyright to that code is
|
||||
// included below.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// The following notice applies to jzlib:
|
||||
//
|
||||
// Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in
|
||||
// the documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. The names of the authors may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
// INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
// jzlib is based on zlib-1.1.3.
|
||||
//
|
||||
// The following notice applies to zlib:
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (C) 1995-2004 Jean-loup Gailly and Mark Adler
|
||||
//
|
||||
// The ZLIB software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
// Jean-loup Gailly jloup@gzip.org
|
||||
// Mark Adler madler@alumni.caltech.edu
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace SabreTools.IO.Compression.Deflate
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes options for how the compression algorithm is executed. Different strategies
|
||||
/// work better on different sorts of data. The strategy parameter can affect the compression
|
||||
/// ratio and the speed of compression but not the correctness of the compresssion.
|
||||
/// </summary>
|
||||
public enum CompressionStrategy
|
||||
{
|
||||
/// <summary>
|
||||
/// The default strategy is probably the best for normal data.
|
||||
/// </summary>
|
||||
Default = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The <c>Filtered</c> strategy is intended to be used most effectively with data produced by a
|
||||
/// filter or predictor. By this definition, filtered data consists mostly of small
|
||||
/// values with a somewhat random distribution. In this case, the compression algorithm
|
||||
/// is tuned to compress them better. The effect of <c>Filtered</c> is to force more Huffman
|
||||
/// coding and less string matching; it is a half-step between <c>Default</c> and <c>HuffmanOnly</c>.
|
||||
/// </summary>
|
||||
Filtered = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Using <c>HuffmanOnly</c> will force the compressor to do Huffman encoding only, with no
|
||||
/// string matching.
|
||||
/// </summary>
|
||||
HuffmanOnly = 2,
|
||||
}
|
||||
|
||||
}
|
||||
77
SabreTools.IO/Compression/Deflate/DeflateFlavor.cs
Normal file
77
SabreTools.IO/Compression/Deflate/DeflateFlavor.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
// Deflate.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009 Dino Chiesa and Microsoft Corporation.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2011-August-03 19:52:15>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines logic for handling the Deflate or compression.
|
||||
//
|
||||
// This code is based on multiple sources:
|
||||
// - the original zlib v1.2.3 source, which is Copyright (C) 1995-2005 Jean-loup Gailly.
|
||||
// - the original jzlib, which is Copyright (c) 2000-2003 ymnk, JCraft,Inc.
|
||||
//
|
||||
// However, this code is significantly different from both.
|
||||
// The object model is not the same, and many of the behaviors are different.
|
||||
//
|
||||
// In keeping with the license for these other works, the copyrights for
|
||||
// jzlib and zlib are here.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in
|
||||
// the documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. The names of the authors may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
// INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
// This program is based on zlib-1.1.3; credit to authors
|
||||
// Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu)
|
||||
// and contributors of zlib.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace SabreTools.IO.Compression.Deflate
|
||||
{
|
||||
internal enum DeflateFlavor
|
||||
{
|
||||
Store,
|
||||
Fast,
|
||||
Slow
|
||||
}
|
||||
}
|
||||
26
SabreTools.IO/Compression/Deflate/DeflateInfo.cs
Normal file
26
SabreTools.IO/Compression/Deflate/DeflateInfo.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
namespace SabreTools.IO.Compression.Deflate
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents information about a DEFLATE stream
|
||||
/// </summary>
|
||||
public class DeflateInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Size of the deflated data
|
||||
/// </summary>
|
||||
/// <remarks>Set to a value less than 0 to ignore</remarks>
|
||||
public long InputSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Size of the inflated data
|
||||
/// </summary>
|
||||
/// <remarks>Set to a value less than 0 to ignore</remarks>
|
||||
public long OutputSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// CRC-32 of the inflated data
|
||||
/// </summary>
|
||||
/// <remarks>Set to a value of 0 to ignore</remarks>
|
||||
public uint Crc32 { get; set; }
|
||||
}
|
||||
}
|
||||
1871
SabreTools.IO/Compression/Deflate/DeflateManager.cs
Normal file
1871
SabreTools.IO/Compression/Deflate/DeflateManager.cs
Normal file
File diff suppressed because it is too large
Load Diff
743
SabreTools.IO/Compression/Deflate/DeflateStream.cs
Normal file
743
SabreTools.IO/Compression/Deflate/DeflateStream.cs
Normal file
@@ -0,0 +1,743 @@
|
||||
// DeflateStream.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009-2010 Dino Chiesa.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2011-July-31 14:48:11>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines the DeflateStream class, which can be used as a replacement for
|
||||
// the System.IO.Compression.DeflateStream class in the .NET BCL.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
|
||||
namespace SabreTools.IO.Compression.Deflate
|
||||
{
|
||||
/// <summary>
|
||||
/// A class for compressing and decompressing streams using the Deflate algorithm.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// The DeflateStream is a <see
|
||||
/// href="http://en.wikipedia.org/wiki/Decorator_pattern">Decorator</see> on a <see
|
||||
/// cref="System.IO.Stream"/>. It adds DEFLATE compression or decompression to any
|
||||
/// stream.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Using this stream, applications can compress or decompress data via stream
|
||||
/// <c>Read</c> and <c>Write</c> operations. Either compresssion or decompression
|
||||
/// can occur through either reading or writing. The compression format used is
|
||||
/// DEFLATE, which is documented in <see
|
||||
/// href="http://www.ietf.org/rfc/rfc1951.txt">IETF RFC 1951</see>, "DEFLATE
|
||||
/// Compressed Data Format Specification version 1.3.".
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// This class is similar to <see cref="ZlibStream"/>, except that
|
||||
/// <c>ZlibStream</c> adds the <see href="http://www.ietf.org/rfc/rfc1950.txt">RFC
|
||||
/// 1950 - ZLIB</see> framing bytes to a compressed stream when compressing, or
|
||||
/// expects the RFC1950 framing bytes when decompressing. The <c>DeflateStream</c>
|
||||
/// does not.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <seealso cref="ZlibStream" />
|
||||
/// <seealso cref="GZipStream" />
|
||||
public class DeflateStream : System.IO.Stream
|
||||
{
|
||||
internal ZlibBaseStream _baseStream;
|
||||
internal System.IO.Stream _innerStream;
|
||||
bool _disposed;
|
||||
|
||||
/// <summary>
|
||||
/// Create a DeflateStream using the specified CompressionMode.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// When mode is <c>CompressionMode.Compress</c>, the DeflateStream will use
|
||||
/// the default compression level. The "captive" stream will be closed when
|
||||
/// the DeflateStream is closed.
|
||||
/// </remarks>
|
||||
///
|
||||
/// <example>
|
||||
/// This example uses a DeflateStream to compress data from a file, and writes
|
||||
/// the compressed data to another file.
|
||||
/// <code>
|
||||
/// using (System.IO.Stream input = System.IO.File.OpenRead(fileToCompress))
|
||||
/// {
|
||||
/// using (var raw = System.IO.File.Create(fileToCompress + ".deflated"))
|
||||
/// {
|
||||
/// using (Stream compressor = new DeflateStream(raw, CompressionMode.Compress))
|
||||
/// {
|
||||
/// byte[] buffer = new byte[WORKING_BUFFER_SIZE];
|
||||
/// int n;
|
||||
/// while ((n= input.Read(buffer, 0, buffer.Length)) != 0)
|
||||
/// {
|
||||
/// compressor.Write(buffer, 0, n);
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
///
|
||||
/// <code lang="VB">
|
||||
/// Using input As Stream = File.OpenRead(fileToCompress)
|
||||
/// Using raw As FileStream = File.Create(fileToCompress & ".deflated")
|
||||
/// Using compressor As Stream = New DeflateStream(raw, CompressionMode.Compress)
|
||||
/// Dim buffer As Byte() = New Byte(4096) {}
|
||||
/// Dim n As Integer = -1
|
||||
/// Do While (n <> 0)
|
||||
/// If (n > 0) Then
|
||||
/// compressor.Write(buffer, 0, n)
|
||||
/// End If
|
||||
/// n = input.Read(buffer, 0, buffer.Length)
|
||||
/// Loop
|
||||
/// End Using
|
||||
/// End Using
|
||||
/// End Using
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="stream">The stream which will be read or written.</param>
|
||||
/// <param name="mode">Indicates whether the DeflateStream will compress or decompress.</param>
|
||||
public DeflateStream(System.IO.Stream stream, CompressionMode mode)
|
||||
: this(stream, mode, CompressionLevel.Default, false)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a DeflateStream using the specified CompressionMode and the specified CompressionLevel.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// When mode is <c>CompressionMode.Decompress</c>, the level parameter is
|
||||
/// ignored. The "captive" stream will be closed when the DeflateStream is
|
||||
/// closed.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <example>
|
||||
///
|
||||
/// This example uses a DeflateStream to compress data from a file, and writes
|
||||
/// the compressed data to another file.
|
||||
///
|
||||
/// <code>
|
||||
/// using (System.IO.Stream input = System.IO.File.OpenRead(fileToCompress))
|
||||
/// {
|
||||
/// using (var raw = System.IO.File.Create(fileToCompress + ".deflated"))
|
||||
/// {
|
||||
/// using (Stream compressor = new DeflateStream(raw,
|
||||
/// CompressionMode.Compress,
|
||||
/// CompressionLevel.BestCompression))
|
||||
/// {
|
||||
/// byte[] buffer = new byte[WORKING_BUFFER_SIZE];
|
||||
/// int n= -1;
|
||||
/// while (n != 0)
|
||||
/// {
|
||||
/// if (n > 0)
|
||||
/// compressor.Write(buffer, 0, n);
|
||||
/// n= input.Read(buffer, 0, buffer.Length);
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
///
|
||||
/// <code lang="VB">
|
||||
/// Using input As Stream = File.OpenRead(fileToCompress)
|
||||
/// Using raw As FileStream = File.Create(fileToCompress & ".deflated")
|
||||
/// Using compressor As Stream = New DeflateStream(raw, CompressionMode.Compress, CompressionLevel.BestCompression)
|
||||
/// Dim buffer As Byte() = New Byte(4096) {}
|
||||
/// Dim n As Integer = -1
|
||||
/// Do While (n <> 0)
|
||||
/// If (n > 0) Then
|
||||
/// compressor.Write(buffer, 0, n)
|
||||
/// End If
|
||||
/// n = input.Read(buffer, 0, buffer.Length)
|
||||
/// Loop
|
||||
/// End Using
|
||||
/// End Using
|
||||
/// End Using
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="stream">The stream to be read or written while deflating or inflating.</param>
|
||||
/// <param name="mode">Indicates whether the <c>DeflateStream</c> will compress or decompress.</param>
|
||||
/// <param name="level">A tuning knob to trade speed for effectiveness.</param>
|
||||
public DeflateStream(System.IO.Stream stream, CompressionMode mode, CompressionLevel level)
|
||||
: this(stream, mode, level, false)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a <c>DeflateStream</c> using the specified
|
||||
/// <c>CompressionMode</c>, and explicitly specify whether the
|
||||
/// stream should be left open after Deflation or Inflation.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// This constructor allows the application to request that the captive stream
|
||||
/// remain open after the deflation or inflation occurs. By default, after
|
||||
/// <c>Close()</c> is called on the stream, the captive stream is also
|
||||
/// closed. In some cases this is not desired, for example if the stream is a
|
||||
/// memory stream that will be re-read after compression. Specify true for
|
||||
/// the <paramref name="leaveOpen"/> parameter to leave the stream open.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// The <c>DeflateStream</c> will use the default compression level.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// See the other overloads of this constructor for example code.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
///
|
||||
/// <param name="stream">
|
||||
/// The stream which will be read or written. This is called the
|
||||
/// "captive" stream in other places in this documentation.
|
||||
/// </param>
|
||||
///
|
||||
/// <param name="mode">
|
||||
/// Indicates whether the <c>DeflateStream</c> will compress or decompress.
|
||||
/// </param>
|
||||
///
|
||||
/// <param name="leaveOpen">true if the application would like the stream to
|
||||
/// remain open after inflation/deflation.</param>
|
||||
public DeflateStream(System.IO.Stream stream, CompressionMode mode, bool leaveOpen)
|
||||
: this(stream, mode, CompressionLevel.Default, leaveOpen)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a <c>DeflateStream</c> using the specified <c>CompressionMode</c>
|
||||
/// and the specified <c>CompressionLevel</c>, and explicitly specify whether
|
||||
/// the stream should be left open after Deflation or Inflation.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// When mode is <c>CompressionMode.Decompress</c>, the level parameter is ignored.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// This constructor allows the application to request that the captive stream
|
||||
/// remain open after the deflation or inflation occurs. By default, after
|
||||
/// <c>Close()</c> is called on the stream, the captive stream is also
|
||||
/// closed. In some cases this is not desired, for example if the stream is a
|
||||
/// <see cref="System.IO.MemoryStream"/> that will be re-read after
|
||||
/// compression. Specify true for the <paramref name="leaveOpen"/> parameter
|
||||
/// to leave the stream open.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <example>
|
||||
///
|
||||
/// This example shows how to use a <c>DeflateStream</c> to compress data from
|
||||
/// a file, and store the compressed data into another file.
|
||||
///
|
||||
/// <code>
|
||||
/// using (var output = System.IO.File.Create(fileToCompress + ".deflated"))
|
||||
/// {
|
||||
/// using (System.IO.Stream input = System.IO.File.OpenRead(fileToCompress))
|
||||
/// {
|
||||
/// using (Stream compressor = new DeflateStream(output, CompressionMode.Compress, CompressionLevel.BestCompression, true))
|
||||
/// {
|
||||
/// byte[] buffer = new byte[WORKING_BUFFER_SIZE];
|
||||
/// int n= -1;
|
||||
/// while (n != 0)
|
||||
/// {
|
||||
/// if (n > 0)
|
||||
/// compressor.Write(buffer, 0, n);
|
||||
/// n= input.Read(buffer, 0, buffer.Length);
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// // can write additional data to the output stream here
|
||||
/// }
|
||||
/// </code>
|
||||
///
|
||||
/// <code lang="VB">
|
||||
/// Using output As FileStream = File.Create(fileToCompress & ".deflated")
|
||||
/// Using input As Stream = File.OpenRead(fileToCompress)
|
||||
/// Using compressor As Stream = New DeflateStream(output, CompressionMode.Compress, CompressionLevel.BestCompression, True)
|
||||
/// Dim buffer As Byte() = New Byte(4096) {}
|
||||
/// Dim n As Integer = -1
|
||||
/// Do While (n <> 0)
|
||||
/// If (n > 0) Then
|
||||
/// compressor.Write(buffer, 0, n)
|
||||
/// End If
|
||||
/// n = input.Read(buffer, 0, buffer.Length)
|
||||
/// Loop
|
||||
/// End Using
|
||||
/// End Using
|
||||
/// ' can write additional data to the output stream here.
|
||||
/// End Using
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="stream">The stream which will be read or written.</param>
|
||||
/// <param name="mode">Indicates whether the DeflateStream will compress or decompress.</param>
|
||||
/// <param name="leaveOpen">true if the application would like the stream to remain open after inflation/deflation.</param>
|
||||
/// <param name="level">A tuning knob to trade speed for effectiveness.</param>
|
||||
public DeflateStream(System.IO.Stream stream, CompressionMode mode, CompressionLevel level, bool leaveOpen)
|
||||
{
|
||||
_innerStream = stream;
|
||||
_baseStream = new ZlibBaseStream(stream, mode, level, ZlibStreamFlavor.DEFLATE, leaveOpen);
|
||||
}
|
||||
|
||||
#region Zlib properties
|
||||
|
||||
/// <summary>
|
||||
/// This property sets the flush behavior on the stream.
|
||||
/// </summary>
|
||||
/// <remarks> See the ZLIB documentation for the meaning of the flush behavior.
|
||||
/// </remarks>
|
||||
virtual public FlushType FlushMode
|
||||
{
|
||||
get { return (this._baseStream._flushMode); }
|
||||
set
|
||||
{
|
||||
if (_disposed) throw new ObjectDisposedException("DeflateStream");
|
||||
this._baseStream._flushMode = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The size of the working buffer for the compression codec.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The working buffer is used for all stream operations. The default size is
|
||||
/// 1024 bytes. The minimum size is 128 bytes. You may get better performance
|
||||
/// with a larger buffer. Then again, you might not. You would have to test
|
||||
/// it.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Set this before the first call to <c>Read()</c> or <c>Write()</c> on the
|
||||
/// stream. If you try to set it afterwards, it will throw.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public int BufferSize
|
||||
{
|
||||
get
|
||||
{
|
||||
return this._baseStream._bufferSize;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_disposed) throw new ObjectDisposedException("DeflateStream");
|
||||
if (this._baseStream._workingBuffer != null)
|
||||
throw new ZlibException("The working buffer is already set.");
|
||||
if (value < ZlibConstants.WorkingBufferSizeMin)
|
||||
throw new ZlibException(String.Format("Don't be silly. {0} bytes?? Use a bigger buffer, at least {1}.", value, ZlibConstants.WorkingBufferSizeMin));
|
||||
this._baseStream._bufferSize = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The ZLIB strategy to be used during compression.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// By tweaking this parameter, you may be able to optimize the compression for
|
||||
/// data with particular characteristics.
|
||||
/// </remarks>
|
||||
public CompressionStrategy Strategy
|
||||
{
|
||||
get
|
||||
{
|
||||
return this._baseStream.Strategy;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_disposed) throw new ObjectDisposedException("DeflateStream");
|
||||
this._baseStream.Strategy = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Returns the total number of bytes input so far.</summary>
|
||||
virtual public long TotalIn
|
||||
{
|
||||
get
|
||||
{
|
||||
return this._baseStream._z.TotalBytesIn;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Returns the total number of bytes output so far.</summary>
|
||||
virtual public long TotalOut
|
||||
{
|
||||
get
|
||||
{
|
||||
return this._baseStream._z.TotalBytesOut;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region System.IO.Stream methods
|
||||
/// <summary>
|
||||
/// Dispose the stream.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This may or may not result in a <c>Close()</c> call on the captive
|
||||
/// stream. See the constructors that have a <c>leaveOpen</c> parameter
|
||||
/// for more information.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Application code won't call this code directly. This method may be
|
||||
/// invoked in two distinct scenarios. If disposing == true, the method
|
||||
/// has been called directly or indirectly by a user's code, for example
|
||||
/// via the public Dispose() method. In this case, both managed and
|
||||
/// unmanaged resources can be referenced and disposed. If disposing ==
|
||||
/// false, the method has been called by the runtime from inside the
|
||||
/// object finalizer and this method should not reference other objects;
|
||||
/// in that case only unmanaged resources must be referenced or
|
||||
/// disposed.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name="disposing">
|
||||
/// true if the Dispose method was invoked by user code.
|
||||
/// </param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
if (disposing && (this._baseStream != null))
|
||||
this._baseStream.Close();
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the stream can be read.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The return value depends on whether the captive stream supports reading.
|
||||
/// </remarks>
|
||||
public override bool CanRead
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_disposed) throw new ObjectDisposedException("DeflateStream");
|
||||
return _baseStream._stream.CanRead;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the stream supports Seek operations.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Always returns false.
|
||||
/// </remarks>
|
||||
public override bool CanSeek
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the stream can be written.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The return value depends on whether the captive stream supports writing.
|
||||
/// </remarks>
|
||||
public override bool CanWrite
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_disposed) throw new ObjectDisposedException("DeflateStream");
|
||||
return _baseStream._stream.CanWrite;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flush the stream.
|
||||
/// </summary>
|
||||
public override void Flush()
|
||||
{
|
||||
if (_disposed) throw new ObjectDisposedException("DeflateStream");
|
||||
_baseStream.Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reading this property always throws a <see cref="NotImplementedException"/>.
|
||||
/// </summary>
|
||||
public override long Length
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The position of the stream pointer.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// Setting this property always throws a <see
|
||||
/// cref="NotImplementedException"/>. Reading will return the total bytes
|
||||
/// written out, if used in writing, or the total bytes read in, if used in
|
||||
/// reading. The count may refer to compressed bytes or uncompressed bytes,
|
||||
/// depending on how you've used the stream.
|
||||
/// </remarks>
|
||||
public override long Position
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this._baseStream._streamMode == SabreTools.IO.Compression.Deflate.ZlibBaseStream.StreamMode.Writer)
|
||||
return this._baseStream._z.TotalBytesOut;
|
||||
if (this._baseStream._streamMode == SabreTools.IO.Compression.Deflate.ZlibBaseStream.StreamMode.Reader)
|
||||
return this._baseStream._z.TotalBytesIn;
|
||||
return 0;
|
||||
}
|
||||
set { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read data from the stream.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// If you wish to use the <c>DeflateStream</c> to compress data while
|
||||
/// reading, you can create a <c>DeflateStream</c> with
|
||||
/// <c>CompressionMode.Compress</c>, providing an uncompressed data stream.
|
||||
/// Then call Read() on that <c>DeflateStream</c>, and the data read will be
|
||||
/// compressed as you read. If you wish to use the <c>DeflateStream</c> to
|
||||
/// decompress data while reading, you can create a <c>DeflateStream</c> with
|
||||
/// <c>CompressionMode.Decompress</c>, providing a readable compressed data
|
||||
/// stream. Then call Read() on that <c>DeflateStream</c>, and the data read
|
||||
/// will be decompressed as you read.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// A <c>DeflateStream</c> can be used for <c>Read()</c> or <c>Write()</c>, but not both.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
/// <param name="buffer">The buffer into which the read data should be placed.</param>
|
||||
/// <param name="offset">the offset within that data array to put the first byte read.</param>
|
||||
/// <param name="count">the number of bytes to read.</param>
|
||||
/// <returns>the number of bytes actually read</returns>
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (_disposed) throw new ObjectDisposedException("DeflateStream");
|
||||
return _baseStream.Read(buffer, offset, count);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Calling this method always throws a <see cref="NotImplementedException"/>.
|
||||
/// </summary>
|
||||
/// <param name="offset">this is irrelevant, since it will always throw!</param>
|
||||
/// <param name="origin">this is irrelevant, since it will always throw!</param>
|
||||
/// <returns>irrelevant!</returns>
|
||||
public override long Seek(long offset, System.IO.SeekOrigin origin)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calling this method always throws a <see cref="NotImplementedException"/>.
|
||||
/// </summary>
|
||||
/// <param name="value">this is irrelevant, since it will always throw!</param>
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write data to the stream.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// If you wish to use the <c>DeflateStream</c> to compress data while
|
||||
/// writing, you can create a <c>DeflateStream</c> with
|
||||
/// <c>CompressionMode.Compress</c>, and a writable output stream. Then call
|
||||
/// <c>Write()</c> on that <c>DeflateStream</c>, providing uncompressed data
|
||||
/// as input. The data sent to the output stream will be the compressed form
|
||||
/// of the data written. If you wish to use the <c>DeflateStream</c> to
|
||||
/// decompress data while writing, you can create a <c>DeflateStream</c> with
|
||||
/// <c>CompressionMode.Decompress</c>, and a writable output stream. Then
|
||||
/// call <c>Write()</c> on that stream, providing previously compressed
|
||||
/// data. The data sent to the output stream will be the decompressed form of
|
||||
/// the data written.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// A <c>DeflateStream</c> can be used for <c>Read()</c> or <c>Write()</c>,
|
||||
/// but not both.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <param name="buffer">The buffer holding data to write to the stream.</param>
|
||||
/// <param name="offset">the offset within that data array to find the first byte to write.</param>
|
||||
/// <param name="count">the number of bytes to write.</param>
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (_disposed) throw new ObjectDisposedException("DeflateStream");
|
||||
_baseStream.Write(buffer, offset, count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the dictionary to be used for either Inflation or Deflation.
|
||||
/// </summary>
|
||||
/// <param name="dictionary">The dictionary bytes to use.</param>
|
||||
/// <param name="check">Determines if dictionary checks are run</param>
|
||||
/// <returns>Z_OK if all goes well.</returns>
|
||||
public int SetDictionary(byte[] dictionary, bool check = true)
|
||||
{
|
||||
return _baseStream.SetDictionary(dictionary, check);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Compress a string into a byte array using DEFLATE (RFC 1951).
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// Uncompress it with <see cref="DeflateStream.UncompressString(byte[])"/>.
|
||||
/// </remarks>
|
||||
///
|
||||
/// <seealso cref="DeflateStream.UncompressString(byte[])">DeflateStream.UncompressString(byte[])</seealso>
|
||||
/// <seealso cref="DeflateStream.CompressBuffer(byte[])">DeflateStream.CompressBuffer(byte[])</seealso>
|
||||
/// <seealso cref="GZipStream.CompressString(string)">GZipStream.CompressString(string)</seealso>
|
||||
/// <seealso cref="ZlibStream.CompressString(string)">ZlibStream.CompressString(string)</seealso>
|
||||
///
|
||||
/// <param name="s">
|
||||
/// A string to compress. The string will first be encoded
|
||||
/// using UTF8, then compressed.
|
||||
/// </param>
|
||||
///
|
||||
/// <returns>The string in compressed form</returns>
|
||||
public static byte[] CompressString(String s)
|
||||
{
|
||||
using var ms = new System.IO.MemoryStream();
|
||||
System.IO.Stream compressor =
|
||||
new DeflateStream(ms, CompressionMode.Compress, CompressionLevel.BestCompression);
|
||||
ZlibBaseStream.CompressString(s, compressor);
|
||||
return ms.ToArray();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Compress a byte array into a new byte array using DEFLATE.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// Uncompress it with <see cref="DeflateStream.UncompressBuffer(byte[])"/>.
|
||||
/// </remarks>
|
||||
///
|
||||
/// <seealso cref="DeflateStream.CompressString(string)">DeflateStream.CompressString(string)</seealso>
|
||||
/// <seealso cref="DeflateStream.UncompressBuffer(byte[])">DeflateStream.UncompressBuffer(byte[])</seealso>
|
||||
/// <seealso cref="GZipStream.CompressBuffer(byte[])">GZipStream.CompressBuffer(byte[])</seealso>
|
||||
/// <seealso cref="ZlibStream.CompressBuffer(byte[])">ZlibStream.CompressBuffer(byte[])</seealso>
|
||||
///
|
||||
/// <param name="b">
|
||||
/// A buffer to compress.
|
||||
/// </param>
|
||||
///
|
||||
/// <returns>The data in compressed form</returns>
|
||||
public static byte[] CompressBuffer(byte[] b)
|
||||
{
|
||||
using var ms = new System.IO.MemoryStream();
|
||||
System.IO.Stream compressor =
|
||||
new DeflateStream(ms, CompressionMode.Compress, CompressionLevel.BestCompression);
|
||||
|
||||
ZlibBaseStream.CompressBuffer(b, compressor);
|
||||
return ms.ToArray();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Uncompress a DEFLATE'd byte array into a single string.
|
||||
/// </summary>
|
||||
///
|
||||
/// <seealso cref="DeflateStream.CompressString(String)">DeflateStream.CompressString(String)</seealso>
|
||||
/// <seealso cref="DeflateStream.UncompressBuffer(byte[])">DeflateStream.UncompressBuffer(byte[])</seealso>
|
||||
/// <seealso cref="GZipStream.UncompressString(byte[])">GZipStream.UncompressString(byte[])</seealso>
|
||||
/// <seealso cref="ZlibStream.UncompressString(byte[])">ZlibStream.UncompressString(byte[])</seealso>
|
||||
///
|
||||
/// <param name="compressed">
|
||||
/// A buffer containing DEFLATE-compressed data.
|
||||
/// </param>
|
||||
///
|
||||
/// <returns>The uncompressed string</returns>
|
||||
public static String UncompressString(byte[] compressed)
|
||||
{
|
||||
using var input = new System.IO.MemoryStream(compressed);
|
||||
System.IO.Stream decompressor =
|
||||
new DeflateStream(input, CompressionMode.Decompress);
|
||||
|
||||
return ZlibBaseStream.UncompressString(compressed, decompressor);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Uncompress a DEFLATE'd byte array into a byte array.
|
||||
/// </summary>
|
||||
///
|
||||
/// <seealso cref="DeflateStream.CompressBuffer(byte[])">DeflateStream.CompressBuffer(byte[])</seealso>
|
||||
/// <seealso cref="DeflateStream.UncompressString(byte[])">DeflateStream.UncompressString(byte[])</seealso>
|
||||
/// <seealso cref="GZipStream.UncompressBuffer(byte[])">GZipStream.UncompressBuffer(byte[])</seealso>
|
||||
/// <seealso cref="ZlibStream.UncompressBuffer(byte[])">ZlibStream.UncompressBuffer(byte[])</seealso>
|
||||
///
|
||||
/// <param name="compressed">
|
||||
/// A buffer containing data that has been compressed with DEFLATE.
|
||||
/// </param>
|
||||
///
|
||||
/// <returns>The data in uncompressed form</returns>
|
||||
public static byte[] UncompressBuffer(byte[] compressed)
|
||||
{
|
||||
using var input = new System.IO.MemoryStream(compressed);
|
||||
System.IO.Stream decompressor =
|
||||
new DeflateStream(input, CompressionMode.Decompress);
|
||||
|
||||
return ZlibBaseStream.UncompressBuffer(compressed, decompressor);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
34
SabreTools.IO/Compression/Deflate/Enums.cs
Normal file
34
SabreTools.IO/Compression/Deflate/Enums.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
namespace SabreTools.IO.Compression.Deflate
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the status returned from extracting
|
||||
/// </summary>
|
||||
public enum ExtractionStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// Extraction wasn't performed because the inputs were invalid
|
||||
/// </summary>
|
||||
INVALID,
|
||||
|
||||
/// <summary>
|
||||
/// No issues with the extraction
|
||||
/// </summary>
|
||||
GOOD,
|
||||
|
||||
/// <summary>
|
||||
/// File extracted but was the wrong size
|
||||
/// </summary>
|
||||
/// <remarks>Rewinds the stream and deletes the bad file</remarks>
|
||||
WRONG_SIZE,
|
||||
|
||||
/// <summary>
|
||||
/// File extracted but had the wrong CRC-32 value
|
||||
/// </summary>
|
||||
BAD_CRC,
|
||||
|
||||
/// <summary>
|
||||
/// Extraction failed entirely
|
||||
/// </summary>
|
||||
FAIL,
|
||||
}
|
||||
}
|
||||
133
SabreTools.IO/Compression/Deflate/FlushType.cs
Normal file
133
SabreTools.IO/Compression/Deflate/FlushType.cs
Normal file
@@ -0,0 +1,133 @@
|
||||
// Zlib.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009-2011 Dino Chiesa and Microsoft Corporation.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Last Saved: <2011-August-03 19:52:28>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines classes for ZLIB compression and
|
||||
// decompression. This code is derived from the jzlib implementation of
|
||||
// zlib, but significantly modified. The object model is not the same,
|
||||
// and many of the behaviors are new or different. Nonetheless, in
|
||||
// keeping with the license for jzlib, the copyright to that code is
|
||||
// included below.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// The following notice applies to jzlib:
|
||||
//
|
||||
// Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in
|
||||
// the documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. The names of the authors may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
// INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
// jzlib is based on zlib-1.1.3.
|
||||
//
|
||||
// The following notice applies to zlib:
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (C) 1995-2004 Jean-loup Gailly and Mark Adler
|
||||
//
|
||||
// The ZLIB software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
// Jean-loup Gailly jloup@gzip.org
|
||||
// Mark Adler madler@alumni.caltech.edu
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace SabreTools.IO.Compression.Deflate
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes how to flush the current deflate operation.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The different FlushType values are useful when using a Deflate in a streaming application.
|
||||
/// </remarks>
|
||||
public enum FlushType
|
||||
{
|
||||
/// <summary>No flush at all.</summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>Closes the current block, but doesn't flush it to
|
||||
/// the output. Used internally only in hypothetical
|
||||
/// scenarios. This was supposed to be removed by Zlib, but it is
|
||||
/// still in use in some edge cases.
|
||||
/// </summary>
|
||||
Partial,
|
||||
|
||||
/// <summary>
|
||||
/// Use this during compression to specify that all pending output should be
|
||||
/// flushed to the output buffer and the output should be aligned on a byte
|
||||
/// boundary. You might use this in a streaming communication scenario, so that
|
||||
/// the decompressor can get all input data available so far. When using this
|
||||
/// with a ZlibCodec, <c>AvailableBytesIn</c> will be zero after the call if
|
||||
/// enough output space has been provided before the call. Flushing will
|
||||
/// degrade compression and so it should be used only when necessary.
|
||||
/// </summary>
|
||||
Sync,
|
||||
|
||||
/// <summary>
|
||||
/// Use this during compression to specify that all output should be flushed, as
|
||||
/// with <c>FlushType.Sync</c>, but also, the compression state should be reset
|
||||
/// so that decompression can restart from this point if previous compressed
|
||||
/// data has been damaged or if random access is desired. Using
|
||||
/// <c>FlushType.Full</c> too often can significantly degrade the compression.
|
||||
/// </summary>
|
||||
Full,
|
||||
|
||||
/// <summary>Signals the end of the compression/decompression stream.</summary>
|
||||
Finish,
|
||||
}
|
||||
|
||||
}
|
||||
1026
SabreTools.IO/Compression/Deflate/GZipStream.cs
Normal file
1026
SabreTools.IO/Compression/Deflate/GZipStream.cs
Normal file
File diff suppressed because it is too large
Load Diff
435
SabreTools.IO/Compression/Deflate/InfTree.cs
Normal file
435
SabreTools.IO/Compression/Deflate/InfTree.cs
Normal file
@@ -0,0 +1,435 @@
|
||||
// Inftree.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009 Dino Chiesa and Microsoft Corporation.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2009-October-28 12:43:54>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines classes used in decompression. This code is derived
|
||||
// from the jzlib implementation of zlib. In keeping with the license for jzlib,
|
||||
// the copyright to that code is below.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in
|
||||
// the documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. The names of the authors may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
// INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
// This program is based on zlib-1.1.3; credit to authors
|
||||
// Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu)
|
||||
// and contributors of zlib.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
|
||||
#nullable disable
|
||||
namespace SabreTools.IO.Compression.Deflate
|
||||
{
|
||||
sealed class InfTree
|
||||
{
|
||||
|
||||
private const int MANY = 1440;
|
||||
|
||||
private const int Z_OK = 0;
|
||||
private const int Z_STREAM_END = 1;
|
||||
private const int Z_NEED_DICT = 2;
|
||||
private const int Z_ERRNO = -1;
|
||||
private const int Z_STREAM_ERROR = -2;
|
||||
private const int Z_DATA_ERROR = -3;
|
||||
private const int Z_MEM_ERROR = -4;
|
||||
private const int Z_BUF_ERROR = -5;
|
||||
private const int Z_VERSION_ERROR = -6;
|
||||
|
||||
internal const int fixed_bl = 9;
|
||||
internal const int fixed_bd = 5;
|
||||
|
||||
//UPGRADE_NOTE: Final was removed from the declaration of 'fixed_tl'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
|
||||
internal static readonly int[] fixed_tl = [96, 7, 256, 0, 8, 80, 0, 8, 16, 84, 8, 115, 82, 7, 31, 0, 8, 112, 0, 8, 48, 0, 9, 192, 80, 7, 10, 0, 8, 96, 0, 8, 32, 0, 9, 160, 0, 8, 0, 0, 8, 128, 0, 8, 64, 0, 9, 224, 80, 7, 6, 0, 8, 88, 0, 8, 24, 0, 9, 144, 83, 7, 59, 0, 8, 120, 0, 8, 56, 0, 9, 208, 81, 7, 17, 0, 8, 104, 0, 8, 40, 0, 9, 176, 0, 8, 8, 0, 8, 136, 0, 8, 72, 0, 9, 240, 80, 7, 4, 0, 8, 84, 0, 8, 20, 85, 8, 227, 83, 7, 43, 0, 8, 116, 0, 8, 52, 0, 9, 200, 81, 7, 13, 0, 8, 100, 0, 8, 36, 0, 9, 168, 0, 8, 4, 0, 8, 132, 0, 8, 68, 0, 9, 232, 80, 7, 8, 0, 8, 92, 0, 8, 28, 0, 9, 152, 84, 7, 83, 0, 8, 124, 0, 8, 60, 0, 9, 216, 82, 7, 23, 0, 8, 108, 0, 8, 44, 0, 9, 184, 0, 8, 12, 0, 8, 140, 0, 8, 76, 0, 9, 248, 80, 7, 3, 0, 8, 82, 0, 8, 18, 85, 8, 163, 83, 7, 35, 0, 8, 114, 0, 8, 50, 0, 9, 196, 81, 7, 11, 0, 8, 98, 0, 8, 34, 0, 9, 164, 0, 8, 2, 0, 8, 130, 0, 8, 66, 0, 9, 228, 80, 7, 7, 0, 8, 90, 0, 8, 26, 0, 9, 148, 84, 7, 67, 0, 8, 122, 0, 8, 58, 0, 9, 212, 82, 7, 19, 0, 8, 106, 0, 8, 42, 0, 9, 180, 0, 8, 10, 0, 8, 138, 0, 8, 74, 0, 9, 244, 80, 7, 5, 0, 8, 86, 0, 8, 22, 192, 8, 0, 83, 7, 51, 0, 8, 118, 0, 8, 54, 0, 9, 204, 81, 7, 15, 0, 8, 102, 0, 8, 38, 0, 9, 172, 0, 8, 6, 0, 8, 134, 0, 8, 70, 0, 9, 236, 80, 7, 9, 0, 8, 94, 0, 8, 30, 0, 9, 156, 84, 7, 99, 0, 8, 126, 0, 8, 62, 0, 9, 220, 82, 7, 27, 0, 8, 110, 0, 8, 46, 0, 9, 188, 0, 8, 14, 0, 8, 142, 0, 8, 78, 0, 9, 252, 96, 7, 256, 0, 8, 81, 0, 8, 17, 85, 8, 131, 82, 7, 31, 0, 8, 113, 0, 8, 49, 0, 9, 194, 80, 7, 10, 0, 8, 97, 0, 8, 33, 0, 9, 162, 0, 8, 1, 0, 8, 129, 0, 8, 65, 0, 9, 226, 80, 7, 6, 0, 8, 89, 0, 8, 25, 0, 9, 146, 83, 7, 59, 0, 8, 121, 0, 8, 57, 0, 9, 210, 81, 7, 17, 0, 8, 105, 0, 8, 41, 0, 9, 178, 0, 8, 9, 0, 8, 137, 0, 8, 73, 0, 9, 242, 80, 7, 4, 0, 8, 85, 0, 8, 21, 80, 8, 258, 83, 7, 43, 0, 8, 117, 0, 8, 53, 0, 9, 202, 81, 7, 13, 0, 8, 101, 0, 8, 37, 0, 9, 170, 0, 8, 5, 0, 8, 133, 0, 8, 69, 0, 9, 234, 80, 7, 8, 0, 8, 93, 0, 8, 29, 0, 9, 154, 84, 7, 83, 0, 8, 125, 0, 8, 61, 0, 9, 218, 82, 7, 23, 0, 8, 109, 0, 8, 45, 0, 9, 186,
|
||||
0, 8, 13, 0, 8, 141, 0, 8, 77, 0, 9, 250, 80, 7, 3, 0, 8, 83, 0, 8, 19, 85, 8, 195, 83, 7, 35, 0, 8, 115, 0, 8, 51, 0, 9, 198, 81, 7, 11, 0, 8, 99, 0, 8, 35, 0, 9, 166, 0, 8, 3, 0, 8, 131, 0, 8, 67, 0, 9, 230, 80, 7, 7, 0, 8, 91, 0, 8, 27, 0, 9, 150, 84, 7, 67, 0, 8, 123, 0, 8, 59, 0, 9, 214, 82, 7, 19, 0, 8, 107, 0, 8, 43, 0, 9, 182, 0, 8, 11, 0, 8, 139, 0, 8, 75, 0, 9, 246, 80, 7, 5, 0, 8, 87, 0, 8, 23, 192, 8, 0, 83, 7, 51, 0, 8, 119, 0, 8, 55, 0, 9, 206, 81, 7, 15, 0, 8, 103, 0, 8, 39, 0, 9, 174, 0, 8, 7, 0, 8, 135, 0, 8, 71, 0, 9, 238, 80, 7, 9, 0, 8, 95, 0, 8, 31, 0, 9, 158, 84, 7, 99, 0, 8, 127, 0, 8, 63, 0, 9, 222, 82, 7, 27, 0, 8, 111, 0, 8, 47, 0, 9, 190, 0, 8, 15, 0, 8, 143, 0, 8, 79, 0, 9, 254, 96, 7, 256, 0, 8, 80, 0, 8, 16, 84, 8, 115, 82, 7, 31, 0, 8, 112, 0, 8, 48, 0, 9, 193, 80, 7, 10, 0, 8, 96, 0, 8, 32, 0, 9, 161, 0, 8, 0, 0, 8, 128, 0, 8, 64, 0, 9, 225, 80, 7, 6, 0, 8, 88, 0, 8, 24, 0, 9, 145, 83, 7, 59, 0, 8, 120, 0, 8, 56, 0, 9, 209, 81, 7, 17, 0, 8, 104, 0, 8, 40, 0, 9, 177, 0, 8, 8, 0, 8, 136, 0, 8, 72, 0, 9, 241, 80, 7, 4, 0, 8, 84, 0, 8, 20, 85, 8, 227, 83, 7, 43, 0, 8, 116, 0, 8, 52, 0, 9, 201, 81, 7, 13, 0, 8, 100, 0, 8, 36, 0, 9, 169, 0, 8, 4, 0, 8, 132, 0, 8, 68, 0, 9, 233, 80, 7, 8, 0, 8, 92, 0, 8, 28, 0, 9, 153, 84, 7, 83, 0, 8, 124, 0, 8, 60, 0, 9, 217, 82, 7, 23, 0, 8, 108, 0, 8, 44, 0, 9, 185, 0, 8, 12, 0, 8, 140, 0, 8, 76, 0, 9, 249, 80, 7, 3, 0, 8, 82, 0, 8, 18, 85, 8, 163, 83, 7, 35, 0, 8, 114, 0, 8, 50, 0, 9, 197, 81, 7, 11, 0, 8, 98, 0, 8, 34, 0, 9, 165, 0, 8, 2, 0, 8, 130, 0, 8, 66, 0, 9, 229, 80, 7, 7, 0, 8, 90, 0, 8, 26, 0, 9, 149, 84, 7, 67, 0, 8, 122, 0, 8, 58, 0, 9, 213, 82, 7, 19, 0, 8, 106, 0, 8, 42, 0, 9, 181, 0, 8, 10, 0, 8, 138, 0, 8, 74, 0, 9, 245, 80, 7, 5, 0, 8, 86, 0, 8, 22, 192, 8, 0, 83, 7, 51, 0, 8, 118, 0, 8, 54, 0, 9, 205, 81, 7, 15, 0, 8, 102, 0, 8, 38, 0, 9, 173, 0, 8, 6, 0, 8, 134, 0, 8, 70, 0, 9, 237, 80, 7, 9, 0, 8, 94, 0, 8, 30, 0, 9, 157, 84, 7, 99, 0, 8, 126, 0, 8, 62, 0, 9, 221, 82, 7, 27, 0, 8, 110, 0, 8, 46, 0, 9, 189, 0, 8,
|
||||
14, 0, 8, 142, 0, 8, 78, 0, 9, 253, 96, 7, 256, 0, 8, 81, 0, 8, 17, 85, 8, 131, 82, 7, 31, 0, 8, 113, 0, 8, 49, 0, 9, 195, 80, 7, 10, 0, 8, 97, 0, 8, 33, 0, 9, 163, 0, 8, 1, 0, 8, 129, 0, 8, 65, 0, 9, 227, 80, 7, 6, 0, 8, 89, 0, 8, 25, 0, 9, 147, 83, 7, 59, 0, 8, 121, 0, 8, 57, 0, 9, 211, 81, 7, 17, 0, 8, 105, 0, 8, 41, 0, 9, 179, 0, 8, 9, 0, 8, 137, 0, 8, 73, 0, 9, 243, 80, 7, 4, 0, 8, 85, 0, 8, 21, 80, 8, 258, 83, 7, 43, 0, 8, 117, 0, 8, 53, 0, 9, 203, 81, 7, 13, 0, 8, 101, 0, 8, 37, 0, 9, 171, 0, 8, 5, 0, 8, 133, 0, 8, 69, 0, 9, 235, 80, 7, 8, 0, 8, 93, 0, 8, 29, 0, 9, 155, 84, 7, 83, 0, 8, 125, 0, 8, 61, 0, 9, 219, 82, 7, 23, 0, 8, 109, 0, 8, 45, 0, 9, 187, 0, 8, 13, 0, 8, 141, 0, 8, 77, 0, 9, 251, 80, 7, 3, 0, 8, 83, 0, 8, 19, 85, 8, 195, 83, 7, 35, 0, 8, 115, 0, 8, 51, 0, 9, 199, 81, 7, 11, 0, 8, 99, 0, 8, 35, 0, 9, 167, 0, 8, 3, 0, 8, 131, 0, 8, 67, 0, 9, 231, 80, 7, 7, 0, 8, 91, 0, 8, 27, 0, 9, 151, 84, 7, 67, 0, 8, 123, 0, 8, 59, 0, 9, 215, 82, 7, 19, 0, 8, 107, 0, 8, 43, 0, 9, 183, 0, 8, 11, 0, 8, 139, 0, 8, 75, 0, 9, 247, 80, 7, 5, 0, 8, 87, 0, 8, 23, 192, 8, 0, 83, 7, 51, 0, 8, 119, 0, 8, 55, 0, 9, 207, 81, 7, 15, 0, 8, 103, 0, 8, 39, 0, 9, 175, 0, 8, 7, 0, 8, 135, 0, 8, 71, 0, 9, 239, 80, 7, 9, 0, 8, 95, 0, 8, 31, 0, 9, 159, 84, 7, 99, 0, 8, 127, 0, 8, 63, 0, 9, 223, 82, 7, 27, 0, 8, 111, 0, 8, 47, 0, 9, 191, 0, 8, 15, 0, 8, 143, 0, 8, 79, 0, 9, 255];
|
||||
//UPGRADE_NOTE: Final was removed from the declaration of 'fixed_td'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
|
||||
internal static readonly int[] fixed_td = [80, 5, 1, 87, 5, 257, 83, 5, 17, 91, 5, 4097, 81, 5, 5, 89, 5, 1025, 85, 5, 65, 93, 5, 16385, 80, 5, 3, 88, 5, 513, 84, 5, 33, 92, 5, 8193, 82, 5, 9, 90, 5, 2049, 86, 5, 129, 192, 5, 24577, 80, 5, 2, 87, 5, 385, 83, 5, 25, 91, 5, 6145, 81, 5, 7, 89, 5, 1537, 85, 5, 97, 93, 5, 24577, 80, 5, 4, 88, 5, 769, 84, 5, 49, 92, 5, 12289, 82, 5, 13, 90, 5, 3073, 86, 5, 193, 192, 5, 24577];
|
||||
|
||||
// Tables for deflate from PKZIP's appnote.txt.
|
||||
//UPGRADE_NOTE: Final was removed from the declaration of 'cplens'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
|
||||
internal static readonly int[] cplens = [3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0];
|
||||
|
||||
// see note #13 above about 258
|
||||
//UPGRADE_NOTE: Final was removed from the declaration of 'cplext'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
|
||||
internal static readonly int[] cplext = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112];
|
||||
|
||||
//UPGRADE_NOTE: Final was removed from the declaration of 'cpdist'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
|
||||
internal static readonly int[] cpdist = [1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577];
|
||||
|
||||
//UPGRADE_NOTE: Final was removed from the declaration of 'cpdext'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
|
||||
internal static readonly int[] cpdext = [0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13];
|
||||
|
||||
// If BMAX needs to be larger than 16, then h and x[] should be uLong.
|
||||
internal const int BMAX = 15; // maximum bit length of any code
|
||||
|
||||
internal int[] hn = null; // hufts used in space
|
||||
internal int[] v = null; // work area for huft_build
|
||||
internal int[] c = null; // bit length count table
|
||||
internal int[] r = null; // table entry for structure assignment
|
||||
internal int[] u = null; // table stack
|
||||
internal int[] x = null; // bit offsets, then code stack
|
||||
|
||||
private int huft_build(int[] b, int bindex, int n, int s, int[] d, int[] e, int[] t, int[] m, int[] hp, int[] hn, int[] v)
|
||||
{
|
||||
// Given a list of code lengths and a maximum table size, make a set of
|
||||
// tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR
|
||||
// if the given code set is incomplete (the tables are still built in this
|
||||
// case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of
|
||||
// lengths), or Z_MEM_ERROR if not enough memory.
|
||||
|
||||
int a; // counter for codes of length k
|
||||
int f; // i repeats in table every f entries
|
||||
int g; // maximum code length
|
||||
int h; // table level
|
||||
int i; // counter, current code
|
||||
int j; // counter
|
||||
int k; // number of bits in current code
|
||||
int l; // bits per table (returned in m)
|
||||
int mask; // (1 << w) - 1, to avoid cc -O bug on HP
|
||||
int p; // pointer into c[], b[], or v[]
|
||||
int q; // points to current table
|
||||
int w; // bits before this table == (l * h)
|
||||
int xp; // pointer into x
|
||||
int y; // number of dummy codes added
|
||||
int z; // number of entries in current table
|
||||
|
||||
// Generate counts for each bit length
|
||||
|
||||
p = 0; i = n;
|
||||
do
|
||||
{
|
||||
c[b[bindex + p]]++; p++; i--; // assume all entries <= BMAX
|
||||
}
|
||||
while (i != 0);
|
||||
|
||||
if (c[0] == n)
|
||||
{
|
||||
// null input--all zero length codes
|
||||
t[0] = -1;
|
||||
m[0] = 0;
|
||||
return Z_OK;
|
||||
}
|
||||
|
||||
// Find minimum and maximum length, bound *m by those
|
||||
l = m[0];
|
||||
for (j = 1; j <= BMAX; j++)
|
||||
if (c[j] != 0)
|
||||
break;
|
||||
k = j; // minimum code length
|
||||
if (l < j)
|
||||
{
|
||||
l = j;
|
||||
}
|
||||
for (i = BMAX; i != 0; i--)
|
||||
{
|
||||
if (c[i] != 0)
|
||||
break;
|
||||
}
|
||||
g = i; // maximum code length
|
||||
if (l > i)
|
||||
{
|
||||
l = i;
|
||||
}
|
||||
m[0] = l;
|
||||
|
||||
// Adjust last length count to fill out codes, if needed
|
||||
for (y = 1 << j; j < i; j++, y <<= 1)
|
||||
{
|
||||
if ((y -= c[j]) < 0)
|
||||
{
|
||||
return Z_DATA_ERROR;
|
||||
}
|
||||
}
|
||||
if ((y -= c[i]) < 0)
|
||||
{
|
||||
return Z_DATA_ERROR;
|
||||
}
|
||||
c[i] += y;
|
||||
|
||||
// Generate starting offsets into the value table for each length
|
||||
x[1] = j = 0;
|
||||
p = 1; xp = 2;
|
||||
while (--i != 0)
|
||||
{
|
||||
// note that i == g from above
|
||||
x[xp] = (j += c[p]);
|
||||
xp++;
|
||||
p++;
|
||||
}
|
||||
|
||||
// Make a table of values in order of bit lengths
|
||||
i = 0; p = 0;
|
||||
do
|
||||
{
|
||||
if ((j = b[bindex + p]) != 0)
|
||||
{
|
||||
v[x[j]++] = i;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
while (++i < n);
|
||||
n = x[g]; // set n to length of v
|
||||
|
||||
// Generate the Huffman codes and for each, make the table entries
|
||||
x[0] = i = 0; // first Huffman code is zero
|
||||
p = 0; // grab values in bit order
|
||||
h = -1; // no tables yet--level -1
|
||||
w = -l; // bits decoded == (l * h)
|
||||
u[0] = 0; // just to keep compilers happy
|
||||
q = 0; // ditto
|
||||
z = 0; // ditto
|
||||
|
||||
// go through the bit lengths (k already is bits in shortest code)
|
||||
for (; k <= g; k++)
|
||||
{
|
||||
a = c[k];
|
||||
while (a-- != 0)
|
||||
{
|
||||
// here i is the Huffman code of length k bits for value *p
|
||||
// make tables up to required level
|
||||
while (k > w + l)
|
||||
{
|
||||
h++;
|
||||
w += l; // previous table always l bits
|
||||
// compute minimum size table less than or equal to l bits
|
||||
z = g - w;
|
||||
z = (z > l) ? l : z; // table size upper limit
|
||||
if ((f = 1 << (j = k - w)) > a + 1)
|
||||
{
|
||||
// try a k-w bit table
|
||||
// too few codes for k-w bit table
|
||||
f -= (a + 1); // deduct codes from patterns left
|
||||
xp = k;
|
||||
if (j < z)
|
||||
{
|
||||
while (++j < z)
|
||||
{
|
||||
// try smaller tables up to z bits
|
||||
if ((f <<= 1) <= c[++xp])
|
||||
break; // enough codes to use up j bits
|
||||
f -= c[xp]; // else deduct codes from patterns
|
||||
}
|
||||
}
|
||||
}
|
||||
z = 1 << j; // table entries for j-bit table
|
||||
|
||||
// allocate new table
|
||||
if (hn[0] + z > MANY)
|
||||
{
|
||||
// (note: doesn't matter for fixed)
|
||||
return Z_DATA_ERROR; // overflow of MANY
|
||||
}
|
||||
u[h] = q = hn[0]; // DEBUG
|
||||
hn[0] += z;
|
||||
|
||||
// connect to last table, if there is one
|
||||
if (h != 0)
|
||||
{
|
||||
x[h] = i; // save pattern for backing up
|
||||
r[0] = (sbyte)j; // bits in this table
|
||||
r[1] = (sbyte)l; // bits to dump before this table
|
||||
j = SharedUtils.URShift(i, (w - l));
|
||||
r[2] = (int)(q - u[h - 1] - j); // offset to this table
|
||||
Array.Copy(r, 0, hp, (u[h - 1] + j) * 3, 3); // connect to last table
|
||||
}
|
||||
else
|
||||
{
|
||||
t[0] = q; // first table is returned result
|
||||
}
|
||||
}
|
||||
|
||||
// set up table entry in r
|
||||
r[1] = (sbyte)(k - w);
|
||||
if (p >= n)
|
||||
{
|
||||
r[0] = 128 + 64; // out of values--invalid code
|
||||
}
|
||||
else if (v[p] < s)
|
||||
{
|
||||
r[0] = (sbyte)(v[p] < 256 ? 0 : 32 + 64); // 256 is end-of-block
|
||||
r[2] = v[p++]; // simple code is just the value
|
||||
}
|
||||
else
|
||||
{
|
||||
r[0] = (sbyte)(e[v[p] - s] + 16 + 64); // non-simple--look up in lists
|
||||
r[2] = d[v[p++] - s];
|
||||
}
|
||||
|
||||
// fill code-like entries with r
|
||||
f = 1 << (k - w);
|
||||
for (j = SharedUtils.URShift(i, w); j < z; j += f)
|
||||
{
|
||||
Array.Copy(r, 0, hp, (q + j) * 3, 3);
|
||||
}
|
||||
|
||||
// backwards increment the k-bit code i
|
||||
for (j = 1 << (k - 1); (i & j) != 0; j = SharedUtils.URShift(j, 1))
|
||||
{
|
||||
i ^= j;
|
||||
}
|
||||
i ^= j;
|
||||
|
||||
// backup over finished tables
|
||||
mask = (1 << w) - 1; // needed on HP, cc -O bug
|
||||
while ((i & mask) != x[h])
|
||||
{
|
||||
h--; // don't need to update q
|
||||
w -= l;
|
||||
mask = (1 << w) - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Return Z_BUF_ERROR if we were given an incomplete table
|
||||
return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK;
|
||||
}
|
||||
|
||||
internal int inflate_trees_bits(int[] c, int[] bb, int[] tb, int[] hp, ZlibCodec z)
|
||||
{
|
||||
int result;
|
||||
initWorkArea(19);
|
||||
hn[0] = 0;
|
||||
result = huft_build(c, 0, 19, 19, null, null, tb, bb, hp, hn, v);
|
||||
|
||||
if (result == Z_DATA_ERROR)
|
||||
{
|
||||
z.Message = "oversubscribed dynamic bit lengths tree";
|
||||
}
|
||||
else if (result == Z_BUF_ERROR || bb[0] == 0)
|
||||
{
|
||||
z.Message = "incomplete dynamic bit lengths tree";
|
||||
result = Z_DATA_ERROR;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal int inflate_trees_dynamic(int nl, int nd, int[] c, int[] bl, int[] bd, int[] tl, int[] td, int[] hp, ZlibCodec z)
|
||||
{
|
||||
int result;
|
||||
|
||||
// build literal/length tree
|
||||
initWorkArea(288);
|
||||
hn[0] = 0;
|
||||
result = huft_build(c, 0, nl, 257, cplens, cplext, tl, bl, hp, hn, v);
|
||||
if (result != Z_OK || bl[0] == 0)
|
||||
{
|
||||
if (result == Z_DATA_ERROR)
|
||||
{
|
||||
z.Message = "oversubscribed literal/length tree";
|
||||
}
|
||||
else if (result != Z_MEM_ERROR)
|
||||
{
|
||||
z.Message = "incomplete literal/length tree";
|
||||
result = Z_DATA_ERROR;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// build distance tree
|
||||
initWorkArea(288);
|
||||
result = huft_build(c, nl, nd, 0, cpdist, cpdext, td, bd, hp, hn, v);
|
||||
|
||||
if (result != Z_OK || (bd[0] == 0 && nl > 257))
|
||||
{
|
||||
if (result == Z_DATA_ERROR)
|
||||
{
|
||||
z.Message = "oversubscribed distance tree";
|
||||
}
|
||||
else if (result == Z_BUF_ERROR)
|
||||
{
|
||||
z.Message = "incomplete distance tree";
|
||||
result = Z_DATA_ERROR;
|
||||
}
|
||||
else if (result != Z_MEM_ERROR)
|
||||
{
|
||||
z.Message = "empty distance tree with lengths";
|
||||
result = Z_DATA_ERROR;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
return Z_OK;
|
||||
}
|
||||
|
||||
internal static int inflate_trees_fixed(int[] bl, int[] bd, int[][] tl, int[][] td, ZlibCodec z)
|
||||
{
|
||||
bl[0] = fixed_bl;
|
||||
bd[0] = fixed_bd;
|
||||
tl[0] = fixed_tl;
|
||||
td[0] = fixed_td;
|
||||
return Z_OK;
|
||||
}
|
||||
|
||||
private void initWorkArea(int vsize)
|
||||
{
|
||||
if (hn == null)
|
||||
{
|
||||
hn = new int[1];
|
||||
v = new int[vsize];
|
||||
c = new int[BMAX + 1];
|
||||
r = new int[3];
|
||||
u = new int[BMAX];
|
||||
x = new int[BMAX + 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (v.Length < vsize)
|
||||
{
|
||||
v = new int[vsize];
|
||||
}
|
||||
Array.Clear(v, 0, vsize);
|
||||
Array.Clear(c, 0, BMAX + 1);
|
||||
r[0] = 0; r[1] = 0; r[2] = 0;
|
||||
// for(int i=0; i<BMAX; i++){u[i]=0;}
|
||||
//Array.Copy(c, 0, u, 0, BMAX);
|
||||
Array.Clear(u, 0, BMAX);
|
||||
// for(int i=0; i<BMAX+1; i++){x[i]=0;}
|
||||
//Array.Copy(c, 0, x, 0, BMAX + 1);
|
||||
Array.Clear(x, 0, BMAX + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
736
SabreTools.IO/Compression/Deflate/InflateBlocks.cs
Normal file
736
SabreTools.IO/Compression/Deflate/InflateBlocks.cs
Normal file
@@ -0,0 +1,736 @@
|
||||
// Inflate.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009 Dino Chiesa and Microsoft Corporation.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2010-January-08 18:32:12>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines classes for decompression. This code is derived
|
||||
// from the jzlib implementation of zlib, but significantly modified.
|
||||
// The object model is not the same, and many of the behaviors are
|
||||
// different. Nonetheless, in keeping with the license for jzlib, I am
|
||||
// reproducing the copyright to that code here.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in
|
||||
// the documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. The names of the authors may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
// INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
// This program is based on zlib-1.1.3; credit to authors
|
||||
// Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu)
|
||||
// and contributors of zlib.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
|
||||
#nullable disable
|
||||
namespace SabreTools.IO.Compression.Deflate
|
||||
{
|
||||
sealed class InflateBlocks
|
||||
{
|
||||
private const int MANY = 1440;
|
||||
|
||||
// Table for deflate from PKZIP's appnote.txt.
|
||||
internal static readonly int[] border = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15];
|
||||
|
||||
private enum InflateBlockMode
|
||||
{
|
||||
TYPE = 0, // get type bits (3, including end bit)
|
||||
LENS = 1, // get lengths for stored
|
||||
STORED = 2, // processing stored block
|
||||
TABLE = 3, // get table lengths
|
||||
BTREE = 4, // get bit lengths tree for a dynamic block
|
||||
DTREE = 5, // get length, distance trees for a dynamic block
|
||||
CODES = 6, // processing fixed or dynamic block
|
||||
DRY = 7, // output remaining window bytes
|
||||
DONE = 8, // finished last block, done
|
||||
BAD = 9, // ot a data error--stuck here
|
||||
}
|
||||
|
||||
private InflateBlockMode mode; // current inflate_block mode
|
||||
|
||||
internal int left; // if STORED, bytes left to copy
|
||||
|
||||
internal int table; // table lengths (14 bits)
|
||||
internal int index; // index into blens (or border)
|
||||
internal int[] blens; // bit lengths of codes
|
||||
internal int[] bb = new int[1]; // bit length tree depth
|
||||
internal int[] tb = new int[1]; // bit length decoding tree
|
||||
|
||||
internal InflateCodes codes = new(); // if CODES, current state
|
||||
|
||||
internal int last; // true if this block is the last block
|
||||
|
||||
internal ZlibCodec _codec; // pointer back to this zlib stream
|
||||
|
||||
// mode independent information
|
||||
internal int bitk; // bits in bit buffer
|
||||
internal int bitb; // bit buffer
|
||||
internal int[] hufts; // single malloc for tree space
|
||||
internal byte[] window; // sliding window
|
||||
internal int end; // one byte after sliding window
|
||||
internal int readAt; // window read pointer
|
||||
internal int writeAt; // window write pointer
|
||||
internal System.Object checkfn; // check function
|
||||
internal uint check; // check on output
|
||||
|
||||
internal InfTree inftree = new();
|
||||
|
||||
internal InflateBlocks(ZlibCodec codec, System.Object checkfn, int w)
|
||||
{
|
||||
_codec = codec;
|
||||
hufts = new int[MANY * 3];
|
||||
window = new byte[w];
|
||||
end = w;
|
||||
this.checkfn = checkfn;
|
||||
mode = InflateBlockMode.TYPE;
|
||||
Reset();
|
||||
}
|
||||
|
||||
internal uint Reset()
|
||||
{
|
||||
uint oldCheck = check;
|
||||
mode = InflateBlockMode.TYPE;
|
||||
bitk = 0;
|
||||
bitb = 0;
|
||||
readAt = writeAt = 0;
|
||||
|
||||
if (checkfn != null)
|
||||
_codec._Adler32 = check = Adler.Adler32(0, null, 0, 0);
|
||||
return oldCheck;
|
||||
}
|
||||
|
||||
|
||||
internal int Process(int r)
|
||||
{
|
||||
int t; // temporary storage
|
||||
int b; // bit buffer
|
||||
int k; // bits in bit buffer
|
||||
int p; // input data pointer
|
||||
int n; // bytes available there
|
||||
int q; // output window write pointer
|
||||
int m; // bytes to end of window or read pointer
|
||||
|
||||
// copy input/output information to locals (UPDATE macro restores)
|
||||
|
||||
p = _codec.NextIn;
|
||||
n = _codec.AvailableBytesIn;
|
||||
b = bitb;
|
||||
k = bitk;
|
||||
|
||||
q = writeAt;
|
||||
m = (int)(q < readAt ? readAt - q - 1 : end - q);
|
||||
|
||||
|
||||
// process input based on current state
|
||||
while (true)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case InflateBlockMode.TYPE:
|
||||
|
||||
while (k < (3))
|
||||
{
|
||||
if (n != 0)
|
||||
{
|
||||
r = ZlibConstants.Z_OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
bitb = b; bitk = k;
|
||||
_codec.AvailableBytesIn = n;
|
||||
_codec.TotalBytesIn += p - _codec.NextIn;
|
||||
_codec.NextIn = p;
|
||||
writeAt = q;
|
||||
return Flush(r);
|
||||
}
|
||||
|
||||
n--;
|
||||
b |= (_codec.InputBuffer[p++] & 0xff) << k;
|
||||
k += 8;
|
||||
}
|
||||
t = (int)(b & 7);
|
||||
last = t & 1;
|
||||
|
||||
switch ((uint)t >> 1)
|
||||
{
|
||||
case 0: // stored
|
||||
b >>= 3; k -= (3);
|
||||
t = k & 7; // go to byte boundary
|
||||
b >>= t; k -= t;
|
||||
mode = InflateBlockMode.LENS; // get length of stored block
|
||||
break;
|
||||
|
||||
case 1: // fixed
|
||||
int[] bl = new int[1];
|
||||
int[] bd = new int[1];
|
||||
int[][] tl = new int[1][];
|
||||
int[][] td = new int[1][];
|
||||
InfTree.inflate_trees_fixed(bl, bd, tl, td, _codec);
|
||||
codes.Init(bl[0], bd[0], tl[0], 0, td[0], 0);
|
||||
b >>= 3; k -= 3;
|
||||
mode = InflateBlockMode.CODES;
|
||||
break;
|
||||
|
||||
case 2: // dynamic
|
||||
b >>= 3; k -= 3;
|
||||
mode = InflateBlockMode.TABLE;
|
||||
break;
|
||||
|
||||
case 3: // illegal
|
||||
b >>= 3; k -= 3;
|
||||
mode = InflateBlockMode.BAD;
|
||||
_codec.Message = "invalid block type";
|
||||
r = ZlibConstants.Z_DATA_ERROR;
|
||||
bitb = b; bitk = k;
|
||||
_codec.AvailableBytesIn = n;
|
||||
_codec.TotalBytesIn += p - _codec.NextIn;
|
||||
_codec.NextIn = p;
|
||||
writeAt = q;
|
||||
return Flush(r);
|
||||
}
|
||||
break;
|
||||
|
||||
case InflateBlockMode.LENS:
|
||||
|
||||
while (k < (32))
|
||||
{
|
||||
if (n != 0)
|
||||
{
|
||||
r = ZlibConstants.Z_OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
bitb = b; bitk = k;
|
||||
_codec.AvailableBytesIn = n;
|
||||
_codec.TotalBytesIn += p - _codec.NextIn;
|
||||
_codec.NextIn = p;
|
||||
writeAt = q;
|
||||
return Flush(r);
|
||||
}
|
||||
;
|
||||
n--;
|
||||
b |= (_codec.InputBuffer[p++] & 0xff) << k;
|
||||
k += 8;
|
||||
}
|
||||
|
||||
if ((((~b) >> 16) & 0xffff) != (b & 0xffff))
|
||||
{
|
||||
mode = InflateBlockMode.BAD;
|
||||
_codec.Message = "invalid stored block lengths";
|
||||
r = ZlibConstants.Z_DATA_ERROR;
|
||||
|
||||
bitb = b; bitk = k;
|
||||
_codec.AvailableBytesIn = n;
|
||||
_codec.TotalBytesIn += p - _codec.NextIn;
|
||||
_codec.NextIn = p;
|
||||
writeAt = q;
|
||||
return Flush(r);
|
||||
}
|
||||
left = (b & 0xffff);
|
||||
b = k = 0; // dump bits
|
||||
mode = left != 0 ? InflateBlockMode.STORED : (last != 0 ? InflateBlockMode.DRY : InflateBlockMode.TYPE);
|
||||
break;
|
||||
|
||||
case InflateBlockMode.STORED:
|
||||
if (n == 0)
|
||||
{
|
||||
bitb = b; bitk = k;
|
||||
_codec.AvailableBytesIn = n;
|
||||
_codec.TotalBytesIn += p - _codec.NextIn;
|
||||
_codec.NextIn = p;
|
||||
writeAt = q;
|
||||
return Flush(r);
|
||||
}
|
||||
|
||||
if (m == 0)
|
||||
{
|
||||
if (q == end && readAt != 0)
|
||||
{
|
||||
q = 0; m = (int)(q < readAt ? readAt - q - 1 : end - q);
|
||||
}
|
||||
if (m == 0)
|
||||
{
|
||||
writeAt = q;
|
||||
r = Flush(r);
|
||||
q = writeAt; m = (int)(q < readAt ? readAt - q - 1 : end - q);
|
||||
if (q == end && readAt != 0)
|
||||
{
|
||||
q = 0; m = (int)(q < readAt ? readAt - q - 1 : end - q);
|
||||
}
|
||||
if (m == 0)
|
||||
{
|
||||
bitb = b; bitk = k;
|
||||
_codec.AvailableBytesIn = n;
|
||||
_codec.TotalBytesIn += p - _codec.NextIn;
|
||||
_codec.NextIn = p;
|
||||
writeAt = q;
|
||||
return Flush(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
r = ZlibConstants.Z_OK;
|
||||
|
||||
t = left;
|
||||
if (t > n)
|
||||
t = n;
|
||||
if (t > m)
|
||||
t = m;
|
||||
Array.Copy(_codec.InputBuffer, p, window, q, t);
|
||||
p += t; n -= t;
|
||||
q += t; m -= t;
|
||||
if ((left -= t) != 0)
|
||||
break;
|
||||
mode = last != 0 ? InflateBlockMode.DRY : InflateBlockMode.TYPE;
|
||||
break;
|
||||
|
||||
case InflateBlockMode.TABLE:
|
||||
|
||||
while (k < (14))
|
||||
{
|
||||
if (n != 0)
|
||||
{
|
||||
r = ZlibConstants.Z_OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
bitb = b; bitk = k;
|
||||
_codec.AvailableBytesIn = n;
|
||||
_codec.TotalBytesIn += p - _codec.NextIn;
|
||||
_codec.NextIn = p;
|
||||
writeAt = q;
|
||||
return Flush(r);
|
||||
}
|
||||
|
||||
n--;
|
||||
b |= (_codec.InputBuffer[p++] & 0xff) << k;
|
||||
k += 8;
|
||||
}
|
||||
|
||||
table = t = (b & 0x3fff);
|
||||
if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29)
|
||||
{
|
||||
mode = InflateBlockMode.BAD;
|
||||
_codec.Message = "too many length or distance symbols";
|
||||
r = ZlibConstants.Z_DATA_ERROR;
|
||||
|
||||
bitb = b; bitk = k;
|
||||
_codec.AvailableBytesIn = n;
|
||||
_codec.TotalBytesIn += p - _codec.NextIn;
|
||||
_codec.NextIn = p;
|
||||
writeAt = q;
|
||||
return Flush(r);
|
||||
}
|
||||
t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f);
|
||||
if (blens == null || blens.Length < t)
|
||||
{
|
||||
blens = new int[t];
|
||||
}
|
||||
else
|
||||
{
|
||||
Array.Clear(blens, 0, t);
|
||||
// for (int i = 0; i < t; i++)
|
||||
// {
|
||||
// blens[i] = 0;
|
||||
// }
|
||||
}
|
||||
|
||||
b >>= 14;
|
||||
k -= 14;
|
||||
|
||||
|
||||
index = 0;
|
||||
mode = InflateBlockMode.BTREE;
|
||||
goto case InflateBlockMode.BTREE;
|
||||
|
||||
case InflateBlockMode.BTREE:
|
||||
while (index < 4 + (table >> 10))
|
||||
{
|
||||
while (k < (3))
|
||||
{
|
||||
if (n != 0)
|
||||
{
|
||||
r = ZlibConstants.Z_OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
bitb = b; bitk = k;
|
||||
_codec.AvailableBytesIn = n;
|
||||
_codec.TotalBytesIn += p - _codec.NextIn;
|
||||
_codec.NextIn = p;
|
||||
writeAt = q;
|
||||
return Flush(r);
|
||||
}
|
||||
|
||||
n--;
|
||||
b |= (_codec.InputBuffer[p++] & 0xff) << k;
|
||||
k += 8;
|
||||
}
|
||||
|
||||
blens[border[index++]] = b & 7;
|
||||
|
||||
b >>= 3; k -= 3;
|
||||
}
|
||||
|
||||
while (index < 19)
|
||||
{
|
||||
blens[border[index++]] = 0;
|
||||
}
|
||||
|
||||
bb[0] = 7;
|
||||
t = inftree.inflate_trees_bits(blens, bb, tb, hufts, _codec);
|
||||
if (t != ZlibConstants.Z_OK)
|
||||
{
|
||||
r = t;
|
||||
if (r == ZlibConstants.Z_DATA_ERROR)
|
||||
{
|
||||
blens = null;
|
||||
mode = InflateBlockMode.BAD;
|
||||
}
|
||||
|
||||
bitb = b; bitk = k;
|
||||
_codec.AvailableBytesIn = n;
|
||||
_codec.TotalBytesIn += p - _codec.NextIn;
|
||||
_codec.NextIn = p;
|
||||
writeAt = q;
|
||||
return Flush(r);
|
||||
}
|
||||
|
||||
index = 0;
|
||||
mode = InflateBlockMode.DTREE;
|
||||
goto case InflateBlockMode.DTREE;
|
||||
|
||||
case InflateBlockMode.DTREE:
|
||||
while (true)
|
||||
{
|
||||
t = table;
|
||||
if (!(index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f)))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
int i, j, c;
|
||||
|
||||
t = bb[0];
|
||||
|
||||
while (k < t)
|
||||
{
|
||||
if (n != 0)
|
||||
{
|
||||
r = ZlibConstants.Z_OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
bitb = b; bitk = k;
|
||||
_codec.AvailableBytesIn = n;
|
||||
_codec.TotalBytesIn += p - _codec.NextIn;
|
||||
_codec.NextIn = p;
|
||||
writeAt = q;
|
||||
return Flush(r);
|
||||
}
|
||||
|
||||
n--;
|
||||
b |= (_codec.InputBuffer[p++] & 0xff) << k;
|
||||
k += 8;
|
||||
}
|
||||
|
||||
t = hufts[(tb[0] + (b & InternalInflateConstants.InflateMask[t])) * 3 + 1];
|
||||
c = hufts[(tb[0] + (b & InternalInflateConstants.InflateMask[t])) * 3 + 2];
|
||||
|
||||
if (c < 16)
|
||||
{
|
||||
b >>= t; k -= t;
|
||||
blens[index++] = c;
|
||||
}
|
||||
else
|
||||
{
|
||||
// c == 16..18
|
||||
i = c == 18 ? 7 : c - 14;
|
||||
j = c == 18 ? 11 : 3;
|
||||
|
||||
while (k < (t + i))
|
||||
{
|
||||
if (n != 0)
|
||||
{
|
||||
r = ZlibConstants.Z_OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
bitb = b; bitk = k;
|
||||
_codec.AvailableBytesIn = n;
|
||||
_codec.TotalBytesIn += p - _codec.NextIn;
|
||||
_codec.NextIn = p;
|
||||
writeAt = q;
|
||||
return Flush(r);
|
||||
}
|
||||
|
||||
n--;
|
||||
b |= (_codec.InputBuffer[p++] & 0xff) << k;
|
||||
k += 8;
|
||||
}
|
||||
|
||||
b >>= t; k -= t;
|
||||
|
||||
j += (b & InternalInflateConstants.InflateMask[i]);
|
||||
|
||||
b >>= i; k -= i;
|
||||
|
||||
i = index;
|
||||
t = table;
|
||||
if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || (c == 16 && i < 1))
|
||||
{
|
||||
blens = null;
|
||||
mode = InflateBlockMode.BAD;
|
||||
_codec.Message = "invalid bit length repeat";
|
||||
r = ZlibConstants.Z_DATA_ERROR;
|
||||
|
||||
bitb = b; bitk = k;
|
||||
_codec.AvailableBytesIn = n;
|
||||
_codec.TotalBytesIn += p - _codec.NextIn;
|
||||
_codec.NextIn = p;
|
||||
writeAt = q;
|
||||
return Flush(r);
|
||||
}
|
||||
|
||||
c = (c == 16) ? blens[i - 1] : 0;
|
||||
do
|
||||
{
|
||||
blens[i++] = c;
|
||||
}
|
||||
while (--j != 0);
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
|
||||
tb[0] = -1;
|
||||
{
|
||||
int[] bl = [9]; // must be <= 9 for lookahead assumptions
|
||||
int[] bd = [6]; // must be <= 9 for lookahead assumptions
|
||||
int[] tl = new int[1];
|
||||
int[] td = new int[1];
|
||||
|
||||
t = table;
|
||||
t = inftree.inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f), blens, bl, bd, tl, td, hufts, _codec);
|
||||
|
||||
if (t != ZlibConstants.Z_OK)
|
||||
{
|
||||
if (t == ZlibConstants.Z_DATA_ERROR)
|
||||
{
|
||||
blens = null;
|
||||
mode = InflateBlockMode.BAD;
|
||||
}
|
||||
r = t;
|
||||
|
||||
bitb = b; bitk = k;
|
||||
_codec.AvailableBytesIn = n;
|
||||
_codec.TotalBytesIn += p - _codec.NextIn;
|
||||
_codec.NextIn = p;
|
||||
writeAt = q;
|
||||
return Flush(r);
|
||||
}
|
||||
codes.Init(bl[0], bd[0], hufts, tl[0], hufts, td[0]);
|
||||
}
|
||||
mode = InflateBlockMode.CODES;
|
||||
goto case InflateBlockMode.CODES;
|
||||
|
||||
case InflateBlockMode.CODES:
|
||||
bitb = b; bitk = k;
|
||||
_codec.AvailableBytesIn = n;
|
||||
_codec.TotalBytesIn += p - _codec.NextIn;
|
||||
_codec.NextIn = p;
|
||||
writeAt = q;
|
||||
|
||||
r = codes.Process(this, r);
|
||||
if (r != ZlibConstants.Z_STREAM_END)
|
||||
{
|
||||
return Flush(r);
|
||||
}
|
||||
|
||||
r = ZlibConstants.Z_OK;
|
||||
p = _codec.NextIn;
|
||||
n = _codec.AvailableBytesIn;
|
||||
b = bitb;
|
||||
k = bitk;
|
||||
q = writeAt;
|
||||
m = (int)(q < readAt ? readAt - q - 1 : end - q);
|
||||
|
||||
if (last == 0)
|
||||
{
|
||||
mode = InflateBlockMode.TYPE;
|
||||
break;
|
||||
}
|
||||
mode = InflateBlockMode.DRY;
|
||||
goto case InflateBlockMode.DRY;
|
||||
|
||||
case InflateBlockMode.DRY:
|
||||
writeAt = q;
|
||||
r = Flush(r);
|
||||
q = writeAt; m = (int)(q < readAt ? readAt - q - 1 : end - q);
|
||||
if (readAt != writeAt)
|
||||
{
|
||||
bitb = b; bitk = k;
|
||||
_codec.AvailableBytesIn = n;
|
||||
_codec.TotalBytesIn += p - _codec.NextIn;
|
||||
_codec.NextIn = p;
|
||||
writeAt = q;
|
||||
return Flush(r);
|
||||
}
|
||||
mode = InflateBlockMode.DONE;
|
||||
goto case InflateBlockMode.DONE;
|
||||
|
||||
case InflateBlockMode.DONE:
|
||||
r = ZlibConstants.Z_STREAM_END;
|
||||
bitb = b;
|
||||
bitk = k;
|
||||
_codec.AvailableBytesIn = n;
|
||||
_codec.TotalBytesIn += p - _codec.NextIn;
|
||||
_codec.NextIn = p;
|
||||
writeAt = q;
|
||||
return Flush(r);
|
||||
|
||||
case InflateBlockMode.BAD:
|
||||
r = ZlibConstants.Z_DATA_ERROR;
|
||||
|
||||
bitb = b; bitk = k;
|
||||
_codec.AvailableBytesIn = n;
|
||||
_codec.TotalBytesIn += p - _codec.NextIn;
|
||||
_codec.NextIn = p;
|
||||
writeAt = q;
|
||||
return Flush(r);
|
||||
|
||||
|
||||
default:
|
||||
r = ZlibConstants.Z_STREAM_ERROR;
|
||||
|
||||
bitb = b; bitk = k;
|
||||
_codec.AvailableBytesIn = n;
|
||||
_codec.TotalBytesIn += p - _codec.NextIn;
|
||||
_codec.NextIn = p;
|
||||
writeAt = q;
|
||||
return Flush(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal void Free()
|
||||
{
|
||||
Reset();
|
||||
window = null;
|
||||
hufts = null;
|
||||
}
|
||||
|
||||
internal void SetDictionary(byte[] d, int start, int n)
|
||||
{
|
||||
Array.Copy(d, start, window, 0, n);
|
||||
readAt = writeAt = n;
|
||||
}
|
||||
|
||||
// Returns true if inflate is currently at the end of a block generated
|
||||
// by Z_SYNC_FLUSH or Z_FULL_FLUSH.
|
||||
internal int SyncPoint()
|
||||
{
|
||||
return mode == InflateBlockMode.LENS ? 1 : 0;
|
||||
}
|
||||
|
||||
// copy as much as possible from the sliding window to the output area
|
||||
internal int Flush(int r)
|
||||
{
|
||||
int nBytes;
|
||||
|
||||
for (int pass = 0; pass < 2; pass++)
|
||||
{
|
||||
if (pass == 0)
|
||||
{
|
||||
// compute number of bytes to copy as far as end of window
|
||||
nBytes = (int)((readAt <= writeAt ? writeAt : end) - readAt);
|
||||
}
|
||||
else
|
||||
{
|
||||
// compute bytes to copy
|
||||
nBytes = writeAt - readAt;
|
||||
}
|
||||
|
||||
// workitem 8870
|
||||
if (nBytes == 0)
|
||||
{
|
||||
if (r == ZlibConstants.Z_BUF_ERROR)
|
||||
r = ZlibConstants.Z_OK;
|
||||
return r;
|
||||
}
|
||||
|
||||
if (nBytes > _codec.AvailableBytesOut)
|
||||
nBytes = _codec.AvailableBytesOut;
|
||||
|
||||
if (nBytes != 0 && r == ZlibConstants.Z_BUF_ERROR)
|
||||
r = ZlibConstants.Z_OK;
|
||||
|
||||
// update counters
|
||||
_codec.AvailableBytesOut -= nBytes;
|
||||
_codec.TotalBytesOut += nBytes;
|
||||
|
||||
// update check information
|
||||
if (checkfn != null)
|
||||
_codec._Adler32 = check = Adler.Adler32(check, window, readAt, nBytes);
|
||||
|
||||
// copy as far as end of window
|
||||
Array.Copy(window, readAt, _codec.OutputBuffer, _codec.NextOut, nBytes);
|
||||
_codec.NextOut += nBytes;
|
||||
readAt += nBytes;
|
||||
|
||||
// see if more to copy at beginning of window
|
||||
if (readAt == end && pass == 0)
|
||||
{
|
||||
// wrap pointers
|
||||
readAt = 0;
|
||||
if (writeAt == end)
|
||||
writeAt = 0;
|
||||
}
|
||||
else pass++;
|
||||
}
|
||||
|
||||
// done
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
723
SabreTools.IO/Compression/Deflate/InflateCodes.cs
Normal file
723
SabreTools.IO/Compression/Deflate/InflateCodes.cs
Normal file
@@ -0,0 +1,723 @@
|
||||
// Inflate.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009 Dino Chiesa and Microsoft Corporation.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2010-January-08 18:32:12>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines classes for decompression. This code is derived
|
||||
// from the jzlib implementation of zlib, but significantly modified.
|
||||
// The object model is not the same, and many of the behaviors are
|
||||
// different. Nonetheless, in keeping with the license for jzlib, I am
|
||||
// reproducing the copyright to that code here.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in
|
||||
// the documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. The names of the authors may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
// INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
// This program is based on zlib-1.1.3; credit to authors
|
||||
// Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu)
|
||||
// and contributors of zlib.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
|
||||
#nullable disable
|
||||
namespace SabreTools.IO.Compression.Deflate
|
||||
{
|
||||
sealed class InflateCodes
|
||||
{
|
||||
// waiting for "i:"=input,
|
||||
// "o:"=output,
|
||||
// "x:"=nothing
|
||||
private const int START = 0; // x: set up for LEN
|
||||
private const int LEN = 1; // i: get length/literal/eob next
|
||||
private const int LENEXT = 2; // i: getting length extra (have base)
|
||||
private const int DIST = 3; // i: get distance next
|
||||
private const int DISTEXT = 4; // i: getting distance extra
|
||||
private const int COPY = 5; // o: copying bytes in window, waiting for space
|
||||
private const int LIT = 6; // o: got literal, waiting for output space
|
||||
private const int WASH = 7; // o: got eob, possibly still output waiting
|
||||
private const int END = 8; // x: got eob and all data flushed
|
||||
private const int BADCODE = 9; // x: got error
|
||||
|
||||
internal int mode; // current inflate_codes mode
|
||||
|
||||
// mode dependent information
|
||||
internal int len;
|
||||
|
||||
internal int[] tree; // pointer into tree
|
||||
internal int tree_index = 0;
|
||||
internal int need; // bits needed
|
||||
|
||||
internal int lit;
|
||||
|
||||
// if EXT or COPY, where and how much
|
||||
internal int bitsToGet; // bits to get for extra
|
||||
internal int dist; // distance back to copy from
|
||||
|
||||
internal byte lbits; // ltree bits decoded per branch
|
||||
internal byte dbits; // dtree bits decoder per branch
|
||||
internal int[] ltree; // literal/length/eob tree
|
||||
internal int ltree_index; // literal/length/eob tree
|
||||
internal int[] dtree; // distance tree
|
||||
internal int dtree_index; // distance tree
|
||||
|
||||
internal InflateCodes()
|
||||
{
|
||||
}
|
||||
|
||||
internal void Init(int bl, int bd, int[] tl, int tl_index, int[] td, int td_index)
|
||||
{
|
||||
mode = START;
|
||||
lbits = (byte)bl;
|
||||
dbits = (byte)bd;
|
||||
ltree = tl;
|
||||
ltree_index = tl_index;
|
||||
dtree = td;
|
||||
dtree_index = td_index;
|
||||
tree = null;
|
||||
}
|
||||
|
||||
internal int Process(InflateBlocks blocks, int r)
|
||||
{
|
||||
int j; // temporary storage
|
||||
int tindex; // temporary pointer
|
||||
int e; // extra bits or operation
|
||||
int b = 0; // bit buffer
|
||||
int k = 0; // bits in bit buffer
|
||||
int p = 0; // input data pointer
|
||||
int n; // bytes available there
|
||||
int q; // output window write pointer
|
||||
int m; // bytes to end of window or read pointer
|
||||
int f; // pointer to copy strings from
|
||||
|
||||
ZlibCodec z = blocks._codec;
|
||||
|
||||
// copy input/output information to locals (UPDATE macro restores)
|
||||
p = z.NextIn;
|
||||
n = z.AvailableBytesIn;
|
||||
b = blocks.bitb;
|
||||
k = blocks.bitk;
|
||||
q = blocks.writeAt; m = q < blocks.readAt ? blocks.readAt - q - 1 : blocks.end - q;
|
||||
|
||||
// process input and output based on current state
|
||||
while (true)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
// waiting for "i:"=input, "o:"=output, "x:"=nothing
|
||||
case START: // x: set up for LEN
|
||||
if (m >= 258 && n >= 10)
|
||||
{
|
||||
blocks.bitb = b; blocks.bitk = k;
|
||||
z.AvailableBytesIn = n;
|
||||
z.TotalBytesIn += p - z.NextIn;
|
||||
z.NextIn = p;
|
||||
blocks.writeAt = q;
|
||||
r = InflateFast(lbits, dbits, ltree, ltree_index, dtree, dtree_index, blocks, z);
|
||||
|
||||
p = z.NextIn;
|
||||
n = z.AvailableBytesIn;
|
||||
b = blocks.bitb;
|
||||
k = blocks.bitk;
|
||||
q = blocks.writeAt; m = q < blocks.readAt ? blocks.readAt - q - 1 : blocks.end - q;
|
||||
|
||||
if (r != ZlibConstants.Z_OK)
|
||||
{
|
||||
mode = (r == ZlibConstants.Z_STREAM_END) ? WASH : BADCODE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
need = lbits;
|
||||
tree = ltree;
|
||||
tree_index = ltree_index;
|
||||
|
||||
mode = LEN;
|
||||
goto case LEN;
|
||||
|
||||
case LEN: // i: get length/literal/eob next
|
||||
j = need;
|
||||
|
||||
while (k < j)
|
||||
{
|
||||
if (n != 0)
|
||||
r = ZlibConstants.Z_OK;
|
||||
else
|
||||
{
|
||||
blocks.bitb = b; blocks.bitk = k;
|
||||
z.AvailableBytesIn = n;
|
||||
z.TotalBytesIn += p - z.NextIn;
|
||||
z.NextIn = p;
|
||||
blocks.writeAt = q;
|
||||
return blocks.Flush(r);
|
||||
}
|
||||
n--;
|
||||
b |= (z.InputBuffer[p++] & 0xff) << k;
|
||||
k += 8;
|
||||
}
|
||||
|
||||
tindex = (tree_index + (b & InternalInflateConstants.InflateMask[j])) * 3;
|
||||
|
||||
b >>= (tree[tindex + 1]);
|
||||
k -= (tree[tindex + 1]);
|
||||
|
||||
e = tree[tindex];
|
||||
|
||||
if (e == 0)
|
||||
{
|
||||
// literal
|
||||
lit = tree[tindex + 2];
|
||||
mode = LIT;
|
||||
break;
|
||||
}
|
||||
if ((e & 16) != 0)
|
||||
{
|
||||
// length
|
||||
bitsToGet = e & 15;
|
||||
len = tree[tindex + 2];
|
||||
mode = LENEXT;
|
||||
break;
|
||||
}
|
||||
if ((e & 64) == 0)
|
||||
{
|
||||
// next table
|
||||
need = e;
|
||||
tree_index = tindex / 3 + tree[tindex + 2];
|
||||
break;
|
||||
}
|
||||
if ((e & 32) != 0)
|
||||
{
|
||||
// end of block
|
||||
mode = WASH;
|
||||
break;
|
||||
}
|
||||
mode = BADCODE; // invalid code
|
||||
z.Message = "invalid literal/length code";
|
||||
r = ZlibConstants.Z_DATA_ERROR;
|
||||
|
||||
blocks.bitb = b; blocks.bitk = k;
|
||||
z.AvailableBytesIn = n;
|
||||
z.TotalBytesIn += p - z.NextIn;
|
||||
z.NextIn = p;
|
||||
blocks.writeAt = q;
|
||||
return blocks.Flush(r);
|
||||
|
||||
|
||||
case LENEXT: // i: getting length extra (have base)
|
||||
j = bitsToGet;
|
||||
|
||||
while (k < j)
|
||||
{
|
||||
if (n != 0)
|
||||
r = ZlibConstants.Z_OK;
|
||||
else
|
||||
{
|
||||
blocks.bitb = b; blocks.bitk = k;
|
||||
z.AvailableBytesIn = n; z.TotalBytesIn += p - z.NextIn; z.NextIn = p;
|
||||
blocks.writeAt = q;
|
||||
return blocks.Flush(r);
|
||||
}
|
||||
n--; b |= (z.InputBuffer[p++] & 0xff) << k;
|
||||
k += 8;
|
||||
}
|
||||
|
||||
len += (b & InternalInflateConstants.InflateMask[j]);
|
||||
|
||||
b >>= j;
|
||||
k -= j;
|
||||
|
||||
need = dbits;
|
||||
tree = dtree;
|
||||
tree_index = dtree_index;
|
||||
mode = DIST;
|
||||
goto case DIST;
|
||||
|
||||
case DIST: // i: get distance next
|
||||
j = need;
|
||||
|
||||
while (k < j)
|
||||
{
|
||||
if (n != 0)
|
||||
r = ZlibConstants.Z_OK;
|
||||
else
|
||||
{
|
||||
blocks.bitb = b; blocks.bitk = k;
|
||||
z.AvailableBytesIn = n; z.TotalBytesIn += p - z.NextIn; z.NextIn = p;
|
||||
blocks.writeAt = q;
|
||||
return blocks.Flush(r);
|
||||
}
|
||||
n--; b |= (z.InputBuffer[p++] & 0xff) << k;
|
||||
k += 8;
|
||||
}
|
||||
|
||||
tindex = (tree_index + (b & InternalInflateConstants.InflateMask[j])) * 3;
|
||||
|
||||
b >>= tree[tindex + 1];
|
||||
k -= tree[tindex + 1];
|
||||
|
||||
e = (tree[tindex]);
|
||||
if ((e & 0x10) != 0)
|
||||
{
|
||||
// distance
|
||||
bitsToGet = e & 15;
|
||||
dist = tree[tindex + 2];
|
||||
mode = DISTEXT;
|
||||
break;
|
||||
}
|
||||
if ((e & 64) == 0)
|
||||
{
|
||||
// next table
|
||||
need = e;
|
||||
tree_index = tindex / 3 + tree[tindex + 2];
|
||||
break;
|
||||
}
|
||||
mode = BADCODE; // invalid code
|
||||
z.Message = "invalid distance code";
|
||||
r = ZlibConstants.Z_DATA_ERROR;
|
||||
|
||||
blocks.bitb = b; blocks.bitk = k;
|
||||
z.AvailableBytesIn = n; z.TotalBytesIn += p - z.NextIn; z.NextIn = p;
|
||||
blocks.writeAt = q;
|
||||
return blocks.Flush(r);
|
||||
|
||||
|
||||
case DISTEXT: // i: getting distance extra
|
||||
j = bitsToGet;
|
||||
|
||||
while (k < j)
|
||||
{
|
||||
if (n != 0)
|
||||
r = ZlibConstants.Z_OK;
|
||||
else
|
||||
{
|
||||
blocks.bitb = b; blocks.bitk = k;
|
||||
z.AvailableBytesIn = n; z.TotalBytesIn += p - z.NextIn; z.NextIn = p;
|
||||
blocks.writeAt = q;
|
||||
return blocks.Flush(r);
|
||||
}
|
||||
n--; b |= (z.InputBuffer[p++] & 0xff) << k;
|
||||
k += 8;
|
||||
}
|
||||
|
||||
dist += (b & InternalInflateConstants.InflateMask[j]);
|
||||
|
||||
b >>= j;
|
||||
k -= j;
|
||||
|
||||
mode = COPY;
|
||||
goto case COPY;
|
||||
|
||||
case COPY: // o: copying bytes in window, waiting for space
|
||||
f = q - dist;
|
||||
while (f < 0)
|
||||
{
|
||||
// modulo window size-"while" instead
|
||||
f += blocks.end; // of "if" handles invalid distances
|
||||
}
|
||||
while (len != 0)
|
||||
{
|
||||
if (m == 0)
|
||||
{
|
||||
if (q == blocks.end && blocks.readAt != 0)
|
||||
{
|
||||
q = 0; m = q < blocks.readAt ? blocks.readAt - q - 1 : blocks.end - q;
|
||||
}
|
||||
if (m == 0)
|
||||
{
|
||||
blocks.writeAt = q; r = blocks.Flush(r);
|
||||
q = blocks.writeAt; m = q < blocks.readAt ? blocks.readAt - q - 1 : blocks.end - q;
|
||||
|
||||
if (q == blocks.end && blocks.readAt != 0)
|
||||
{
|
||||
q = 0; m = q < blocks.readAt ? blocks.readAt - q - 1 : blocks.end - q;
|
||||
}
|
||||
|
||||
if (m == 0)
|
||||
{
|
||||
blocks.bitb = b; blocks.bitk = k;
|
||||
z.AvailableBytesIn = n;
|
||||
z.TotalBytesIn += p - z.NextIn;
|
||||
z.NextIn = p;
|
||||
blocks.writeAt = q;
|
||||
return blocks.Flush(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
blocks.window[q++] = blocks.window[f++]; m--;
|
||||
|
||||
if (f == blocks.end)
|
||||
f = 0;
|
||||
len--;
|
||||
}
|
||||
mode = START;
|
||||
break;
|
||||
|
||||
case LIT: // o: got literal, waiting for output space
|
||||
if (m == 0)
|
||||
{
|
||||
if (q == blocks.end && blocks.readAt != 0)
|
||||
{
|
||||
q = 0; m = q < blocks.readAt ? blocks.readAt - q - 1 : blocks.end - q;
|
||||
}
|
||||
if (m == 0)
|
||||
{
|
||||
blocks.writeAt = q; r = blocks.Flush(r);
|
||||
q = blocks.writeAt; m = q < blocks.readAt ? blocks.readAt - q - 1 : blocks.end - q;
|
||||
|
||||
if (q == blocks.end && blocks.readAt != 0)
|
||||
{
|
||||
q = 0; m = q < blocks.readAt ? blocks.readAt - q - 1 : blocks.end - q;
|
||||
}
|
||||
if (m == 0)
|
||||
{
|
||||
blocks.bitb = b; blocks.bitk = k;
|
||||
z.AvailableBytesIn = n; z.TotalBytesIn += p - z.NextIn; z.NextIn = p;
|
||||
blocks.writeAt = q;
|
||||
return blocks.Flush(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
r = ZlibConstants.Z_OK;
|
||||
|
||||
blocks.window[q++] = (byte)lit; m--;
|
||||
|
||||
mode = START;
|
||||
break;
|
||||
|
||||
case WASH: // o: got eob, possibly more output
|
||||
if (k > 7)
|
||||
{
|
||||
// return unused byte, if any
|
||||
k -= 8;
|
||||
n++;
|
||||
p--; // can always return one
|
||||
}
|
||||
|
||||
blocks.writeAt = q; r = blocks.Flush(r);
|
||||
q = blocks.writeAt; m = q < blocks.readAt ? blocks.readAt - q - 1 : blocks.end - q;
|
||||
|
||||
if (blocks.readAt != blocks.writeAt)
|
||||
{
|
||||
blocks.bitb = b; blocks.bitk = k;
|
||||
z.AvailableBytesIn = n; z.TotalBytesIn += p - z.NextIn; z.NextIn = p;
|
||||
blocks.writeAt = q;
|
||||
return blocks.Flush(r);
|
||||
}
|
||||
mode = END;
|
||||
goto case END;
|
||||
|
||||
case END:
|
||||
r = ZlibConstants.Z_STREAM_END;
|
||||
blocks.bitb = b; blocks.bitk = k;
|
||||
z.AvailableBytesIn = n; z.TotalBytesIn += p - z.NextIn; z.NextIn = p;
|
||||
blocks.writeAt = q;
|
||||
return blocks.Flush(r);
|
||||
|
||||
case BADCODE: // x: got error
|
||||
|
||||
r = ZlibConstants.Z_DATA_ERROR;
|
||||
|
||||
blocks.bitb = b; blocks.bitk = k;
|
||||
z.AvailableBytesIn = n; z.TotalBytesIn += p - z.NextIn; z.NextIn = p;
|
||||
blocks.writeAt = q;
|
||||
return blocks.Flush(r);
|
||||
|
||||
default:
|
||||
r = ZlibConstants.Z_STREAM_ERROR;
|
||||
|
||||
blocks.bitb = b; blocks.bitk = k;
|
||||
z.AvailableBytesIn = n; z.TotalBytesIn += p - z.NextIn; z.NextIn = p;
|
||||
blocks.writeAt = q;
|
||||
return blocks.Flush(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Called with number of bytes left to write in window at least 258
|
||||
// (the maximum string length) and number of input bytes available
|
||||
// at least ten. The ten bytes are six bytes for the longest length/
|
||||
// distance pair plus four bytes for overloading the bit buffer.
|
||||
|
||||
internal int InflateFast(int bl, int bd, int[] tl, int tl_index, int[] td, int td_index, InflateBlocks s, ZlibCodec z)
|
||||
{
|
||||
int t; // temporary pointer
|
||||
int[] tp; // temporary pointer
|
||||
int tp_index; // temporary pointer
|
||||
int e; // extra bits or operation
|
||||
int b; // bit buffer
|
||||
int k; // bits in bit buffer
|
||||
int p; // input data pointer
|
||||
int n; // bytes available there
|
||||
int q; // output window write pointer
|
||||
int m; // bytes to end of window or read pointer
|
||||
int ml; // mask for literal/length tree
|
||||
int md; // mask for distance tree
|
||||
int c; // bytes to copy
|
||||
int d; // distance back to copy from
|
||||
int r; // copy source pointer
|
||||
|
||||
int tp_index_t_3; // (tp_index+t)*3
|
||||
|
||||
// load input, output, bit values
|
||||
p = z.NextIn; n = z.AvailableBytesIn; b = s.bitb; k = s.bitk;
|
||||
q = s.writeAt; m = q < s.readAt ? s.readAt - q - 1 : s.end - q;
|
||||
|
||||
// initialize masks
|
||||
ml = InternalInflateConstants.InflateMask[bl];
|
||||
md = InternalInflateConstants.InflateMask[bd];
|
||||
|
||||
// do until not enough input or output space for fast loop
|
||||
do
|
||||
{
|
||||
// assume called with m >= 258 && n >= 10
|
||||
// get literal/length code
|
||||
while (k < (20))
|
||||
{
|
||||
// max bits for literal/length code
|
||||
n--;
|
||||
b |= (z.InputBuffer[p++] & 0xff) << k; k += 8;
|
||||
}
|
||||
|
||||
t = b & ml;
|
||||
tp = tl;
|
||||
tp_index = tl_index;
|
||||
tp_index_t_3 = (tp_index + t) * 3;
|
||||
if ((e = tp[tp_index_t_3]) == 0)
|
||||
{
|
||||
b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]);
|
||||
|
||||
s.window[q++] = (byte)tp[tp_index_t_3 + 2];
|
||||
m--;
|
||||
continue;
|
||||
}
|
||||
do
|
||||
{
|
||||
|
||||
b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]);
|
||||
|
||||
if ((e & 16) != 0)
|
||||
{
|
||||
e &= 15;
|
||||
c = tp[tp_index_t_3 + 2] + ((int)b & InternalInflateConstants.InflateMask[e]);
|
||||
|
||||
b >>= e; k -= e;
|
||||
|
||||
// decode distance base of block to copy
|
||||
while (k < 15)
|
||||
{
|
||||
// max bits for distance code
|
||||
n--;
|
||||
b |= (z.InputBuffer[p++] & 0xff) << k; k += 8;
|
||||
}
|
||||
|
||||
t = b & md;
|
||||
tp = td;
|
||||
tp_index = td_index;
|
||||
tp_index_t_3 = (tp_index + t) * 3;
|
||||
e = tp[tp_index_t_3];
|
||||
|
||||
do
|
||||
{
|
||||
|
||||
b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]);
|
||||
|
||||
if ((e & 16) != 0)
|
||||
{
|
||||
// get extra bits to add to distance base
|
||||
e &= 15;
|
||||
while (k < e)
|
||||
{
|
||||
// get extra bits (up to 13)
|
||||
n--;
|
||||
b |= (z.InputBuffer[p++] & 0xff) << k; k += 8;
|
||||
}
|
||||
|
||||
d = tp[tp_index_t_3 + 2] + (b & InternalInflateConstants.InflateMask[e]);
|
||||
|
||||
b >>= e; k -= e;
|
||||
|
||||
// do the copy
|
||||
m -= c;
|
||||
if (q >= d)
|
||||
{
|
||||
// offset before dest
|
||||
// just copy
|
||||
r = q - d;
|
||||
if (q - r > 0 && 2 > (q - r))
|
||||
{
|
||||
s.window[q++] = s.window[r++]; // minimum count is three,
|
||||
s.window[q++] = s.window[r++]; // so unroll loop a little
|
||||
c -= 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
Array.Copy(s.window, r, s.window, q, 2);
|
||||
q += 2; r += 2; c -= 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// else offset after destination
|
||||
r = q - d;
|
||||
do
|
||||
{
|
||||
r += s.end; // force pointer in window
|
||||
}
|
||||
while (r < 0); // covers invalid distances
|
||||
e = s.end - r;
|
||||
if (c > e)
|
||||
{
|
||||
// if source crosses,
|
||||
c -= e; // wrapped copy
|
||||
if (q - r > 0 && e > (q - r))
|
||||
{
|
||||
do
|
||||
{
|
||||
s.window[q++] = s.window[r++];
|
||||
}
|
||||
while (--e != 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
Array.Copy(s.window, r, s.window, q, e);
|
||||
q += e; r += e; e = 0;
|
||||
}
|
||||
r = 0; // copy rest from start of window
|
||||
}
|
||||
}
|
||||
|
||||
// copy all or what's left
|
||||
if (q - r > 0 && c > (q - r))
|
||||
{
|
||||
do
|
||||
{
|
||||
s.window[q++] = s.window[r++];
|
||||
}
|
||||
while (--c != 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
Array.Copy(s.window, r, s.window, q, c);
|
||||
q += c; r += c; c = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else if ((e & 64) == 0)
|
||||
{
|
||||
t += tp[tp_index_t_3 + 2];
|
||||
t += (b & InternalInflateConstants.InflateMask[e]);
|
||||
tp_index_t_3 = (tp_index + t) * 3;
|
||||
e = tp[tp_index_t_3];
|
||||
}
|
||||
else
|
||||
{
|
||||
z.Message = "invalid distance code";
|
||||
|
||||
c = z.AvailableBytesIn - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= (c << 3);
|
||||
|
||||
s.bitb = b; s.bitk = k;
|
||||
z.AvailableBytesIn = n; z.TotalBytesIn += p - z.NextIn; z.NextIn = p;
|
||||
s.writeAt = q;
|
||||
|
||||
return ZlibConstants.Z_DATA_ERROR;
|
||||
}
|
||||
}
|
||||
while (true);
|
||||
break;
|
||||
}
|
||||
|
||||
if ((e & 64) == 0)
|
||||
{
|
||||
t += tp[tp_index_t_3 + 2];
|
||||
t += (b & InternalInflateConstants.InflateMask[e]);
|
||||
tp_index_t_3 = (tp_index + t) * 3;
|
||||
if ((e = tp[tp_index_t_3]) == 0)
|
||||
{
|
||||
b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]);
|
||||
s.window[q++] = (byte)tp[tp_index_t_3 + 2];
|
||||
m--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if ((e & 32) != 0)
|
||||
{
|
||||
c = z.AvailableBytesIn - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= (c << 3);
|
||||
|
||||
s.bitb = b; s.bitk = k;
|
||||
z.AvailableBytesIn = n; z.TotalBytesIn += p - z.NextIn; z.NextIn = p;
|
||||
s.writeAt = q;
|
||||
|
||||
return ZlibConstants.Z_STREAM_END;
|
||||
}
|
||||
else
|
||||
{
|
||||
z.Message = "invalid literal/length code";
|
||||
|
||||
c = z.AvailableBytesIn - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= (c << 3);
|
||||
|
||||
s.bitb = b; s.bitk = k;
|
||||
z.AvailableBytesIn = n; z.TotalBytesIn += p - z.NextIn; z.NextIn = p;
|
||||
s.writeAt = q;
|
||||
|
||||
return ZlibConstants.Z_DATA_ERROR;
|
||||
}
|
||||
}
|
||||
while (true);
|
||||
}
|
||||
while (m >= 258 && n >= 10);
|
||||
|
||||
// not enough input or output--restore pointers and return
|
||||
c = z.AvailableBytesIn - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= (c << 3);
|
||||
|
||||
s.bitb = b; s.bitk = k;
|
||||
z.AvailableBytesIn = n; z.TotalBytesIn += p - z.NextIn; z.NextIn = p;
|
||||
s.writeAt = q;
|
||||
|
||||
return ZlibConstants.Z_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
464
SabreTools.IO/Compression/Deflate/InflateManager.cs
Normal file
464
SabreTools.IO/Compression/Deflate/InflateManager.cs
Normal file
@@ -0,0 +1,464 @@
|
||||
// Inflate.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009 Dino Chiesa and Microsoft Corporation.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2010-January-08 18:32:12>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines classes for decompression. This code is derived
|
||||
// from the jzlib implementation of zlib, but significantly modified.
|
||||
// The object model is not the same, and many of the behaviors are
|
||||
// different. Nonetheless, in keeping with the license for jzlib, I am
|
||||
// reproducing the copyright to that code here.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in
|
||||
// the documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. The names of the authors may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
// INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
// This program is based on zlib-1.1.3; credit to authors
|
||||
// Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu)
|
||||
// and contributors of zlib.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
|
||||
using System;
|
||||
|
||||
#nullable disable
|
||||
namespace SabreTools.IO.Compression.Deflate
|
||||
{
|
||||
internal sealed class InflateManager
|
||||
{
|
||||
// preset dictionary flag in zlib header
|
||||
private const int PRESET_DICT = 0x20;
|
||||
|
||||
private const int Z_DEFLATED = 8;
|
||||
|
||||
private enum InflateManagerMode
|
||||
{
|
||||
METHOD = 0, // waiting for method byte
|
||||
FLAG = 1, // waiting for flag byte
|
||||
DICT4 = 2, // four dictionary check bytes to go
|
||||
DICT3 = 3, // three dictionary check bytes to go
|
||||
DICT2 = 4, // two dictionary check bytes to go
|
||||
DICT1 = 5, // one dictionary check byte to go
|
||||
DICT0 = 6, // waiting for inflateSetDictionary
|
||||
BLOCKS = 7, // decompressing blocks
|
||||
CHECK4 = 8, // four check bytes to go
|
||||
CHECK3 = 9, // three check bytes to go
|
||||
CHECK2 = 10, // two check bytes to go
|
||||
CHECK1 = 11, // one check byte to go
|
||||
DONE = 12, // finished check, done
|
||||
BAD = 13, // got an error--stay here
|
||||
}
|
||||
|
||||
private InflateManagerMode mode; // current inflate mode
|
||||
internal ZlibCodec _codec; // pointer back to this zlib stream
|
||||
|
||||
// mode dependent information
|
||||
internal int method; // if FLAGS, method byte
|
||||
|
||||
// if CHECK, check values to compare
|
||||
internal uint computedCheck; // computed check value
|
||||
internal uint expectedCheck; // stream check value
|
||||
|
||||
// if BAD, inflateSync's marker bytes count
|
||||
internal int marker;
|
||||
|
||||
// mode independent information
|
||||
//internal int nowrap; // flag for no wrapper
|
||||
private bool _handleRfc1950HeaderBytes = true;
|
||||
internal bool HandleRfc1950HeaderBytes
|
||||
{
|
||||
get { return _handleRfc1950HeaderBytes; }
|
||||
set { _handleRfc1950HeaderBytes = value; }
|
||||
}
|
||||
internal int wbits; // log2(window size) (8..15, defaults to 15)
|
||||
|
||||
internal InflateBlocks blocks; // current inflate_blocks state
|
||||
|
||||
public InflateManager() { }
|
||||
|
||||
public InflateManager(bool expectRfc1950HeaderBytes)
|
||||
{
|
||||
_handleRfc1950HeaderBytes = expectRfc1950HeaderBytes;
|
||||
}
|
||||
|
||||
internal int Reset()
|
||||
{
|
||||
_codec.TotalBytesIn = _codec.TotalBytesOut = 0;
|
||||
_codec.Message = null;
|
||||
mode = HandleRfc1950HeaderBytes ? InflateManagerMode.METHOD : InflateManagerMode.BLOCKS;
|
||||
blocks.Reset();
|
||||
return ZlibConstants.Z_OK;
|
||||
}
|
||||
|
||||
internal int End()
|
||||
{
|
||||
blocks?.Free();
|
||||
blocks = null;
|
||||
return ZlibConstants.Z_OK;
|
||||
}
|
||||
|
||||
internal int Initialize(ZlibCodec codec, int w)
|
||||
{
|
||||
_codec = codec;
|
||||
_codec.Message = null;
|
||||
blocks = null;
|
||||
|
||||
// handle undocumented nowrap option (no zlib header or check)
|
||||
//nowrap = 0;
|
||||
//if (w < 0)
|
||||
//{
|
||||
// w = - w;
|
||||
// nowrap = 1;
|
||||
//}
|
||||
|
||||
// set window size
|
||||
if (w < 8 || w > 15)
|
||||
{
|
||||
End();
|
||||
throw new ZlibException("Bad window size.");
|
||||
|
||||
//return ZlibConstants.Z_STREAM_ERROR;
|
||||
}
|
||||
wbits = w;
|
||||
|
||||
blocks = new InflateBlocks(codec,
|
||||
HandleRfc1950HeaderBytes ? this : null,
|
||||
1 << w);
|
||||
|
||||
// reset state
|
||||
Reset();
|
||||
return ZlibConstants.Z_OK;
|
||||
}
|
||||
|
||||
|
||||
internal int Inflate(FlushType flush)
|
||||
{
|
||||
int b;
|
||||
|
||||
if (_codec.InputBuffer == null)
|
||||
throw new ZlibException("InputBuffer is null. ");
|
||||
|
||||
// int f = (flush == FlushType.Finish)
|
||||
// ? ZlibConstants.Z_BUF_ERROR
|
||||
// : ZlibConstants.Z_OK;
|
||||
|
||||
// workitem 8870
|
||||
int f = ZlibConstants.Z_OK;
|
||||
int r = ZlibConstants.Z_BUF_ERROR;
|
||||
|
||||
while (true)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case InflateManagerMode.METHOD:
|
||||
if (_codec.AvailableBytesIn == 0) return r;
|
||||
r = f;
|
||||
_codec.AvailableBytesIn--;
|
||||
_codec.TotalBytesIn++;
|
||||
if (((method = _codec.InputBuffer[_codec.NextIn++]) & 0xf) != Z_DEFLATED)
|
||||
{
|
||||
mode = InflateManagerMode.BAD;
|
||||
_codec.Message = String.Format("unknown compression method (0x{0:X2})", method);
|
||||
marker = 5; // can't try inflateSync
|
||||
break;
|
||||
}
|
||||
if ((method >> 4) + 8 > wbits)
|
||||
{
|
||||
mode = InflateManagerMode.BAD;
|
||||
_codec.Message = String.Format("invalid window size ({0})", (method >> 4) + 8);
|
||||
marker = 5; // can't try inflateSync
|
||||
break;
|
||||
}
|
||||
mode = InflateManagerMode.FLAG;
|
||||
break;
|
||||
|
||||
|
||||
case InflateManagerMode.FLAG:
|
||||
if (_codec.AvailableBytesIn == 0) return r;
|
||||
r = f;
|
||||
_codec.AvailableBytesIn--;
|
||||
_codec.TotalBytesIn++;
|
||||
b = (_codec.InputBuffer[_codec.NextIn++]) & 0xff;
|
||||
|
||||
if ((((method << 8) + b) % 31) != 0)
|
||||
{
|
||||
mode = InflateManagerMode.BAD;
|
||||
_codec.Message = "incorrect header check";
|
||||
marker = 5; // can't try inflateSync
|
||||
break;
|
||||
}
|
||||
|
||||
mode = ((b & PRESET_DICT) == 0)
|
||||
? InflateManagerMode.BLOCKS
|
||||
: InflateManagerMode.DICT4;
|
||||
break;
|
||||
|
||||
case InflateManagerMode.DICT4:
|
||||
if (_codec.AvailableBytesIn == 0) return r;
|
||||
r = f;
|
||||
_codec.AvailableBytesIn--;
|
||||
_codec.TotalBytesIn++;
|
||||
expectedCheck = (uint)((_codec.InputBuffer[_codec.NextIn++] << 24) & 0xff000000);
|
||||
mode = InflateManagerMode.DICT3;
|
||||
break;
|
||||
|
||||
case InflateManagerMode.DICT3:
|
||||
if (_codec.AvailableBytesIn == 0) return r;
|
||||
r = f;
|
||||
_codec.AvailableBytesIn--;
|
||||
_codec.TotalBytesIn++;
|
||||
expectedCheck += (uint)((_codec.InputBuffer[_codec.NextIn++] << 16) & 0x00ff0000);
|
||||
mode = InflateManagerMode.DICT2;
|
||||
break;
|
||||
|
||||
case InflateManagerMode.DICT2:
|
||||
|
||||
if (_codec.AvailableBytesIn == 0) return r;
|
||||
r = f;
|
||||
_codec.AvailableBytesIn--;
|
||||
_codec.TotalBytesIn++;
|
||||
expectedCheck += (uint)((_codec.InputBuffer[_codec.NextIn++] << 8) & 0x0000ff00);
|
||||
mode = InflateManagerMode.DICT1;
|
||||
break;
|
||||
|
||||
|
||||
case InflateManagerMode.DICT1:
|
||||
if (_codec.AvailableBytesIn == 0) return r;
|
||||
r = f;
|
||||
_codec.AvailableBytesIn--; _codec.TotalBytesIn++;
|
||||
expectedCheck += (uint)(_codec.InputBuffer[_codec.NextIn++] & 0x000000ff);
|
||||
_codec._Adler32 = expectedCheck;
|
||||
mode = InflateManagerMode.DICT0;
|
||||
return ZlibConstants.Z_NEED_DICT;
|
||||
|
||||
|
||||
case InflateManagerMode.DICT0:
|
||||
mode = InflateManagerMode.BAD;
|
||||
_codec.Message = "need dictionary";
|
||||
marker = 0; // can try inflateSync
|
||||
return ZlibConstants.Z_STREAM_ERROR;
|
||||
|
||||
|
||||
case InflateManagerMode.BLOCKS:
|
||||
r = blocks.Process(r);
|
||||
if (r == ZlibConstants.Z_DATA_ERROR)
|
||||
{
|
||||
mode = InflateManagerMode.BAD;
|
||||
marker = 0; // can try inflateSync
|
||||
break;
|
||||
}
|
||||
|
||||
if (r == ZlibConstants.Z_OK) r = f;
|
||||
|
||||
if (r != ZlibConstants.Z_STREAM_END)
|
||||
return r;
|
||||
|
||||
r = f;
|
||||
computedCheck = blocks.Reset();
|
||||
if (!HandleRfc1950HeaderBytes)
|
||||
{
|
||||
mode = InflateManagerMode.DONE;
|
||||
return ZlibConstants.Z_STREAM_END;
|
||||
}
|
||||
mode = InflateManagerMode.CHECK4;
|
||||
break;
|
||||
|
||||
case InflateManagerMode.CHECK4:
|
||||
if (_codec.AvailableBytesIn == 0) return r;
|
||||
r = f;
|
||||
_codec.AvailableBytesIn--;
|
||||
_codec.TotalBytesIn++;
|
||||
expectedCheck = (uint)((_codec.InputBuffer[_codec.NextIn++] << 24) & 0xff000000);
|
||||
mode = InflateManagerMode.CHECK3;
|
||||
break;
|
||||
|
||||
case InflateManagerMode.CHECK3:
|
||||
if (_codec.AvailableBytesIn == 0) return r;
|
||||
r = f;
|
||||
_codec.AvailableBytesIn--; _codec.TotalBytesIn++;
|
||||
expectedCheck += (uint)((_codec.InputBuffer[_codec.NextIn++] << 16) & 0x00ff0000);
|
||||
mode = InflateManagerMode.CHECK2;
|
||||
break;
|
||||
|
||||
case InflateManagerMode.CHECK2:
|
||||
if (_codec.AvailableBytesIn == 0) return r;
|
||||
r = f;
|
||||
_codec.AvailableBytesIn--;
|
||||
_codec.TotalBytesIn++;
|
||||
expectedCheck += (uint)((_codec.InputBuffer[_codec.NextIn++] << 8) & 0x0000ff00);
|
||||
mode = InflateManagerMode.CHECK1;
|
||||
break;
|
||||
|
||||
case InflateManagerMode.CHECK1:
|
||||
if (_codec.AvailableBytesIn == 0) return r;
|
||||
r = f;
|
||||
_codec.AvailableBytesIn--; _codec.TotalBytesIn++;
|
||||
expectedCheck += (uint)(_codec.InputBuffer[_codec.NextIn++] & 0x000000ff);
|
||||
if (computedCheck != expectedCheck)
|
||||
{
|
||||
mode = InflateManagerMode.BAD;
|
||||
_codec.Message = "incorrect data check";
|
||||
marker = 5; // can't try inflateSync
|
||||
break;
|
||||
}
|
||||
mode = InflateManagerMode.DONE;
|
||||
return ZlibConstants.Z_STREAM_END;
|
||||
|
||||
case InflateManagerMode.DONE:
|
||||
return ZlibConstants.Z_STREAM_END;
|
||||
|
||||
case InflateManagerMode.BAD:
|
||||
throw new ZlibException(String.Format("Bad state ({0})", _codec.Message));
|
||||
|
||||
default:
|
||||
throw new ZlibException("Stream error.");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
internal int SetDictionary(byte[] dictionary, bool check = true)
|
||||
{
|
||||
int index = 0;
|
||||
int length = dictionary.Length;
|
||||
|
||||
if (check)
|
||||
{
|
||||
if (mode != InflateManagerMode.DICT0)
|
||||
throw new ZlibException("Stream error.");
|
||||
|
||||
if (Adler.Adler32(1, dictionary, 0, dictionary.Length) != _codec._Adler32)
|
||||
{
|
||||
return ZlibConstants.Z_DATA_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
_codec._Adler32 = Adler.Adler32(0, null, 0, 0);
|
||||
|
||||
if (length >= (1 << wbits))
|
||||
{
|
||||
length = (1 << wbits) - 1;
|
||||
index = dictionary.Length - length;
|
||||
}
|
||||
blocks.SetDictionary(dictionary, index, length);
|
||||
mode = InflateManagerMode.BLOCKS;
|
||||
return ZlibConstants.Z_OK;
|
||||
}
|
||||
|
||||
|
||||
private static readonly byte[] mark = [0, 0, 0xff, 0xff];
|
||||
|
||||
internal int Sync()
|
||||
{
|
||||
int n; // number of bytes to look at
|
||||
int p; // pointer to bytes
|
||||
int m; // number of marker bytes found in a row
|
||||
long r, w; // temporaries to save total_in and total_out
|
||||
|
||||
// set up
|
||||
if (mode != InflateManagerMode.BAD)
|
||||
{
|
||||
mode = InflateManagerMode.BAD;
|
||||
marker = 0;
|
||||
}
|
||||
if ((n = _codec.AvailableBytesIn) == 0)
|
||||
return ZlibConstants.Z_BUF_ERROR;
|
||||
p = _codec.NextIn;
|
||||
m = marker;
|
||||
|
||||
// search
|
||||
while (n != 0 && m < 4)
|
||||
{
|
||||
if (_codec.InputBuffer[p] == mark[m])
|
||||
{
|
||||
m++;
|
||||
}
|
||||
else if (_codec.InputBuffer[p] != 0)
|
||||
{
|
||||
m = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m = 4 - m;
|
||||
}
|
||||
p++; n--;
|
||||
}
|
||||
|
||||
// restore
|
||||
_codec.TotalBytesIn += p - _codec.NextIn;
|
||||
_codec.NextIn = p;
|
||||
_codec.AvailableBytesIn = n;
|
||||
marker = m;
|
||||
|
||||
// return no joy or set up to restart on a new block
|
||||
if (m != 4)
|
||||
{
|
||||
return ZlibConstants.Z_DATA_ERROR;
|
||||
}
|
||||
r = _codec.TotalBytesIn;
|
||||
w = _codec.TotalBytesOut;
|
||||
Reset();
|
||||
_codec.TotalBytesIn = r;
|
||||
_codec.TotalBytesOut = w;
|
||||
mode = InflateManagerMode.BLOCKS;
|
||||
return ZlibConstants.Z_OK;
|
||||
}
|
||||
|
||||
|
||||
// Returns true if inflate is currently at the end of a block generated
|
||||
// by Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP
|
||||
// implementation to provide an additional safety check. PPP uses Z_SYNC_FLUSH
|
||||
// but removes the length bytes of the resulting empty stored block. When
|
||||
// decompressing, PPP checks that at the end of input packet, inflate is
|
||||
// waiting for these length bytes.
|
||||
internal int SyncPoint(ZlibCodec z)
|
||||
{
|
||||
return blocks.SyncPoint();
|
||||
}
|
||||
}
|
||||
}
|
||||
470
SabreTools.IO/Compression/Deflate/InflateWrapper.cs
Normal file
470
SabreTools.IO/Compression/Deflate/InflateWrapper.cs
Normal file
@@ -0,0 +1,470 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SabreTools.Hashing;
|
||||
using SabreTools.IO.Extensions;
|
||||
|
||||
namespace SabreTools.IO.Compression.Deflate
|
||||
{
|
||||
/// <summary>
|
||||
/// Wrapper to handle DEFLATE decompression with data verification
|
||||
/// </summary>
|
||||
public class InflateWrapper
|
||||
{
|
||||
#region Constants
|
||||
|
||||
/// <summary>
|
||||
/// Buffer size for decompression
|
||||
/// </summary>
|
||||
private const int BufferSize = 1024 * 1024;
|
||||
|
||||
/// <summary>
|
||||
/// Local file header signature
|
||||
/// </summary>
|
||||
private const uint LocalFileHeaderSignature = 0x04034B50;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Classes
|
||||
|
||||
/// <summary>
|
||||
/// Minimal PKZIP local file header information
|
||||
/// </summary>
|
||||
private class MinLocalFileHeader
|
||||
{
|
||||
/// <summary>
|
||||
/// Signature (0x04034B50)
|
||||
/// </summary>
|
||||
public uint Signature { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// CRC-32
|
||||
/// </summary>
|
||||
public uint CRC32 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Compressed size
|
||||
/// </summary>
|
||||
public uint CompressedSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Uncompressed size
|
||||
/// </summary>
|
||||
public uint UncompressedSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// File name (variable size)
|
||||
/// </summary>
|
||||
public string? FileName { get; set; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Extraction
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to extract a file defined by a filename
|
||||
/// </summary>
|
||||
/// <param name="source">Stream representing the deflated data</param>
|
||||
/// <param name="filename">Output filename, null to auto-generate</param>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <param name="expected">Expected DEFLATE stream information</param>
|
||||
/// <param name="pkzip">Indicates if PKZIP containers are used</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>Extraction status representing the final state</returns>
|
||||
/// <remarks>Assumes that the current stream position is where the compressed data lives</remarks>
|
||||
public static ExtractionStatus ExtractFile(Stream source,
|
||||
string? filename,
|
||||
string outputDirectory,
|
||||
DeflateInfo expected,
|
||||
bool pkzip,
|
||||
bool includeDebug)
|
||||
{
|
||||
// Debug output
|
||||
if (includeDebug) Console.WriteLine($"Attempting to extract {filename}");
|
||||
|
||||
// Extract the file
|
||||
var destination = new MemoryStream();
|
||||
ExtractionStatus status = ExtractStream(source,
|
||||
destination,
|
||||
expected,
|
||||
pkzip,
|
||||
includeDebug,
|
||||
out var foundFilename);
|
||||
|
||||
// If the extracted data is invalid
|
||||
if (status != ExtractionStatus.GOOD || destination == null)
|
||||
return status;
|
||||
|
||||
// Ensure directory separators are consistent
|
||||
filename ??= foundFilename ?? $"FILE_[{expected.InputSize}, {expected.OutputSize}, {expected.Crc32}]";
|
||||
if (Path.DirectorySeparatorChar == '\\')
|
||||
filename = filename.Replace('/', '\\');
|
||||
else if (Path.DirectorySeparatorChar == '/')
|
||||
filename = filename.Replace('\\', '/');
|
||||
|
||||
// Ensure the full output directory exists
|
||||
filename = Path.Combine(outputDirectory, filename);
|
||||
var directoryName = Path.GetDirectoryName(filename);
|
||||
if (directoryName != null && !Directory.Exists(directoryName))
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
// Write the output file
|
||||
File.WriteAllBytes(filename, destination.ToArray());
|
||||
return status;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to extract a file to a stream
|
||||
/// </summary>
|
||||
/// <param name="source">Stream representing the deflated data</param>
|
||||
/// <param name="destination">Stream where the inflated data will be written</param>
|
||||
/// <param name="expected">Expected DEFLATE stream information</param>
|
||||
/// <param name="pkzip">Indicates if PKZIP containers are used</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <param name="filename">Output filename if extracted from the data, null otherwise</param>
|
||||
/// <returns>Extraction status representing the final state</returns>
|
||||
/// <remarks>Assumes that the current stream position is where the compressed data lives</remarks>
|
||||
public static ExtractionStatus ExtractStream(Stream source,
|
||||
Stream destination,
|
||||
DeflateInfo expected,
|
||||
bool pkzip,
|
||||
bool includeDebug,
|
||||
out string? filename)
|
||||
{
|
||||
// If PKZIP containers are used
|
||||
if (pkzip)
|
||||
return ExtractStreamWithContainer(source, destination, expected, includeDebug, out filename);
|
||||
|
||||
// If post-data checksums are used
|
||||
filename = null;
|
||||
return ExtractStreamWithChecksum(source, destination, expected, includeDebug);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract source data in a PKZIP container
|
||||
/// </summary>
|
||||
/// <param name="source">Stream representing the deflated data</param>
|
||||
/// <param name="destination">Stream where the inflated data will be written</param>
|
||||
/// <param name="expected">Expected DEFLATE stream information</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <param name="filename">Filename from the PKZIP header, if it exists</param>
|
||||
/// <returns></returns>
|
||||
public static ExtractionStatus ExtractStreamWithContainer(Stream source,
|
||||
Stream destination,
|
||||
DeflateInfo expected,
|
||||
bool includeDebug,
|
||||
out string? filename)
|
||||
{
|
||||
// Set default values
|
||||
filename = null;
|
||||
|
||||
// Debug output
|
||||
if (includeDebug) Console.WriteLine($"Offset: {source.Position:X8}, Expected Read: {expected.InputSize}, Expected Write: {expected.OutputSize}, Expected CRC-32: {expected.Crc32:X8}");
|
||||
|
||||
// Check the validity of the inputs
|
||||
if (expected.InputSize == 0)
|
||||
{
|
||||
if (includeDebug) Console.Error.WriteLine($"Not attempting to extract, expected to read 0 bytes");
|
||||
return ExtractionStatus.INVALID;
|
||||
}
|
||||
else if (expected.InputSize > (source.Length - source.Position))
|
||||
{
|
||||
if (includeDebug) Console.Error.WriteLine($"Not attempting to extract, expected to read {expected.InputSize} bytes but only {source.Length - source.Position} bytes remain");
|
||||
return ExtractionStatus.INVALID;
|
||||
}
|
||||
|
||||
// Cache the current offset
|
||||
long current = source.Position;
|
||||
|
||||
// Parse the PKZIP header, if it exists
|
||||
MinLocalFileHeader? zipHeader = ParseLocalFileHeader(source);
|
||||
long zipHeaderBytes = source.Position - current;
|
||||
|
||||
// Always trust the PKZIP CRC-32 value over what is supplied
|
||||
if (zipHeader != null)
|
||||
expected.Crc32 = zipHeader.CRC32;
|
||||
|
||||
// If the filename is [NULL], replace with the zip filename
|
||||
if (zipHeader?.FileName != null)
|
||||
{
|
||||
filename = zipHeader.FileName;
|
||||
if (includeDebug) Console.WriteLine($"Filename from PKZIP header: {filename}");
|
||||
}
|
||||
|
||||
// Debug output
|
||||
if (includeDebug) Console.WriteLine($"PKZIP Filename: {zipHeader?.FileName}, PKZIP Expected Read: {zipHeader?.CompressedSize}, PKZIP Expected Write: {zipHeader?.UncompressedSize}, PKZIP Expected CRC-32: {zipHeader?.CRC32:X4}");
|
||||
|
||||
// Extract the file
|
||||
var actual = Inflate(source, destination);
|
||||
if (actual == null)
|
||||
{
|
||||
if (includeDebug) Console.Error.WriteLine($"Could not extract {filename}");
|
||||
return ExtractionStatus.FAIL;
|
||||
}
|
||||
|
||||
// Account for the header bytes read
|
||||
actual.InputSize += zipHeaderBytes;
|
||||
source.Seek(current + actual.InputSize, SeekOrigin.Begin);
|
||||
|
||||
// Verify the extracted data
|
||||
return VerifyExtractedData(source, current, expected, actual, includeDebug);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract source data with a trailing CRC-32 checksum
|
||||
/// </summary>
|
||||
/// <param name="source">Stream representing the deflated data</param>
|
||||
/// <param name="destination">Stream where the inflated data will be written</param>
|
||||
/// <param name="expected">Expected DEFLATE stream information</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns></returns>
|
||||
public static ExtractionStatus ExtractStreamWithChecksum(Stream source,
|
||||
Stream destination,
|
||||
DeflateInfo expected,
|
||||
bool includeDebug)
|
||||
{
|
||||
// Debug output
|
||||
if (includeDebug) Console.WriteLine($"Offset: {source.Position:X8}, Expected Read: {expected.InputSize}, Expected Write: {expected.OutputSize}, Expected CRC-32: {expected.Crc32:X8}");
|
||||
|
||||
// Check the validity of the inputs
|
||||
if (expected.InputSize == 0)
|
||||
{
|
||||
if (includeDebug) Console.Error.WriteLine($"Not attempting to extract, expected to read 0 bytes");
|
||||
return ExtractionStatus.INVALID;
|
||||
}
|
||||
else if (expected.InputSize > (source.Length - source.Position))
|
||||
{
|
||||
if (includeDebug) Console.Error.WriteLine($"Not attempting to extract, expected to read {expected.InputSize} bytes but only {source.Length - source.Position} bytes remain");
|
||||
return ExtractionStatus.INVALID;
|
||||
}
|
||||
|
||||
// Cache the current offset
|
||||
long current = source.Position;
|
||||
|
||||
// Extract the file
|
||||
var actual = Inflate(source, destination);
|
||||
if (actual == null)
|
||||
{
|
||||
if (includeDebug) Console.Error.WriteLine($"Could not extract");
|
||||
return ExtractionStatus.FAIL;
|
||||
}
|
||||
|
||||
// Seek to the true end of the data
|
||||
source.Seek(current + actual.InputSize, SeekOrigin.Begin);
|
||||
|
||||
// If the read value is off-by-one after checksum
|
||||
if (actual.InputSize == expected.InputSize - 5)
|
||||
{
|
||||
// If not at the end of the file, get the corrected offset
|
||||
if (source.Position + 5 < source.Length)
|
||||
{
|
||||
// TODO: What does this byte represent?
|
||||
byte padding = source.ReadByteValue();
|
||||
actual.InputSize += 1;
|
||||
|
||||
// Debug output
|
||||
if (includeDebug) Console.WriteLine($"Off-by-one padding byte detected: 0x{padding:X2}");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Debug output
|
||||
if (includeDebug) Console.WriteLine($"Not enough data to adjust offset");
|
||||
}
|
||||
}
|
||||
|
||||
// If there is enough data to read the full CRC
|
||||
uint deflateCrc;
|
||||
if (source.Position + 4 < source.Length)
|
||||
{
|
||||
deflateCrc = source.ReadUInt32LittleEndian();
|
||||
actual.InputSize += 4;
|
||||
}
|
||||
// Otherwise, read what is possible and pad with 0x00
|
||||
else
|
||||
{
|
||||
byte[] deflateCrcBytes = new byte[4];
|
||||
int realCrcLength = source.Read(deflateCrcBytes, 0, (int)(source.Length - source.Position));
|
||||
|
||||
// Parse as a little-endian 32-bit value
|
||||
deflateCrc = (uint)(deflateCrcBytes[0]
|
||||
| (deflateCrcBytes[1] << 8)
|
||||
| (deflateCrcBytes[2] << 16)
|
||||
| (deflateCrcBytes[3] << 24));
|
||||
|
||||
actual.InputSize += realCrcLength;
|
||||
}
|
||||
|
||||
// If the CRC to check isn't set
|
||||
if (expected.Crc32 == 0)
|
||||
expected.Crc32 = deflateCrc;
|
||||
|
||||
// Debug output
|
||||
if (includeDebug) Console.WriteLine($"DeflateStream CRC-32: {deflateCrc:X8}");
|
||||
|
||||
// Verify the extracted data
|
||||
return VerifyExtractedData(source, current, expected, actual, includeDebug);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a minimal local file header
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled minimal local file header on success, null on error</returns>
|
||||
/// <remarks>Partial mirror of method in Serialization</remarks>
|
||||
private static MinLocalFileHeader? ParseLocalFileHeader(Stream data)
|
||||
{
|
||||
var header = new MinLocalFileHeader();
|
||||
|
||||
header.Signature = data.ReadUInt32LittleEndian();
|
||||
if (header.Signature != LocalFileHeaderSignature)
|
||||
return null;
|
||||
|
||||
_ = data.ReadUInt16LittleEndian(); // Version
|
||||
_ = data.ReadUInt16LittleEndian(); // Flags
|
||||
_ = data.ReadUInt16LittleEndian(); // CompressionMethod
|
||||
_ = data.ReadUInt16LittleEndian(); // LastModifedFileTime
|
||||
_ = data.ReadUInt16LittleEndian(); // LastModifiedFileDate
|
||||
header.CRC32 = data.ReadUInt32LittleEndian();
|
||||
header.CompressedSize = data.ReadUInt32LittleEndian();
|
||||
header.UncompressedSize = data.ReadUInt32LittleEndian();
|
||||
ushort fileNameLength = data.ReadUInt16LittleEndian();
|
||||
ushort extraFieldLength = data.ReadUInt16LittleEndian();
|
||||
|
||||
if (fileNameLength > 0 && data.Position + fileNameLength <= data.Length)
|
||||
{
|
||||
byte[] filenameBytes = data.ReadBytes(fileNameLength);
|
||||
header.FileName = Encoding.ASCII.GetString(filenameBytes);
|
||||
}
|
||||
|
||||
// Parsing extras is skipped here, unlike in Serialization
|
||||
if (extraFieldLength > 0 && data.Position + extraFieldLength <= data.Length)
|
||||
_ = data.ReadBytes(extraFieldLength);
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify the extracted stream data, seeking to the original location on failure
|
||||
/// </summary>
|
||||
/// <param name="source">Stream representing the deflated data</param>
|
||||
/// <param name="start">Position representing the start of the deflated data</param>
|
||||
/// <param name="expected">Expected deflation info</param>
|
||||
/// <param name="actual">Actual deflation info</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>Extraction status representing the final state</returns>
|
||||
private static ExtractionStatus VerifyExtractedData(Stream source,
|
||||
long start,
|
||||
DeflateInfo expected,
|
||||
DeflateInfo actual,
|
||||
bool includeDebug)
|
||||
{
|
||||
// Debug output
|
||||
if (includeDebug) Console.WriteLine($"Actual Read: {actual.InputSize}, Actual Write: {actual.OutputSize}, Actual CRC-32: {actual.Crc32:X8}");
|
||||
|
||||
// If there's a mismatch during both reading and writing
|
||||
if (expected.InputSize >= 0 && expected.InputSize != actual.InputSize)
|
||||
{
|
||||
// This in/out check helps catch false positives, such as
|
||||
// files that have an off-by-one mismatch for read values
|
||||
// but properly match the output written values.
|
||||
|
||||
// If the written bytes not correct as well
|
||||
if (expected.OutputSize >= 0 && expected.OutputSize != actual.OutputSize)
|
||||
{
|
||||
if (includeDebug) Console.Error.WriteLine($"Mismatched read/write values!");
|
||||
source.Seek(start, SeekOrigin.Begin);
|
||||
return ExtractionStatus.WRONG_SIZE;
|
||||
}
|
||||
|
||||
// If the written bytes are not being verified
|
||||
else if (expected.OutputSize < 0)
|
||||
{
|
||||
if (includeDebug) Console.Error.WriteLine($"Mismatched read/write values!");
|
||||
source.Seek(start, SeekOrigin.Begin);
|
||||
return ExtractionStatus.WRONG_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
// If there's just a mismatch during only writing
|
||||
if (expected.InputSize >= 0 && expected.InputSize == actual.InputSize)
|
||||
{
|
||||
// We want to log this but ignore the error
|
||||
if (expected.OutputSize >= 0 && expected.OutputSize != actual.OutputSize)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine($"Ignoring mismatched write values because read values match!");
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, the write size should be checked normally
|
||||
else if (expected.InputSize == 0 && expected.OutputSize >= 0 && expected.OutputSize != actual.OutputSize)
|
||||
{
|
||||
if (includeDebug) Console.Error.WriteLine($"Mismatched write values!");
|
||||
source.Seek(start, SeekOrigin.Begin);
|
||||
return ExtractionStatus.WRONG_SIZE;
|
||||
}
|
||||
|
||||
// If there's a mismatch with the CRC-32
|
||||
if (expected.Crc32 != 0 && expected.Crc32 != actual.Crc32)
|
||||
{
|
||||
if (includeDebug) Console.Error.WriteLine($"Mismatched CRC-32 values!");
|
||||
source.Seek(start, SeekOrigin.Begin);
|
||||
return ExtractionStatus.BAD_CRC;
|
||||
}
|
||||
|
||||
return ExtractionStatus.GOOD;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Inflation
|
||||
|
||||
/// <summary>
|
||||
/// Inflate an input stream to an output stream
|
||||
/// </summary>
|
||||
/// <param name="source">Stream representing the deflated data</param>
|
||||
/// <param name="destination">Stream where the inflated data will be written</param>
|
||||
/// <returns>Deflate information representing the processed data on success, null on error</returns>
|
||||
public static DeflateInfo? Inflate(Stream source, Stream destination)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Setup the hasher for CRC-32 calculation
|
||||
using var hasher = new HashWrapper(HashType.CRC32);
|
||||
|
||||
// Create a DeflateStream from the input
|
||||
using var ds = new DeflateStream(source, CompressionMode.Decompress, leaveOpen: true);
|
||||
|
||||
// Decompress in blocks
|
||||
while (true)
|
||||
{
|
||||
byte[] buf = new byte[BufferSize];
|
||||
int read = ds.Read(buf, 0, buf.Length);
|
||||
if (read == 0)
|
||||
break;
|
||||
|
||||
hasher.Process(buf, 0, read);
|
||||
destination.Write(buf, 0, read);
|
||||
}
|
||||
|
||||
// Finalize the hash
|
||||
hasher.Terminate();
|
||||
byte[] hashBytes = hasher.CurrentHashBytes!;
|
||||
|
||||
// Save the deflate values
|
||||
return new DeflateInfo
|
||||
{
|
||||
InputSize = ds.TotalIn,
|
||||
OutputSize = ds.TotalOut,
|
||||
Crc32 = BitConverter.ToUInt32(hashBytes, 0),
|
||||
};
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
114
SabreTools.IO/Compression/Deflate/InternalConstants.cs
Normal file
114
SabreTools.IO/Compression/Deflate/InternalConstants.cs
Normal file
@@ -0,0 +1,114 @@
|
||||
// Zlib.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009-2011 Dino Chiesa and Microsoft Corporation.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Last Saved: <2011-August-03 19:52:28>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines classes for ZLIB compression and
|
||||
// decompression. This code is derived from the jzlib implementation of
|
||||
// zlib, but significantly modified. The object model is not the same,
|
||||
// and many of the behaviors are new or different. Nonetheless, in
|
||||
// keeping with the license for jzlib, the copyright to that code is
|
||||
// included below.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// The following notice applies to jzlib:
|
||||
//
|
||||
// Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in
|
||||
// the documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. The names of the authors may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
// INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
// jzlib is based on zlib-1.1.3.
|
||||
//
|
||||
// The following notice applies to zlib:
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (C) 1995-2004 Jean-loup Gailly and Mark Adler
|
||||
//
|
||||
// The ZLIB software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
// Jean-loup Gailly jloup@gzip.org
|
||||
// Mark Adler madler@alumni.caltech.edu
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace SabreTools.IO.Compression.Deflate
|
||||
{
|
||||
internal static class InternalConstants
|
||||
{
|
||||
internal static readonly int MAX_BITS = 15;
|
||||
internal static readonly int BL_CODES = 19;
|
||||
internal static readonly int D_CODES = 30;
|
||||
internal static readonly int LITERALS = 256;
|
||||
internal static readonly int LENGTH_CODES = 29;
|
||||
internal static readonly int L_CODES = (LITERALS + 1 + LENGTH_CODES);
|
||||
|
||||
// Bit length codes must not exceed MAX_BL_BITS bits
|
||||
internal static readonly int MAX_BL_BITS = 7;
|
||||
|
||||
// repeat previous bit length 3-6 times (2 bits of repeat count)
|
||||
internal static readonly int REP_3_6 = 16;
|
||||
|
||||
// repeat a zero length 3-10 times (3 bits of repeat count)
|
||||
internal static readonly int REPZ_3_10 = 17;
|
||||
|
||||
// repeat a zero length 11-138 times (7 bits of repeat count)
|
||||
internal static readonly int REPZ_11_138 = 18;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
// Inflate.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009 Dino Chiesa and Microsoft Corporation.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2010-January-08 18:32:12>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines classes for decompression. This code is derived
|
||||
// from the jzlib implementation of zlib, but significantly modified.
|
||||
// The object model is not the same, and many of the behaviors are
|
||||
// different. Nonetheless, in keeping with the license for jzlib, I am
|
||||
// reproducing the copyright to that code here.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in
|
||||
// the documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. The names of the authors may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
// INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
// This program is based on zlib-1.1.3; credit to authors
|
||||
// Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu)
|
||||
// and contributors of zlib.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace SabreTools.IO.Compression.Deflate
|
||||
{
|
||||
internal static class InternalInflateConstants
|
||||
{
|
||||
// And'ing with mask[n] masks the lower n bits
|
||||
internal static readonly int[] InflateMask = [
|
||||
0x00000000, 0x00000001, 0x00000003, 0x00000007,
|
||||
0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f,
|
||||
0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff,
|
||||
0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff ];
|
||||
}
|
||||
}
|
||||
32
SabreTools.IO/Compression/Deflate/LICENSE.jzlib.txt
Normal file
32
SabreTools.IO/Compression/Deflate/LICENSE.jzlib.txt
Normal file
@@ -0,0 +1,32 @@
|
||||
The ZLIB library, available as SabreTools.IO.Compression.Deflate.dll or as part of DotNetZip,
|
||||
is a ported-then-modified version of jzlib. The following applies to jzlib:
|
||||
|
||||
JZlib 0.0.* were released under the GNU LGPL license. Later, we have switched
|
||||
over to a BSD-style license.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
31
SabreTools.IO/Compression/Deflate/License.zlib.txt
Normal file
31
SabreTools.IO/Compression/Deflate/License.zlib.txt
Normal file
@@ -0,0 +1,31 @@
|
||||
The ZLIB library, available as SabreTools.IO.Compression.Deflate.dll or as part of DotNetZip,
|
||||
is a ported-then-modified version of jzlib, which itself is based on
|
||||
zlib-1.1.3, the well-known C-language compression library.
|
||||
|
||||
The following notice applies to zlib:
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
Copyright (C) 1995-2004 Jean-loup Gailly and Mark Adler
|
||||
|
||||
The ZLIB software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
Jean-loup Gailly jloup@gzip.org
|
||||
Mark Adler madler@alumni.caltech.edu
|
||||
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
162
SabreTools.IO/Compression/Deflate/SharedUtils.cs
Normal file
162
SabreTools.IO/Compression/Deflate/SharedUtils.cs
Normal file
@@ -0,0 +1,162 @@
|
||||
// Zlib.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009-2011 Dino Chiesa and Microsoft Corporation.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Last Saved: <2011-August-03 19:52:28>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines classes for ZLIB compression and
|
||||
// decompression. This code is derived from the jzlib implementation of
|
||||
// zlib, but significantly modified. The object model is not the same,
|
||||
// and many of the behaviors are new or different. Nonetheless, in
|
||||
// keeping with the license for jzlib, the copyright to that code is
|
||||
// included below.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// The following notice applies to jzlib:
|
||||
//
|
||||
// Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in
|
||||
// the documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. The names of the authors may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
// INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
// jzlib is based on zlib-1.1.3.
|
||||
//
|
||||
// The following notice applies to zlib:
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (C) 1995-2004 Jean-loup Gailly and Mark Adler
|
||||
//
|
||||
// The ZLIB software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
// Jean-loup Gailly jloup@gzip.org
|
||||
// Mark Adler madler@alumni.caltech.edu
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace SabreTools.IO.Compression.Deflate
|
||||
{
|
||||
internal class SharedUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Performs an unsigned bitwise right shift with the specified number
|
||||
/// </summary>
|
||||
/// <param name="number">Number to operate on</param>
|
||||
/// <param name="bits">Ammount of bits to shift</param>
|
||||
/// <returns>The resulting number from the shift operation</returns>
|
||||
public static int URShift(int number, int bits)
|
||||
{
|
||||
return (int)((uint)number >> bits);
|
||||
}
|
||||
|
||||
#if NOT
|
||||
/// <summary>
|
||||
/// Performs an unsigned bitwise right shift with the specified number
|
||||
/// </summary>
|
||||
/// <param name="number">Number to operate on</param>
|
||||
/// <param name="bits">Ammount of bits to shift</param>
|
||||
/// <returns>The resulting number from the shift operation</returns>
|
||||
public static long URShift(long number, int bits)
|
||||
{
|
||||
return (long) ((UInt64)number >> bits);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Reads a number of characters from the current source TextReader and writes
|
||||
/// the data to the target array at the specified index.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="sourceTextReader">The source TextReader to read from</param>
|
||||
/// <param name="target">Contains the array of characteres read from the source TextReader.</param>
|
||||
/// <param name="start">The starting index of the target array.</param>
|
||||
/// <param name="count">The maximum number of characters to read from the source TextReader.</param>
|
||||
///
|
||||
/// <returns>
|
||||
/// The number of characters read. The number will be less than or equal to
|
||||
/// count depending on the data available in the source TextReader. Returns -1
|
||||
/// if the end of the stream is reached.
|
||||
/// </returns>
|
||||
public static System.Int32 ReadInput(System.IO.TextReader sourceTextReader, byte[] target, int start, int count)
|
||||
{
|
||||
// Returns 0 bytes if not enough space in target
|
||||
if (target.Length == 0) return 0;
|
||||
|
||||
char[] charArray = new char[target.Length];
|
||||
int bytesRead = sourceTextReader.Read(charArray, start, count);
|
||||
|
||||
// Returns -1 if EOF
|
||||
if (bytesRead == 0) return -1;
|
||||
|
||||
for (int index = start; index < start + bytesRead; index++)
|
||||
target[index] = (byte)charArray[index];
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
|
||||
internal static byte[] ToByteArray(System.String sourceString)
|
||||
{
|
||||
return System.Text.UTF8Encoding.UTF8.GetBytes(sourceString);
|
||||
}
|
||||
|
||||
|
||||
internal static char[] ToCharArray(byte[] byteArray)
|
||||
{
|
||||
return System.Text.UTF8Encoding.UTF8.GetChars(byteArray);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
164
SabreTools.IO/Compression/Deflate/StaticTree.cs
Normal file
164
SabreTools.IO/Compression/Deflate/StaticTree.cs
Normal file
@@ -0,0 +1,164 @@
|
||||
// Zlib.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009-2011 Dino Chiesa and Microsoft Corporation.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Last Saved: <2011-August-03 19:52:28>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines classes for ZLIB compression and
|
||||
// decompression. This code is derived from the jzlib implementation of
|
||||
// zlib, but significantly modified. The object model is not the same,
|
||||
// and many of the behaviors are new or different. Nonetheless, in
|
||||
// keeping with the license for jzlib, the copyright to that code is
|
||||
// included below.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// The following notice applies to jzlib:
|
||||
//
|
||||
// Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in
|
||||
// the documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. The names of the authors may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
// INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
// jzlib is based on zlib-1.1.3.
|
||||
//
|
||||
// The following notice applies to zlib:
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (C) 1995-2004 Jean-loup Gailly and Mark Adler
|
||||
//
|
||||
// The ZLIB software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
// Jean-loup Gailly jloup@gzip.org
|
||||
// Mark Adler madler@alumni.caltech.edu
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace SabreTools.IO.Compression.Deflate
|
||||
{
|
||||
internal sealed class StaticTree
|
||||
{
|
||||
internal static readonly short[] lengthAndLiteralsTreeCodes = [
|
||||
12, 8, 140, 8, 76, 8, 204, 8, 44, 8, 172, 8, 108, 8, 236, 8,
|
||||
28, 8, 156, 8, 92, 8, 220, 8, 60, 8, 188, 8, 124, 8, 252, 8,
|
||||
2, 8, 130, 8, 66, 8, 194, 8, 34, 8, 162, 8, 98, 8, 226, 8,
|
||||
18, 8, 146, 8, 82, 8, 210, 8, 50, 8, 178, 8, 114, 8, 242, 8,
|
||||
10, 8, 138, 8, 74, 8, 202, 8, 42, 8, 170, 8, 106, 8, 234, 8,
|
||||
26, 8, 154, 8, 90, 8, 218, 8, 58, 8, 186, 8, 122, 8, 250, 8,
|
||||
6, 8, 134, 8, 70, 8, 198, 8, 38, 8, 166, 8, 102, 8, 230, 8,
|
||||
22, 8, 150, 8, 86, 8, 214, 8, 54, 8, 182, 8, 118, 8, 246, 8,
|
||||
14, 8, 142, 8, 78, 8, 206, 8, 46, 8, 174, 8, 110, 8, 238, 8,
|
||||
30, 8, 158, 8, 94, 8, 222, 8, 62, 8, 190, 8, 126, 8, 254, 8,
|
||||
1, 8, 129, 8, 65, 8, 193, 8, 33, 8, 161, 8, 97, 8, 225, 8,
|
||||
17, 8, 145, 8, 81, 8, 209, 8, 49, 8, 177, 8, 113, 8, 241, 8,
|
||||
9, 8, 137, 8, 73, 8, 201, 8, 41, 8, 169, 8, 105, 8, 233, 8,
|
||||
25, 8, 153, 8, 89, 8, 217, 8, 57, 8, 185, 8, 121, 8, 249, 8,
|
||||
5, 8, 133, 8, 69, 8, 197, 8, 37, 8, 165, 8, 101, 8, 229, 8,
|
||||
21, 8, 149, 8, 85, 8, 213, 8, 53, 8, 181, 8, 117, 8, 245, 8,
|
||||
13, 8, 141, 8, 77, 8, 205, 8, 45, 8, 173, 8, 109, 8, 237, 8,
|
||||
29, 8, 157, 8, 93, 8, 221, 8, 61, 8, 189, 8, 125, 8, 253, 8,
|
||||
19, 9, 275, 9, 147, 9, 403, 9, 83, 9, 339, 9, 211, 9, 467, 9,
|
||||
51, 9, 307, 9, 179, 9, 435, 9, 115, 9, 371, 9, 243, 9, 499, 9,
|
||||
11, 9, 267, 9, 139, 9, 395, 9, 75, 9, 331, 9, 203, 9, 459, 9,
|
||||
43, 9, 299, 9, 171, 9, 427, 9, 107, 9, 363, 9, 235, 9, 491, 9,
|
||||
27, 9, 283, 9, 155, 9, 411, 9, 91, 9, 347, 9, 219, 9, 475, 9,
|
||||
59, 9, 315, 9, 187, 9, 443, 9, 123, 9, 379, 9, 251, 9, 507, 9,
|
||||
7, 9, 263, 9, 135, 9, 391, 9, 71, 9, 327, 9, 199, 9, 455, 9,
|
||||
39, 9, 295, 9, 167, 9, 423, 9, 103, 9, 359, 9, 231, 9, 487, 9,
|
||||
23, 9, 279, 9, 151, 9, 407, 9, 87, 9, 343, 9, 215, 9, 471, 9,
|
||||
55, 9, 311, 9, 183, 9, 439, 9, 119, 9, 375, 9, 247, 9, 503, 9,
|
||||
15, 9, 271, 9, 143, 9, 399, 9, 79, 9, 335, 9, 207, 9, 463, 9,
|
||||
47, 9, 303, 9, 175, 9, 431, 9, 111, 9, 367, 9, 239, 9, 495, 9,
|
||||
31, 9, 287, 9, 159, 9, 415, 9, 95, 9, 351, 9, 223, 9, 479, 9,
|
||||
63, 9, 319, 9, 191, 9, 447, 9, 127, 9, 383, 9, 255, 9, 511, 9,
|
||||
0, 7, 64, 7, 32, 7, 96, 7, 16, 7, 80, 7, 48, 7, 112, 7,
|
||||
8, 7, 72, 7, 40, 7, 104, 7, 24, 7, 88, 7, 56, 7, 120, 7,
|
||||
4, 7, 68, 7, 36, 7, 100, 7, 20, 7, 84, 7, 52, 7, 116, 7,
|
||||
3, 8, 131, 8, 67, 8, 195, 8, 35, 8, 163, 8, 99, 8, 227, 8
|
||||
];
|
||||
|
||||
internal static readonly short[] distTreeCodes = [
|
||||
0, 5, 16, 5, 8, 5, 24, 5, 4, 5, 20, 5, 12, 5, 28, 5,
|
||||
2, 5, 18, 5, 10, 5, 26, 5, 6, 5, 22, 5, 14, 5, 30, 5,
|
||||
1, 5, 17, 5, 9, 5, 25, 5, 5, 5, 21, 5, 13, 5, 29, 5,
|
||||
3, 5, 19, 5, 11, 5, 27, 5, 7, 5, 23, 5 ];
|
||||
|
||||
internal static readonly StaticTree Literals;
|
||||
internal static readonly StaticTree Distances;
|
||||
internal static readonly StaticTree BitLengths;
|
||||
|
||||
internal short[]? treeCodes; // static tree or null
|
||||
internal int[]? extraBits; // extra bits for each code or null
|
||||
internal int extraBase; // base index for extra_bits
|
||||
internal int elems; // max number of elements in the tree
|
||||
internal int maxLength; // max bit length for the codes
|
||||
|
||||
private StaticTree(short[]? treeCodes, int[]? extraBits, int extraBase, int elems, int maxLength)
|
||||
{
|
||||
this.treeCodes = treeCodes;
|
||||
this.extraBits = extraBits;
|
||||
this.extraBase = extraBase;
|
||||
this.elems = elems;
|
||||
this.maxLength = maxLength;
|
||||
}
|
||||
static StaticTree()
|
||||
{
|
||||
Literals = new StaticTree(lengthAndLiteralsTreeCodes, Tree.ExtraLengthBits, InternalConstants.LITERALS + 1, InternalConstants.L_CODES, InternalConstants.MAX_BITS);
|
||||
Distances = new StaticTree(distTreeCodes, Tree.ExtraDistanceBits, 0, InternalConstants.D_CODES, InternalConstants.MAX_BITS);
|
||||
BitLengths = new StaticTree(null, Tree.extra_blbits, 0, InternalConstants.BL_CODES, InternalConstants.MAX_BL_BITS);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
422
SabreTools.IO/Compression/Deflate/Tree.cs
Normal file
422
SabreTools.IO/Compression/Deflate/Tree.cs
Normal file
@@ -0,0 +1,422 @@
|
||||
// Tree.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009 Dino Chiesa and Microsoft Corporation.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2009-October-28 13:29:50>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines classes for zlib compression and
|
||||
// decompression. This code is derived from the jzlib implementation of
|
||||
// zlib. In keeping with the license for jzlib, the copyright to that
|
||||
// code is below.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in
|
||||
// the documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. The names of the authors may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
// INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
// This program is based on zlib-1.1.3; credit to authors
|
||||
// Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu)
|
||||
// and contributors of zlib.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
#nullable disable
|
||||
namespace SabreTools.IO.Compression.Deflate
|
||||
{
|
||||
sealed class Tree
|
||||
{
|
||||
private static readonly int HEAP_SIZE = (2 * InternalConstants.L_CODES + 1);
|
||||
|
||||
// extra bits for each length code
|
||||
internal static readonly int[] ExtraLengthBits =
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
|
||||
3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0
|
||||
];
|
||||
|
||||
// extra bits for each distance code
|
||||
internal static readonly int[] ExtraDistanceBits =
|
||||
[
|
||||
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
|
||||
7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13
|
||||
];
|
||||
|
||||
// extra bits for each bit length code
|
||||
internal static readonly int[] extra_blbits = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 7];
|
||||
|
||||
internal static readonly sbyte[] bl_order = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15];
|
||||
|
||||
|
||||
// The lengths of the bit length codes are sent in order of decreasing
|
||||
// probability, to avoid transmitting the lengths for unused bit
|
||||
// length codes.
|
||||
|
||||
internal const int Buf_size = 8 * 2;
|
||||
|
||||
// see definition of array dist_code below
|
||||
//internal const int DIST_CODE_LEN = 512;
|
||||
|
||||
private static readonly sbyte[] _dist_code =
|
||||
[
|
||||
0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7,
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9,
|
||||
10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
|
||||
11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
|
||||
12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
|
||||
12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
|
||||
13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
|
||||
13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
|
||||
14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
|
||||
14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
|
||||
14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
|
||||
14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
|
||||
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||
0, 0, 16, 17, 18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23,
|
||||
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||||
25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
|
||||
26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||
26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||
27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
|
||||
27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
|
||||
28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
|
||||
28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
|
||||
28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
|
||||
28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
|
||||
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
|
||||
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
|
||||
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
|
||||
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29
|
||||
];
|
||||
|
||||
internal static readonly sbyte[] LengthCode =
|
||||
[
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11,
|
||||
12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15,
|
||||
16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17,
|
||||
18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19,
|
||||
20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
|
||||
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
|
||||
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||||
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||||
25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
|
||||
25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
|
||||
26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||
26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||
27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
|
||||
27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28
|
||||
];
|
||||
|
||||
|
||||
internal static readonly int[] LengthBase =
|
||||
[
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28,
|
||||
32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 0
|
||||
];
|
||||
|
||||
|
||||
internal static readonly int[] DistanceBase =
|
||||
[
|
||||
0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192,
|
||||
256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576
|
||||
];
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Map from a distance to a distance code.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// No side effects. _dist_code[256] and _dist_code[257] are never used.
|
||||
/// </remarks>
|
||||
internal static int DistanceCode(int dist)
|
||||
{
|
||||
return (dist < 256)
|
||||
? _dist_code[dist]
|
||||
: _dist_code[256 + SharedUtils.URShift(dist, 7)];
|
||||
}
|
||||
|
||||
internal short[] dyn_tree; // the dynamic tree
|
||||
internal int max_code; // largest code with non zero frequency
|
||||
internal StaticTree staticTree; // the corresponding static tree
|
||||
|
||||
// Compute the optimal bit lengths for a tree and update the total bit length
|
||||
// for the current block.
|
||||
// IN assertion: the fields freq and dad are set, heap[heap_max] and
|
||||
// above are the tree nodes sorted by increasing frequency.
|
||||
// OUT assertions: the field len is set to the optimal bit length, the
|
||||
// array bl_count contains the frequencies for each bit length.
|
||||
// The length opt_len is updated; static_len is also updated if stree is
|
||||
// not null.
|
||||
internal void gen_bitlen(DeflateManager s)
|
||||
{
|
||||
short[] tree = dyn_tree;
|
||||
short[] stree = staticTree.treeCodes;
|
||||
int[] extra = staticTree.extraBits;
|
||||
int base_Renamed = staticTree.extraBase;
|
||||
int max_length = staticTree.maxLength;
|
||||
int h; // heap index
|
||||
int n, m; // iterate over the tree elements
|
||||
int bits; // bit length
|
||||
int xbits; // extra bits
|
||||
short f; // frequency
|
||||
int overflow = 0; // number of elements with bit length too large
|
||||
|
||||
for (bits = 0; bits <= InternalConstants.MAX_BITS; bits++)
|
||||
s.bl_count[bits] = 0;
|
||||
|
||||
// In a first pass, compute the optimal bit lengths (which may
|
||||
// overflow in the case of the bit length tree).
|
||||
tree[s.heap[s.heap_max] * 2 + 1] = 0; // root of the heap
|
||||
|
||||
for (h = s.heap_max + 1; h < HEAP_SIZE; h++)
|
||||
{
|
||||
n = s.heap[h];
|
||||
bits = tree[tree[n * 2 + 1] * 2 + 1] + 1;
|
||||
if (bits > max_length)
|
||||
{
|
||||
bits = max_length; overflow++;
|
||||
}
|
||||
tree[n * 2 + 1] = (short)bits;
|
||||
// We overwrite tree[n*2+1] which is no longer needed
|
||||
|
||||
if (n > max_code)
|
||||
continue; // not a leaf node
|
||||
|
||||
s.bl_count[bits]++;
|
||||
xbits = 0;
|
||||
if (n >= base_Renamed)
|
||||
xbits = extra[n - base_Renamed];
|
||||
f = tree[n * 2];
|
||||
s.opt_len += f * (bits + xbits);
|
||||
if (stree != null)
|
||||
s.static_len += f * (stree[n * 2 + 1] + xbits);
|
||||
}
|
||||
if (overflow == 0)
|
||||
return;
|
||||
|
||||
// This happens for example on obj2 and pic of the Calgary corpus
|
||||
// Find the first bit length which could increase:
|
||||
do
|
||||
{
|
||||
bits = max_length - 1;
|
||||
while (s.bl_count[bits] == 0)
|
||||
bits--;
|
||||
s.bl_count[bits]--; // move one leaf down the tree
|
||||
s.bl_count[bits + 1] = (short)(s.bl_count[bits + 1] + 2); // move one overflow item as its brother
|
||||
s.bl_count[max_length]--;
|
||||
// The brother of the overflow item also moves one step up,
|
||||
// but this does not affect bl_count[max_length]
|
||||
overflow -= 2;
|
||||
}
|
||||
while (overflow > 0);
|
||||
|
||||
for (bits = max_length; bits != 0; bits--)
|
||||
{
|
||||
n = s.bl_count[bits];
|
||||
while (n != 0)
|
||||
{
|
||||
m = s.heap[--h];
|
||||
if (m > max_code)
|
||||
continue;
|
||||
if (tree[m * 2 + 1] != bits)
|
||||
{
|
||||
s.opt_len = (int)(s.opt_len + ((long)bits - (long)tree[m * 2 + 1]) * (long)tree[m * 2]);
|
||||
tree[m * 2 + 1] = (short)bits;
|
||||
}
|
||||
n--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Construct one Huffman tree and assigns the code bit strings and lengths.
|
||||
// Update the total bit length for the current block.
|
||||
// IN assertion: the field freq is set for all tree elements.
|
||||
// OUT assertions: the fields len and code are set to the optimal bit length
|
||||
// and corresponding code. The length opt_len is updated; static_len is
|
||||
// also updated if stree is not null. The field max_code is set.
|
||||
internal void build_tree(DeflateManager s)
|
||||
{
|
||||
short[] tree = dyn_tree;
|
||||
short[] stree = staticTree.treeCodes;
|
||||
int elems = staticTree.elems;
|
||||
int n, m; // iterate over heap elements
|
||||
int max_code = -1; // largest code with non zero frequency
|
||||
int node; // new node being created
|
||||
|
||||
// Construct the initial heap, with least frequent element in
|
||||
// heap[1]. The sons of heap[n] are heap[2*n] and heap[2*n+1].
|
||||
// heap[0] is not used.
|
||||
s.heap_len = 0;
|
||||
s.heap_max = HEAP_SIZE;
|
||||
|
||||
for (n = 0; n < elems; n++)
|
||||
{
|
||||
if (tree[n * 2] != 0)
|
||||
{
|
||||
s.heap[++s.heap_len] = max_code = n;
|
||||
s.depth[n] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
tree[n * 2 + 1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// The pkzip format requires that at least one distance code exists,
|
||||
// and that at least one bit should be sent even if there is only one
|
||||
// possible code. So to avoid special checks later on we force at least
|
||||
// two codes of non zero frequency.
|
||||
while (s.heap_len < 2)
|
||||
{
|
||||
node = s.heap[++s.heap_len] = (max_code < 2 ? ++max_code : 0);
|
||||
tree[node * 2] = 1;
|
||||
s.depth[node] = 0;
|
||||
s.opt_len--;
|
||||
if (stree != null)
|
||||
s.static_len -= stree[node * 2 + 1];
|
||||
// node is 0 or 1 so it does not have extra bits
|
||||
}
|
||||
this.max_code = max_code;
|
||||
|
||||
// The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree,
|
||||
// establish sub-heaps of increasing lengths:
|
||||
|
||||
for (n = s.heap_len / 2; n >= 1; n--)
|
||||
s.pqdownheap(tree, n);
|
||||
|
||||
// Construct the Huffman tree by repeatedly combining the least two
|
||||
// frequent nodes.
|
||||
|
||||
node = elems; // next internal node of the tree
|
||||
do
|
||||
{
|
||||
// n = node of least frequency
|
||||
n = s.heap[1];
|
||||
s.heap[1] = s.heap[s.heap_len--];
|
||||
s.pqdownheap(tree, 1);
|
||||
m = s.heap[1]; // m = node of next least frequency
|
||||
|
||||
s.heap[--s.heap_max] = n; // keep the nodes sorted by frequency
|
||||
s.heap[--s.heap_max] = m;
|
||||
|
||||
// Create a new node father of n and m
|
||||
tree[node * 2] = unchecked((short)(tree[n * 2] + tree[m * 2]));
|
||||
s.depth[node] = (sbyte)(System.Math.Max((byte)s.depth[n], (byte)s.depth[m]) + 1);
|
||||
tree[n * 2 + 1] = tree[m * 2 + 1] = (short)node;
|
||||
|
||||
// and insert the new node in the heap
|
||||
s.heap[1] = node++;
|
||||
s.pqdownheap(tree, 1);
|
||||
}
|
||||
while (s.heap_len >= 2);
|
||||
|
||||
s.heap[--s.heap_max] = s.heap[1];
|
||||
|
||||
// At this point, the fields freq and dad are set. We can now
|
||||
// generate the bit lengths.
|
||||
|
||||
gen_bitlen(s);
|
||||
|
||||
// The field len is now set, we can generate the bit codes
|
||||
gen_codes(tree, max_code, s.bl_count);
|
||||
}
|
||||
|
||||
// Generate the codes for a given tree and bit counts (which need not be
|
||||
// optimal).
|
||||
// IN assertion: the array bl_count contains the bit length statistics for
|
||||
// the given tree and the field len is set for all tree elements.
|
||||
// OUT assertion: the field code is set for all tree elements of non
|
||||
// zero code length.
|
||||
internal static void gen_codes(short[] tree, int max_code, short[] bl_count)
|
||||
{
|
||||
short[] next_code = new short[InternalConstants.MAX_BITS + 1]; // next code value for each bit length
|
||||
short code = 0; // running code value
|
||||
int bits; // bit index
|
||||
int n; // code index
|
||||
|
||||
// The distribution counts are first used to generate the code values
|
||||
// without bit reversal.
|
||||
for (bits = 1; bits <= InternalConstants.MAX_BITS; bits++)
|
||||
unchecked
|
||||
{
|
||||
next_code[bits] = code = (short)((code + bl_count[bits - 1]) << 1);
|
||||
}
|
||||
|
||||
// Check that the bit counts in bl_count are consistent. The last code
|
||||
// must be all ones.
|
||||
//Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1,
|
||||
// "inconsistent bit counts");
|
||||
//Tracev((stderr,"\ngen_codes: max_code %d ", max_code));
|
||||
|
||||
for (n = 0; n <= max_code; n++)
|
||||
{
|
||||
int len = tree[n * 2 + 1];
|
||||
if (len == 0)
|
||||
continue;
|
||||
// Now reverse the bits
|
||||
tree[n * 2] = unchecked((short)(bi_reverse(next_code[len]++, len)));
|
||||
}
|
||||
}
|
||||
|
||||
// Reverse the first len bits of a code, using straightforward code (a faster
|
||||
// method would use a table)
|
||||
// IN assertion: 1 <= len <= 15
|
||||
internal static int bi_reverse(int code, int len)
|
||||
{
|
||||
int res = 0;
|
||||
do
|
||||
{
|
||||
res |= code & 1;
|
||||
code >>= 1; //SharedUtils.URShift(code, 1);
|
||||
res <<= 1;
|
||||
}
|
||||
while (--len > 0);
|
||||
return res >> 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
61
SabreTools.IO/Compression/Deflate/WorkItem.cs
Normal file
61
SabreTools.IO/Compression/Deflate/WorkItem.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
//#define Trace
|
||||
|
||||
// ParallelDeflateOutputStream.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// A DeflateStream that does compression only, it uses a
|
||||
// divide-and-conquer approach with multiple threads to exploit multiple
|
||||
// CPUs for the DEFLATE computation.
|
||||
//
|
||||
// last saved: <2011-July-31 14:49:40>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009-2011 by Dino Chiesa
|
||||
// All rights reserved!
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
#nullable disable
|
||||
#pragma warning disable CS0649
|
||||
namespace SabreTools.IO.Compression.Deflate
|
||||
{
|
||||
internal class WorkItem
|
||||
{
|
||||
public byte[] buffer;
|
||||
public byte[] compressed;
|
||||
public int crc;
|
||||
public int index;
|
||||
public int ordinal;
|
||||
public int inputBytesAvailable;
|
||||
public int compressedBytesAvailable;
|
||||
public ZlibCodec compressor;
|
||||
|
||||
public WorkItem(int size,
|
||||
SabreTools.IO.Compression.Deflate.CompressionLevel compressLevel,
|
||||
CompressionStrategy strategy,
|
||||
int ix)
|
||||
{
|
||||
this.buffer = new byte[size];
|
||||
// alloc 5 bytes overhead for every block (margin of safety= 2)
|
||||
int n = size + ((size / 32768) + 1) * 5 * 2;
|
||||
this.compressed = new byte[n];
|
||||
this.compressor = new ZlibCodec();
|
||||
this.compressor.InitializeDeflate(compressLevel, false);
|
||||
this.compressor.OutputBuffer = this.compressed;
|
||||
this.compressor.InputBuffer = this.buffer;
|
||||
this.index = ix;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
629
SabreTools.IO/Compression/Deflate/ZlibBaseStream.cs
Normal file
629
SabreTools.IO/Compression/Deflate/ZlibBaseStream.cs
Normal file
@@ -0,0 +1,629 @@
|
||||
// ZlibBaseStream.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009 Dino Chiesa and Microsoft Corporation.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2011-August-06 21:22:38>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines the ZlibBaseStream class, which is an intnernal
|
||||
// base class for DeflateStream, ZlibStream and GZipStream.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
#nullable disable
|
||||
namespace SabreTools.IO.Compression.Deflate
|
||||
{
|
||||
internal class ZlibBaseStream : System.IO.Stream
|
||||
{
|
||||
protected internal ZlibCodec _z = null; // deferred init... new ZlibCodec();
|
||||
|
||||
protected internal StreamMode _streamMode = StreamMode.Undefined;
|
||||
protected internal FlushType _flushMode;
|
||||
protected internal ZlibStreamFlavor _flavor;
|
||||
protected internal CompressionMode _compressionMode;
|
||||
protected internal CompressionLevel _level;
|
||||
protected internal bool _leaveOpen;
|
||||
protected internal byte[] _workingBuffer;
|
||||
protected internal int _bufferSize = ZlibConstants.WorkingBufferSizeDefault;
|
||||
protected internal byte[] _buf1 = new byte[1];
|
||||
|
||||
protected internal System.IO.Stream _stream;
|
||||
protected internal CompressionStrategy Strategy = CompressionStrategy.Default;
|
||||
|
||||
// workitem 7159
|
||||
readonly CRC32 crc;
|
||||
protected internal string _GzipFileName;
|
||||
protected internal string _GzipComment;
|
||||
protected internal DateTime _GzipMtime;
|
||||
protected internal int _gzipHeaderByteCount;
|
||||
|
||||
internal int Crc32 { get { if (crc == null) return 0; return crc.Crc32Result; } }
|
||||
|
||||
public ZlibBaseStream(System.IO.Stream stream,
|
||||
CompressionMode compressionMode,
|
||||
CompressionLevel level,
|
||||
ZlibStreamFlavor flavor,
|
||||
bool leaveOpen)
|
||||
: base()
|
||||
{
|
||||
this._flushMode = FlushType.None;
|
||||
//this._workingBuffer = new byte[WORKING_BUFFER_SIZE_DEFAULT];
|
||||
this._stream = stream;
|
||||
this._leaveOpen = leaveOpen;
|
||||
this._compressionMode = compressionMode;
|
||||
this._flavor = flavor;
|
||||
this._level = level;
|
||||
// workitem 7159
|
||||
if (flavor == ZlibStreamFlavor.GZIP)
|
||||
{
|
||||
this.crc = new CRC32();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected internal bool _wantCompress
|
||||
{
|
||||
get
|
||||
{
|
||||
return (this._compressionMode == CompressionMode.Compress);
|
||||
}
|
||||
}
|
||||
|
||||
private ZlibCodec z
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_z == null)
|
||||
{
|
||||
bool wantRfc1950Header = (this._flavor == ZlibStreamFlavor.ZLIB);
|
||||
_z = new ZlibCodec();
|
||||
if (this._compressionMode == CompressionMode.Decompress)
|
||||
{
|
||||
_z.InitializeInflate(wantRfc1950Header);
|
||||
}
|
||||
else
|
||||
{
|
||||
_z.Strategy = Strategy;
|
||||
_z.InitializeDeflate(this._level, wantRfc1950Header);
|
||||
}
|
||||
}
|
||||
return _z;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private byte[] workingBuffer
|
||||
{
|
||||
get
|
||||
{
|
||||
_workingBuffer ??= new byte[_bufferSize];
|
||||
return _workingBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public override void Write(System.Byte[] buffer, int offset, int count)
|
||||
{
|
||||
// workitem 7159
|
||||
// calculate the CRC on the unccompressed data (before writing)
|
||||
crc?.SlurpBlock(buffer, offset, count);
|
||||
|
||||
if (_streamMode == StreamMode.Undefined)
|
||||
_streamMode = StreamMode.Writer;
|
||||
else if (_streamMode != StreamMode.Writer)
|
||||
throw new ZlibException("Cannot Write after Reading.");
|
||||
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
// first reference of z property will initialize the private var _z
|
||||
z.InputBuffer = buffer;
|
||||
_z.NextIn = offset;
|
||||
_z.AvailableBytesIn = count;
|
||||
bool done;
|
||||
do
|
||||
{
|
||||
_z.OutputBuffer = workingBuffer;
|
||||
_z.NextOut = 0;
|
||||
_z.AvailableBytesOut = _workingBuffer.Length;
|
||||
int rc = (_wantCompress)
|
||||
? _z.Deflate(_flushMode)
|
||||
: _z.Inflate(_flushMode);
|
||||
if (rc != ZlibConstants.Z_OK && rc != ZlibConstants.Z_STREAM_END)
|
||||
throw new ZlibException((_wantCompress ? "de" : "in") + "flating: " + _z.Message);
|
||||
|
||||
//if (_workingBuffer.Length - _z.AvailableBytesOut > 0)
|
||||
_stream.Write(_workingBuffer, 0, _workingBuffer.Length - _z.AvailableBytesOut);
|
||||
|
||||
done = _z.AvailableBytesIn == 0 && _z.AvailableBytesOut != 0;
|
||||
|
||||
// If GZIP and de-compress, we're done when 8 bytes remain.
|
||||
if (_flavor == ZlibStreamFlavor.GZIP && !_wantCompress)
|
||||
done = (_z.AvailableBytesIn == 8 && _z.AvailableBytesOut != 0);
|
||||
|
||||
}
|
||||
while (!done);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void finish()
|
||||
{
|
||||
if (_z == null) return;
|
||||
|
||||
if (_streamMode == StreamMode.Writer)
|
||||
{
|
||||
bool done;
|
||||
do
|
||||
{
|
||||
_z.OutputBuffer = workingBuffer;
|
||||
_z.NextOut = 0;
|
||||
_z.AvailableBytesOut = _workingBuffer.Length;
|
||||
int rc = (_wantCompress)
|
||||
? _z.Deflate(FlushType.Finish)
|
||||
: _z.Inflate(FlushType.Finish);
|
||||
|
||||
if (rc != ZlibConstants.Z_STREAM_END && rc != ZlibConstants.Z_OK)
|
||||
{
|
||||
string verb = (_wantCompress ? "de" : "in") + "flating";
|
||||
if (_z.Message == null)
|
||||
throw new ZlibException(String.Format("{0}: (rc = {1})", verb, rc));
|
||||
else
|
||||
throw new ZlibException(verb + ": " + _z.Message);
|
||||
}
|
||||
|
||||
if (_workingBuffer.Length - _z.AvailableBytesOut > 0)
|
||||
{
|
||||
_stream.Write(_workingBuffer, 0, _workingBuffer.Length - _z.AvailableBytesOut);
|
||||
}
|
||||
|
||||
done = _z.AvailableBytesIn == 0 && _z.AvailableBytesOut != 0;
|
||||
// If GZIP and de-compress, we're done when 8 bytes remain.
|
||||
if (_flavor == ZlibStreamFlavor.GZIP && !_wantCompress)
|
||||
done = (_z.AvailableBytesIn == 8 && _z.AvailableBytesOut != 0);
|
||||
|
||||
}
|
||||
while (!done);
|
||||
|
||||
Flush();
|
||||
|
||||
// workitem 7159
|
||||
if (_flavor == ZlibStreamFlavor.GZIP)
|
||||
{
|
||||
if (_wantCompress)
|
||||
{
|
||||
// Emit the GZIP trailer: CRC32 and size mod 2^32
|
||||
int c1 = crc.Crc32Result;
|
||||
_stream.Write(BitConverter.GetBytes(c1), 0, 4);
|
||||
int c2 = (Int32)(crc.TotalBytesRead & 0x00000000FFFFFFFF);
|
||||
_stream.Write(BitConverter.GetBytes(c2), 0, 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ZlibException("Writing with decompression is not supported.");
|
||||
}
|
||||
}
|
||||
}
|
||||
// workitem 7159
|
||||
else if (_streamMode == StreamMode.Reader)
|
||||
{
|
||||
if (_flavor == ZlibStreamFlavor.GZIP)
|
||||
{
|
||||
if (!_wantCompress)
|
||||
{
|
||||
// workitem 8501: handle edge case (decompress empty stream)
|
||||
if (_z.TotalBytesOut == 0L)
|
||||
return;
|
||||
|
||||
// Read and potentially verify the GZIP trailer:
|
||||
// CRC32 and size mod 2^32
|
||||
byte[] trailer = new byte[8];
|
||||
|
||||
// workitems 8679 & 12554
|
||||
if (_z.AvailableBytesIn < 8)
|
||||
{
|
||||
// Make sure we have read to the end of the stream
|
||||
Array.Copy(_z.InputBuffer, _z.NextIn, trailer, 0, _z.AvailableBytesIn);
|
||||
int bytesNeeded = 8 - _z.AvailableBytesIn;
|
||||
int bytesRead = _stream.Read(trailer,
|
||||
_z.AvailableBytesIn,
|
||||
bytesNeeded);
|
||||
if (bytesNeeded != bytesRead)
|
||||
{
|
||||
throw new ZlibException(String.Format("Missing or incomplete GZIP trailer. Expected 8 bytes, got {0}.",
|
||||
_z.AvailableBytesIn + bytesRead));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Array.Copy(_z.InputBuffer, _z.NextIn, trailer, 0, trailer.Length);
|
||||
}
|
||||
|
||||
Int32 crc32_expected = BitConverter.ToInt32(trailer, 0);
|
||||
Int32 crc32_actual = crc.Crc32Result;
|
||||
Int32 isize_expected = BitConverter.ToInt32(trailer, 4);
|
||||
Int32 isize_actual = (Int32)(_z.TotalBytesOut & 0x00000000FFFFFFFF);
|
||||
|
||||
if (crc32_actual != crc32_expected)
|
||||
throw new ZlibException(String.Format("Bad CRC32 in GZIP trailer. (actual({0:X8})!=expected({1:X8}))", crc32_actual, crc32_expected));
|
||||
|
||||
if (isize_actual != isize_expected)
|
||||
throw new ZlibException(String.Format("Bad size in GZIP trailer. (actual({0})!=expected({1}))", isize_actual, isize_expected));
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ZlibException("Reading with compression is not supported.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void end()
|
||||
{
|
||||
if (z == null)
|
||||
return;
|
||||
if (_wantCompress)
|
||||
{
|
||||
_z.EndDeflate();
|
||||
}
|
||||
else
|
||||
{
|
||||
_z.EndInflate();
|
||||
}
|
||||
_z = null;
|
||||
}
|
||||
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
if (_stream == null) return;
|
||||
try
|
||||
{
|
||||
finish();
|
||||
}
|
||||
finally
|
||||
{
|
||||
end();
|
||||
if (!_leaveOpen) _stream.Close();
|
||||
_stream = null;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
_stream.Flush();
|
||||
}
|
||||
|
||||
public override System.Int64 Seek(System.Int64 offset, System.IO.SeekOrigin origin)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
//_outStream.Seek(offset, origin);
|
||||
}
|
||||
public override void SetLength(System.Int64 value)
|
||||
{
|
||||
_stream.SetLength(value);
|
||||
}
|
||||
|
||||
|
||||
#if NOT
|
||||
public int Read()
|
||||
{
|
||||
if (Read(_buf1, 0, 1) == 0)
|
||||
return 0;
|
||||
// calculate CRC after reading
|
||||
if (crc!=null)
|
||||
crc.SlurpBlock(_buf1,0,1);
|
||||
return (_buf1[0] & 0xFF);
|
||||
}
|
||||
#endif
|
||||
|
||||
private bool nomoreinput = false;
|
||||
|
||||
|
||||
|
||||
private string ReadZeroTerminatedString()
|
||||
{
|
||||
var list = new System.Collections.Generic.List<byte>();
|
||||
bool done = false;
|
||||
do
|
||||
{
|
||||
// workitem 7740
|
||||
int n = _stream.Read(_buf1, 0, 1);
|
||||
if (n != 1)
|
||||
throw new ZlibException("Unexpected EOF reading GZIP header.");
|
||||
else
|
||||
{
|
||||
if (_buf1[0] == 0)
|
||||
done = true;
|
||||
else
|
||||
list.Add(_buf1[0]);
|
||||
}
|
||||
} while (!done);
|
||||
byte[] a = [.. list];
|
||||
return GZipStream.iso8859dash1.GetString(a, 0, a.Length);
|
||||
}
|
||||
|
||||
|
||||
private int _ReadAndValidateGzipHeader()
|
||||
{
|
||||
int totalBytesRead = 0;
|
||||
// read the header on the first read
|
||||
byte[] header = new byte[10];
|
||||
int n = _stream.Read(header, 0, header.Length);
|
||||
|
||||
// workitem 8501: handle edge case (decompress empty stream)
|
||||
if (n == 0)
|
||||
return 0;
|
||||
|
||||
if (n != 10)
|
||||
throw new ZlibException("Not a valid GZIP stream.");
|
||||
|
||||
if (header[0] != 0x1F || header[1] != 0x8B || header[2] != 8)
|
||||
throw new ZlibException("Bad GZIP header.");
|
||||
|
||||
Int32 timet = BitConverter.ToInt32(header, 4);
|
||||
_GzipMtime = GZipStream._unixEpoch.AddSeconds(timet);
|
||||
totalBytesRead += n;
|
||||
if ((header[3] & 0x04) == 0x04)
|
||||
{
|
||||
// read and discard extra field
|
||||
n = _stream.Read(header, 0, 2); // 2-byte length field
|
||||
totalBytesRead += n;
|
||||
|
||||
Int16 extraLength = (Int16)(header[0] + header[1] * 256);
|
||||
byte[] extra = new byte[extraLength];
|
||||
n = _stream.Read(extra, 0, extra.Length);
|
||||
if (n != extraLength)
|
||||
throw new ZlibException("Unexpected end-of-file reading GZIP header.");
|
||||
totalBytesRead += n;
|
||||
}
|
||||
if ((header[3] & 0x08) == 0x08)
|
||||
_GzipFileName = ReadZeroTerminatedString();
|
||||
if ((header[3] & 0x10) == 0x010)
|
||||
_GzipComment = ReadZeroTerminatedString();
|
||||
if ((header[3] & 0x02) == 0x02)
|
||||
_ = Read(_buf1, 0, 1); // CRC16, ignore
|
||||
|
||||
return totalBytesRead;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public override System.Int32 Read(System.Byte[] buffer, System.Int32 offset, System.Int32 count)
|
||||
{
|
||||
// According to MS documentation, any implementation of the IO.Stream.Read function must:
|
||||
// (a) throw an exception if offset & count reference an invalid part of the buffer,
|
||||
// or if count < 0, or if buffer is null
|
||||
// (b) return 0 only upon EOF, or if count = 0
|
||||
// (c) if not EOF, then return at least 1 byte, up to <count> bytes
|
||||
|
||||
if (_streamMode == StreamMode.Undefined)
|
||||
{
|
||||
if (!this._stream.CanRead) throw new ZlibException("The stream is not readable.");
|
||||
// for the first read, set up some controls.
|
||||
_streamMode = StreamMode.Reader;
|
||||
// (The first reference to _z goes through the private accessor which
|
||||
// may initialize it.)
|
||||
z.AvailableBytesIn = 0;
|
||||
if (_flavor == ZlibStreamFlavor.GZIP)
|
||||
{
|
||||
_gzipHeaderByteCount = _ReadAndValidateGzipHeader();
|
||||
// workitem 8501: handle edge case (decompress empty stream)
|
||||
if (_gzipHeaderByteCount == 0)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (_streamMode != StreamMode.Reader)
|
||||
throw new ZlibException("Cannot Read after Writing.");
|
||||
|
||||
if (count == 0) return 0;
|
||||
if (nomoreinput && _wantCompress) return 0; // workitem 8557
|
||||
if (buffer == null) throw new ArgumentNullException("buffer");
|
||||
if (count < 0) throw new ArgumentOutOfRangeException("count");
|
||||
if (offset < buffer.GetLowerBound(0)) throw new ArgumentOutOfRangeException("offset");
|
||||
if ((offset + count) > buffer.GetLength(0)) throw new ArgumentOutOfRangeException("count");
|
||||
|
||||
int rc;
|
||||
|
||||
// set up the output of the deflate/inflate codec:
|
||||
_z.OutputBuffer = buffer;
|
||||
_z.NextOut = offset;
|
||||
_z.AvailableBytesOut = count;
|
||||
|
||||
// This is necessary in case _workingBuffer has been resized. (new byte[])
|
||||
// (The first reference to _workingBuffer goes through the private accessor which
|
||||
// may initialize it.)
|
||||
_z.InputBuffer = workingBuffer;
|
||||
|
||||
do
|
||||
{
|
||||
// need data in _workingBuffer in order to deflate/inflate. Here, we check if we have any.
|
||||
if ((_z.AvailableBytesIn == 0) && (!nomoreinput))
|
||||
{
|
||||
// No data available, so try to Read data from the captive stream.
|
||||
_z.NextIn = 0;
|
||||
_z.AvailableBytesIn = _stream.Read(_workingBuffer, 0, _workingBuffer.Length);
|
||||
if (_z.AvailableBytesIn == 0)
|
||||
nomoreinput = true;
|
||||
|
||||
}
|
||||
// we have data in InputBuffer; now compress or decompress as appropriate
|
||||
rc = (_wantCompress)
|
||||
? _z.Deflate(_flushMode)
|
||||
: _z.Inflate(_flushMode);
|
||||
|
||||
if (nomoreinput && (rc == ZlibConstants.Z_BUF_ERROR))
|
||||
return 0;
|
||||
|
||||
if (rc != ZlibConstants.Z_OK && rc != ZlibConstants.Z_STREAM_END)
|
||||
throw new ZlibException(String.Format("{0}flating: rc={1} msg={2}", (_wantCompress ? "de" : "in"), rc, _z.Message));
|
||||
|
||||
if ((nomoreinput || rc == ZlibConstants.Z_STREAM_END) && (_z.AvailableBytesOut == count))
|
||||
break; // nothing more to read
|
||||
}
|
||||
//while (_z.AvailableBytesOut == count && rc == ZlibConstants.Z_OK);
|
||||
while (_z.AvailableBytesOut > 0 && !nomoreinput && rc == ZlibConstants.Z_OK);
|
||||
|
||||
|
||||
// workitem 8557
|
||||
// is there more room in output?
|
||||
if (_z.AvailableBytesOut > 0)
|
||||
{
|
||||
if (rc == ZlibConstants.Z_OK && _z.AvailableBytesIn == 0)
|
||||
{
|
||||
// deferred
|
||||
}
|
||||
|
||||
// are we completely done reading?
|
||||
if (nomoreinput)
|
||||
{
|
||||
// and in compression?
|
||||
if (_wantCompress)
|
||||
{
|
||||
// no more input data available; therefore we flush to
|
||||
// try to complete the read
|
||||
rc = _z.Deflate(FlushType.Finish);
|
||||
|
||||
if (rc != ZlibConstants.Z_OK && rc != ZlibConstants.Z_STREAM_END)
|
||||
throw new ZlibException(String.Format("Deflating: rc={0} msg={1}", rc, _z.Message));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
rc = (count - _z.AvailableBytesOut);
|
||||
|
||||
// calculate CRC after reading
|
||||
crc?.SlurpBlock(buffer, offset, rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public override System.Boolean CanRead
|
||||
{
|
||||
get { return this._stream.CanRead; }
|
||||
}
|
||||
|
||||
public override System.Boolean CanSeek
|
||||
{
|
||||
get { return this._stream.CanSeek; }
|
||||
}
|
||||
|
||||
public override System.Boolean CanWrite
|
||||
{
|
||||
get { return this._stream.CanWrite; }
|
||||
}
|
||||
|
||||
public override System.Int64 Length
|
||||
{
|
||||
get { return _stream.Length; }
|
||||
}
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
set { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
internal enum StreamMode
|
||||
{
|
||||
Writer,
|
||||
Reader,
|
||||
Undefined,
|
||||
}
|
||||
|
||||
|
||||
public static void CompressString(String s, Stream compressor)
|
||||
{
|
||||
byte[] uncompressed = System.Text.Encoding.UTF8.GetBytes(s);
|
||||
using (compressor)
|
||||
{
|
||||
compressor.Write(uncompressed, 0, uncompressed.Length);
|
||||
}
|
||||
}
|
||||
|
||||
public static void CompressBuffer(byte[] b, Stream compressor)
|
||||
{
|
||||
// workitem 8460
|
||||
using (compressor)
|
||||
{
|
||||
compressor.Write(b, 0, b.Length);
|
||||
}
|
||||
}
|
||||
|
||||
public static String UncompressString(byte[] compressed, Stream decompressor)
|
||||
{
|
||||
// workitem 8460
|
||||
byte[] working = new byte[1024];
|
||||
var encoding = System.Text.Encoding.UTF8;
|
||||
using var output = new MemoryStream();
|
||||
using (decompressor)
|
||||
{
|
||||
int n;
|
||||
while ((n = decompressor.Read(working, 0, working.Length)) != 0)
|
||||
{
|
||||
output.Write(working, 0, n);
|
||||
}
|
||||
}
|
||||
|
||||
// reset to allow read from start
|
||||
output.Seek(0, SeekOrigin.Begin);
|
||||
var sr = new StreamReader(output, encoding);
|
||||
return sr.ReadToEnd();
|
||||
}
|
||||
|
||||
public static byte[] UncompressBuffer(byte[] compressed, Stream decompressor)
|
||||
{
|
||||
// workitem 8460
|
||||
byte[] working = new byte[1024];
|
||||
using var output = new MemoryStream();
|
||||
using (decompressor)
|
||||
{
|
||||
int n;
|
||||
while ((n = decompressor.Read(working, 0, working.Length)) != 0)
|
||||
{
|
||||
output.Write(working, 0, n);
|
||||
}
|
||||
}
|
||||
return output.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the dictionary to be used for either Inflation or Deflation.
|
||||
/// </summary>
|
||||
/// <param name="dictionary">The dictionary bytes to use.</param>
|
||||
/// <param name="check">Determines if dictionary checks are run</param>
|
||||
/// <returns>Z_OK if all goes well.</returns>
|
||||
public int SetDictionary(byte[] dictionary, bool check = true)
|
||||
{
|
||||
return z.SetDictionary(dictionary, check);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
721
SabreTools.IO/Compression/Deflate/ZlibCodec.cs
Normal file
721
SabreTools.IO/Compression/Deflate/ZlibCodec.cs
Normal file
@@ -0,0 +1,721 @@
|
||||
// ZlibCodec.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009 Dino Chiesa and Microsoft Corporation.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2009-November-03 15:40:51>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines a Codec for ZLIB compression and
|
||||
// decompression. This code extends code that was based the jzlib
|
||||
// implementation of zlib, but this code is completely novel. The codec
|
||||
// class is new, and encapsulates some behaviors that are new, and some
|
||||
// that were present in other classes in the jzlib code base. In
|
||||
// keeping with the license for jzlib, the copyright to the jzlib code
|
||||
// is included below.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in
|
||||
// the documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. The names of the authors may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
// INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
// This program is based on zlib-1.1.3; credit to authors
|
||||
// Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu)
|
||||
// and contributors of zlib.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
|
||||
using System;
|
||||
using Interop=System.Runtime.InteropServices;
|
||||
|
||||
#nullable disable
|
||||
namespace SabreTools.IO.Compression.Deflate
|
||||
{
|
||||
/// <summary>
|
||||
/// Encoder and Decoder for ZLIB and DEFLATE (IETF RFC1950 and RFC1951).
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// This class compresses and decompresses data according to the Deflate algorithm
|
||||
/// and optionally, the ZLIB format, as documented in <see
|
||||
/// href="http://www.ietf.org/rfc/rfc1950.txt">RFC 1950 - ZLIB</see> and <see
|
||||
/// href="http://www.ietf.org/rfc/rfc1951.txt">RFC 1951 - DEFLATE</see>.
|
||||
/// </remarks>
|
||||
[Interop.GuidAttribute("ebc25cf6-9120-4283-b972-0e5520d0000D")]
|
||||
[Interop.ComVisible(true)]
|
||||
#if !NETCF
|
||||
[Interop.ClassInterface(Interop.ClassInterfaceType.AutoDispatch)]
|
||||
#endif
|
||||
sealed public class ZlibCodec
|
||||
{
|
||||
/// <summary>
|
||||
/// The buffer from which data is taken.
|
||||
/// </summary>
|
||||
public byte[] InputBuffer;
|
||||
|
||||
/// <summary>
|
||||
/// An index into the InputBuffer array, indicating where to start reading.
|
||||
/// </summary>
|
||||
public int NextIn;
|
||||
|
||||
/// <summary>
|
||||
/// The number of bytes available in the InputBuffer, starting at NextIn.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Generally you should set this to InputBuffer.Length before the first Inflate() or Deflate() call.
|
||||
/// The class will update this number as calls to Inflate/Deflate are made.
|
||||
/// </remarks>
|
||||
public int AvailableBytesIn;
|
||||
|
||||
/// <summary>
|
||||
/// Total number of bytes read so far, through all calls to Inflate()/Deflate().
|
||||
/// </summary>
|
||||
public long TotalBytesIn;
|
||||
|
||||
/// <summary>
|
||||
/// Buffer to store output data.
|
||||
/// </summary>
|
||||
public byte[] OutputBuffer;
|
||||
|
||||
/// <summary>
|
||||
/// An index into the OutputBuffer array, indicating where to start writing.
|
||||
/// </summary>
|
||||
public int NextOut;
|
||||
|
||||
/// <summary>
|
||||
/// The number of bytes available in the OutputBuffer, starting at NextOut.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Generally you should set this to OutputBuffer.Length before the first Inflate() or Deflate() call.
|
||||
/// The class will update this number as calls to Inflate/Deflate are made.
|
||||
/// </remarks>
|
||||
public int AvailableBytesOut;
|
||||
|
||||
/// <summary>
|
||||
/// Total number of bytes written to the output so far, through all calls to Inflate()/Deflate().
|
||||
/// </summary>
|
||||
public long TotalBytesOut;
|
||||
|
||||
/// <summary>
|
||||
/// used for diagnostics, when something goes wrong!
|
||||
/// </summary>
|
||||
public System.String Message;
|
||||
|
||||
internal DeflateManager dstate;
|
||||
internal InflateManager istate;
|
||||
|
||||
internal uint _Adler32;
|
||||
|
||||
/// <summary>
|
||||
/// The compression level to use in this codec. Useful only in compression mode.
|
||||
/// </summary>
|
||||
public CompressionLevel CompressLevel = CompressionLevel.Default;
|
||||
|
||||
/// <summary>
|
||||
/// The number of Window Bits to use.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This gauges the size of the sliding window, and hence the
|
||||
/// compression effectiveness as well as memory consumption. It's best to just leave this
|
||||
/// setting alone if you don't know what it is. The maximum value is 15 bits, which implies
|
||||
/// a 32k window.
|
||||
/// </remarks>
|
||||
public int WindowBits = ZlibConstants.WindowBitsDefault;
|
||||
|
||||
/// <summary>
|
||||
/// The compression strategy to use.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is only effective in compression. The theory offered by ZLIB is that different
|
||||
/// strategies could potentially produce significant differences in compression behavior
|
||||
/// for different data sets. Unfortunately I don't have any good recommendations for how
|
||||
/// to set it differently. When I tested changing the strategy I got minimally different
|
||||
/// compression performance. It's best to leave this property alone if you don't have a
|
||||
/// good feel for it. Or, you may want to produce a test harness that runs through the
|
||||
/// different strategy options and evaluates them on different file types. If you do that,
|
||||
/// let me know your results.
|
||||
/// </remarks>
|
||||
public CompressionStrategy Strategy = CompressionStrategy.Default;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The Adler32 checksum on the data transferred through the codec so far. You probably don't need to look at this.
|
||||
/// </summary>
|
||||
public int Adler32 { get { return (int)_Adler32; } }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create a ZlibCodec.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If you use this default constructor, you will later have to explicitly call
|
||||
/// InitializeInflate() or InitializeDeflate() before using the ZlibCodec to compress
|
||||
/// or decompress.
|
||||
/// </remarks>
|
||||
public ZlibCodec() { }
|
||||
|
||||
/// <summary>
|
||||
/// Create a ZlibCodec that either compresses or decompresses.
|
||||
/// </summary>
|
||||
/// <param name="mode">
|
||||
/// Indicates whether the codec should compress (deflate) or decompress (inflate).
|
||||
/// </param>
|
||||
public ZlibCodec(CompressionMode mode)
|
||||
{
|
||||
if (mode == CompressionMode.Compress)
|
||||
{
|
||||
int rc = InitializeDeflate();
|
||||
if (rc != ZlibConstants.Z_OK) throw new ZlibException("Cannot initialize for deflate.");
|
||||
}
|
||||
else if (mode == CompressionMode.Decompress)
|
||||
{
|
||||
int rc = InitializeInflate();
|
||||
if (rc != ZlibConstants.Z_OK) throw new ZlibException("Cannot initialize for inflate.");
|
||||
}
|
||||
else throw new ZlibException("Invalid ZlibStreamFlavor.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the inflation state.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// It is not necessary to call this before using the ZlibCodec to inflate data;
|
||||
/// It is implicitly called when you call the constructor.
|
||||
/// </remarks>
|
||||
/// <returns>Z_OK if everything goes well.</returns>
|
||||
public int InitializeInflate()
|
||||
{
|
||||
return InitializeInflate(this.WindowBits);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the inflation state with an explicit flag to
|
||||
/// govern the handling of RFC1950 header bytes.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// By default, the ZLIB header defined in <see
|
||||
/// href="http://www.ietf.org/rfc/rfc1950.txt">RFC 1950</see> is expected. If
|
||||
/// you want to read a zlib stream you should specify true for
|
||||
/// expectRfc1950Header. If you have a deflate stream, you will want to specify
|
||||
/// false. It is only necessary to invoke this initializer explicitly if you
|
||||
/// want to specify false.
|
||||
/// </remarks>
|
||||
///
|
||||
/// <param name="expectRfc1950Header">whether to expect an RFC1950 header byte
|
||||
/// pair when reading the stream of data to be inflated.</param>
|
||||
///
|
||||
/// <returns>Z_OK if everything goes well.</returns>
|
||||
public int InitializeInflate(bool expectRfc1950Header)
|
||||
{
|
||||
return InitializeInflate(this.WindowBits, expectRfc1950Header);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the ZlibCodec for inflation, with the specified number of window bits.
|
||||
/// </summary>
|
||||
/// <param name="windowBits">The number of window bits to use. If you need to ask what that is,
|
||||
/// then you shouldn't be calling this initializer.</param>
|
||||
/// <returns>Z_OK if all goes well.</returns>
|
||||
public int InitializeInflate(int windowBits)
|
||||
{
|
||||
this.WindowBits = windowBits;
|
||||
return InitializeInflate(windowBits, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the inflation state with an explicit flag to govern the handling of
|
||||
/// RFC1950 header bytes.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// If you want to read a zlib stream you should specify true for
|
||||
/// expectRfc1950Header. In this case, the library will expect to find a ZLIB
|
||||
/// header, as defined in <see href="http://www.ietf.org/rfc/rfc1950.txt">RFC
|
||||
/// 1950</see>, in the compressed stream. If you will be reading a DEFLATE or
|
||||
/// GZIP stream, which does not have such a header, you will want to specify
|
||||
/// false.
|
||||
/// </remarks>
|
||||
///
|
||||
/// <param name="expectRfc1950Header">whether to expect an RFC1950 header byte pair when reading
|
||||
/// the stream of data to be inflated.</param>
|
||||
/// <param name="windowBits">The number of window bits to use. If you need to ask what that is,
|
||||
/// then you shouldn't be calling this initializer.</param>
|
||||
/// <returns>Z_OK if everything goes well.</returns>
|
||||
public int InitializeInflate(int windowBits, bool expectRfc1950Header)
|
||||
{
|
||||
this.WindowBits = windowBits;
|
||||
if (dstate != null) throw new ZlibException("You may not call InitializeInflate() after calling InitializeDeflate().");
|
||||
istate = new InflateManager(expectRfc1950Header);
|
||||
return istate.Initialize(this, windowBits);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inflate the data in the InputBuffer, placing the result in the OutputBuffer.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// You must have set InputBuffer and OutputBuffer, NextIn and NextOut, and AvailableBytesIn and
|
||||
/// AvailableBytesOut before calling this method.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// private void InflateBuffer()
|
||||
/// {
|
||||
/// int bufferSize = 1024;
|
||||
/// byte[] buffer = new byte[bufferSize];
|
||||
/// ZlibCodec decompressor = new ZlibCodec();
|
||||
///
|
||||
/// Console.WriteLine("\n============================================");
|
||||
/// Console.WriteLine("Size of Buffer to Inflate: {0} bytes.", CompressedBytes.Length);
|
||||
/// MemoryStream ms = new MemoryStream(DecompressedBytes);
|
||||
///
|
||||
/// int rc = decompressor.InitializeInflate();
|
||||
///
|
||||
/// decompressor.InputBuffer = CompressedBytes;
|
||||
/// decompressor.NextIn = 0;
|
||||
/// decompressor.AvailableBytesIn = CompressedBytes.Length;
|
||||
///
|
||||
/// decompressor.OutputBuffer = buffer;
|
||||
///
|
||||
/// // pass 1: inflate
|
||||
/// do
|
||||
/// {
|
||||
/// decompressor.NextOut = 0;
|
||||
/// decompressor.AvailableBytesOut = buffer.Length;
|
||||
/// rc = decompressor.Inflate(FlushType.None);
|
||||
///
|
||||
/// if (rc != ZlibConstants.Z_OK && rc != ZlibConstants.Z_STREAM_END)
|
||||
/// throw new Exception("inflating: " + decompressor.Message);
|
||||
///
|
||||
/// ms.Write(decompressor.OutputBuffer, 0, buffer.Length - decompressor.AvailableBytesOut);
|
||||
/// }
|
||||
/// while (decompressor.AvailableBytesIn > 0 || decompressor.AvailableBytesOut == 0);
|
||||
///
|
||||
/// // pass 2: finish and flush
|
||||
/// do
|
||||
/// {
|
||||
/// decompressor.NextOut = 0;
|
||||
/// decompressor.AvailableBytesOut = buffer.Length;
|
||||
/// rc = decompressor.Inflate(FlushType.Finish);
|
||||
///
|
||||
/// if (rc != ZlibConstants.Z_STREAM_END && rc != ZlibConstants.Z_OK)
|
||||
/// throw new Exception("inflating: " + decompressor.Message);
|
||||
///
|
||||
/// if (buffer.Length - decompressor.AvailableBytesOut > 0)
|
||||
/// ms.Write(buffer, 0, buffer.Length - decompressor.AvailableBytesOut);
|
||||
/// }
|
||||
/// while (decompressor.AvailableBytesIn > 0 || decompressor.AvailableBytesOut == 0);
|
||||
///
|
||||
/// decompressor.EndInflate();
|
||||
/// }
|
||||
///
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="flush">The flush to use when inflating.</param>
|
||||
/// <returns>Z_OK if everything goes well.</returns>
|
||||
public int Inflate(FlushType flush)
|
||||
{
|
||||
if (istate == null)
|
||||
throw new ZlibException("No Inflate State!");
|
||||
return istate.Inflate(flush);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Ends an inflation session.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Call this after successively calling Inflate(). This will cause all buffers to be flushed.
|
||||
/// After calling this you cannot call Inflate() without a intervening call to one of the
|
||||
/// InitializeInflate() overloads.
|
||||
/// </remarks>
|
||||
/// <returns>Z_OK if everything goes well.</returns>
|
||||
public int EndInflate()
|
||||
{
|
||||
if (istate == null)
|
||||
throw new ZlibException("No Inflate State!");
|
||||
int ret = istate.End();
|
||||
istate = null;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// I don't know what this does!
|
||||
/// </summary>
|
||||
/// <returns>Z_OK if everything goes well.</returns>
|
||||
public int SyncInflate()
|
||||
{
|
||||
if (istate == null)
|
||||
throw new ZlibException("No Inflate State!");
|
||||
return istate.Sync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the ZlibCodec for deflation operation.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The codec will use the MAX window bits and the default level of compression.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// int bufferSize = 40000;
|
||||
/// byte[] CompressedBytes = new byte[bufferSize];
|
||||
/// byte[] DecompressedBytes = new byte[bufferSize];
|
||||
///
|
||||
/// ZlibCodec compressor = new ZlibCodec();
|
||||
///
|
||||
/// compressor.InitializeDeflate(CompressionLevel.Default);
|
||||
///
|
||||
/// compressor.InputBuffer = System.Text.ASCIIEncoding.ASCII.GetBytes(TextToCompress);
|
||||
/// compressor.NextIn = 0;
|
||||
/// compressor.AvailableBytesIn = compressor.InputBuffer.Length;
|
||||
///
|
||||
/// compressor.OutputBuffer = CompressedBytes;
|
||||
/// compressor.NextOut = 0;
|
||||
/// compressor.AvailableBytesOut = CompressedBytes.Length;
|
||||
///
|
||||
/// while (compressor.TotalBytesIn != TextToCompress.Length && compressor.TotalBytesOut < bufferSize)
|
||||
/// {
|
||||
/// compressor.Deflate(FlushType.None);
|
||||
/// }
|
||||
///
|
||||
/// while (true)
|
||||
/// {
|
||||
/// int rc= compressor.Deflate(FlushType.Finish);
|
||||
/// if (rc == ZlibConstants.Z_STREAM_END) break;
|
||||
/// }
|
||||
///
|
||||
/// compressor.EndDeflate();
|
||||
///
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <returns>Z_OK if all goes well. You generally don't need to check the return code.</returns>
|
||||
public int InitializeDeflate()
|
||||
{
|
||||
return _InternalInitializeDeflate(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the ZlibCodec for deflation operation, using the specified CompressionLevel.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The codec will use the maximum window bits (15) and the specified
|
||||
/// CompressionLevel. It will emit a ZLIB stream as it compresses.
|
||||
/// </remarks>
|
||||
/// <param name="level">The compression level for the codec.</param>
|
||||
/// <returns>Z_OK if all goes well.</returns>
|
||||
public int InitializeDeflate(CompressionLevel level)
|
||||
{
|
||||
this.CompressLevel = level;
|
||||
return _InternalInitializeDeflate(true);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the ZlibCodec for deflation operation, using the specified CompressionLevel,
|
||||
/// and the explicit flag governing whether to emit an RFC1950 header byte pair.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The codec will use the maximum window bits (15) and the specified CompressionLevel.
|
||||
/// If you want to generate a zlib stream, you should specify true for
|
||||
/// wantRfc1950Header. In this case, the library will emit a ZLIB
|
||||
/// header, as defined in <see href="http://www.ietf.org/rfc/rfc1950.txt">RFC
|
||||
/// 1950</see>, in the compressed stream.
|
||||
/// </remarks>
|
||||
/// <param name="level">The compression level for the codec.</param>
|
||||
/// <param name="wantRfc1950Header">whether to emit an initial RFC1950 byte pair in the compressed stream.</param>
|
||||
/// <returns>Z_OK if all goes well.</returns>
|
||||
public int InitializeDeflate(CompressionLevel level, bool wantRfc1950Header)
|
||||
{
|
||||
this.CompressLevel = level;
|
||||
return _InternalInitializeDeflate(wantRfc1950Header);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the ZlibCodec for deflation operation, using the specified CompressionLevel,
|
||||
/// and the specified number of window bits.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The codec will use the specified number of window bits and the specified CompressionLevel.
|
||||
/// </remarks>
|
||||
/// <param name="level">The compression level for the codec.</param>
|
||||
/// <param name="bits">the number of window bits to use. If you don't know what this means, don't use this method.</param>
|
||||
/// <returns>Z_OK if all goes well.</returns>
|
||||
public int InitializeDeflate(CompressionLevel level, int bits)
|
||||
{
|
||||
this.CompressLevel = level;
|
||||
this.WindowBits = bits;
|
||||
return _InternalInitializeDeflate(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the ZlibCodec for deflation operation, using the specified
|
||||
/// CompressionLevel, the specified number of window bits, and the explicit flag
|
||||
/// governing whether to emit an RFC1950 header byte pair.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="level">The compression level for the codec.</param>
|
||||
/// <param name="wantRfc1950Header">whether to emit an initial RFC1950 byte pair in the compressed stream.</param>
|
||||
/// <param name="bits">the number of window bits to use. If you don't know what this means, don't use this method.</param>
|
||||
/// <returns>Z_OK if all goes well.</returns>
|
||||
public int InitializeDeflate(CompressionLevel level, int bits, bool wantRfc1950Header)
|
||||
{
|
||||
this.CompressLevel = level;
|
||||
this.WindowBits = bits;
|
||||
return _InternalInitializeDeflate(wantRfc1950Header);
|
||||
}
|
||||
|
||||
private int _InternalInitializeDeflate(bool wantRfc1950Header)
|
||||
{
|
||||
if (istate != null) throw new ZlibException("You may not call InitializeDeflate() after calling InitializeInflate().");
|
||||
dstate = new DeflateManager
|
||||
{
|
||||
WantRfc1950HeaderBytes = wantRfc1950Header
|
||||
};
|
||||
|
||||
return dstate.Initialize(this, this.CompressLevel, this.WindowBits, this.Strategy);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deflate one batch of data.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// You must have set InputBuffer and OutputBuffer before calling this method.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// private void DeflateBuffer(CompressionLevel level)
|
||||
/// {
|
||||
/// int bufferSize = 1024;
|
||||
/// byte[] buffer = new byte[bufferSize];
|
||||
/// ZlibCodec compressor = new ZlibCodec();
|
||||
///
|
||||
/// Console.WriteLine("\n============================================");
|
||||
/// Console.WriteLine("Size of Buffer to Deflate: {0} bytes.", UncompressedBytes.Length);
|
||||
/// MemoryStream ms = new MemoryStream();
|
||||
///
|
||||
/// int rc = compressor.InitializeDeflate(level);
|
||||
///
|
||||
/// compressor.InputBuffer = UncompressedBytes;
|
||||
/// compressor.NextIn = 0;
|
||||
/// compressor.AvailableBytesIn = UncompressedBytes.Length;
|
||||
///
|
||||
/// compressor.OutputBuffer = buffer;
|
||||
///
|
||||
/// // pass 1: deflate
|
||||
/// do
|
||||
/// {
|
||||
/// compressor.NextOut = 0;
|
||||
/// compressor.AvailableBytesOut = buffer.Length;
|
||||
/// rc = compressor.Deflate(FlushType.None);
|
||||
///
|
||||
/// if (rc != ZlibConstants.Z_OK && rc != ZlibConstants.Z_STREAM_END)
|
||||
/// throw new Exception("deflating: " + compressor.Message);
|
||||
///
|
||||
/// ms.Write(compressor.OutputBuffer, 0, buffer.Length - compressor.AvailableBytesOut);
|
||||
/// }
|
||||
/// while (compressor.AvailableBytesIn > 0 || compressor.AvailableBytesOut == 0);
|
||||
///
|
||||
/// // pass 2: finish and flush
|
||||
/// do
|
||||
/// {
|
||||
/// compressor.NextOut = 0;
|
||||
/// compressor.AvailableBytesOut = buffer.Length;
|
||||
/// rc = compressor.Deflate(FlushType.Finish);
|
||||
///
|
||||
/// if (rc != ZlibConstants.Z_STREAM_END && rc != ZlibConstants.Z_OK)
|
||||
/// throw new Exception("deflating: " + compressor.Message);
|
||||
///
|
||||
/// if (buffer.Length - compressor.AvailableBytesOut > 0)
|
||||
/// ms.Write(buffer, 0, buffer.Length - compressor.AvailableBytesOut);
|
||||
/// }
|
||||
/// while (compressor.AvailableBytesIn > 0 || compressor.AvailableBytesOut == 0);
|
||||
///
|
||||
/// compressor.EndDeflate();
|
||||
///
|
||||
/// ms.Seek(0, SeekOrigin.Begin);
|
||||
/// CompressedBytes = new byte[compressor.TotalBytesOut];
|
||||
/// ms.Read(CompressedBytes, 0, CompressedBytes.Length);
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="flush">whether to flush all data as you deflate. Generally you will want to
|
||||
/// use Z_NO_FLUSH here, in a series of calls to Deflate(), and then call EndDeflate() to
|
||||
/// flush everything.
|
||||
/// </param>
|
||||
/// <returns>Z_OK if all goes well.</returns>
|
||||
public int Deflate(FlushType flush)
|
||||
{
|
||||
if (dstate == null)
|
||||
throw new ZlibException("No Deflate State!");
|
||||
return dstate.Deflate(flush);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// End a deflation session.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Call this after making a series of one or more calls to Deflate(). All buffers are flushed.
|
||||
/// </remarks>
|
||||
/// <returns>Z_OK if all goes well.</returns>
|
||||
public int EndDeflate()
|
||||
{
|
||||
if (dstate == null)
|
||||
throw new ZlibException("No Deflate State!");
|
||||
// TODO: dinoch Tue, 03 Nov 2009 15:39 (test this)
|
||||
//int ret = dstate.End();
|
||||
dstate = null;
|
||||
return ZlibConstants.Z_OK; //ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset a codec for another deflation session.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Call this to reset the deflation state. For example if a thread is deflating
|
||||
/// non-consecutive blocks, you can call Reset() after the Deflate(Sync) of the first
|
||||
/// block and before the next Deflate(None) of the second block.
|
||||
/// </remarks>
|
||||
/// <returns>Z_OK if all goes well.</returns>
|
||||
public void ResetDeflate()
|
||||
{
|
||||
if (dstate == null)
|
||||
throw new ZlibException("No Deflate State!");
|
||||
dstate.Reset();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Set the CompressionStrategy and CompressionLevel for a deflation session.
|
||||
/// </summary>
|
||||
/// <param name="level">the level of compression to use.</param>
|
||||
/// <param name="strategy">the strategy to use for compression.</param>
|
||||
/// <returns>Z_OK if all goes well.</returns>
|
||||
public int SetDeflateParams(CompressionLevel level, CompressionStrategy strategy)
|
||||
{
|
||||
if (dstate == null)
|
||||
throw new ZlibException("No Deflate State!");
|
||||
return dstate.SetParams(level, strategy);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Set the dictionary to be used for either Inflation or Deflation.
|
||||
/// </summary>
|
||||
/// <param name="dictionary">The dictionary bytes to use.</param>
|
||||
/// <param name="check">Determines if dictionary checks are run</param>
|
||||
/// <returns>Z_OK if all goes well.</returns>
|
||||
public int SetDictionary(byte[] dictionary, bool check = true)
|
||||
{
|
||||
if (istate != null)
|
||||
return istate.SetDictionary(dictionary, check);
|
||||
|
||||
if (dstate != null)
|
||||
return dstate.SetDictionary(dictionary, check);
|
||||
|
||||
throw new ZlibException("No Inflate or Deflate state!");
|
||||
}
|
||||
|
||||
// Flush as much pending output as possible. All deflate() output goes
|
||||
// through this function so some applications may wish to modify it
|
||||
// to avoid allocating a large strm->next_out buffer and copying into it.
|
||||
// (See also read_buf()).
|
||||
internal void flush_pending()
|
||||
{
|
||||
int len = dstate.pendingCount;
|
||||
|
||||
if (len > AvailableBytesOut)
|
||||
len = AvailableBytesOut;
|
||||
if (len == 0)
|
||||
return;
|
||||
|
||||
if (dstate.pending.Length <= dstate.nextPending ||
|
||||
OutputBuffer.Length <= NextOut ||
|
||||
dstate.pending.Length < (dstate.nextPending + len) ||
|
||||
OutputBuffer.Length < (NextOut + len))
|
||||
{
|
||||
throw new ZlibException(String.Format("Invalid State. (pending.Length={0}, pendingCount={1})",
|
||||
dstate.pending.Length, dstate.pendingCount));
|
||||
}
|
||||
|
||||
Array.Copy(dstate.pending, dstate.nextPending, OutputBuffer, NextOut, len);
|
||||
|
||||
NextOut += len;
|
||||
dstate.nextPending += len;
|
||||
TotalBytesOut += len;
|
||||
AvailableBytesOut -= len;
|
||||
dstate.pendingCount -= len;
|
||||
if (dstate.pendingCount == 0)
|
||||
{
|
||||
dstate.nextPending = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Read a new buffer from the current input stream, update the adler32
|
||||
// and total number of bytes read. All deflate() input goes through
|
||||
// this function so some applications may wish to modify it to avoid
|
||||
// allocating a large strm->next_in buffer and copying from it.
|
||||
// (See also flush_pending()).
|
||||
internal int read_buf(byte[] buf, int start, int size)
|
||||
{
|
||||
int len = AvailableBytesIn;
|
||||
|
||||
if (len > size)
|
||||
len = size;
|
||||
if (len == 0)
|
||||
return 0;
|
||||
|
||||
AvailableBytesIn -= len;
|
||||
|
||||
if (dstate.WantRfc1950HeaderBytes)
|
||||
{
|
||||
_Adler32 = Adler.Adler32(_Adler32, InputBuffer, NextIn, len);
|
||||
}
|
||||
Array.Copy(InputBuffer, NextIn, buf, start, len);
|
||||
NextIn += len;
|
||||
TotalBytesIn += len;
|
||||
return len;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
128
SabreTools.IO/Compression/Deflate/ZlibConstants.cs
Normal file
128
SabreTools.IO/Compression/Deflate/ZlibConstants.cs
Normal file
@@ -0,0 +1,128 @@
|
||||
// ZlibConstants.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009 Dino Chiesa and Microsoft Corporation.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2009-November-03 18:50:19>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines constants used by the zlib class library. This
|
||||
// code is derived from the jzlib implementation of zlib, but
|
||||
// significantly modified. In keeping with the license for jzlib, the
|
||||
// copyright to that code is included here.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in
|
||||
// the documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. The names of the authors may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
// INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
// This program is based on zlib-1.1.3; credit to authors
|
||||
// Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu)
|
||||
// and contributors of zlib.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
|
||||
using System;
|
||||
|
||||
namespace SabreTools.IO.Compression.Deflate
|
||||
{
|
||||
/// <summary>
|
||||
/// A bunch of constants used in the Zlib interface.
|
||||
/// </summary>
|
||||
public static class ZlibConstants
|
||||
{
|
||||
/// <summary>
|
||||
/// The maximum number of window bits for the Deflate algorithm.
|
||||
/// </summary>
|
||||
public const int WindowBitsMax = 15; // 32K LZ77 window
|
||||
|
||||
/// <summary>
|
||||
/// The default number of window bits for the Deflate algorithm.
|
||||
/// </summary>
|
||||
public const int WindowBitsDefault = WindowBitsMax;
|
||||
|
||||
/// <summary>
|
||||
/// indicates everything is A-OK
|
||||
/// </summary>
|
||||
public const int Z_OK = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the last operation reached the end of the stream.
|
||||
/// </summary>
|
||||
public const int Z_STREAM_END = 1;
|
||||
|
||||
/// <summary>
|
||||
/// The operation ended in need of a dictionary.
|
||||
/// </summary>
|
||||
public const int Z_NEED_DICT = 2;
|
||||
|
||||
/// <summary>
|
||||
/// There was an error with the stream - not enough data, not open and readable, etc.
|
||||
/// </summary>
|
||||
public const int Z_STREAM_ERROR = -2;
|
||||
|
||||
/// <summary>
|
||||
/// There was an error with the data - not enough data, bad data, etc.
|
||||
/// </summary>
|
||||
public const int Z_DATA_ERROR = -3;
|
||||
|
||||
/// <summary>
|
||||
/// There was an error with the working buffer.
|
||||
/// </summary>
|
||||
public const int Z_BUF_ERROR = -5;
|
||||
|
||||
/// <summary>
|
||||
/// The size of the working buffer used in the ZlibCodec class. Defaults to 8192 bytes.
|
||||
/// </summary>
|
||||
#if NETCF
|
||||
public const int WorkingBufferSizeDefault = 8192;
|
||||
#else
|
||||
public const int WorkingBufferSizeDefault = 16384;
|
||||
#endif
|
||||
/// <summary>
|
||||
/// The minimum size of the working buffer used in the ZlibCodec class. Currently it is 128 bytes.
|
||||
/// </summary>
|
||||
public const int WorkingBufferSizeMin = 1024;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
120
SabreTools.IO/Compression/Deflate/ZlibException.cs
Normal file
120
SabreTools.IO/Compression/Deflate/ZlibException.cs
Normal file
@@ -0,0 +1,120 @@
|
||||
// Zlib.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009-2011 Dino Chiesa and Microsoft Corporation.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Last Saved: <2011-August-03 19:52:28>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines classes for ZLIB compression and
|
||||
// decompression. This code is derived from the jzlib implementation of
|
||||
// zlib, but significantly modified. The object model is not the same,
|
||||
// and many of the behaviors are new or different. Nonetheless, in
|
||||
// keeping with the license for jzlib, the copyright to that code is
|
||||
// included below.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// The following notice applies to jzlib:
|
||||
//
|
||||
// Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in
|
||||
// the documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. The names of the authors may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
// INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
// jzlib is based on zlib-1.1.3.
|
||||
//
|
||||
// The following notice applies to zlib:
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (C) 1995-2004 Jean-loup Gailly and Mark Adler
|
||||
//
|
||||
// The ZLIB software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
// Jean-loup Gailly jloup@gzip.org
|
||||
// Mark Adler madler@alumni.caltech.edu
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
using Interop = System.Runtime.InteropServices;
|
||||
|
||||
namespace SabreTools.IO.Compression.Deflate
|
||||
{
|
||||
/// <summary>
|
||||
/// A general purpose exception class for exceptions in the Zlib library.
|
||||
/// </summary>
|
||||
[Interop.GuidAttribute("ebc25cf6-9120-4283-b972-0e5520d0000E")]
|
||||
public class ZlibException : System.Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// The ZlibException class captures exception information generated
|
||||
/// by the Zlib library.
|
||||
/// </summary>
|
||||
public ZlibException()
|
||||
: base()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This ctor collects a message attached to the exception.
|
||||
/// </summary>
|
||||
/// <param name="s">the message for the exception.</param>
|
||||
public ZlibException(System.String s)
|
||||
: base(s)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
717
SabreTools.IO/Compression/Deflate/ZlibStream.cs
Normal file
717
SabreTools.IO/Compression/Deflate/ZlibStream.cs
Normal file
@@ -0,0 +1,717 @@
|
||||
// ZlibStream.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009 Dino Chiesa and Microsoft Corporation.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2011-July-31 14:53:33>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines the ZlibStream class, which is similar in idea to
|
||||
// the System.IO.Compression.DeflateStream and
|
||||
// System.IO.Compression.GZipStream classes in the .NET BCL.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace SabreTools.IO.Compression.Deflate
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Represents a Zlib stream for compression or decompression.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// The ZlibStream is a <see
|
||||
/// href="http://en.wikipedia.org/wiki/Decorator_pattern">Decorator</see> on a <see
|
||||
/// cref="System.IO.Stream"/>. It adds ZLIB compression or decompression to any
|
||||
/// stream.
|
||||
/// </para>
|
||||
///
|
||||
/// <para> Using this stream, applications can compress or decompress data via
|
||||
/// stream <c>Read()</c> and <c>Write()</c> operations. Either compresssion or
|
||||
/// decompression can occur through either reading or writing. The compression
|
||||
/// format used is ZLIB, which is documented in <see
|
||||
/// href="http://www.ietf.org/rfc/rfc1950.txt">IETF RFC 1950</see>, "ZLIB Compressed
|
||||
/// Data Format Specification version 3.3". This implementation of ZLIB always uses
|
||||
/// DEFLATE as the compression method. (see <see
|
||||
/// href="http://www.ietf.org/rfc/rfc1951.txt">IETF RFC 1951</see>, "DEFLATE
|
||||
/// Compressed Data Format Specification version 1.3.") </para>
|
||||
///
|
||||
/// <para>
|
||||
/// The ZLIB format allows for varying compression methods, window sizes, and dictionaries.
|
||||
/// This implementation always uses the DEFLATE compression method, a preset dictionary,
|
||||
/// and 15 window bits by default.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// This class is similar to <see cref="DeflateStream"/>, except that it adds the
|
||||
/// RFC1950 header and trailer bytes to a compressed stream when compressing, or expects
|
||||
/// the RFC1950 header and trailer bytes when decompressing. It is also similar to the
|
||||
/// <see cref="GZipStream"/>.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <seealso cref="DeflateStream" />
|
||||
/// <seealso cref="GZipStream" />
|
||||
public class ZlibStream : System.IO.Stream
|
||||
{
|
||||
internal ZlibBaseStream _baseStream;
|
||||
bool _disposed;
|
||||
|
||||
/// <summary>
|
||||
/// Create a <c>ZlibStream</c> using the specified <c>CompressionMode</c>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// When mode is <c>CompressionMode.Compress</c>, the <c>ZlibStream</c>
|
||||
/// will use the default compression level. The "captive" stream will be
|
||||
/// closed when the <c>ZlibStream</c> is closed.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <example>
|
||||
/// This example uses a <c>ZlibStream</c> to compress a file, and writes the
|
||||
/// compressed data to another file.
|
||||
/// <code>
|
||||
/// using (System.IO.Stream input = System.IO.File.OpenRead(fileToCompress))
|
||||
/// {
|
||||
/// using (var raw = System.IO.File.Create(fileToCompress + ".zlib"))
|
||||
/// {
|
||||
/// using (Stream compressor = new ZlibStream(raw, CompressionMode.Compress))
|
||||
/// {
|
||||
/// byte[] buffer = new byte[WORKING_BUFFER_SIZE];
|
||||
/// int n;
|
||||
/// while ((n= input.Read(buffer, 0, buffer.Length)) != 0)
|
||||
/// {
|
||||
/// compressor.Write(buffer, 0, n);
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// <code lang="VB">
|
||||
/// Using input As Stream = File.OpenRead(fileToCompress)
|
||||
/// Using raw As FileStream = File.Create(fileToCompress & ".zlib")
|
||||
/// Using compressor As Stream = New ZlibStream(raw, CompressionMode.Compress)
|
||||
/// Dim buffer As Byte() = New Byte(4096) {}
|
||||
/// Dim n As Integer = -1
|
||||
/// Do While (n <> 0)
|
||||
/// If (n > 0) Then
|
||||
/// compressor.Write(buffer, 0, n)
|
||||
/// End If
|
||||
/// n = input.Read(buffer, 0, buffer.Length)
|
||||
/// Loop
|
||||
/// End Using
|
||||
/// End Using
|
||||
/// End Using
|
||||
/// </code>
|
||||
/// </example>
|
||||
///
|
||||
/// <param name="stream">The stream which will be read or written.</param>
|
||||
/// <param name="mode">Indicates whether the ZlibStream will compress or decompress.</param>
|
||||
public ZlibStream(System.IO.Stream stream, CompressionMode mode)
|
||||
: this(stream, mode, CompressionLevel.Default, false)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a <c>ZlibStream</c> using the specified <c>CompressionMode</c> and
|
||||
/// the specified <c>CompressionLevel</c>.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// When mode is <c>CompressionMode.Decompress</c>, the level parameter is ignored.
|
||||
/// The "captive" stream will be closed when the <c>ZlibStream</c> is closed.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <example>
|
||||
/// This example uses a <c>ZlibStream</c> to compress data from a file, and writes the
|
||||
/// compressed data to another file.
|
||||
///
|
||||
/// <code>
|
||||
/// using (System.IO.Stream input = System.IO.File.OpenRead(fileToCompress))
|
||||
/// {
|
||||
/// using (var raw = System.IO.File.Create(fileToCompress + ".zlib"))
|
||||
/// {
|
||||
/// using (Stream compressor = new ZlibStream(raw,
|
||||
/// CompressionMode.Compress,
|
||||
/// CompressionLevel.BestCompression))
|
||||
/// {
|
||||
/// byte[] buffer = new byte[WORKING_BUFFER_SIZE];
|
||||
/// int n;
|
||||
/// while ((n= input.Read(buffer, 0, buffer.Length)) != 0)
|
||||
/// {
|
||||
/// compressor.Write(buffer, 0, n);
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
///
|
||||
/// <code lang="VB">
|
||||
/// Using input As Stream = File.OpenRead(fileToCompress)
|
||||
/// Using raw As FileStream = File.Create(fileToCompress & ".zlib")
|
||||
/// Using compressor As Stream = New ZlibStream(raw, CompressionMode.Compress, CompressionLevel.BestCompression)
|
||||
/// Dim buffer As Byte() = New Byte(4096) {}
|
||||
/// Dim n As Integer = -1
|
||||
/// Do While (n <> 0)
|
||||
/// If (n > 0) Then
|
||||
/// compressor.Write(buffer, 0, n)
|
||||
/// End If
|
||||
/// n = input.Read(buffer, 0, buffer.Length)
|
||||
/// Loop
|
||||
/// End Using
|
||||
/// End Using
|
||||
/// End Using
|
||||
/// </code>
|
||||
/// </example>
|
||||
///
|
||||
/// <param name="stream">The stream to be read or written while deflating or inflating.</param>
|
||||
/// <param name="mode">Indicates whether the ZlibStream will compress or decompress.</param>
|
||||
/// <param name="level">A tuning knob to trade speed for effectiveness.</param>
|
||||
public ZlibStream(System.IO.Stream stream, CompressionMode mode, CompressionLevel level)
|
||||
: this(stream, mode, level, false)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a <c>ZlibStream</c> using the specified <c>CompressionMode</c>, and
|
||||
/// explicitly specify whether the captive stream should be left open after
|
||||
/// Deflation or Inflation.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// When mode is <c>CompressionMode.Compress</c>, the <c>ZlibStream</c> will use
|
||||
/// the default compression level.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// This constructor allows the application to request that the captive stream
|
||||
/// remain open after the deflation or inflation occurs. By default, after
|
||||
/// <c>Close()</c> is called on the stream, the captive stream is also
|
||||
/// closed. In some cases this is not desired, for example if the stream is a
|
||||
/// <see cref="System.IO.MemoryStream"/> that will be re-read after
|
||||
/// compression. Specify true for the <paramref name="leaveOpen"/> parameter to leave the stream
|
||||
/// open.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// See the other overloads of this constructor for example code.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <param name="stream">The stream which will be read or written. This is called the
|
||||
/// "captive" stream in other places in this documentation.</param>
|
||||
/// <param name="mode">Indicates whether the ZlibStream will compress or decompress.</param>
|
||||
/// <param name="leaveOpen">true if the application would like the stream to remain
|
||||
/// open after inflation/deflation.</param>
|
||||
public ZlibStream(System.IO.Stream stream, CompressionMode mode, bool leaveOpen)
|
||||
: this(stream, mode, CompressionLevel.Default, leaveOpen)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a <c>ZlibStream</c> using the specified <c>CompressionMode</c>
|
||||
/// and the specified <c>CompressionLevel</c>, and explicitly specify
|
||||
/// whether the stream should be left open after Deflation or Inflation.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// This constructor allows the application to request that the captive
|
||||
/// stream remain open after the deflation or inflation occurs. By
|
||||
/// default, after <c>Close()</c> is called on the stream, the captive
|
||||
/// stream is also closed. In some cases this is not desired, for example
|
||||
/// if the stream is a <see cref="System.IO.MemoryStream"/> that will be
|
||||
/// re-read after compression. Specify true for the <paramref
|
||||
/// name="leaveOpen"/> parameter to leave the stream open.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// When mode is <c>CompressionMode.Decompress</c>, the level parameter is
|
||||
/// ignored.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <example>
|
||||
///
|
||||
/// This example shows how to use a ZlibStream to compress the data from a file,
|
||||
/// and store the result into another file. The filestream remains open to allow
|
||||
/// additional data to be written to it.
|
||||
///
|
||||
/// <code>
|
||||
/// using (var output = System.IO.File.Create(fileToCompress + ".zlib"))
|
||||
/// {
|
||||
/// using (System.IO.Stream input = System.IO.File.OpenRead(fileToCompress))
|
||||
/// {
|
||||
/// using (Stream compressor = new ZlibStream(output, CompressionMode.Compress, CompressionLevel.BestCompression, true))
|
||||
/// {
|
||||
/// byte[] buffer = new byte[WORKING_BUFFER_SIZE];
|
||||
/// int n;
|
||||
/// while ((n= input.Read(buffer, 0, buffer.Length)) != 0)
|
||||
/// {
|
||||
/// compressor.Write(buffer, 0, n);
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// // can write additional data to the output stream here
|
||||
/// }
|
||||
/// </code>
|
||||
/// <code lang="VB">
|
||||
/// Using output As FileStream = File.Create(fileToCompress & ".zlib")
|
||||
/// Using input As Stream = File.OpenRead(fileToCompress)
|
||||
/// Using compressor As Stream = New ZlibStream(output, CompressionMode.Compress, CompressionLevel.BestCompression, True)
|
||||
/// Dim buffer As Byte() = New Byte(4096) {}
|
||||
/// Dim n As Integer = -1
|
||||
/// Do While (n <> 0)
|
||||
/// If (n > 0) Then
|
||||
/// compressor.Write(buffer, 0, n)
|
||||
/// End If
|
||||
/// n = input.Read(buffer, 0, buffer.Length)
|
||||
/// Loop
|
||||
/// End Using
|
||||
/// End Using
|
||||
/// ' can write additional data to the output stream here.
|
||||
/// End Using
|
||||
/// </code>
|
||||
/// </example>
|
||||
///
|
||||
/// <param name="stream">The stream which will be read or written.</param>
|
||||
///
|
||||
/// <param name="mode">Indicates whether the ZlibStream will compress or decompress.</param>
|
||||
///
|
||||
/// <param name="leaveOpen">
|
||||
/// true if the application would like the stream to remain open after
|
||||
/// inflation/deflation.
|
||||
/// </param>
|
||||
///
|
||||
/// <param name="level">
|
||||
/// A tuning knob to trade speed for effectiveness. This parameter is
|
||||
/// effective only when mode is <c>CompressionMode.Compress</c>.
|
||||
/// </param>
|
||||
public ZlibStream(System.IO.Stream stream, CompressionMode mode, CompressionLevel level, bool leaveOpen)
|
||||
{
|
||||
_baseStream = new ZlibBaseStream(stream, mode, level, ZlibStreamFlavor.ZLIB, leaveOpen);
|
||||
}
|
||||
|
||||
#region Zlib properties
|
||||
|
||||
/// <summary>
|
||||
/// This property sets the flush behavior on the stream.
|
||||
/// Sorry, though, not sure exactly how to describe all the various settings.
|
||||
/// </summary>
|
||||
virtual public FlushType FlushMode
|
||||
{
|
||||
get { return (this._baseStream._flushMode); }
|
||||
set
|
||||
{
|
||||
if (_disposed) throw new ObjectDisposedException("ZlibStream");
|
||||
this._baseStream._flushMode = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The size of the working buffer for the compression codec.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The working buffer is used for all stream operations. The default size is
|
||||
/// 1024 bytes. The minimum size is 128 bytes. You may get better performance
|
||||
/// with a larger buffer. Then again, you might not. You would have to test
|
||||
/// it.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Set this before the first call to <c>Read()</c> or <c>Write()</c> on the
|
||||
/// stream. If you try to set it afterwards, it will throw.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public int BufferSize
|
||||
{
|
||||
get
|
||||
{
|
||||
return this._baseStream._bufferSize;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_disposed) throw new ObjectDisposedException("ZlibStream");
|
||||
if (this._baseStream._workingBuffer != null)
|
||||
throw new ZlibException("The working buffer is already set.");
|
||||
if (value < ZlibConstants.WorkingBufferSizeMin)
|
||||
throw new ZlibException(String.Format("Don't be silly. {0} bytes?? Use a bigger buffer, at least {1}.", value, ZlibConstants.WorkingBufferSizeMin));
|
||||
this._baseStream._bufferSize = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Returns the total number of bytes input so far.</summary>
|
||||
virtual public long TotalIn
|
||||
{
|
||||
get { return this._baseStream._z.TotalBytesIn; }
|
||||
}
|
||||
|
||||
/// <summary> Returns the total number of bytes output so far.</summary>
|
||||
virtual public long TotalOut
|
||||
{
|
||||
get { return this._baseStream._z.TotalBytesOut; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region System.IO.Stream methods
|
||||
|
||||
/// <summary>
|
||||
/// Dispose the stream.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This may or may not result in a <c>Close()</c> call on the captive
|
||||
/// stream. See the constructors that have a <c>leaveOpen</c> parameter
|
||||
/// for more information.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This method may be invoked in two distinct scenarios. If disposing
|
||||
/// == true, the method has been called directly or indirectly by a
|
||||
/// user's code, for example via the public Dispose() method. In this
|
||||
/// case, both managed and unmanaged resources can be referenced and
|
||||
/// disposed. If disposing == false, the method has been called by the
|
||||
/// runtime from inside the object finalizer and this method should not
|
||||
/// reference other objects; in that case only unmanaged resources must
|
||||
/// be referenced or disposed.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name="disposing">
|
||||
/// indicates whether the Dispose method was invoked by user code.
|
||||
/// </param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
if (disposing && (this._baseStream != null))
|
||||
this._baseStream.Close();
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the stream can be read.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The return value depends on whether the captive stream supports reading.
|
||||
/// </remarks>
|
||||
public override bool CanRead
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_disposed) throw new ObjectDisposedException("ZlibStream");
|
||||
return _baseStream._stream.CanRead;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the stream supports Seek operations.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Always returns false.
|
||||
/// </remarks>
|
||||
public override bool CanSeek
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the stream can be written.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The return value depends on whether the captive stream supports writing.
|
||||
/// </remarks>
|
||||
public override bool CanWrite
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_disposed) throw new ObjectDisposedException("ZlibStream");
|
||||
return _baseStream._stream.CanWrite;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flush the stream.
|
||||
/// </summary>
|
||||
public override void Flush()
|
||||
{
|
||||
if (_disposed) throw new ObjectDisposedException("ZlibStream");
|
||||
_baseStream.Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reading this property always throws a <see cref="NotSupportedException"/>.
|
||||
/// </summary>
|
||||
public override long Length
|
||||
{
|
||||
get { throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The position of the stream pointer.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// Setting this property always throws a <see
|
||||
/// cref="NotSupportedException"/>. Reading will return the total bytes
|
||||
/// written out, if used in writing, or the total bytes read in, if used in
|
||||
/// reading. The count may refer to compressed bytes or uncompressed bytes,
|
||||
/// depending on how you've used the stream.
|
||||
/// </remarks>
|
||||
public override long Position
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this._baseStream._streamMode == SabreTools.IO.Compression.Deflate.ZlibBaseStream.StreamMode.Writer)
|
||||
return this._baseStream._z.TotalBytesOut;
|
||||
if (this._baseStream._streamMode == SabreTools.IO.Compression.Deflate.ZlibBaseStream.StreamMode.Reader)
|
||||
return this._baseStream._z.TotalBytesIn;
|
||||
return 0;
|
||||
}
|
||||
|
||||
set { throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read data from the stream.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// If you wish to use the <c>ZlibStream</c> to compress data while reading,
|
||||
/// you can create a <c>ZlibStream</c> with <c>CompressionMode.Compress</c>,
|
||||
/// providing an uncompressed data stream. Then call <c>Read()</c> on that
|
||||
/// <c>ZlibStream</c>, and the data read will be compressed. If you wish to
|
||||
/// use the <c>ZlibStream</c> to decompress data while reading, you can create
|
||||
/// a <c>ZlibStream</c> with <c>CompressionMode.Decompress</c>, providing a
|
||||
/// readable compressed data stream. Then call <c>Read()</c> on that
|
||||
/// <c>ZlibStream</c>, and the data will be decompressed as it is read.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// A <c>ZlibStream</c> can be used for <c>Read()</c> or <c>Write()</c>, but
|
||||
/// not both.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <param name="buffer">
|
||||
/// The buffer into which the read data should be placed.</param>
|
||||
///
|
||||
/// <param name="offset">
|
||||
/// the offset within that data array to put the first byte read.</param>
|
||||
///
|
||||
/// <param name="count">the number of bytes to read.</param>
|
||||
///
|
||||
/// <returns>the number of bytes read</returns>
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (_disposed) throw new ObjectDisposedException("ZlibStream");
|
||||
return _baseStream.Read(buffer, offset, count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calling this method always throws a <see cref="NotSupportedException"/>.
|
||||
/// </summary>
|
||||
/// <param name="offset">
|
||||
/// The offset to seek to....
|
||||
/// IF THIS METHOD ACTUALLY DID ANYTHING.
|
||||
/// </param>
|
||||
/// <param name="origin">
|
||||
/// The reference specifying how to apply the offset.... IF
|
||||
/// THIS METHOD ACTUALLY DID ANYTHING.
|
||||
/// </param>
|
||||
///
|
||||
/// <returns>nothing. This method always throws.</returns>
|
||||
public override long Seek(long offset, System.IO.SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calling this method always throws a <see cref="NotSupportedException"/>.
|
||||
/// </summary>
|
||||
/// <param name="value">
|
||||
/// The new value for the stream length.... IF
|
||||
/// THIS METHOD ACTUALLY DID ANYTHING.
|
||||
/// </param>
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write data to the stream.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// If you wish to use the <c>ZlibStream</c> to compress data while writing,
|
||||
/// you can create a <c>ZlibStream</c> with <c>CompressionMode.Compress</c>,
|
||||
/// and a writable output stream. Then call <c>Write()</c> on that
|
||||
/// <c>ZlibStream</c>, providing uncompressed data as input. The data sent to
|
||||
/// the output stream will be the compressed form of the data written. If you
|
||||
/// wish to use the <c>ZlibStream</c> to decompress data while writing, you
|
||||
/// can create a <c>ZlibStream</c> with <c>CompressionMode.Decompress</c>, and a
|
||||
/// writable output stream. Then call <c>Write()</c> on that stream,
|
||||
/// providing previously compressed data. The data sent to the output stream
|
||||
/// will be the decompressed form of the data written.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// A <c>ZlibStream</c> can be used for <c>Read()</c> or <c>Write()</c>, but not both.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name="buffer">The buffer holding data to write to the stream.</param>
|
||||
/// <param name="offset">the offset within that data array to find the first byte to write.</param>
|
||||
/// <param name="count">the number of bytes to write.</param>
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (_disposed) throw new ObjectDisposedException("ZlibStream");
|
||||
_baseStream.Write(buffer, offset, count);
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Compress a string into a byte array using ZLIB.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// Uncompress it with <see cref="ZlibStream.UncompressString(byte[])"/>.
|
||||
/// </remarks>
|
||||
///
|
||||
/// <seealso cref="ZlibStream.UncompressString(byte[])"/>
|
||||
/// <seealso cref="ZlibStream.CompressBuffer(byte[])"/>
|
||||
/// <seealso cref="GZipStream.CompressString(string)"/>
|
||||
///
|
||||
/// <param name="s">
|
||||
/// A string to compress. The string will first be encoded
|
||||
/// using UTF8, then compressed.
|
||||
/// </param>
|
||||
///
|
||||
/// <returns>The string in compressed form</returns>
|
||||
public static byte[] CompressString(String s)
|
||||
{
|
||||
using var ms = new MemoryStream();
|
||||
Stream compressor =
|
||||
new ZlibStream(ms, CompressionMode.Compress, CompressionLevel.BestCompression);
|
||||
ZlibBaseStream.CompressString(s, compressor);
|
||||
return ms.ToArray();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Compress a byte array into a new byte array using ZLIB.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// Uncompress it with <see cref="ZlibStream.UncompressBuffer(byte[])"/>.
|
||||
/// </remarks>
|
||||
///
|
||||
/// <seealso cref="ZlibStream.CompressString(string)"/>
|
||||
/// <seealso cref="ZlibStream.UncompressBuffer(byte[])"/>
|
||||
///
|
||||
/// <param name="b">
|
||||
/// A buffer to compress.
|
||||
/// </param>
|
||||
///
|
||||
/// <returns>The data in compressed form</returns>
|
||||
public static byte[] CompressBuffer(byte[] b)
|
||||
{
|
||||
using var ms = new MemoryStream();
|
||||
Stream compressor =
|
||||
new ZlibStream(ms, CompressionMode.Compress, CompressionLevel.BestCompression);
|
||||
|
||||
ZlibBaseStream.CompressBuffer(b, compressor);
|
||||
return ms.ToArray();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Uncompress a ZLIB-compressed byte array into a single string.
|
||||
/// </summary>
|
||||
///
|
||||
/// <seealso cref="ZlibStream.CompressString(String)"/>
|
||||
/// <seealso cref="ZlibStream.UncompressBuffer(byte[])"/>
|
||||
///
|
||||
/// <param name="compressed">
|
||||
/// A buffer containing ZLIB-compressed data.
|
||||
/// </param>
|
||||
///
|
||||
/// <returns>The uncompressed string</returns>
|
||||
public static String UncompressString(byte[] compressed)
|
||||
{
|
||||
using var input = new MemoryStream(compressed);
|
||||
Stream decompressor =
|
||||
new ZlibStream(input, CompressionMode.Decompress);
|
||||
|
||||
return ZlibBaseStream.UncompressString(compressed, decompressor);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Uncompress a ZLIB-compressed byte array into a byte array.
|
||||
/// </summary>
|
||||
///
|
||||
/// <seealso cref="ZlibStream.CompressBuffer(byte[])"/>
|
||||
/// <seealso cref="ZlibStream.UncompressString(byte[])"/>
|
||||
///
|
||||
/// <param name="compressed">
|
||||
/// A buffer containing ZLIB-compressed data.
|
||||
/// </param>
|
||||
///
|
||||
/// <returns>The data in uncompressed form</returns>
|
||||
public static byte[] UncompressBuffer(byte[] compressed)
|
||||
{
|
||||
using var input = new MemoryStream(compressed);
|
||||
Stream decompressor =
|
||||
new ZlibStream(input, CompressionMode.Decompress);
|
||||
|
||||
return ZlibBaseStream.UncompressBuffer(compressed, decompressor);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
35
SabreTools.IO/Compression/Deflate/ZlibStreamFlavor.cs
Normal file
35
SabreTools.IO/Compression/Deflate/ZlibStreamFlavor.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
// ZlibBaseStream.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009 Dino Chiesa and Microsoft Corporation.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2011-August-06 21:22:38>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines the ZlibBaseStream class, which is an intnernal
|
||||
// base class for DeflateStream, ZlibStream and GZipStream.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
namespace SabreTools.IO.Compression.Deflate
|
||||
{
|
||||
internal enum ZlibStreamFlavor
|
||||
{
|
||||
ZLIB = 1950,
|
||||
DEFLATE = 1951,
|
||||
GZIP = 1952
|
||||
}
|
||||
}
|
||||
58
SabreTools.IO/Compression/LZX/AlignedOffsetBlockData.cs
Normal file
58
SabreTools.IO/Compression/LZX/AlignedOffsetBlockData.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
namespace SabreTools.IO.Compression.LZX
|
||||
{
|
||||
/// <summary>
|
||||
/// An aligned offset block is identical to the verbatim block except for the presence of the aligned offset
|
||||
/// tree preceding the other trees.
|
||||
/// </summary>
|
||||
/// <see href="https://interoperability.blob.core.windows.net/files/MS-PATCH/%5bMS-PATCH%5d.pdf"/>
|
||||
internal class AlignedOffsetBlockData : BlockData
|
||||
{
|
||||
/// <summary>
|
||||
/// Aligned offset tree
|
||||
/// </summary>
|
||||
/// <remarks>8 elements, 3 bits each</remarks>
|
||||
public byte[]? AlignedOffsetTree { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Pretree for first 256 elements of main tree
|
||||
/// </summary>
|
||||
/// <remarks>20 elements, 4 bits each</remarks>
|
||||
public byte[]? PretreeFirst256 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Path lengths of first 256 elements of main tree
|
||||
/// </summary>
|
||||
/// <remarks>Encoded using pretree</remarks>
|
||||
public int[]? PathLengthsFirst256 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Pretree for remainder of main tree
|
||||
/// </summary>
|
||||
/// <remarks>20 elements, 4 bits each</remarks>
|
||||
public byte[]? PretreeRemainder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Path lengths of remaining elements of main tree
|
||||
/// </summary>
|
||||
/// <remarks>Encoded using pretree</remarks>
|
||||
public int[]? PathLengthsRemainder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Pretree for length tree
|
||||
/// </summary>
|
||||
/// <remarks>20 elements, 4 bits each</remarks>
|
||||
public byte[]? PretreeLengthTree { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Path lengths of elements in length tree
|
||||
/// </summary>
|
||||
/// <remarks>Encoded using pretree</remarks>
|
||||
public int[]? PathLengthsLengthTree { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Token sequence (matches and literals)
|
||||
/// </summary>
|
||||
/// <remarks>Variable</remarks>
|
||||
public byte[]? TokenSequence { get; set; }
|
||||
}
|
||||
}
|
||||
24
SabreTools.IO/Compression/LZX/Block.cs
Normal file
24
SabreTools.IO/Compression/LZX/Block.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
namespace SabreTools.IO.Compression.LZX
|
||||
{
|
||||
/// <summary>
|
||||
/// An LZXD block represents a sequence of compressed data that is encoded with the same set of
|
||||
/// Huffman trees, or a sequence of uncompressed data. There can be one or more LZXD blocks in a
|
||||
/// compressed stream, each with its own set of Huffman trees. Blocks do not have to start or end on a
|
||||
/// chunk boundary; blocks can span multiple chunks, or a single chunk can contain multiple blocks. The
|
||||
/// number of chunks is related to the size of the data being compressed, while the number of blocks is
|
||||
/// related to how well the data is compressed.
|
||||
/// </summary>
|
||||
/// <see href="https://interoperability.blob.core.windows.net/files/MS-PATCH/%5bMS-PATCH%5d.pdf"/>
|
||||
internal class Block
|
||||
{
|
||||
/// <summary>
|
||||
/// Block header
|
||||
/// </summary>
|
||||
public BlockHeader? Header { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Block data
|
||||
/// </summary>
|
||||
public BlockData? BlockData { get; set; }
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user