182 Commits

Author SHA1 Message Date
Matt Nadareski
9ef17819a3 Split tests as well 2025-12-02 08:21:25 -05:00
Matt Nadareski
b4d87e53a0 Try out split libraries in the same repo 2025-12-02 08:15:48 -05:00
Matt Nadareski
ae87869bb9 Add GrindCore package 2025-11-30 19:39:15 -05:00
Matt Nadareski
46ad576668 Format GHA definitions 2025-11-17 08:40:06 -05:00
Matt Nadareski
37b09b07a6 Bump version 2025-11-13 20:02:30 -05:00
Matt Nadareski
448e43dd05 Add MergeWith dictionary extension 2025-11-13 10:35:02 -05:00
Matt Nadareski
e29b8ab4db Add support for .NET 10 2025-11-13 08:56:30 -05:00
Matt Nadareski
6075aa25a2 Name some type parameters 2025-11-11 09:54:18 -05:00
Matt Nadareski
c53fb33278 Add remaining encryption constants 2025-11-04 10:10:20 -05:00
Matt Nadareski
fb1fc5d85d Add IsNumericArray extension 2025-11-04 10:08:56 -05:00
Matt Nadareski
2763d3915b Add MPQ hashing methods 2025-11-02 22:54:10 -05:00
Matt Nadareski
16b238539b Bump version 2025-10-27 22:09:15 -04:00
Matt Nadareski
68f49eeb48 Add Bitwise Binary Operators tests 2025-10-27 16:47:36 -04:00
Matt Nadareski
c5ecd41a8f Add Shift Binary Operators tests 2025-10-27 16:43:05 -04:00
Matt Nadareski
9ab9dd4ff8 Add Bitwise Unary Operators tests 2025-10-27 16:38:45 -04:00
Matt Nadareski
867c8d11da Fix unary operator tests 2025-10-27 16:28:14 -04:00
Matt Nadareski
65dbb7a31a Add Arithmetic Binary Operators tests 2025-10-27 16:18:45 -04:00
Matt Nadareski
1eaf7954fe Add Arithmetic Unary Operators tests 2025-10-27 16:12:12 -04:00
Matt Nadareski
a602a07514 Fill out bitwise operators for both-endian 2025-10-27 16:06:01 -04:00
Matt Nadareski
466b0e90e7 Fill out arithmetic operators for both-endian 2025-10-27 15:52:48 -04:00
Matt Nadareski
abdf50c9e0 Add bitwise AND and OR operations to both-endian 2025-10-27 15:34:32 -04:00
Matt Nadareski
12341ba6aa Simplify Numerics namespace 2025-10-27 14:12:52 -04:00
Matt Nadareski
70b78f861c Revert "Add base type operator support to both-endian"
This reverts commit 5b306ce9e8.
2025-10-27 14:07:01 -04:00
Matt Nadareski
5b306ce9e8 Add base type operator support to both-endian 2025-10-27 14:02:52 -04:00
Matt Nadareski
2b6fc200e2 Add Latin1 and BigEndianUnicode extensions 2025-10-27 12:22:02 -04:00
Matt Nadareski
7c63f44c75 Add both-endian write extensions 2025-10-27 12:01:52 -04:00
Matt Nadareski
edd3e6eef2 Add both-endian try read extensions 2025-10-27 11:21:29 -04:00
Matt Nadareski
244b7411d4 Add both-endian peek read extensions 2025-10-27 10:43:54 -04:00
Matt Nadareski
fb60f1fed5 Add both-endian exact read extensions 2025-10-27 10:03:17 -04:00
Matt Nadareski
8f06bf5859 Add both-endian numeric types 2025-10-27 09:09:00 -04:00
Matt Nadareski
2c5d7ad56b Update rolling tag 2025-10-26 20:31:52 -04:00
Matt Nadareski
46996c10e5 Add Peek implementations for reading 2025-10-15 09:59:07 -04:00
Matt Nadareski
7491821679 Add origin-based SeekIfPossible 2025-10-15 09:37:41 -04:00
Matt Nadareski
8fe404e732 Remove some nonsensical endian methods 2025-10-14 20:45:02 -04:00
Matt Nadareski
793168fbe5 Add TryGet implementations for reading 2025-10-14 20:42:32 -04:00
Matt Nadareski
67b6118cc1 Add functionality from Transform tool 2025-10-14 13:58:40 -04:00
Matt Nadareski
b12d122721 Bump version 2025-10-07 09:28:17 -04:00
Matt Nadareski
20f1679557 Update Hashing to 1.5.1 2025-10-07 09:23:21 -04:00
Matt Nadareski
7ccedbeac5 Move Compare to better namespace 2025-09-30 21:25:56 -04:00
Matt Nadareski
72910cc1c0 Add AES/CTR encryption helpers 2025-09-30 19:33:28 -04:00
Matt Nadareski
8f4ea0da16 Add BouncyCastle as a dependency 2025-09-30 19:30:23 -04:00
Matt Nadareski
eb4975b261 Fix namespace in readme 2025-09-30 18:29:30 -04:00
Matt Nadareski
995c19d903 Add byte array math operations from NDecrypt; fix issues and add tests 2025-09-30 18:02:05 -04:00
Matt Nadareski
f0fe9af467 Require exact versions for build 2025-09-30 11:06:54 -04:00
Matt Nadareski
d33b47d15a Revert "Start allowing larger numeric types for reads"
This reverts commit e4a0a08d13.
2025-09-25 12:24:06 -04:00
Matt Nadareski
e4a0a08d13 Start allowing larger numeric types for reads 2025-09-25 12:08:22 -04:00
Matt Nadareski
24a69166f0 Add more info about namespaces to the readme 2025-09-25 09:43:38 -04:00
Matt Nadareski
6c13cdcf31 Bump version 2025-09-24 08:07:49 -04:00
Matt Nadareski
4138c271e5 This should be internal to the Compare namespace 2025-09-23 11:06:40 -04:00
Matt Nadareski
f80d31597b Reintegrate Matching and reorganize as needed 2025-09-23 10:59:01 -04:00
Matt Nadareski
5054aeb077 Bump version 2025-09-22 17:48:18 -04:00
Matt Nadareski
d2e9b8d6e5 Fix byte array test 2025-09-22 17:44:41 -04:00
Matt Nadareski
2c29aee834 Remove Models from references 2025-09-22 11:03:40 -04:00
Matt Nadareski
576bafcb87 Create minimal model for InflateWrapper 2025-09-22 11:03:07 -04:00
Matt Nadareski
2b310ac528 SZDD no longer uses models 2025-09-22 10:55:14 -04:00
Matt Nadareski
4f6b6d7b59 Reduce Models use another notch 2025-09-22 10:52:26 -04:00
Matt Nadareski
17e55ee233 Move BufferedStream out of SZDD 2025-09-22 10:50:53 -04:00
Matt Nadareski
8b78906d1d Move MoPaQ encryption constants from Models 2025-09-22 10:37:27 -04:00
Matt Nadareski
cff2dcf4cc Move LZX models from Models 2025-09-22 10:35:04 -04:00
Matt Nadareski
a56942cb73 Move Quantum compression models from Models 2025-09-22 10:31:14 -04:00
Matt Nadareski
5ed661b77c Move MSZIP "model" from Models 2025-09-22 10:27:03 -04:00
Matt Nadareski
a0a0cd0386 Add more complete UTF-8 first-byte tests 2025-09-21 16:34:46 -04:00
Matt Nadareski
bcc0fca4ad Ensure 7-bit ASCII never reads above 0x7F 2025-09-21 16:27:03 -04:00
Matt Nadareski
843e821e5f Use extended check in slow path too 2025-09-21 16:07:20 -04:00
Matt Nadareski
630b01283e Latin1 instead of ASCII for .NET 5.0 and beyond 2025-09-21 15:12:16 -04:00
Matt Nadareski
22abb96013 Add remarks about what encodings are used 2025-09-21 15:02:33 -04:00
Matt Nadareski
314de12661 Fix tests, remove UTF-8 checks from irrelevant places 2025-09-21 14:03:45 -04:00
Matt Nadareski
a0b24031b5 Remove duplicate code from Stream implementation 2025-09-21 13:58:46 -04:00
Matt Nadareski
b4628485c3 Sync stream implementation with byte one 2025-09-21 13:58:13 -04:00
Matt Nadareski
4610ddc9b9 Don't read the string unless it's long enough 2025-09-21 13:53:16 -04:00
Matt Nadareski
e392ddc8d7 Fix code formatting 2025-09-21 13:52:05 -04:00
Matt Nadareski
1908d1b32e More generically support single-byte encodings 2025-09-21 13:50:08 -04:00
Matt Nadareski
9d73195f86 Big-endian unicode support because it's there 2025-09-21 13:42:05 -04:00
Matt Nadareski
335a486f17 Special handling of empty string builders 2025-09-21 13:41:06 -04:00
Matt Nadareski
d3e41ac187 Handle invalid offsets in byte array extensions 2025-09-21 11:43:07 -04:00
Matt Nadareski
8ddd9f3f78 Bump version 2025-09-20 22:16:06 -04:00
Matt Nadareski
54ad538c08 Short-circuit fixed-width encodings 2025-09-20 22:10:54 -04:00
Matt Nadareski
e6bc9ab3e3 Add OptionalEndsWith string extension 2025-09-20 18:04:37 -04:00
Matt Nadareski
94934b00a9 There 2025-09-10 21:53:52 -04:00
Matt Nadareski
e49f56fccc Add an enumerable extension from BOS 2025-09-06 15:42:48 -04:00
Matt Nadareski
79c64ddfa8 .NET Standard had issues with that last one 2025-09-06 15:37:24 -04:00
Matt Nadareski
b22384d5f3 Add neat string extensions from BOS 2025-09-06 15:32:36 -04:00
Matt Nadareski
955c1b5641 Bump version 2025-09-05 09:46:17 -04:00
Matt Nadareski
535f9f928d Update Models to 1.7.1 2025-09-05 09:21:15 -04:00
Matt Nadareski
f0cb15c2e4 Fix comments 2025-09-05 09:15:05 -04:00
Matt Nadareski
ec99304c51 Implement the 16KiB limit 2025-09-03 09:05:08 -04:00
Matt Nadareski
aefc931055 Of all things 2025-09-03 01:29:06 -04:00
Matt Nadareski
e7fe342379 Fix missed compatibility issue in string reading 2025-09-03 01:04:34 -04:00
Matt Nadareski
f372999b1b So that's why 2025-09-03 00:23:46 -04:00
Matt Nadareski
2679975945 TFM support thing 2025-09-03 00:22:46 -04:00
Matt Nadareski
54dd7f2f8f Add new extension tests 2025-09-03 00:20:02 -04:00
Matt Nadareski
aee5891c50 Backport thing 2025-09-03 00:15:41 -04:00
Matt Nadareski
b81d3314ea Bump version 2025-09-01 15:25:09 -04:00
Matt Nadareski
4a3ffa5f90 Update fixes, port needed code 2025-09-01 15:21:53 -04:00
Matt Nadareski
a20c7529d6 Handle an edge case 2025-08-28 19:46:50 -04:00
Matt Nadareski
baea5cb0d7 Allow alignment outside of range of byte 2025-08-28 08:57:25 -04:00
Matt Nadareski
659674dd4a Port ReadStrings extensions from Serialization 2025-08-25 12:44:04 -04:00
Matt Nadareski
5c199a143b Add ReadFrom extension, move SegmentValid as extension 2025-08-25 10:50:16 -04:00
Matt Nadareski
99ec814808 Minor fixes to view stream read 2025-08-23 21:26:02 -04:00
Matt Nadareski
ea1f02798c Reorganize composite stream tests 2025-08-23 21:24:04 -04:00
Matt Nadareski
e3d4cc5e45 Cleanup and sync 2025-08-23 21:16:31 -04:00
Matt Nadareski
c98eb5c42a Add "here to the end" constructors 2025-08-23 21:11:41 -04:00
Matt Nadareski
d0392be2d8 Add view stream type 2025-08-23 21:07:57 -04:00
Matt Nadareski
8761629828 Upstream wrapper from WiseUnpacker 2025-08-15 11:21:10 -04:00
Matt Nadareski
a3b258dfeb Upstream wrapper from WiseUnpacker 2025-08-11 10:48:38 -04:00
Matt Nadareski
f7505effa1 Fix seeking issue in composite streams 2025-08-01 14:22:15 -04:00
Matt Nadareski
40d6ccf804 Bump version 2025-07-24 08:05:34 -04:00
Matt Nadareski
31c41f8775 Be consistent about end-of-file newlines 2025-07-23 15:22:28 -04:00
Matt Nadareski
cf7daff040 Add disabled Quantum test 2025-07-23 15:08:49 -04:00
Matt Nadareski
8570b107da MS-ZIP confirmed working now 2025-07-23 14:49:33 -04:00
Matt Nadareski
cf4f613a65 Minor fixes and tests for MS-ZIP 2025-07-23 14:45:33 -04:00
Matt Nadareski
16248ef02f MS-ZIP might be broken? 2025-07-23 14:03:33 -04:00
Matt Nadareski
abeaa92112 Fix Blast decompression 2025-07-23 13:58:11 -04:00
Matt Nadareski
4b9b4a22cc Add BZip2 tests 2025-07-23 13:13:01 -04:00
Matt Nadareski
bd1ac0c01c Add Compression as a namespace 2025-07-23 12:46:00 -04:00
Matt Nadareski
670594d13f Further clarifications 2025-07-23 12:38:03 -04:00
Matt Nadareski
89441103b3 Update README to be more accurate 2025-07-23 12:36:56 -04:00
Matt Nadareski
71021c61f7 Port NormalizeFilePath from MPF 2025-07-23 12:03:42 -04:00
Matt Nadareski
d49c211882 Add GetHomeDirectory 2025-07-23 11:40:05 -04:00
Matt Nadareski
f2bdc815b8 Private readers and writers don't have to be nullable 2025-07-23 11:06:21 -04:00
Matt Nadareski
e89450c4c1 Better layout for readers/writers 2025-07-23 10:58:57 -04:00
Matt Nadareski
44d5227c14 Fix some .NET Standard 2.1 support 2025-07-23 10:39:58 -04:00
Matt Nadareski
a914b554d1 Fix GetRuntimeDirectory
Previously, it would use CWD instead of where the library was actually being invoked from. This fixes the method so that it gets the library invocation folder instead.
2025-07-23 10:30:54 -04:00
Matt Nadareski
20382360ee Be consistent about end-of-file newlines 2025-07-23 10:25:19 -04:00
Matt Nadareski
ff26c0a4b9 Add .NET Standard 2.0 and 2.1 2025-07-23 10:20:55 -04:00
Matt Nadareski
6028032c89 Update nuget packages 2025-07-23 10:19:41 -04:00
Matt Nadareski
8f3a9d0ede Bump version 2025-05-12 08:19:12 -04:00
Matt Nadareski
4400f0bb87 Update to Matching 1.5.2 2025-05-12 08:18:40 -04:00
Matt Nadareski
9f12ee642d Update copyright 2024-12-30 21:22:05 -05:00
Matt Nadareski
b084b13ec3 Remove unnecessary action step 2024-12-30 21:21:45 -05:00
Matt Nadareski
93b3accbfd Ensure .NET versions are installed for testing 2024-12-19 10:50:26 -05:00
Matt Nadareski
e96c3114d9 Ensure .NET versions are installed for testing 2024-12-19 10:47:29 -05:00
Matt Nadareski
04f095018c Bump version 2024-12-16 13:59:19 -05:00
Matt Nadareski
ff18049832 Add explicit value building to extensions 2024-12-16 13:55:32 -05:00
Matt Nadareski
5e7d2901c8 Update IO to 1.5.1 2024-12-16 12:22:32 -05:00
Matt Nadareski
add7bc8ea5 Allow symbols to be packed 2024-12-16 12:20:05 -05:00
Matt Nadareski
f95853cc87 Use publish script and update README 2024-12-06 10:59:42 -05:00
Matt Nadareski
3f146d45a8 Bump version 2024-11-29 19:54:37 -05:00
Matt Nadareski
f3689087e6 Add alignment tests 2024-11-28 21:31:38 -05:00
Matt Nadareski
d2d191d86f Add boundary alignment stream extension 2024-11-28 21:17:33 -05:00
Matt Nadareski
d6cc7faea8 Bump version 2024-11-26 09:47:52 -05:00
Matt Nadareski
3b56150cc9 Fix getopts in publish script 2024-11-26 00:37:08 -05:00
Matt Nadareski
a7b50dfdf2 Fix bitstream read naming again 2024-11-25 23:12:05 -05:00
Matt Nadareski
0b7ab5b932 Add more Bitstream tests 2024-11-25 22:44:40 -05:00
Matt Nadareski
e43560cbbd Add logging tests 2024-11-25 22:33:11 -05:00
Matt Nadareski
861bfdc4f4 Update SV reader/writer and add tests 2024-11-25 22:10:41 -05:00
Matt Nadareski
933dd70654 Add INI reader/writer tests 2024-11-25 21:59:36 -05:00
Matt Nadareski
0d2a2a3b7d Update CMP reader/writer and add tests 2024-11-25 21:52:53 -05:00
Matt Nadareski
6ad4872bd4 Update PathTool and add tests 2024-11-25 21:19:45 -05:00
Matt Nadareski
8c0f54c059 Fix build for old .NET 2024-11-25 21:05:43 -05:00
Matt Nadareski
ab1b0646c4 Minor cleanup in ParentablePath 2024-11-25 21:04:09 -05:00
Matt Nadareski
450d8aab11 Update INI file and add tests 2024-11-25 21:01:52 -05:00
Matt Nadareski
ec8908aec0 Update XML writer extensions and add tests 2024-11-25 20:35:02 -05:00
Matt Nadareski
fb7ca7cde0 Update stream extensions and add tests 2024-11-25 17:09:33 -05:00
Matt Nadareski
67ca20f71b Update IO extensions and add tests 2024-11-25 16:52:49 -05:00
Matt Nadareski
ae3a27eee1 Update writer extensions and add tests 2024-11-25 16:15:12 -05:00
Matt Nadareski
3b7f910a98 Cover some bad SafeEnumerate paths 2024-11-25 15:23:51 -05:00
Matt Nadareski
244edc132f Remove unnecessary try/catch 2024-11-25 15:16:04 -05:00
Matt Nadareski
cec495d55a Update byte array extensions and add tests 2024-11-25 15:14:50 -05:00
Matt Nadareski
7656734bb2 Update reader extensions and add tests 2024-11-25 15:03:07 -05:00
Matt Nadareski
fa9310de39 Update packages 2024-11-25 13:06:17 -05:00
Matt Nadareski
fe466dfe25 Move IsNullOrEmpty from Matching 2024-11-25 11:29:22 -05:00
Matt Nadareski
d2c59c565f Make bitstream tests more robust; add notes 2024-11-18 11:26:54 -05:00
Matt Nadareski
4e221f33d5 Had it right the first time 2024-11-18 10:58:10 -05:00
Matt Nadareski
c5c8ce67ba Rename LSB/MSB to LE/BE 2024-11-18 10:30:55 -05:00
Matt Nadareski
8e3d204329 Fix LSB bit reads 2024-11-18 10:26:00 -05:00
Matt Nadareski
200d947f30 Bump version 2024-11-15 20:41:39 -05:00
Matt Nadareski
dfccdcfb05 Update Matching to 1.4.1 2024-11-15 20:40:31 -05:00
Matt Nadareski
72ff3ead48 Port extension attribute instead of framework gating 2024-11-15 20:40:01 -05:00
Matt Nadareski
fb15aecb87 Framework only matters for executable 2024-11-15 20:35:51 -05:00
Matt Nadareski
897e54ca61 Bump version 2024-11-13 00:44:44 -05:00
Matt Nadareski
3af6bc8365 Add .NET 9 to target frameworks 2024-11-13 00:44:16 -05:00
Matt Nadareski
de05bae3f8 Be smarter about framework gating 2024-11-05 21:47:43 -05:00
Matt Nadareski
97d603abb7 Update Matching to 1.3.4 2024-11-05 20:57:53 -05:00
Matt Nadareski
ed32302447 Update Matching to 1.3.3 2024-10-26 19:45:12 -04:00
Matt Nadareski
c125dc4ec0 Bump version 2024-10-24 00:22:28 -04:00
Matt Nadareski
f154ae47c0 Disable warnings, don't ignore them 2024-10-24 00:20:46 -04:00
Matt Nadareski
0d0e960b98 Use converters properly 2024-10-24 00:17:09 -04:00
Matt Nadareski
4a9f84ab66 Add LogLevel enum converters 2024-10-24 00:10:50 -04:00
Matt Nadareski
39277ee443 Add generic byte array extensions 2024-10-24 00:06:58 -04:00
Matt Nadareski
ed367ace6d Import logger from SabreTools 2024-10-24 00:04:50 -04:00
Matt Nadareski
80e72832a4 Add WORD/DWORD extensions, for fun 2024-10-15 20:52:32 -04:00
220 changed files with 55049 additions and 2499 deletions

48
.github/workflows/build_and_test.yml vendored Normal file
View 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

View File

@@ -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: 8.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

View File

@@ -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: 8.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
View 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.

View File

@@ -1,11 +1,11 @@
# SabreTools.IO
This library comprises I/O functionality for the following file types:
[![Build and Test](https://github.com/SabreTools/SabreTools.IO/actions/workflows/build_and_test.yml/badge.svg)](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)

View 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);
}
}
}

View 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);
}
}
}

View 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);
}
}
}

View 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

View File

@@ -1,17 +1,13 @@
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;
namespace SabreTools.IO.Test.Extensions
{
// TODO: Add byte[], char[] tests
// TODO: Add string writing tests
public class BinaryWriterExtensionsTests
{
/// <summary>
@@ -37,37 +33,71 @@ 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());
}
[Fact]
public void WriteBytesBigEndianTest()
{
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
var bw = new BinaryWriter(stream);
byte[] expected = [.. _bytes.Take(4)];
bw.WriteBigEndian([0x03, 0x02, 0x01, 0x00]);
ValidateBytes(expected, stream.GetBuffer());
}
[Fact]
public void WriteSByteTest()
{
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());
}
@@ -87,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());
}
@@ -97,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());
}
@@ -118,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());
}
@@ -140,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());
}
@@ -162,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());
@@ -173,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());
}
@@ -183,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());
@@ -194,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());
}
@@ -204,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());
}
@@ -225,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());
}
@@ -246,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());
@@ -257,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());
}
@@ -267,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());
@@ -278,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());
}
@@ -288,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());
@@ -299,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());
}
@@ -309,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());
}
@@ -330,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());
}
@@ -351,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());
@@ -362,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());
}
@@ -372,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());
@@ -383,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());
@@ -394,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());
@@ -417,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());
}
@@ -428,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());
@@ -439,12 +538,122 @@ 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()
{
var stream = new MemoryStream(new byte[4], 0, 4, true, true);
var bw = new BinaryWriter(stream);
byte[] expected = [0x41, 0x42, 0x43, 0x00];
bool write = bw.WriteNullTerminatedAnsiString("ABC");
Assert.True(write);
ValidateBytes(expected, stream.GetBuffer());
}
[Fact]
public void WriteNullTerminatedUTF8StringTest()
{
var stream = new MemoryStream(new byte[4], 0, 4, true, true);
var bw = new BinaryWriter(stream);
byte[] expected = [0x41, 0x42, 0x43, 0x00];
bool write = bw.WriteNullTerminatedUTF8String("ABC");
Assert.True(write);
ValidateBytes(expected, stream.GetBuffer());
}
[Fact]
public void WriteNullTerminatedUnicodeStringTest()
{
var stream = new MemoryStream(new byte[8], 0, 8, true, true);
var bw = new BinaryWriter(stream);
byte[] expected = [0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x00];
bool write = bw.WriteNullTerminatedUnicodeString("ABC");
Assert.True(write);
ValidateBytes(expected, stream.GetBuffer());
}
[Fact]
public void WriteNullTerminatedUTF32StringTest()
{
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
var bw = new BinaryWriter(stream);
byte[] expected = [0x41, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
bool write = bw.WriteNullTerminatedUTF32String("ABC");
Assert.True(write);
ValidateBytes(expected, stream.GetBuffer());
}
[Fact]
public void WritePrefixedAnsiStringTest()
{
var stream = new MemoryStream(new byte[4], 0, 4, true, true);
var bw = new BinaryWriter(stream);
byte[] expected = [0x03, 0x41, 0x42, 0x43];
bool write = bw.WritePrefixedAnsiString("ABC");
Assert.True(write);
ValidateBytes(expected, stream.GetBuffer());
}
[Fact]
public void WritePrefixedUnicodeStringTest()
{
var stream = new MemoryStream(new byte[8], 0, 8, true, true);
var bw = new BinaryWriter(stream);
byte[] expected = [0x03, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00];
bool write = bw.WritePrefixedUnicodeString("ABC");
Assert.True(write);
ValidateBytes(expected, stream.GetBuffer());
}
[Fact]
public void WriteTypeTest()
{
// Guid
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
var bw = new BinaryWriter(stream);
bool actual = bw.WriteType<Guid>(new Guid(_bytes));
Assert.True(actual);
ValidateBytes(_bytes, stream.GetBuffer());
// 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());
// Int128
stream = new MemoryStream(new byte[16], 0, 16, true, true);
bw = new BinaryWriter(stream);
actual = bw.WriteType<Int128>((Int128)new BigInteger(_bytes));
Assert.True(actual);
ValidateBytes(_bytes, stream.GetBuffer());
// UInt128
stream = new MemoryStream(new byte[16], 0, 16, true, true);
bw = new BinaryWriter(stream);
actual = bw.WriteType<UInt128>((UInt128)new BigInteger(_bytes));
Assert.True(actual);
ValidateBytes(_bytes, stream.GetBuffer());
// Enum
stream = new MemoryStream(new byte[4], 0, 4, true, true);
bw = new BinaryWriter(stream);
actual = bw.WriteType<TestEnum>((TestEnum)0x03020100);
Assert.True(actual);
ValidateBytes([.. _bytes.Take(4)], stream.GetBuffer());
}
[Fact]
public void WriteTypeExplicitTest()
@@ -463,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());
@@ -488,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());
@@ -505,4 +714,4 @@ namespace SabreTools.IO.Test.Extensions
}
}
}
}
}

View File

@@ -1,547 +0,0 @@
using System;
using System.Linq;
#if NET7_0_OR_GREATER
using System.Numerics;
#endif
using SabreTools.IO.Extensions;
using Xunit;
namespace SabreTools.IO.Test.Extensions
{
// TODO: Add string reading tests
public class ByteArrayExtensionsReadTests
{
/// <summary>
/// Test pattern from 0x00-0x0F
/// </summary>
private static readonly byte[] _bytes =
[
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
];
/// <summary>
/// Represents the decimal value 0.0123456789
/// </summary>
private static readonly byte[] _decimalBytes =
[
0x15, 0xCD, 0x5B, 0x07, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00,
];
[Fact]
public void ReadByteTest()
{
int offset = 0;
byte read = _bytes.ReadByte(ref offset);
Assert.Equal(0x00, read);
}
[Fact]
public void ReadByteValueTest()
{
int offset = 0;
byte read = _bytes.ReadByteValue(ref offset);
Assert.Equal(0x00, read);
}
[Fact]
public void ReadBytesTest()
{
int offset = 0, length = 4;
byte[] read = _bytes.ReadBytes(ref offset, length);
Assert.Equal(length, read.Length);
Assert.True(read.SequenceEqual(_bytes.Take(length)));
}
[Fact]
public void ReadSByteTest()
{
int offset = 0;
sbyte read = _bytes.ReadSByte(ref offset);
Assert.Equal(0x00, read);
}
[Fact]
public void ReadCharTest()
{
int offset = 0;
char read = _bytes.ReadChar(ref offset);
Assert.Equal('\0', read);
}
[Fact]
public void ReadInt16Test()
{
int offset = 0;
short read = _bytes.ReadInt16(ref offset);
Assert.Equal(0x0100, read);
}
[Fact]
public void ReadInt16BigEndianTest()
{
int offset = 0;
short read = _bytes.ReadInt16BigEndian(ref offset);
Assert.Equal(0x0001, read);
}
[Fact]
public void ReadUInt16Test()
{
int offset = 0;
ushort read = _bytes.ReadUInt16(ref offset);
Assert.Equal(0x0100, read);
}
[Fact]
public void ReadUInt16BigEndianTest()
{
int offset = 0;
ushort read = _bytes.ReadUInt16BigEndian(ref offset);
Assert.Equal(0x0001, read);
}
#if NET6_0_OR_GREATER
[Fact]
public void ReadHalfTest()
{
int offset = 0;
Half expected = BitConverter.Int16BitsToHalf(0x0100);
Half read = _bytes.ReadHalf(ref offset);
Assert.Equal(expected, read);
}
[Fact]
public void ReadHalfBigEndianTest()
{
int offset = 0;
Half expected = BitConverter.Int16BitsToHalf(0x0001);
Half read = _bytes.ReadHalfBigEndian(ref offset);
Assert.Equal(expected, read);
}
#endif
[Fact]
public void ReadInt24Test()
{
int offset = 0;
int read = _bytes.ReadInt24(ref offset);
Assert.Equal(0x020100, read);
}
[Fact]
public void ReadInt24BigEndianTest()
{
int offset = 0;
int read = _bytes.ReadInt24BigEndian(ref offset);
Assert.Equal(0x000102, read);
}
[Fact]
public void ReadUInt24Test()
{
int offset = 0;
uint read = _bytes.ReadUInt24(ref offset);
Assert.Equal((uint)0x020100, read);
}
[Fact]
public void ReadUInt24BigEndianTest()
{
int offset = 0;
uint read = _bytes.ReadUInt24BigEndian(ref offset);
Assert.Equal((uint)0x000102, read);
}
[Fact]
public void ReadInt32Test()
{
int offset = 0;
int read = _bytes.ReadInt32(ref offset);
Assert.Equal(0x03020100, read);
}
[Fact]
public void ReadInt32BigEndianTest()
{
int offset = 0;
int read = _bytes.ReadInt32BigEndian(ref offset);
Assert.Equal(0x00010203, read);
}
[Fact]
public void ReadUInt32Test()
{
int offset = 0;
uint read = _bytes.ReadUInt32(ref offset);
Assert.Equal((uint)0x03020100, read);
}
[Fact]
public void ReadUInt32BigEndianTest()
{
int offset = 0;
uint read = _bytes.ReadUInt32BigEndian(ref offset);
Assert.Equal((uint)0x00010203, read);
}
[Fact]
public void ReadSingleTest()
{
int offset = 0;
float expected = BitConverter.Int32BitsToSingle(0x03020100);
float read = _bytes.ReadSingle(ref offset);
Assert.Equal(expected, read);
}
[Fact]
public void ReadSingleBigEndianTest()
{
int offset = 0;
float expected = BitConverter.Int32BitsToSingle(0x00010203);
float read = _bytes.ReadSingleBigEndian(ref offset);
Assert.Equal(expected, read);
}
[Fact]
public void ReadInt48Test()
{
int offset = 0;
long read = _bytes.ReadInt48(ref offset);
Assert.Equal(0x050403020100, read);
}
[Fact]
public void ReadInt48BigEndianTest()
{
int offset = 0;
long read = _bytes.ReadInt48BigEndian(ref offset);
Assert.Equal(0x000102030405, read);
}
[Fact]
public void ReadUInt48Test()
{
int offset = 0;
ulong read = _bytes.ReadUInt48(ref offset);
Assert.Equal((ulong)0x050403020100, read);
}
[Fact]
public void ReadUInt48BigEndianTest()
{
int offset = 0;
ulong read = _bytes.ReadUInt48BigEndian(ref offset);
Assert.Equal((ulong)0x000102030405, read);
}
[Fact]
public void ReadInt64Test()
{
int offset = 0;
long read = _bytes.ReadInt64(ref offset);
Assert.Equal(0x0706050403020100, read);
}
[Fact]
public void ReadInt64BigEndianTest()
{
int offset = 0;
long read = _bytes.ReadInt64BigEndian(ref offset);
Assert.Equal(0x0001020304050607, read);
}
[Fact]
public void ReadUInt64Test()
{
int offset = 0;
ulong read = _bytes.ReadUInt64(ref offset);
Assert.Equal((ulong)0x0706050403020100, read);
}
[Fact]
public void ReadUInt64BigEndianTest()
{
int offset = 0;
ulong read = _bytes.ReadUInt64BigEndian(ref offset);
Assert.Equal((ulong)0x0001020304050607, read);
}
[Fact]
public void ReadDoubleTest()
{
int offset = 0;
double expected = BitConverter.Int64BitsToDouble(0x0706050403020100);
double read = _bytes.ReadDouble(ref offset);
Assert.Equal(expected, read);
}
[Fact]
public void ReadDoubleBigEndianTest()
{
int offset = 0;
double expected = BitConverter.Int64BitsToDouble(0x0001020304050607);
double read = _bytes.ReadDoubleBigEndian(ref offset);
Assert.Equal(expected, read);
}
[Fact]
public void ReadDecimalTest()
{
int offset = 0;
decimal expected = 0.0123456789M;
decimal read = _decimalBytes.ReadDecimal(ref offset);
Assert.Equal(expected, read);
}
[Fact]
public void ReadDecimalBigEndianTest()
{
int offset = 0;
decimal expected = 0.0123456789M;
decimal read = _decimalBytes.Reverse().ToArray().ReadDecimalBigEndian(ref offset);
Assert.Equal(expected, read);
}
[Fact]
public void ReadGuidTest()
{
int offset = 0;
var expected = new Guid(_bytes);
Guid read = _bytes.ReadGuid(ref offset);
Assert.Equal(expected, read);
}
[Fact]
public void ReadGuidBigEndianTest()
{
int offset = 0;
var expected = new Guid(_bytes.Reverse().ToArray());
Guid read = _bytes.ReadGuidBigEndian(ref offset);
Assert.Equal(expected, read);
}
#if NET7_0_OR_GREATER
[Fact]
public void ReadInt128Test()
{
int offset = 0;
var expected = (Int128)new BigInteger(_bytes);
Int128 read = _bytes.ReadInt128(ref offset);
Assert.Equal(expected, read);
}
[Fact]
public void ReadInt128BigEndianTest()
{
int offset = 0;
var reversed = _bytes.Reverse().ToArray();
var expected = (Int128)new BigInteger(reversed);
Int128 read = _bytes.ReadInt128BigEndian(ref offset);
Assert.Equal(expected, read);
}
[Fact]
public void ReadUInt128Test()
{
int offset = 0;
var expected = (UInt128)new BigInteger(_bytes);
UInt128 read = _bytes.ReadUInt128(ref offset);
Assert.Equal(expected, read);
}
[Fact]
public void ReadUInt128BigEndianTest()
{
int offset = 0;
var reversed = _bytes.Reverse().ToArray();
var expected = (UInt128)new BigInteger(reversed);
UInt128 read = _bytes.ReadUInt128BigEndian(ref offset);
Assert.Equal(expected, read);
}
#endif
[Fact]
public void ReadTypeExplicitTest()
{
byte[] bytesWithString =
[
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x41, 0x42, 0x43, 0x00,
];
int offset = 0;
var expected = new TestStructExplicit
{
FirstValue = TestEnum.RecognizedTestValue,
SecondValue = 0x07060504,
ThirdValue = 0x0504,
FourthValue = 0x0706,
FifthValue = "ABC",
};
var read = bytesWithString.ReadType<TestStructExplicit>(ref offset);
Assert.Equal(expected.FirstValue, read.FirstValue);
Assert.Equal(expected.SecondValue, read.SecondValue);
Assert.Equal(expected.ThirdValue, read.ThirdValue);
Assert.Equal(expected.FourthValue, read.FourthValue);
}
[Fact]
public void ReadTypeSequentialTest()
{
byte[] bytesWithString =
[
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x41, 0x42, 0x43, 0x00,
];
int offset = 0;
var expected = new TestStructSequential
{
FirstValue = TestEnum.RecognizedTestValue,
SecondValue = 0x07060504,
ThirdValue = 0x0908,
FourthValue = 0x0B0A,
FifthValue = "ABC",
};
var read = bytesWithString.ReadType<TestStructSequential>(ref offset);
Assert.Equal(expected.FirstValue, read.FirstValue);
Assert.Equal(expected.SecondValue, read.SecondValue);
Assert.Equal(expected.ThirdValue, read.ThirdValue);
Assert.Equal(expected.FourthValue, read.FourthValue);
Assert.Equal(expected.FifthValue, read.FifthValue);
}
[Fact]
public void ReadTypeStringsTest()
{
byte[] structBytes =
[
0x03, 0x41, 0x42, 0x43, // AnsiBStr
0x03, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, // BStr
0x41, 0x42, 0x43, // ByValTStr
0x41, 0x42, 0x43, 0x00, // LPStr
0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x00, 0x00, // LPWStr
];
int offset = 0;
var expected = new TestStructStrings
{
AnsiBStr = "ABC",
BStr = "ABC",
ByValTStr = "ABC",
LPStr = "ABC",
LPWStr = "ABC",
};
var read = structBytes.ReadType<TestStructStrings>(ref offset);
Assert.Equal(expected.AnsiBStr, read.AnsiBStr);
Assert.Equal(expected.BStr, read.BStr);
Assert.Equal(expected.ByValTStr, read.ByValTStr);
Assert.Equal(expected.LPStr, read.LPStr);
Assert.Equal(expected.LPWStr, read.LPWStr);
}
[Fact]
public void ReadTypeArraysTest()
{
byte[] structBytes =
[
// Byte Array
0x00, 0x01, 0x02, 0x03,
// Int Array
0x03, 0x02, 0x01, 0x00,
0x04, 0x03, 0x02, 0x01,
0x05, 0x04, 0x03, 0x02,
0x06, 0x05, 0x04, 0x03,
// Struct Array (X, Y)
0xFF, 0x00, 0x00, 0xFF,
0x00, 0xFF, 0xFF, 0x00,
0xAA, 0x55, 0x55, 0xAA,
0x55, 0xAA, 0xAA, 0x55,
// LPArray
0x04, 0x00,
0x00, 0x01, 0x02, 0x03,
];
int offset = 0;
var expected = new TestStructArrays
{
ByteArray = [0x00, 0x01, 0x02, 0x03],
IntArray = [0x00010203, 0x01020304, 0x02030405, 0x03040506],
StructArray =
[
new TestStructPoint { X = 0x00FF, Y = 0xFF00 },
new TestStructPoint { X = 0xFF00, Y = 0x00FF },
new TestStructPoint { X = 0x55AA, Y = 0xAA55 },
new TestStructPoint { X = 0xAA55, Y = 0x55AA },
],
LPByteArrayLength = 0x0004,
LPByteArray = [0x00, 0x01, 0x02, 0x03],
};
var read = structBytes.ReadType<TestStructArrays>(ref offset);
Assert.NotNull(read.ByteArray);
Assert.True(expected.ByteArray.SequenceEqual(read.ByteArray));
Assert.NotNull(read.IntArray);
Assert.True(expected.IntArray.SequenceEqual(read.IntArray));
Assert.NotNull(read.StructArray);
Assert.True(expected.StructArray.SequenceEqual(read.StructArray));
Assert.Equal(expected.LPByteArrayLength, read.LPByteArrayLength);
Assert.NotNull(read.LPByteArray);
Assert.True(expected.LPByteArray.SequenceEqual(read.LPByteArray));
}
[Fact]
public void ReadTypeInheritanceTest()
{
byte[] structBytes1 =
[
0x41, 0x42, 0x43, 0x44, // Signature
0x00, 0xFF, 0x00, 0xFF, // IdentifierType
0xAA, 0x55, 0xAA, 0x55, // FieldA
0x55, 0xAA, 0x55, 0xAA, // FieldB
];
int offset1 = 0;
var expected1 = new TestStructInheritanceChild1
{
Signature = [0x41, 0x42, 0x43, 0x44],
IdentifierType = 0xFF00FF00,
FieldA = 0x55AA55AA,
FieldB = 0xAA55AA55,
};
var read1 = structBytes1.ReadType<TestStructInheritanceChild1>(ref offset1);
Assert.NotNull(read1?.Signature);
Assert.Equal(expected1.Signature, read1.Signature);
Assert.Equal(expected1.IdentifierType, read1.IdentifierType);
Assert.Equal(expected1.FieldA, read1.FieldA);
Assert.Equal(expected1.FieldB, read1.FieldB);
byte[] structBytes2 =
[
0x41, 0x42, 0x43, 0x44, // Signature
0x00, 0xFF, 0x00, 0xFF, // IdentifierType
0xAA, 0x55, // FieldA
0x55, 0xAA, // FieldB
];
int offset2 = 0;
var expected2 = new TestStructInheritanceChild2
{
Signature = [0x41, 0x42, 0x43, 0x44],
IdentifierType = 0xFF00FF00,
FieldA = 0x55AA,
FieldB = 0xAA55,
};
var read2 = structBytes2.ReadType<TestStructInheritanceChild2>(ref offset2);
Assert.NotNull(read2?.Signature);
Assert.Equal(expected2.Signature, read2.Signature);
Assert.Equal(expected2.IdentifierType, read2.IdentifierType);
Assert.Equal(expected2.FieldA, read2.FieldA);
Assert.Equal(expected2.FieldB, read2.FieldB);
}
}
}

View File

@@ -0,0 +1,977 @@
using System;
using System.Linq;
using System.Text;
using SabreTools.IO.Extensions;
using Xunit;
namespace SabreTools.IO.Test.Extensions
{
public class ByteArrayExtensionsTests
{
#region IsNullOrEmpty
[Fact]
public void IsNullOrEmpty_Null_True()
{
byte[]? arr = null;
bool actual = arr.IsNullOrEmpty();
Assert.True(actual);
}
[Fact]
public void IsNullOrEmpty_Empty_True()
{
byte[]? arr = [];
bool actual = arr.IsNullOrEmpty();
Assert.True(actual);
}
[Fact]
public void IsNullOrEmpty_NonEmpty_False()
{
byte[]? arr = [0x01];
bool actual = arr.IsNullOrEmpty();
Assert.False(actual);
}
#endregion
#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()
{
byte[]? arr = null;
string? actual = arr.ToHexString();
Assert.Null(actual);
}
[Fact]
public void ToHexString_Valid()
{
byte[]? arr = [0x01, 0x02, 0x03, 0x04];
string expected = "01020304";
string? actual = arr.ToHexString();
Assert.NotNull(actual);
Assert.Equal(expected, actual);
}
#endregion
#region FromHexString
[Fact]
public void FromHexString_Null()
{
string? str = null;
byte[]? actual = str.FromHexString();
Assert.Null(actual);
}
[Fact]
public void FromHexString_Valid()
{
string str = "01020304";
byte[]? expected = [0x01, 0x02, 0x03, 0x04];
byte[]? actual = str.FromHexString();
Assert.NotNull(actual);
Assert.True(expected.SequenceEqual(actual));
}
[Fact]
public void FromHexString_Invalid()
{
string str = "0102030G";
byte[]? actual = str.FromHexString();
Assert.Null(actual);
}
#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

View File

@@ -1,15 +1,13 @@
using System;
using System.Linq;
#if NET7_0_OR_GREATER
using System.Numerics;
#endif
using System.Text;
using SabreTools.IO.Extensions;
using Xunit;
namespace SabreTools.IO.Test.Extensions
{
// TODO: Add string writing tests
public class ByteArrayExtensionsWriteTests
public class ByteArrayWriterExtensionsTests
{
/// <summary>
/// Test pattern from 0x00-0x0F
@@ -34,51 +32,97 @@ 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);
}
[Fact]
public void WriteBytesBigEndianTest()
{
byte[] buffer = new byte[16];
int offset = 0;
byte[] expected = [.. _bytes.Take(4)];
bool write = buffer.WriteBigEndian(ref offset, [0x03, 0x02, 0x01, 0x00]);
Assert.True(write);
ValidateBytes(expected, buffer);
}
[Fact]
public void WriteSByteTest()
{
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);
}
[Fact]
public void WriteCharEncodingTest()
{
byte[] buffer = new byte[16];
int offset = 0;
byte[] expected = [0x00, 0x00];
bool write = buffer.Write(ref offset, '\0', Encoding.Unicode);
Assert.True(write);
ValidateBytes(expected, buffer);
}
[Fact]
public void WriteInt16Test()
{
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);
@@ -89,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);
@@ -111,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);
@@ -134,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);
@@ -157,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);
@@ -168,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);
@@ -179,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);
@@ -190,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);
@@ -201,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);
@@ -223,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);
@@ -245,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);
@@ -256,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);
@@ -267,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);
@@ -278,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);
@@ -289,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);
@@ -300,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);
@@ -311,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);
@@ -333,18 +435,52 @@ 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)];
bool write = buffer.Write(ref offset, BitConverter.Int64BitsToDouble(0x0706050403020100));
Assert.True(write);
ValidateBytes(expected, buffer);
}
[Fact]
public void WriteDoubleBigEndianTest()
{
byte[] buffer = new byte[16];
int offset = 0;
byte[] expected = [.. _bytes.Take(8)];
bool write = buffer.WriteBigEndian(ref offset, BitConverter.Int64BitsToDouble(0x0001020304050607));
Assert.True(write);
ValidateBytes(expected, buffer);
}
[Fact]
public void WriteDecimalTest()
{
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);
@@ -355,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);
@@ -366,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);
@@ -377,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);
@@ -400,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);
}
@@ -411,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);
@@ -422,12 +557,122 @@ 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()
{
int offset = 0;
byte[] buffer = new byte[4];
byte[] expected = [0x41, 0x42, 0x43, 0x00];
bool write = buffer.WriteNullTerminatedAnsiString(ref offset, "ABC");
Assert.True(write);
ValidateBytes(expected, buffer);
}
[Fact]
public void WriteNullTerminatedUTF8StringTest()
{
int offset = 0;
byte[] buffer = new byte[4];
byte[] expected = [0x41, 0x42, 0x43, 0x00];
bool write = buffer.WriteNullTerminatedUTF8String(ref offset, "ABC");
Assert.True(write);
ValidateBytes(expected, buffer);
}
[Fact]
public void WriteNullTerminatedUnicodeStringTest()
{
int offset = 0;
byte[] buffer = new byte[8];
byte[] expected = [0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x00];
bool write = buffer.WriteNullTerminatedUnicodeString(ref offset, "ABC");
Assert.True(write);
ValidateBytes(expected, buffer);
}
[Fact]
public void WriteNullTerminatedUTF32StringTest()
{
int offset = 0;
byte[] buffer = new byte[16];
byte[] expected = [0x41, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
bool write = buffer.WriteNullTerminatedUTF32String(ref offset, "ABC");
Assert.True(write);
ValidateBytes(expected, buffer);
}
[Fact]
public void WritePrefixedAnsiStringTest()
{
int offset = 0;
byte[] buffer = new byte[4];
byte[] expected = [0x03, 0x41, 0x42, 0x43];
bool write = buffer.WritePrefixedAnsiString(ref offset, "ABC");
Assert.True(write);
ValidateBytes(expected, buffer);
}
[Fact]
public void WritePrefixedUnicodeStringTest()
{
int offset = 0;
byte[] buffer = new byte[8];
byte[] expected = [0x03, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00];
bool write = buffer.WritePrefixedUnicodeString(ref offset, "ABC");
Assert.True(write);
ValidateBytes(expected, buffer);
}
[Fact]
public void WriteTypeTest()
{
// Guid
int offset = 0;
byte[] buffer = new byte[16];
bool actual = buffer.WriteType<Guid>(ref offset, new Guid(_bytes));
Assert.True(actual);
ValidateBytes(_bytes, buffer);
// Half
offset = 0;
buffer = new byte[2];
actual = buffer.WriteType<Half>(ref offset, BitConverter.Int16BitsToHalf(0x0100));
Assert.True(actual);
ValidateBytes([.. _bytes.Take(2)], buffer);
// Int128
offset = 0;
buffer = new byte[16];
actual = buffer.WriteType<Int128>(ref offset, (Int128)new BigInteger(_bytes));
Assert.True(actual);
ValidateBytes(_bytes, buffer);
// UInt128
offset = 0;
buffer = new byte[16];
actual = buffer.WriteType<UInt128>(ref offset, (UInt128)new BigInteger(_bytes));
Assert.True(actual);
ValidateBytes(_bytes, buffer);
// Enum
offset = 0;
buffer = new byte[4];
actual = buffer.WriteType<TestEnum>(ref offset, (TestEnum)0x03020100);
Assert.True(actual);
ValidateBytes([.. _bytes.Take(4)], buffer);
}
[Fact]
public void WriteTypeExplicitTest()
@@ -446,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);
@@ -471,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);
@@ -488,4 +733,4 @@ namespace SabreTools.IO.Test.Extensions
}
}
}
}
}

View 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
}
}

View File

@@ -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,8 +10,24 @@ namespace SabreTools.IO.Test.Extensions
{
public class EnumerableExtensionsTests
{
#region IterateWithAction
[Fact]
public void SafeEnumerateEmptyTest()
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()
{
var source = Enumerable.Empty<string>();
var safe = source.SafeEnumerate();
@@ -19,7 +36,18 @@ namespace SabreTools.IO.Test.Extensions
}
[Fact]
public void SafeEnumerateNoErrorTest()
public void SafeEnumerate_Throws()
{
var source = new List<string> { "a", "ab", "abc" };
var wrapper = new ThrowsEnumerable(source);
var safe = wrapper.SafeEnumerate();
var list = safe.ToList();
Assert.Empty(list);
}
[Fact]
public void SafeEnumerate_NoError()
{
var source = new List<string> { "a", "ab", "abc" };
var safe = source.SafeEnumerate();
@@ -28,7 +56,7 @@ namespace SabreTools.IO.Test.Extensions
}
[Fact]
public void SafeEnumerateErrorMidTest()
public void SafeEnumerate_ErrorMid()
{
var source = new List<string> { "a", "ab", "abc" };
var wrapper = new ErrorEnumerable(source);
@@ -39,18 +67,20 @@ namespace SabreTools.IO.Test.Extensions
}
[Fact]
public void SafeEnumerateErrorLastTest()
public void SafeEnumerate_ErrorLast()
{
var source = new List<string> { "a", "ab", "abc", "abcd" };
var wrapper = new ErrorEnumerable(source);
var safe = wrapper.SafeEnumerate();
var list = safe.ToList();
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>
{
@@ -134,5 +164,19 @@ namespace SabreTools.IO.Test.Extensions
_index = -1;
}
}
/// <summary>
/// Fake enumerable that throws an exception for the enumerator
/// </summary>
private class ThrowsEnumerable : IEnumerable<string>
{
public ThrowsEnumerable(IEnumerable<string> source) { }
/// <inheritdoc/>
public IEnumerator<string> GetEnumerator() => throw new Exception();
/// <inheritdoc/>
IEnumerator IEnumerable.GetEnumerator() => throw new Exception();
}
}
}
}

View File

@@ -1,3 +1,6 @@
using System;
using System.IO;
using System.Text;
using SabreTools.IO.Extensions;
using Xunit;
@@ -5,6 +8,116 @@ namespace SabreTools.IO.Test.Extensions
{
public class IOExtensionsTests
{
#region Ensure
[Theory]
[InlineData(null, null)]
[InlineData("", null)]
[InlineData(" ", " ")] // TODO: This is a bad result
[InlineData("dirname", "dirname")]
[InlineData("\"dirname\"", "dirname")]
public void EnsureTest(string? dir, string? expected)
{
// Handle test setup
expected ??= PathTool.GetRuntimeDirectory();
if (expected != null)
expected = Path.GetFullPath(expected);
string actual = dir.Ensure(create: false);
Assert.Equal(expected, actual);
}
#endregion
#region Get Encoding
[Fact]
public void GetEncoding_EmptyPath()
{
string path = "";
Encoding expected = Encoding.Default;
var actual = path.GetEncoding();
Assert.Equal(expected, actual);
}
[Fact]
public void GetEncoding_InvalidPath()
{
string path = Path.Combine(Environment.CurrentDirectory, "TestData", "INVALID");
Encoding expected = Encoding.Default;
var actual = path.GetEncoding();
Assert.Equal(expected, actual);
}
// Disable warning about UTF7 usage
#pragma warning disable SYSLIB0001
[Fact]
public void GetEncoding_UTF7()
{
string path = Path.Combine(Environment.CurrentDirectory, "TestData", "utf7bom.txt");
Encoding expected = Encoding.UTF7;
var actual = path.GetEncoding();
Assert.Equal(expected, actual);
}
#pragma warning restore SYSLIB0001
[Fact]
public void GetEncoding_UTF8()
{
string path = Path.Combine(Environment.CurrentDirectory, "TestData", "utf8bom.txt");
Encoding expected = Encoding.UTF8;
var actual = path.GetEncoding();
Assert.Equal(expected, actual);
}
[Fact]
public void GetEncoding_Unicode()
{
string path = Path.Combine(Environment.CurrentDirectory, "TestData", "utf16lebom.txt");
Encoding expected = Encoding.Unicode;
var actual = path.GetEncoding();
Assert.Equal(expected, actual);
}
[Fact]
public void GetEncoding_BigEndianUnicode()
{
string path = Path.Combine(Environment.CurrentDirectory, "TestData", "utf16bebom.txt");
Encoding expected = Encoding.BigEndianUnicode;
var actual = path.GetEncoding();
Assert.Equal(expected, actual);
}
[Fact]
public void GetEncoding_UTF32()
{
string path = Path.Combine(Environment.CurrentDirectory, "TestData", "utf32bom.txt");
Encoding expected = Encoding.UTF32;
var actual = path.GetEncoding();
Assert.Equal(expected, actual);
}
[Fact]
public void GetEncoding_ASCII()
{
string path = Path.Combine(Environment.CurrentDirectory, "TestData", "ascii.txt");
Encoding expected = Encoding.Default;
var actual = path.GetEncoding();
Assert.Equal(expected, actual);
}
#endregion
#region Get Normalized Extension
[Theory]
[InlineData(null, null)]
[InlineData("", null)]
@@ -15,10 +128,185 @@ namespace SabreTools.IO.Test.Extensions
[InlineData("NO-EXTENSION.", null)]
[InlineData("filename.ext", "ext")]
[InlineData("FILENAME.EXT", "ext")]
public void NormalizedExtensionTest(string? path, string? expected)
public void GetNormalizedExtensionTest(string? path, string? expected)
{
string? actual = path.GetNormalizedExtension();
Assert.Equal(expected, actual);
}
#endregion
#region Path
[Fact]
public void ListEmpty_NullDirectory()
{
string? dir = null;
var empty = dir.ListEmpty();
Assert.Null(empty);
}
[Fact]
public void ListEmpty_InvalidDirectory()
{
string dir = Path.Combine(Environment.CurrentDirectory, "TestData", "INVALID");
var empty = dir.ListEmpty();
Assert.Null(empty);
}
[Fact]
public void ListEmpty_ValidDirectory()
{
string dir = Path.Combine(Environment.CurrentDirectory, "TestData");
var empty = dir.ListEmpty();
Assert.NotNull(empty);
Assert.Empty(empty);
}
[Fact]
public void SafeGetDirectories_ValidDirectory()
{
string dir = Path.Combine(Environment.CurrentDirectory, "TestData");
var dirs = dir.SafeGetDirectories();
Assert.Single(dirs);
}
[Fact]
public void SafeGetDirectories_ValidDirectory_Pattern()
{
string dir = Path.Combine(Environment.CurrentDirectory, "TestData");
var dirs = dir.SafeGetDirectories("*");
Assert.Single(dirs);
}
[Fact]
public void SafeGetDirectories_ValidDirectory_PatternOption()
{
string dir = Path.Combine(Environment.CurrentDirectory, "TestData");
var dirs = dir.SafeGetDirectories("*", SearchOption.AllDirectories);
Assert.Single(dirs);
}
[Fact]
public void SafeGetFiles_ValidDirectory()
{
string dir = Path.Combine(Environment.CurrentDirectory, "TestData");
var files = dir.SafeGetFiles();
Assert.NotEmpty(files);
}
[Fact]
public void SafeGetFiles_ValidDirectory_Pattern()
{
string dir = Path.Combine(Environment.CurrentDirectory, "TestData");
var files = dir.SafeGetFiles("*");
Assert.NotEmpty(files);
}
[Fact]
public void SafeGetFiles_ValidDirectory_PatternOption()
{
string dir = Path.Combine(Environment.CurrentDirectory, "TestData");
var files = dir.SafeGetFiles("*", SearchOption.AllDirectories);
Assert.NotEmpty(files);
}
[Fact]
public void SafeGetFileSystemEntries_ValidDirectory()
{
string dir = Path.Combine(Environment.CurrentDirectory, "TestData");
var entries = dir.SafeGetFileSystemEntries();
Assert.NotEmpty(entries);
}
[Fact]
public void SafeGetFileSystemEntries_ValidDirectory_Pattern()
{
string dir = Path.Combine(Environment.CurrentDirectory, "TestData");
var entries = dir.SafeGetFileSystemEntries("*");
Assert.NotEmpty(entries);
}
[Fact]
public void SafeGetFileSystemEntries_ValidDirectory_PatternOption()
{
string dir = Path.Combine(Environment.CurrentDirectory, "TestData");
var entries = dir.SafeGetFileSystemEntries("*", SearchOption.AllDirectories);
Assert.NotEmpty(entries);
}
[Fact]
public void SafeEnumerateDirectories_ValidDirectory()
{
string dir = Path.Combine(Environment.CurrentDirectory, "TestData");
var dirs = dir.SafeEnumerateDirectories();
Assert.Single(dirs);
}
[Fact]
public void SafeEnumerateDirectories_ValidDirectory_Pattern()
{
string dir = Path.Combine(Environment.CurrentDirectory, "TestData");
var dirs = dir.SafeEnumerateDirectories("*");
Assert.Single(dirs);
}
[Fact]
public void SafeEnumerateDirectories_ValidDirectory_PatternOption()
{
string dir = Path.Combine(Environment.CurrentDirectory, "TestData");
var dirs = dir.SafeEnumerateDirectories("*", SearchOption.AllDirectories);
Assert.Single(dirs);
}
[Fact]
public void SafeEnumerateFiles_ValidDirectory()
{
string dir = Path.Combine(Environment.CurrentDirectory, "TestData");
var files = dir.SafeEnumerateFiles();
Assert.NotEmpty(files);
}
[Fact]
public void SafeEnumerateFiles_ValidDirectory_Pattern()
{
string dir = Path.Combine(Environment.CurrentDirectory, "TestData");
var files = dir.SafeEnumerateFiles("*");
Assert.NotEmpty(files);
}
[Fact]
public void SafeEnumerateFiles_ValidDirectory_PatternOption()
{
string dir = Path.Combine(Environment.CurrentDirectory, "TestData");
var files = dir.SafeEnumerateFiles("*", SearchOption.AllDirectories);
Assert.NotEmpty(files);
}
[Fact]
public void SafeEnumerateFileSystemEntries_ValidDirectory()
{
string dir = Path.Combine(Environment.CurrentDirectory, "TestData");
var entries = dir.SafeEnumerateFileSystemEntries();
Assert.NotEmpty(entries);
}
[Fact]
public void SafeEnumerateFileSystemEntries_ValidDirectory_Pattern()
{
string dir = Path.Combine(Environment.CurrentDirectory, "TestData");
var entries = dir.SafeEnumerateFileSystemEntries("*");
Assert.NotEmpty(entries);
}
[Fact]
public void SafeEnumerateFileSystemEntries_ValidDirectory_PatternOption()
{
string dir = Path.Combine(Environment.CurrentDirectory, "TestData");
var entries = dir.SafeEnumerateFileSystemEntries("*", SearchOption.AllDirectories);
Assert.NotEmpty(entries);
}
#endregion
}
}
}

View File

@@ -1,541 +0,0 @@
using System;
using System.IO;
using System.Linq;
#if NET7_0_OR_GREATER
using System.Numerics;
#endif
using SabreTools.IO.Extensions;
using Xunit;
namespace SabreTools.IO.Test.Extensions
{
// TODO: Add string reading tests
public class StreamExtensionsReadTests
{
/// <summary>
/// Test pattern from 0x00-0x0F
/// </summary>
private static readonly byte[] _bytes =
[
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
];
/// <summary>
/// Represents the decimal value 0.0123456789
/// </summary>
private static readonly byte[] _decimalBytes =
[
0x15, 0xCD, 0x5B, 0x07, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00,
];
[Fact]
public void ReadByteValueTest()
{
var stream = new MemoryStream(_bytes);
byte read = stream.ReadByteValue();
Assert.Equal(0x00, read);
}
[Fact]
public void ReadBytesTest()
{
var stream = new MemoryStream(_bytes);
int length = 4;
byte[] read = stream.ReadBytes(length);
Assert.Equal(length, read.Length);
Assert.True(read.SequenceEqual(_bytes.Take(length)));
}
[Fact]
public void ReadSByteTest()
{
var stream = new MemoryStream(_bytes);
sbyte read = stream.ReadSByte();
Assert.Equal(0x00, read);
}
[Fact]
public void ReadCharTest()
{
var stream = new MemoryStream(_bytes);
char read = stream.ReadChar();
Assert.Equal('\0', read);
}
[Fact]
public void ReadInt16Test()
{
var stream = new MemoryStream(_bytes);
short read = stream.ReadInt16();
Assert.Equal(0x0100, read);
}
[Fact]
public void ReadInt16BigEndianTest()
{
var stream = new MemoryStream(_bytes);
short read = stream.ReadInt16BigEndian();
Assert.Equal(0x0001, read);
}
[Fact]
public void ReadUInt16Test()
{
var stream = new MemoryStream(_bytes);
ushort read = stream.ReadUInt16();
Assert.Equal(0x0100, read);
}
[Fact]
public void ReadUInt16BigEndianTest()
{
var stream = new MemoryStream(_bytes);
ushort read = stream.ReadUInt16BigEndian();
Assert.Equal(0x0001, read);
}
#if NET6_0_OR_GREATER
[Fact]
public void ReadHalfTest()
{
var stream = new MemoryStream(_bytes);
Half expected = BitConverter.Int16BitsToHalf(0x0100);
Half read = stream.ReadHalf();
Assert.Equal(expected, read);
}
[Fact]
public void ReadHalfBigEndianTest()
{
var stream = new MemoryStream(_bytes);
Half expected = BitConverter.Int16BitsToHalf(0x0001);
Half read = stream.ReadHalfBigEndian();
Assert.Equal(expected, read);
}
#endif
[Fact]
public void ReadInt24Test()
{
var stream = new MemoryStream(_bytes);
int read = stream.ReadInt24();
Assert.Equal(0x020100, read);
}
[Fact]
public void ReadInt24BigEndianTest()
{
var stream = new MemoryStream(_bytes);
int read = stream.ReadInt24BigEndian();
Assert.Equal(0x000102, read);
}
[Fact]
public void ReadUInt24Test()
{
var stream = new MemoryStream(_bytes);
uint read = stream.ReadUInt24();
Assert.Equal((uint)0x020100, read);
}
[Fact]
public void ReadUInt24BigEndianTest()
{
var stream = new MemoryStream(_bytes);
uint read = stream.ReadUInt24BigEndian();
Assert.Equal((uint)0x000102, read);
}
[Fact]
public void ReadInt32Test()
{
var stream = new MemoryStream(_bytes);
int read = stream.ReadInt32();
Assert.Equal(0x03020100, read);
}
[Fact]
public void ReadInt32BigEndianTest()
{
var stream = new MemoryStream(_bytes);
int read = stream.ReadInt32BigEndian();
Assert.Equal(0x00010203, read);
}
[Fact]
public void ReadUInt32Test()
{
var stream = new MemoryStream(_bytes);
uint read = stream.ReadUInt32();
Assert.Equal((uint)0x03020100, read);
}
[Fact]
public void ReadUInt32BigEndianTest()
{
var stream = new MemoryStream(_bytes);
uint read = stream.ReadUInt32BigEndian();
Assert.Equal((uint)0x00010203, read);
}
[Fact]
public void ReadSingleTest()
{
var stream = new MemoryStream(_bytes);
float expected = BitConverter.Int32BitsToSingle(0x03020100);
float read = stream.ReadSingle();
Assert.Equal(expected, read);
}
[Fact]
public void ReadSingleBigEndianTest()
{
var stream = new MemoryStream(_bytes);
float expected = BitConverter.Int32BitsToSingle(0x00010203);
float read = stream.ReadSingleBigEndian();
Assert.Equal(expected, read);
}
[Fact]
public void ReadInt48Test()
{
var stream = new MemoryStream(_bytes);
long read = stream.ReadInt48();
Assert.Equal(0x050403020100, read);
}
[Fact]
public void ReadInt48BigEndianTest()
{
var stream = new MemoryStream(_bytes);
long read = stream.ReadInt48BigEndian();
Assert.Equal(0x000102030405, read);
}
[Fact]
public void ReadUInt48Test()
{
var stream = new MemoryStream(_bytes);
ulong read = stream.ReadUInt48();
Assert.Equal((ulong)0x050403020100, read);
}
[Fact]
public void ReadUInt48BigEndianTest()
{
var stream = new MemoryStream(_bytes);
ulong read = stream.ReadUInt48BigEndian();
Assert.Equal((ulong)0x000102030405, read);
}
[Fact]
public void ReadInt64Test()
{
var stream = new MemoryStream(_bytes);
long read = stream.ReadInt64();
Assert.Equal(0x0706050403020100, read);
}
[Fact]
public void ReadInt64BigEndianTest()
{
var stream = new MemoryStream(_bytes);
long read = stream.ReadInt64BigEndian();
Assert.Equal(0x0001020304050607, read);
}
[Fact]
public void ReadUInt64Test()
{
var stream = new MemoryStream(_bytes);
ulong read = stream.ReadUInt64();
Assert.Equal((ulong)0x0706050403020100, read);
}
[Fact]
public void ReadUInt64BigEndianTest()
{
var stream = new MemoryStream(_bytes);
ulong read = stream.ReadUInt64BigEndian();
Assert.Equal((ulong)0x0001020304050607, read);
}
[Fact]
public void ReadDoubleTest()
{
var stream = new MemoryStream(_bytes);
double expected = BitConverter.Int64BitsToDouble(0x0706050403020100);
double read = stream.ReadDouble();
Assert.Equal(expected, read);
}
[Fact]
public void ReadDoubleBigEndianTest()
{
var stream = new MemoryStream(_bytes);
double expected = BitConverter.Int64BitsToDouble(0x0001020304050607);
double read = stream.ReadDoubleBigEndian();
Assert.Equal(expected, read);
}
[Fact]
public void ReadDecimalTest()
{
var stream = new MemoryStream(_decimalBytes);
decimal expected = 0.0123456789M;
decimal read = stream.ReadDecimal();
Assert.Equal(expected, read);
}
[Fact]
public void ReadDecimalBigEndianTest()
{
var stream = new MemoryStream(_decimalBytes.Reverse().ToArray());
decimal expected = 0.0123456789M;
decimal read = stream.ReadDecimalBigEndian();
Assert.Equal(expected, read);
}
[Fact]
public void ReadGuidTest()
{
var stream = new MemoryStream(_bytes);
var expected = new Guid(_bytes);
Guid read = stream.ReadGuid();
Assert.Equal(expected, read);
}
[Fact]
public void ReadGuidBigEndianTest()
{
var stream = new MemoryStream(_bytes);
var expected = new Guid(_bytes.Reverse().ToArray());
Guid read = stream.ReadGuidBigEndian();
Assert.Equal(expected, read);
}
#if NET7_0_OR_GREATER
[Fact]
public void ReadInt128Test()
{
var stream = new MemoryStream(_bytes);
var expected = (Int128)new BigInteger(_bytes);
Int128 read = stream.ReadInt128();
Assert.Equal(expected, read);
}
[Fact]
public void ReadInt128BigEndianTest()
{
var stream = new MemoryStream(_bytes);
var reversed = _bytes.Reverse().ToArray();
var expected = (Int128)new BigInteger(reversed);
Int128 read = stream.ReadInt128BigEndian();
Assert.Equal(expected, read);
}
[Fact]
public void ReadUInt128Test()
{
var stream = new MemoryStream(_bytes);
var expected = (UInt128)new BigInteger(_bytes);
UInt128 read = stream.ReadUInt128();
Assert.Equal(expected, read);
}
[Fact]
public void ReadUInt128BigEndianTest()
{
var stream = new MemoryStream(_bytes);
var reversed = _bytes.Reverse().ToArray();
var expected = (UInt128)new BigInteger(reversed);
UInt128 read = stream.ReadUInt128BigEndian();
Assert.Equal(expected, read);
}
#endif
[Fact]
public void ReadTypeExplicitTest()
{
byte[] bytesWithString =
[
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x41, 0x42, 0x43, 0x00,
];
var stream = new MemoryStream(bytesWithString);
var expected = new TestStructExplicit
{
FirstValue = TestEnum.RecognizedTestValue,
SecondValue = 0x07060504,
ThirdValue = 0x0504,
FourthValue = 0x0706,
FifthValue = "ABC",
};
var read = stream.ReadType<TestStructExplicit>();
Assert.Equal(expected.FirstValue, read.FirstValue);
Assert.Equal(expected.SecondValue, read.SecondValue);
Assert.Equal(expected.ThirdValue, read.ThirdValue);
Assert.Equal(expected.FourthValue, read.FourthValue);
}
[Fact]
public void ReadTypeSequentialTest()
{
byte[] bytesWithString =
[
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x41, 0x42, 0x43, 0x00,
];
var stream = new MemoryStream(bytesWithString);
var expected = new TestStructSequential
{
FirstValue = TestEnum.RecognizedTestValue,
SecondValue = 0x07060504,
ThirdValue = 0x0908,
FourthValue = 0x0B0A,
FifthValue = "ABC",
};
var read = stream.ReadType<TestStructSequential>();
Assert.Equal(expected.FirstValue, read.FirstValue);
Assert.Equal(expected.SecondValue, read.SecondValue);
Assert.Equal(expected.ThirdValue, read.ThirdValue);
Assert.Equal(expected.FourthValue, read.FourthValue);
Assert.Equal(expected.FifthValue, read.FifthValue);
}
[Fact]
public void ReadTypeStringsTest()
{
byte[] structBytes =
[
0x03, 0x41, 0x42, 0x43, // AnsiBStr
0x03, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, // BStr
0x41, 0x42, 0x43, // ByValTStr
0x41, 0x42, 0x43, 0x00, // LPStr
0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x00, 0x00, // LPWStr
];
var stream = new MemoryStream(structBytes);
var expected = new TestStructStrings
{
AnsiBStr = "ABC",
BStr = "ABC",
ByValTStr = "ABC",
LPStr = "ABC",
LPWStr = "ABC",
};
var read = stream.ReadType<TestStructStrings>();
Assert.Equal(expected.AnsiBStr, read.AnsiBStr);
Assert.Equal(expected.BStr, read.BStr);
Assert.Equal(expected.ByValTStr, read.ByValTStr);
Assert.Equal(expected.LPStr, read.LPStr);
Assert.Equal(expected.LPWStr, read.LPWStr);
}
[Fact]
public void ReadTypeArraysTest()
{
byte[] structBytes =
[
// Byte Array
0x00, 0x01, 0x02, 0x03,
// Int Array
0x03, 0x02, 0x01, 0x00,
0x04, 0x03, 0x02, 0x01,
0x05, 0x04, 0x03, 0x02,
0x06, 0x05, 0x04, 0x03,
// Struct Array (X, Y)
0xFF, 0x00, 0x00, 0xFF,
0x00, 0xFF, 0xFF, 0x00,
0xAA, 0x55, 0x55, 0xAA,
0x55, 0xAA, 0xAA, 0x55,
// LPArray
0x04, 0x00,
0x00, 0x01, 0x02, 0x03,
];
var stream = new MemoryStream(structBytes);
var expected = new TestStructArrays
{
ByteArray = [0x00, 0x01, 0x02, 0x03],
IntArray = [0x00010203, 0x01020304, 0x02030405, 0x03040506],
StructArray =
[
new TestStructPoint { X = 0x00FF, Y = 0xFF00 },
new TestStructPoint { X = 0xFF00, Y = 0x00FF },
new TestStructPoint { X = 0x55AA, Y = 0xAA55 },
new TestStructPoint { X = 0xAA55, Y = 0x55AA },
],
LPByteArrayLength = 0x0004,
LPByteArray = [0x00, 0x01, 0x02, 0x03],
};
var read = stream.ReadType<TestStructArrays>();
Assert.NotNull(read.ByteArray);
Assert.True(expected.ByteArray.SequenceEqual(read.ByteArray));
Assert.NotNull(read.IntArray);
Assert.True(expected.IntArray.SequenceEqual(read.IntArray));
Assert.NotNull(read.StructArray);
Assert.True(expected.StructArray.SequenceEqual(read.StructArray));
Assert.Equal(expected.LPByteArrayLength, read.LPByteArrayLength);
Assert.NotNull(read.LPByteArray);
Assert.True(expected.LPByteArray.SequenceEqual(read.LPByteArray));
}
[Fact]
public void ReadTypeInheritanceTest()
{
byte[] structBytes1 =
[
0x41, 0x42, 0x43, 0x44, // Signature
0x00, 0xFF, 0x00, 0xFF, // IdentifierType
0xAA, 0x55, 0xAA, 0x55, // FieldA
0x55, 0xAA, 0x55, 0xAA, // FieldB
];
var stream1 = new MemoryStream(structBytes1);
var expected1 = new TestStructInheritanceChild1
{
Signature = [0x41, 0x42, 0x43, 0x44],
IdentifierType = 0xFF00FF00,
FieldA = 0x55AA55AA,
FieldB = 0xAA55AA55,
};
var read1 = stream1.ReadType<TestStructInheritanceChild1>();
Assert.NotNull(read1?.Signature);
Assert.Equal(expected1.Signature, read1.Signature);
Assert.Equal(expected1.IdentifierType, read1.IdentifierType);
Assert.Equal(expected1.FieldA, read1.FieldA);
Assert.Equal(expected1.FieldB, read1.FieldB);
byte[] structBytes2 =
[
0x41, 0x42, 0x43, 0x44, // Signature
0x00, 0xFF, 0x00, 0xFF, // IdentifierType
0xAA, 0x55, // FieldA
0x55, 0xAA, // FieldB
];
var stream2 = new MemoryStream(structBytes2);
var expected2 = new TestStructInheritanceChild2
{
Signature = [0x41, 0x42, 0x43, 0x44],
IdentifierType = 0xFF00FF00,
FieldA = 0x55AA,
FieldB = 0xAA55,
};
var read2 = stream2.ReadType<TestStructInheritanceChild2>();
Assert.NotNull(read2?.Signature);
Assert.Equal(expected2.Signature, read2.Signature);
Assert.Equal(expected2.IdentifierType, read2.IdentifierType);
Assert.Equal(expected2.FieldA, read2.FieldA);
Assert.Equal(expected2.FieldB, read2.FieldB);
}
}
}

View File

@@ -0,0 +1,527 @@
using System;
using System.IO;
using System.Text;
using SabreTools.IO.Extensions;
using Xunit;
namespace SabreTools.IO.Test.Extensions
{
public class StreamExtensionsTests
{
#region AlignToBoundary
[Fact]
public void AlignToBoundary_Null_False()
{
Stream? stream = null;
byte alignment = 4;
bool actual = stream.AlignToBoundary(alignment);
Assert.False(actual);
}
[Fact]
public void AlignToBoundary_Empty_False()
{
Stream? stream = new MemoryStream([]);
byte alignment = 4;
bool actual = stream.AlignToBoundary(alignment);
Assert.False(actual);
}
[Fact]
public void AlignToBoundary_EOF_False()
{
Stream? stream = new MemoryStream([0x01, 0x02]);
byte alignment = 4;
stream.Position = 1;
bool actual = stream.AlignToBoundary(alignment);
Assert.False(actual);
}
[Fact]
public void AlignToBoundary_TooShort_False()
{
Stream? stream = new MemoryStream([0x01, 0x02]);
byte alignment = 4;
stream.Position = 1;
bool actual = stream.AlignToBoundary(alignment);
Assert.False(actual);
}
[Fact]
public void AlignToBoundary_CanAlign_True()
{
Stream? stream = new MemoryStream([0x01, 0x02, 0x03, 0x04, 0x05]);
byte alignment = 4;
stream.Position = 1;
bool actual = stream.AlignToBoundary(alignment);
Assert.True(actual);
}
#endregion
#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()
{
var stream = new NonSeekableStream();
long actual = stream.SeekIfPossible(0);
Assert.Equal(8, actual);
}
[Fact]
public void SeekIfPossible_NonPositionable_InvalidPosition()
{
var stream = new NonPositionableStream();
long actual = stream.SeekIfPossible(0);
Assert.Equal(-1, actual);
}
[Fact]
public void SeekIfPossible_HiddenNonSeekable_InvalidPosition()
{
var stream = new HiddenNonSeekableStream();
long actual = stream.SeekIfPossible(0);
Assert.Equal(-1, actual);
}
[Fact]
public void SeekIfPossible_NonNegative_ValidPosition()
{
var stream = new MemoryStream(new byte[16], 0, 16, false, true);
long actual = stream.SeekIfPossible(5);
Assert.Equal(5, actual);
}
[Fact]
public void SeekIfPossible_Negative_ValidPosition()
{
var stream = new MemoryStream(new byte[16], 0, 16, false, true);
long actual = stream.SeekIfPossible(-3);
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>
/// Represents a hidden non-seekable stream
/// </summary>
private class HiddenNonSeekableStream : Stream
{
public override bool CanRead => true;
public override bool CanSeek => true;
public override bool CanWrite => true;
public override long Length => 16;
public override long Position { get => 8; set => throw new NotSupportedException(); }
public override void Flush()
{
throw new NotImplementedException();
}
public override int Read(byte[] buffer, int offset, int count)
{
throw new NotImplementedException();
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotImplementedException();
}
public override void SetLength(long value)
{
throw new NotImplementedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotImplementedException();
}
}
/// <summary>
/// Represents a non-seekable stream
/// </summary>
private class NonSeekableStream : Stream
{
public override bool CanRead => true;
public override bool CanSeek => false;
public override bool CanWrite => true;
public override long Length => 16;
public override long Position { get => 8; set => throw new NotSupportedException(); }
public override void Flush()
{
throw new NotImplementedException();
}
public override int Read(byte[] buffer, int offset, int count)
{
throw new NotImplementedException();
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotImplementedException();
}
public override void SetLength(long value)
{
throw new NotImplementedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotImplementedException();
}
}
/// <summary>
/// Represents a non-seekable, non-positionable stream
/// </summary>
private class NonPositionableStream : Stream
{
public override bool CanRead => true;
public override bool CanSeek => false;
public override bool CanWrite => true;
public override long Length => 16;
public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); }
public override void Flush()
{
throw new NotImplementedException();
}
public override int Read(byte[] buffer, int offset, int count)
{
throw new NotImplementedException();
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotImplementedException();
}
public override void SetLength(long value)
{
throw new NotImplementedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotImplementedException();
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,16 +1,14 @@
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;
namespace SabreTools.IO.Test.Extensions
{
// TODO: Add string writing tests
public class StreamExtensionsWriteTests
public class StreamWriterExtensionsTests
{
/// <summary>
/// Test pattern from 0x00-0x0F
@@ -34,47 +32,87 @@ 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());
}
[Fact]
public void WriteBytesBigEndianTest()
{
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
byte[] expected = [.. _bytes.Take(4)];
stream.WriteBigEndian([0x03, 0x02, 0x01, 0x00]);
ValidateBytes(expected, stream.GetBuffer());
}
[Fact]
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());
}
[Fact]
public void WriteCharEncodingTest()
{
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
byte[] expected = [0x00, 0x00];
stream.Write('\0', Encoding.Unicode);
ValidateBytes(expected, stream.GetBuffer());
}
[Fact]
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());
@@ -84,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());
@@ -104,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());
@@ -125,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());
@@ -146,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());
@@ -156,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());
@@ -166,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());
@@ -176,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());
@@ -186,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());
@@ -206,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());
@@ -226,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());
@@ -236,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());
@@ -246,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());
@@ -256,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());
@@ -266,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());
@@ -276,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());
@@ -286,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());
@@ -306,17 +397,48 @@ 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)];
bool write = stream.Write(BitConverter.Int64BitsToDouble(0x0706050403020100));
Assert.True(write);
ValidateBytes(expected, stream.GetBuffer());
}
[Fact]
public void WriteDoubleBigEndianTest()
{
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
byte[] expected = [.. _bytes.Take(8)];
bool write = stream.WriteBigEndian(BitConverter.Int64BitsToDouble(0x0001020304050607));
Assert.True(write);
ValidateBytes(expected, stream.GetBuffer());
}
[Fact]
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());
@@ -326,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());
@@ -336,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());
@@ -346,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());
@@ -367,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());
}
@@ -377,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());
@@ -387,12 +508,111 @@ 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()
{
var stream = new MemoryStream(new byte[4], 0, 4, true, true);
byte[] expected = [0x41, 0x42, 0x43, 0x00];
bool write = stream.WriteNullTerminatedAnsiString("ABC");
Assert.True(write);
ValidateBytes(expected, stream.GetBuffer());
}
[Fact]
public void WriteNullTerminatedUTF8StringTest()
{
var stream = new MemoryStream(new byte[4], 0, 4, true, true);
byte[] expected = [0x41, 0x42, 0x43, 0x00];
bool write = stream.WriteNullTerminatedUTF8String("ABC");
Assert.True(write);
ValidateBytes(expected, stream.GetBuffer());
}
[Fact]
public void WriteNullTerminatedUnicodeStringTest()
{
var stream = new MemoryStream(new byte[8], 0, 8, true, true);
byte[] expected = [0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x00];
bool write = stream.WriteNullTerminatedUnicodeString("ABC");
Assert.True(write);
ValidateBytes(expected, stream.GetBuffer());
}
[Fact]
public void WriteNullTerminatedUTF32StringTest()
{
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
byte[] expected = [0x41, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
bool write = stream.WriteNullTerminatedUTF32String("ABC");
Assert.True(write);
ValidateBytes(expected, stream.GetBuffer());
}
[Fact]
public void WritePrefixedAnsiStringTest()
{
var stream = new MemoryStream(new byte[4], 0, 4, true, true);
byte[] expected = [0x03, 0x41, 0x42, 0x43];
bool write = stream.WritePrefixedAnsiString("ABC");
Assert.True(write);
ValidateBytes(expected, stream.GetBuffer());
}
[Fact]
public void WritePrefixedUnicodeStringTest()
{
var stream = new MemoryStream(new byte[8], 0, 8, true, true);
byte[] expected = [0x03, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00];
bool write = stream.WritePrefixedUnicodeString("ABC");
Assert.True(write);
ValidateBytes(expected, stream.GetBuffer());
}
[Fact]
public void WriteTypeTest()
{
// Guid
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
bool actual = stream.WriteType<Guid>(new Guid(_bytes));
Assert.True(actual);
ValidateBytes(_bytes, stream.GetBuffer());
// 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());
// Int128
stream = new MemoryStream(new byte[16], 0, 16, true, true);
actual = stream.WriteType<Int128>((Int128)new BigInteger(_bytes));
Assert.True(actual);
ValidateBytes(_bytes, stream.GetBuffer());
// UInt128
stream = new MemoryStream(new byte[16], 0, 16, true, true);
actual = stream.WriteType<UInt128>((UInt128)new BigInteger(_bytes));
Assert.True(actual);
ValidateBytes(_bytes, stream.GetBuffer());
// Enum
stream = new MemoryStream(new byte[4], 0, 4, true, true);
actual = stream.WriteType<TestEnum>((TestEnum)0x03020100);
Assert.True(actual);
ValidateBytes([.. _bytes.Take(4)], stream.GetBuffer());
}
[Fact]
public void WriteTypeExplicitTest()
@@ -410,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());
@@ -434,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());
@@ -451,4 +671,4 @@ namespace SabreTools.IO.Test.Extensions
}
}
}
}
}

View 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
}
}

View File

@@ -5,5 +5,5 @@ namespace SabreTools.IO.Test.Extensions
None = 0x00000000,
RecognizedTestValue = 0x03020100,
UpperBoundaryValue = 0xFFFFFFFF,
}
}
}
}

View File

@@ -17,6 +17,12 @@ namespace SabreTools.IO.Test.Extensions
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public int[]? IntArray;
/// <summary>
/// 4 entry int array
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public TestEnum[]? EnumArray;
/// <summary>
/// 4 entry struct array
/// </summary>
@@ -24,16 +30,16 @@ namespace SabreTools.IO.Test.Extensions
public TestStructPoint[]? StructArray;
/// <summary>
/// Length of <see cref="LPByteArray"/>
/// Length of <see cref="LPByteArray"/>
/// </summary>
public ushort LPByteArrayLength;
/// <summary>
/// 4 entry byte array whose length is defined by <see cref="LPByteArrayLength"/>
/// </summary>
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)]
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 4)]
public byte[]? LPByteArray;
// /// <summary>
// /// 4 entry nested byte array
// /// </summary>

View File

@@ -18,7 +18,7 @@ namespace SabreTools.IO.Test.Extensions
public uint FieldB;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
internal class TestStructInheritanceChild2 : TestStructInheritanceParent
{

View File

@@ -10,7 +10,7 @@ namespace SabreTools.IO.Test.Extensions
public int SecondValue;
public ushort ThirdValue;
public short FourthValue;
[MarshalAs(UnmanagedType.LPStr)]

View File

@@ -1,5 +1,6 @@
using System.Runtime.InteropServices;
#pragma warning disable CS0618 // Obsolete unmanaged types
namespace SabreTools.IO.Test.Extensions
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]

View File

@@ -0,0 +1,242 @@
using System;
using System.IO;
using System.Text;
using System.Xml;
using SabreTools.IO.Extensions;
using Xunit;
namespace SabreTools.IO.Test.Extensions
{
public class XmlTextWriterExtensionsTests
{
[Fact]
public void WriteRequiredAttributeString_NullInputThrow_Throws()
{
string expected = "<?xml version=\"1.0\" encoding=\"utf-8\"?><element />";
var stream = new MemoryStream();
var writer = new XmlTextWriter(stream, Encoding.UTF8);
writer.WriteStartDocument();
writer.WriteStartElement("element");
Assert.Throws<ArgumentNullException>(()
=> writer.WriteRequiredAttributeString("attr", null, throwOnError: true));
writer.WriteEndElement();
writer.Flush();
// Length includes UTF-8 BOM
Assert.Equal(52, stream.Length);
string actual = Encoding.UTF8.GetString(stream.ToArray(), 3, (int)stream.Length - 3);
Assert.Equal(expected, actual);
}
[Fact]
public void WriteRequiredAttributeString_NullInputNoThrow_Writes()
{
string expected = "<?xml version=\"1.0\" encoding=\"utf-8\"?><element attr=\"\" />";
var stream = new MemoryStream();
var writer = new XmlTextWriter(stream, Encoding.UTF8);
writer.WriteStartDocument();
writer.WriteStartElement("element");
writer.WriteRequiredAttributeString("attr", null, throwOnError: false);
writer.WriteEndElement();
writer.Flush();
// Length includes UTF-8 BOM
Assert.Equal(60, stream.Length);
string actual = Encoding.UTF8.GetString(stream.ToArray(), 3, (int)stream.Length - 3);
Assert.Equal(expected, actual);
}
[Fact]
public void WriteRequiredAttributeString_ValidInput_Writes()
{
string expected = "<?xml version=\"1.0\" encoding=\"utf-8\"?><element attr=\"val\" />";
var stream = new MemoryStream();
var writer = new XmlTextWriter(stream, Encoding.UTF8);
writer.WriteStartDocument();
writer.WriteStartElement("element");
writer.WriteRequiredAttributeString("attr", "val", throwOnError: false);
writer.WriteEndElement();
writer.Flush();
// Length includes UTF-8 BOM
Assert.Equal(63, stream.Length);
string actual = Encoding.UTF8.GetString(stream.ToArray(), 3, (int)stream.Length - 3);
Assert.Equal(expected, actual);
}
[Fact]
public void WriteRequiredElementString_NullInputThrow_Throws()
{
string expected = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
var stream = new MemoryStream();
var writer = new XmlTextWriter(stream, Encoding.UTF8);
writer.WriteStartDocument();
Assert.Throws<ArgumentNullException>(()
=> writer.WriteRequiredElementString("element", null, throwOnError: true));
writer.Flush();
// Length includes UTF-8 BOM
Assert.Equal(41, stream.Length);
string actual = Encoding.UTF8.GetString(stream.ToArray(), 3, (int)stream.Length - 3);
Assert.Equal(expected, actual);
}
[Fact]
public void WriteRequiredElementString_NullInputNoThrow_Writes()
{
string expected = "<?xml version=\"1.0\" encoding=\"utf-8\"?><element></element>";
var stream = new MemoryStream();
var writer = new XmlTextWriter(stream, Encoding.UTF8);
writer.WriteStartDocument();
writer.WriteRequiredElementString("element", null, throwOnError: false);
writer.Flush();
// Length includes UTF-8 BOM
Assert.Equal(60, stream.Length);
string actual = Encoding.UTF8.GetString(stream.ToArray(), 3, (int)stream.Length - 3);
Assert.Equal(expected, actual);
}
[Fact]
public void WriteRequiredElementString_ValidInput_Writes()
{
string expected = "<?xml version=\"1.0\" encoding=\"utf-8\"?><element>val</element>";
var stream = new MemoryStream();
var writer = new XmlTextWriter(stream, Encoding.UTF8);
writer.WriteStartDocument();
writer.WriteRequiredElementString("element", "val", throwOnError: false);
writer.Flush();
// Length includes UTF-8 BOM
Assert.Equal(63, stream.Length);
string actual = Encoding.UTF8.GetString(stream.ToArray(), 3, (int)stream.Length - 3);
Assert.Equal(expected, actual);
}
[Fact]
public void WriteOptionalAttributeString_NullInput_NoWrite()
{
string expected = "<?xml version=\"1.0\" encoding=\"utf-8\"?><element />";
var stream = new MemoryStream();
var writer = new XmlTextWriter(stream, Encoding.UTF8);
writer.WriteStartDocument();
writer.WriteStartElement("element");
writer.WriteOptionalAttributeString("attr", null);
writer.WriteEndElement();
writer.Flush();
// Length includes UTF-8 BOM
Assert.Equal(52, stream.Length);
string actual = Encoding.UTF8.GetString(stream.ToArray(), 3, (int)stream.Length - 3);
Assert.Equal(expected, actual);
}
[Fact]
public void WriteOptionalAttributeString_EmptyInput_NoWrite()
{
string expected = "<?xml version=\"1.0\" encoding=\"utf-8\"?><element />";
var stream = new MemoryStream();
var writer = new XmlTextWriter(stream, Encoding.UTF8);
writer.WriteStartDocument();
writer.WriteStartElement("element");
writer.WriteOptionalAttributeString("attr", string.Empty);
writer.WriteEndElement();
writer.Flush();
// Length includes UTF-8 BOM
Assert.Equal(52, stream.Length);
string actual = Encoding.UTF8.GetString(stream.ToArray(), 3, (int)stream.Length - 3);
Assert.Equal(expected, actual);
}
[Fact]
public void WriteOptionalAttributeString_ValidInput_Writes()
{
string expected = "<?xml version=\"1.0\" encoding=\"utf-8\"?><element attr=\"val\" />";
var stream = new MemoryStream();
var writer = new XmlTextWriter(stream, Encoding.UTF8);
writer.WriteStartDocument();
writer.WriteStartElement("element");
writer.WriteOptionalAttributeString("attr", "val");
writer.WriteEndElement();
writer.Flush();
// Length includes UTF-8 BOM
Assert.Equal(63, stream.Length);
string actual = Encoding.UTF8.GetString(stream.ToArray(), 3, (int)stream.Length - 3);
Assert.Equal(expected, actual);
}
[Fact]
public void WriteOptionalElementString_NullInput_NoWrite()
{
string expected = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
var stream = new MemoryStream();
var writer = new XmlTextWriter(stream, Encoding.UTF8);
writer.WriteStartDocument();
writer.WriteOptionalElementString("element", null);
writer.Flush();
// Length includes UTF-8 BOM
Assert.Equal(41, stream.Length);
string actual = Encoding.UTF8.GetString(stream.ToArray(), 3, (int)stream.Length - 3);
Assert.Equal(expected, actual);
}
[Fact]
public void WriteOptionalElementString_EmptyInput_NoWrite()
{
string expected = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
var stream = new MemoryStream();
var writer = new XmlTextWriter(stream, Encoding.UTF8);
writer.WriteStartDocument();
writer.WriteOptionalElementString("element", string.Empty);
writer.Flush();
// Length includes UTF-8 BOM
Assert.Equal(41, stream.Length);
string actual = Encoding.UTF8.GetString(stream.ToArray(), 3, (int)stream.Length - 3);
Assert.Equal(expected, actual);
}
[Fact]
public void WriteOptionalElementString_ValidInput_Writes()
{
string expected = "<?xml version=\"1.0\" encoding=\"utf-8\"?><element>val</element>";
var stream = new MemoryStream();
var writer = new XmlTextWriter(stream, Encoding.UTF8);
writer.WriteStartDocument();
writer.WriteOptionalElementString("element", "val");
writer.Flush();
// Length includes UTF-8 BOM
Assert.Equal(63, stream.Length);
string actual = Encoding.UTF8.GetString(stream.ToArray(), 3, (int)stream.Length - 3);
Assert.Equal(expected, actual);
}
}
}

View File

@@ -0,0 +1,67 @@
using System.IO;
using System.Text;
using Xunit;
namespace SabreTools.IO.Test
{
public class IniFileTests
{
[Fact]
public void EndToEndTest()
{
string expected = "[section1]\nkey1=value1\nkey2=value2\n";
// Build the INI
var iniFile = new IniFile();
iniFile.AddOrUpdate("section1.key1", "value1");
iniFile["section1.key2"] = "value2";
iniFile["section2.key3"] = "REMOVEME";
bool removed = iniFile.Remove("section2.key3");
Assert.True(removed);
Assert.Equal("value1", iniFile["section1.key1"]);
Assert.Equal("value2", iniFile["section1.key2"]);
// Write the INI
var stream = new MemoryStream();
bool write = iniFile.Write(stream);
// Length includes UTF-8 BOM
Assert.True(write);
Assert.Equal(38, stream.Length);
string actual = Encoding.UTF8.GetString(stream.ToArray(), 3, (int)stream.Length - 3);
Assert.Equal(expected, actual);
// Parse the INI
stream.Seek(0, SeekOrigin.Begin);
var secondIni = new IniFile(stream);
Assert.Equal("value1", secondIni["section1.key1"]);
Assert.Equal("value2", secondIni["section1.key2"]);
}
[Fact]
public void RemoveInvalidKeyTest()
{
var iniFile = new IniFile();
bool removed = iniFile.Remove("invalid.key");
Assert.False(removed);
}
[Fact]
public void ReadEmptyStreamTest()
{
var stream = new MemoryStream();
var iniFile = new IniFile(stream);
Assert.Empty(iniFile);
}
[Fact]
public void WriteEmptyIniFileTest()
{
var iniFile = new IniFile();
var stream = new MemoryStream();
bool write = iniFile.Write(stream);
Assert.False(write);
}
}
}

View File

@@ -0,0 +1,38 @@
using SabreTools.IO.Logging;
using Xunit;
namespace SabreTools.IO.Test.Logging
{
public class ConvertersTests
{
[Theory]
[InlineData(null, LogLevel.VERBOSE)]
[InlineData("", LogLevel.VERBOSE)]
[InlineData("INVALID", LogLevel.VERBOSE)]
[InlineData("verbose", LogLevel.VERBOSE)]
[InlineData("VERBOSE", LogLevel.VERBOSE)]
[InlineData("user", LogLevel.USER)]
[InlineData("USER", LogLevel.USER)]
[InlineData("warning", LogLevel.WARNING)]
[InlineData("WARNING", LogLevel.WARNING)]
[InlineData("error", LogLevel.ERROR)]
[InlineData("ERROR", LogLevel.ERROR)]
public void AsLogLevelTest(string? level, LogLevel expected)
{
LogLevel actual = level.AsLogLevel();
Assert.Equal(expected, actual);
}
[Theory]
[InlineData(LogLevel.VERBOSE, "VERBOSE")]
[InlineData(LogLevel.USER, "USER")]
[InlineData(LogLevel.WARNING, "WARNING")]
[InlineData(LogLevel.ERROR, "ERROR")]
[InlineData((LogLevel)99, null)]
public void FromLogLevelTest(LogLevel level, string? expected)
{
string? actual = level.FromLogLevel();
Assert.Equal(expected, actual);
}
}
}

View File

@@ -0,0 +1,40 @@
using SabreTools.IO.Logging;
using Xunit;
namespace SabreTools.IO.Test.Logging
{
public class InternalStopwatchTests
{
[Fact]
public void Stopwatch_NoSubject_StartNoSubject()
{
var stopwatch = new InternalStopwatch();
stopwatch.Start();
stopwatch.Stop();
}
[Fact]
public void Stopwatch_NoSubject_StartSubject()
{
var stopwatch = new InternalStopwatch();
stopwatch.Start("start");
stopwatch.Stop();
}
[Fact]
public void Stopwatch_Subject_StartNoSubject()
{
var stopwatch = new InternalStopwatch("init");
stopwatch.Start();
stopwatch.Stop();
}
[Fact]
public void Stopwatch_Subject_StartSubject()
{
var stopwatch = new InternalStopwatch("init");
stopwatch.Start("start");
stopwatch.Stop();
}
}
}

View File

@@ -0,0 +1,54 @@
using System;
using SabreTools.IO.Logging;
using Xunit;
namespace SabreTools.IO.Test.Logging
{
public class LoggerTests
{
[Fact]
public void EndToEnd()
{
Assert.Null(LoggerImpl.Filename);
Assert.False(LoggerImpl.LogToFile);
Assert.Null(LoggerImpl.LogDirectory);
Assert.True(LoggerImpl.AppendPrefix);
Assert.False(LoggerImpl.ThrowOnError);
LoggerImpl.Start();
var logger = new Logger();
logger.Verbose("verbose");
logger.Verbose(new Exception());
logger.Verbose(new Exception(), "verbose");
logger.Verbose(1, 1, "verbose");
logger.User("user");
logger.User(new Exception());
logger.User(new Exception(), "user");
logger.User(1, 1, "user");
logger.Warning("warning");
logger.Warning(new Exception());
logger.Warning(new Exception(), "warning");
logger.Warning(1, 1, "warning");
logger.Error("error");
logger.Error(new Exception());
logger.Error(new Exception(), "error");
logger.Error(1, 1, "error");
LoggerImpl.ThrowOnError = true;
Assert.Throws<Exception>(() => logger.Error(new Exception()));
Assert.True(LoggerImpl.StartTime < DateTime.Now);
Assert.True(LoggerImpl.LoggedWarnings);
Assert.True(LoggerImpl.LoggedErrors);
LoggerImpl.SetFilename("logfile.txt", addDate: true);
LoggerImpl.Close();
}
}
}

View 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
}
}

View 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
}
}

View 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
}
}

View 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);
}
}
}

View 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
}
}

View 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
}
}

View File

@@ -84,4 +84,4 @@ namespace SabreTools.IO.Test
Assert.Equal(expected, actual);
}
}
}
}

View File

@@ -0,0 +1,121 @@
using System;
using System.Collections.Generic;
using System.IO;
using Xunit;
namespace SabreTools.IO.Test
{
public class PathToolTests
{
[Fact]
public void GetDirectoriesOnly_NoAppendParent()
{
string expectedParent = Path.Combine(Environment.CurrentDirectory, "TestData");
string expectedCurrent = Path.Combine(expectedParent, "Subdirectory");
List<string> inputs =
[
string.Empty,
Path.Combine(Environment.CurrentDirectory, "TestData"),
Path.Combine(Environment.CurrentDirectory, "TestData", "Subdir*"),
];
var actual = PathTool.GetDirectoriesOnly(inputs, appendParent: true);
Assert.NotEmpty(actual);
var first = actual[0];
Assert.Equal(expectedCurrent, first.CurrentPath);
Assert.Equal(expectedParent, first.ParentPath);
}
[Fact]
public void GetDirectoriesOnly_AppendParent()
{
string expectedParent = Path.Combine(Environment.CurrentDirectory, "TestData");
string expectedCurrent = Path.Combine(expectedParent, "Subdirectory");
List<string> inputs =
[
string.Empty,
Path.Combine(Environment.CurrentDirectory, "TestData"),
Path.Combine(Environment.CurrentDirectory, "TestData", "Subdir*"),
];
var actual = PathTool.GetDirectoriesOnly(inputs, appendParent: false);
Assert.NotEmpty(actual);
var first = actual[0];
Assert.Equal(expectedCurrent, first.CurrentPath);
Assert.Equal(string.Empty, first.ParentPath);
}
[Fact]
public void GetFilesOnly_NoAppendParent()
{
string expectedParent = Path.Combine(Environment.CurrentDirectory, "TestData");
string expectedCurrent = Path.Combine(expectedParent, "ascii.txt");
List<string> inputs =
[
string.Empty,
Path.Combine(Environment.CurrentDirectory, "TestData"),
Path.Combine(Environment.CurrentDirectory, "TestData", "Subdir*"),
Path.Combine(Environment.CurrentDirectory, "TestData", "utf8bom.txt"),
];
var actual = PathTool.GetFilesOnly(inputs, appendParent: true);
Assert.NotEmpty(actual);
var first = actual[0];
Assert.Equal(expectedCurrent, first.CurrentPath);
Assert.Equal(expectedParent, first.ParentPath);
}
[Fact]
public void GetFilesOnly_AppendParent()
{
string expectedParent = Path.Combine(Environment.CurrentDirectory, "TestData");
string expectedCurrent = Path.Combine(expectedParent, "ascii.txt");
List<string> inputs =
[
string.Empty,
Path.Combine(Environment.CurrentDirectory, "TestData"),
Path.Combine(Environment.CurrentDirectory, "TestData", "Subdir*"),
Path.Combine(Environment.CurrentDirectory, "TestData", "utf8bom.txt"),
];
var actual = PathTool.GetFilesOnly(inputs, appendParent: false);
Assert.NotEmpty(actual);
var first = actual[0];
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);
}
}
}

View File

@@ -0,0 +1,60 @@
using System.IO;
using System.Text;
using SabreTools.IO.Readers;
using SabreTools.IO.Writers;
using Xunit;
namespace SabreTools.IO.Test.ReadersWriters
{
public class ClrMameProTests
{
[Fact]
public void EndToEndTest()
{
string expected = "header (\n\tstandalone \"value\"\n)\n\n# Comment\n\ngame (\n\titem ( attr \"value\" )\n)";
// Build and write the CMP file
var stream = new MemoryStream();
var writer = new ClrMameProWriter(stream, Encoding.UTF8);
Assert.True(writer.Quotes);
writer.WriteStartElement("header");
writer.WriteRequiredStandalone("standalone", "value");
writer.WriteOptionalStandalone("optstand", null);
writer.WriteFullEndElement();
writer.WriteString("\n\n# Comment\n");
writer.WriteStartElement("game");
writer.WriteStartElement("item");
writer.WriteRequiredAttributeString("attr", "value");
writer.WriteOptionalAttributeString("optional", null);
writer.WriteEndElement();
writer.WriteFullEndElement();
writer.Flush();
writer.Dispose();
// Length includes UTF-8 BOM
Assert.Equal(77, stream.Length);
string actual = Encoding.UTF8.GetString(stream.ToArray(), 3, (int)stream.Length - 3);
Assert.Equal(expected, actual);
// Parse the CMP file
stream.Seek(0, SeekOrigin.Begin);
var reader = new ClrMameProReader(stream, Encoding.UTF8);
Assert.False(reader.DosCenter);
Assert.True(reader.Quotes);
while (!reader.EndOfStream)
{
bool hasNext = reader.ReadNextLine();
Assert.True(hasNext);
Assert.NotNull(reader.CurrentLine);
Assert.True(reader.LineNumber >= 0);
}
reader.Dispose();
}
}
}

View File

@@ -0,0 +1,50 @@
using System.IO;
using System.Text;
using SabreTools.IO.Readers;
using SabreTools.IO.Writers;
using Xunit;
namespace SabreTools.IO.Test.ReadersWriters
{
public class IniTests
{
[Fact]
public void EndToEndTest()
{
string expected = "[section1]\nkey1=value1\nkey2=value2\n\n;comment\n;string\n";
// Build and write the INI
var stream = new MemoryStream();
var writer = new IniWriter(stream, Encoding.UTF8);
writer.WriteSection("section1");
writer.WriteKeyValuePair("key1", "value1");
writer.WriteKeyValuePair("key2", "value2");
writer.WriteLine();
writer.WriteComment("comment");
writer.WriteString(";string\n");
writer.Flush();
writer.Dispose();
// Length includes UTF-8 BOM
Assert.Equal(56, stream.Length);
string actual = Encoding.UTF8.GetString(stream.ToArray(), 3, (int)stream.Length - 3);
Assert.Equal(expected, actual);
// Parse the INI
stream.Seek(0, SeekOrigin.Begin);
var reader = new IniReader(stream, Encoding.UTF8);
while (!reader.EndOfStream)
{
bool hasNext = reader.ReadNextLine();
Assert.True(hasNext);
Assert.NotNull(reader.CurrentLine);
Assert.True(reader.LineNumber >= 0);
}
reader.Dispose();
}
}
}

View File

@@ -0,0 +1,60 @@
using System.IO;
using System.Text;
using SabreTools.IO.Readers;
using SabreTools.IO.Writers;
using Xunit;
namespace SabreTools.IO.Test.ReadersWriters
{
public class SeparatedValueTests
{
[Fact]
public void EndToEndTest()
{
string expected = "\"col1\",\"col2\",\"col3\"\n\"value1\",\"value2\",\"value3\"\n\"value4\",\"value5\",\"value6\"\n";
// Build and write the CSV
var stream = new MemoryStream();
var writer = new SeparatedValueWriter(stream, Encoding.UTF8);
Assert.True(writer.Quotes);
Assert.Equal(',', writer.Separator);
Assert.True(writer.VerifyFieldCount);
writer.WriteHeader(["col1", "col2", "col3"]);
writer.WriteValues(["value1", "value2", "value3"]);
writer.WriteString("\"value4\",\"value5\",\"value6\"\n");
writer.Flush();
writer.Dispose();
// Length includes UTF-8 BOM
Assert.Equal(78, stream.Length);
string actual = Encoding.UTF8.GetString(stream.ToArray(), 3, (int)stream.Length - 3);
Assert.Equal(expected, actual);
// Parse the CSV
stream.Seek(0, SeekOrigin.Begin);
var reader = new SeparatedValueReader(stream, Encoding.UTF8);
Assert.True(reader.Header);
Assert.True(reader.Quotes);
Assert.Equal(',', reader.Separator);
Assert.True(reader.VerifyFieldCount);
while (!reader.EndOfStream)
{
bool hasNext = reader.ReadNextLine();
Assert.True(hasNext);
Assert.NotNull(reader.CurrentLine);
Assert.True(reader.LineNumber >= 0);
if (reader.LineNumber > 0)
{
Assert.NotNull(reader.GetValue(0));
Assert.NotNull(reader.GetValue("col2"));
}
}
reader.Dispose();
}
}
}

View File

@@ -1,29 +1,39 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
<IsPackable>false</IsPackable>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<WarningsNotAsErrors>CS0618</WarningsNotAsErrors>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SabreTools.IO\SabreTools.IO.csproj" />
</ItemGroup>
</Project>
<PropertyGroup>
<TargetFrameworks>net8.0;net9.0;net10.0</TargetFrameworks>
<IsPackable>false</IsPackable>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<WarningsNotAsErrors>CS0618</WarningsNotAsErrors>
</PropertyGroup>
<ItemGroup>
<None Remove="TestData\**" />
</ItemGroup>
<ItemGroup>
<Content Include="TestData\**">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<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="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>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SabreTools.IO\SabreTools.IO.csproj" />
</ItemGroup>
</Project>

View 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
}
}

View File

@@ -1,4 +1,5 @@
using System.IO;
using System.Linq;
using SabreTools.IO.Streams;
using Xunit;
@@ -24,31 +25,95 @@ namespace SabreTools.IO.Test.Streams
byte[] data = [0b01010101];
var stream = new ReadOnlyBitStream(new MemoryStream(data));
byte? bit = stream.ReadBit();
Assert.NotNull(bit);
Assert.Equal((byte)0b00000001, bit);
Assert.Equal(1, stream.Position);
}
[Fact]
public void ReadBitsLSBTest()
[Theory]
[InlineData(4, 0b00000101, 1)]
[InlineData(9, 0b10101010_1, 2)]
public void ReadBitsBETest(int bits, uint expected, int position)
{
byte[] data = [0b01010101, 0b01010101, 0b01010101, 0b01010101];
var stream = new ReadOnlyBitStream(new MemoryStream(data));
uint? bits = stream.ReadBitsLSB(4);
Assert.NotNull(bits);
Assert.Equal((byte)0b00000101, bits);
Assert.Equal(1, stream.Position);
uint? actual = stream.ReadBitsBE(bits);
Assert.NotNull(actual);
Assert.Equal(expected, actual);
Assert.Equal(position, stream.Position);
}
[Theory]
[InlineData(4, 0b00001010, 1)]
[InlineData(9, 0b10101010_1, 2)]
public void ReadBitsLETest(int bits, uint expected, int position)
{
byte[] data = [0b01010101, 0b01010101, 0b01010101, 0b01010101];
var stream = new ReadOnlyBitStream(new MemoryStream(data));
uint? actual = stream.ReadBitsLE(bits);
Assert.NotNull(actual);
Assert.Equal(expected, actual);
Assert.Equal(position, stream.Position);
}
[Fact]
public void ReadBitsMSBTest()
public void ReadByteTest()
{
byte[] data = [0b01010101, 0b01010101, 0b01010101, 0b01010101];
byte expected = 0b01010101;
var stream = new ReadOnlyBitStream(new MemoryStream(data));
uint? bits = stream.ReadBitsMSB(4);
Assert.NotNull(bits);
Assert.Equal((byte)0b00001010, bits);
Assert.Equal(1, stream.Position);
byte? actual = stream.ReadByte();
Assert.NotNull(actual);
Assert.Equal(expected, actual);
}
[Fact]
public void ReadUInt16Test()
{
byte[] data = [0b01010101, 0b01010101, 0b01010101, 0b01010101];
ushort expected = 0b0101010101010101;
var stream = new ReadOnlyBitStream(new MemoryStream(data));
ushort? actual = stream.ReadUInt16();
Assert.NotNull(actual);
Assert.Equal(expected, actual);
}
[Fact]
public void ReadUInt32Test()
{
byte[] data = [0b01010101, 0b01010101, 0b01010101, 0b01010101];
uint expected = 0b01010101010101010101010101010101;
var stream = new ReadOnlyBitStream(new MemoryStream(data));
uint? actual = stream.ReadUInt32();
Assert.NotNull(actual);
Assert.Equal(expected, actual);
}
[Fact]
public void ReadUInt64Test()
{
byte[] data = [0b01010101, 0b01010101, 0b01010101, 0b01010101];
var stream = new ReadOnlyBitStream(new MemoryStream(data));
ulong? actual = stream.ReadUInt64();
Assert.Null(actual);
}
[Fact]
public void ReadBytesTest()
{
byte[] data = [0b01010101, 0b01010101, 0b01010101, 0b01010101];
var stream = new ReadOnlyBitStream(new MemoryStream(data));
byte[]? actual = stream.ReadBytes(4);
Assert.NotNull(actual);
Assert.True(data.SequenceEqual(actual));
}
}
}
}

View File

@@ -8,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);
@@ -17,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);
@@ -26,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);
@@ -36,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);
@@ -44,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);
@@ -53,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()
{
@@ -71,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();
@@ -85,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);
@@ -97,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);
@@ -109,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);
@@ -121,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);
@@ -133,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);
@@ -143,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
}
}
}

View 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
}
}

View File

@@ -0,0 +1 @@
Sample file for subdirectories

View File

@@ -0,0 +1 @@
This doesn't match anything

View 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.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1 @@

Binary file not shown.

View File

@@ -0,0 +1 @@
+/v

View File

@@ -0,0 +1 @@

View 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
}
}

View 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
}
}

View 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
}
}

View File

@@ -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

View 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;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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
}
}
}
}
}

View 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++;
}
}
}
}

View 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();
}
}
}

View 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];
}
}
}

View 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;
}
}

View 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;
}
}
}

View 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;
}
};
}

View 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;
}
}
}

View 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);
}
}
}

View 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
}
}

View 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();
}
}
}

View 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,
}
}

View 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,
}
}

View 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,
}
}

View 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
}
}

View 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; }
}
}

File diff suppressed because it is too large Load Diff

View 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 &amp; ".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 &lt;&gt; 0)
/// If (n &gt; 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 &gt; 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 &amp; ".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 &lt;&gt; 0)
/// If (n &gt; 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 &gt; 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 &amp; ".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 &lt;&gt; 0)
/// If (n &gt; 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);
}
}
}

View 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,
}
}

View 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,
}
}

File diff suppressed because it is too large Load Diff

View 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);
}
}
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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();
}
}
}

View 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
}
}

View 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;
}
}

View File

@@ -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 ];
}
}

View 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.

View 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
-----------------------------------------------------------------------

View 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);
}
}
}

View 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);
}
}
}

Some files were not shown because too many files have changed in this diff Show More