├── .github └── workflows │ └── dotnet.yml ├── LICENSE ├── README.md ├── SimdBase64.sln ├── benchmark ├── .github │ └── workflows │ │ └── dotnet.yml ├── Benchmark.cs ├── benchmark.csproj └── data │ ├── dns │ └── swedenzonebase.txt │ └── email │ ├── enron1.txt │ ├── enron10.txt │ ├── enron11.txt │ ├── enron12.txt │ ├── enron13.txt │ ├── enron14.txt │ ├── enron15.txt │ ├── enron2.txt │ ├── enron3.txt │ ├── enron4.txt │ ├── enron5.txt │ ├── enron6.txt │ ├── enron7.txt │ ├── enron8.txt │ └── enron9.txt ├── src ├── Base64.cs ├── Base64ARM.cs ├── Base64AVX2UTF16.cs ├── Base64AVX2UTF8.cs ├── Base64SSEUTF16.cs ├── Base64SSEUTF8.cs ├── Base64Scalar.cs ├── Base64ScalarSafe.cs ├── Base64Tables.cs ├── SimdBase64.csproj └── helpers.cs └── test ├── Base64DecodingTestsUTF16.cs ├── Base64DecodingTestsUTF8.cs ├── TestHelpers.cs ├── Usings.cs └── tests.csproj /.github/workflows/dotnet.yml: -------------------------------------------------------------------------------- 1 | name: .NET 2 | 3 | on: [pull_request] # add push later 4 | 5 | jobs: 6 | build: 7 | name: Build and test on ${{ matrix.os }} 8 | runs-on: ${{ matrix.os }} 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | os: [ubuntu-latest] # add later:, windows-latest, macos-latest] 13 | steps: 14 | - uses: actions/checkout@v4 15 | 16 | - name: Setup .NET 9.0 (preview) 17 | uses: actions/setup-dotnet@v4 18 | with: 19 | dotnet-version: 9.0.x 20 | dotnet-quality: preview 21 | - name: Restore dependencies 22 | run: dotnet restore 23 | - name: Build 24 | run: dotnet build --no-restore 25 | - name: Unit tests 26 | run: dotnet test --verbosity m --no-build 27 | continue-on-error: false 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 simdutf: Unicode at gigabytes per second 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SimdBase64 2 | [![.NET](https://github.com/simdutf/SimdBase64/actions/workflows/dotnet.yml/badge.svg)](https://github.com/simdutf/SimdBase64/actions/workflows/dotnet.yml) 3 | 4 | ## Fast WHATWG forgiving base64 decoding in C# 5 | 6 | Base64 is a standard approach to represent any binary data as ASCII. It is part of the email 7 | standard (MIME) and is commonly used to embed data in XML, HTML or JSON files. For example, 8 | images can be encoded as text using base64. Base64 is also used to represent cryptographic keys. 9 | 10 | Our processors have fast instructions (SIMD) that can process blocks of data at once. They are ideally 11 | suited to encode and decode base64. 12 | The C# .NET runtime library has fast (SIMD-based) base64 functions[^1] when the input is UTF-8. 13 | 14 | Encoding is somewhat easier than decoding. Decoding is a more challenging problem than base64 encoding because 15 | of the presence of allowable white space characters and the need to validate the input. Indeed, all 16 | inputs are valid for encoding, but only some inputs are valid for decoding. Having to skip white space 17 | characters makes accelerated decoding somewhat difficult. We refer to this decoding as WHATWG forgiving-base64 decoding. 18 | 19 | To handle spaces and validation, we recently designed faster base64 decoding algorithm. It has been deployed 20 | in the [simdutf](https://github.com/simdutf/simdutf) C++ library and used in production systems (e.g., the JavaScript runtime systems Node.js and Bun). 21 | With this new algorithm, we beat the C# .NET runtime functions by 1.7 x to 2.3 x on realistic inputs of a few kilobytes. 22 | 23 | The algorithm is unpatented (free) and we make our 24 | C# code available under a liberal open-source licence (MIT). 25 | 26 | 27 | ## Results (SimdBase64 vs. fast .NET functions) 28 | 29 | We use the enron base64 data for benchmarking, see benchmark/data/email. 30 | We process the data as UTF-8 (ASCII) using the .NET accelerated functions 31 | as a reference (`System.Buffers.Text.Base64.DecodeFromUtf8`). Our benchmark results are 32 | fully reproducible. 33 | 34 | 35 | | processor and base freq. | SimdBase64 (GB/s) | .NET speed (GB/s) | speed up | 36 | |:----------------|:------------------------|:-------------------|:-------------------| 37 | | Apple M2 processor (ARM, 3.5 Ghz) | 10 | 3.8 | 2.6 x | 38 | | AWS Graviton 3 (ARM, 2.6 GHz) | 5.1 | 2.0 | 2.6 x | 39 | | Intel Ice Lake (2.0 GHz) | 7.6 | 3.4 | 2.2 x | 40 | | AMD EPYC 7R32 (Zen 2, 2.8 GHz) | 6.9 | 3.0 | 2.3 x | 41 | 42 | ## Results (SimdBase64 vs. string .NET functions) 43 | 44 | The .NET runtime did not accelerate the `Convert.FromBase64String(mystring)` functions. 45 | We can multiply the decoding speed compared to the .NET standard library. 46 | 47 | Replace the following code based on the standard library... 48 | 49 | ```C# 50 | byte[] newBytes = Convert.FromBase64String(s); 51 | ``` 52 | 53 | with our version... 54 | 55 | ```C# 56 | byte[] newBytes = SimdBase64.Base64.FromBase64String(s); 57 | ``` 58 | 59 | | processor and base freq. | SimdBase64 (GB/s) | .NET speed (GB/s) | speed up | 60 | |:----------------|:------------------------|:-------------------|:-------------------| 61 | | Apple M2 processor (ARM, 3.5 Ghz) | 4.0 | 1.1 | 3.6 x | 62 | | Intel Ice Lake (2.0 GHz) | 2.5 | 0.65 | 3.8 x | 63 | 64 | ## AVX-512 65 | 66 | As for .NET 9, the support for AVX-512 remains incomplete in C#. In particular, important 67 | VBMI2 instructions are missing. Hence, we are not using AVX-512 under x64 systems at this time. 68 | However, as soon as .NET offers the necessary support, we will update our results. 69 | 70 | ## Requirements 71 | 72 | We require .NET 9 or better: https://dotnet.microsoft.com/en-us/download/dotnet/9.0 73 | 74 | ## Usage 75 | 76 | The library only provides Base64 decoding functions, because the .NET library already has 77 | fast Base64 encoding functions. We support both `Span` (ASCII or UTF-8) and 78 | `Span` (UTF-16) as input. If you have C# string, you can get its `Span` with 79 | the `AsSpan()` method. 80 | 81 | ```c# 82 | string base64 = "SGVsbG8sIFdvcmxkIQ=="; // could be span in UTF-8 as well 83 | byte[] buffer = new byte[SimdBase64.Base64.MaximalBinaryLengthFromBase64(base64.AsSpan())]; 84 | int bytesConsumed; // gives you the number of characters consumed 85 | int bytesWritten; 86 | var result = SimdBase64.Base64.DecodeFromBase64(base64.AsSpan(), buffer, out bytesConsumed, out bytesWritten, false); // false is for regular base64, true for base64url 87 | // result == OperationStatus.Done 88 | var answer = buffer.AsSpan().Slice(0, bytesWritten); // decoded result 89 | // Encoding.UTF8.GetString(answer) == "Hello, World!" 90 | ``` 91 | 92 | ## Running tests 93 | 94 | ``` 95 | dotnet test 96 | ``` 97 | 98 | To get a list of available tests, enter the command: 99 | 100 | ``` 101 | dotnet test --list-tests 102 | ``` 103 | 104 | To run specific tests, it is helpful to use the filter parameter: 105 | 106 | ``` 107 | dotnet test -c Release --filter DecodeBase64CasesScalar 108 | ``` 109 | 110 | ## Running Benchmarks 111 | 112 | To run the benchmarks, run the following command: 113 | ``` 114 | cd benchmark 115 | dotnet run -c Release 116 | ``` 117 | 118 | To run just one benchmark, use a filter: 119 | 120 | ``` 121 | cd benchmark 122 | dotnet run -c Release --filter "SimdUnicodeBenchmarks.RealDataBenchmark.AVX2DecodingRealDataUTF8(FileName: \"data/email/\")" 123 | ``` 124 | 125 | If you are under macOS or Linux, you may want to run the benchmarks in privileged mode: 126 | 127 | ``` 128 | cd benchmark 129 | sudo dotnet run -c Release 130 | ``` 131 | 132 | For UTF-16 benchmarks, you need to pass a flag as they are not enabled by default: 133 | 134 | ``` 135 | cd benchmark 136 | dotnet run -c Release --anyCategories UTF16 137 | ``` 138 | 139 | 140 | ## Building the library 141 | 142 | ``` 143 | cd src 144 | dotnet build 145 | ``` 146 | 147 | ## Code format 148 | 149 | We recommend you use `dotnet format`. E.g., 150 | 151 | ``` 152 | cd test 153 | dotnet format 154 | ``` 155 | 156 | ## Programming tips 157 | 158 | You can print the content of a vector register like so: 159 | 160 | ```C# 161 | public static void ToString(Vector256 v) 162 | { 163 | Span b = stackalloc byte[32]; 164 | v.CopyTo(b); 165 | Console.WriteLine(Convert.ToHexString(b)); 166 | } 167 | public static void ToString(Vector128 v) 168 | { 169 | Span b = stackalloc byte[16]; 170 | v.CopyTo(b); 171 | Console.WriteLine(Convert.ToHexString(b)); 172 | } 173 | ``` 174 | 175 | You can convert an integer to a hex string like so: `$"0x{MyVariable:X}"`. 176 | 177 | ## Performance tips 178 | 179 | - Be careful: `Vector128.Shuffle` is not the same as `Ssse3.Shuffle` nor is `Vector256.Shuffle` the same as `Avx2.Shuffle`. Prefer the latter. 180 | - Similarly `Vector128.Shuffle` is not the same as `AdvSimd.Arm64.VectorTableLookup`, use the latter. 181 | - `stackalloc` arrays should probably not be used in class instances. 182 | - In C#, `struct` might be preferable to `class` instances as it makes it clear that the data is thread local. 183 | - You can ask for an asm dump: `DOTNET_JitDisasm=NEON64HTMLScan dotnet run -c Release`. See [Viewing JIT disassembly and dumps](https://github.com/dotnet/runtime/blob/main/docs/design/coreclr/jit/viewing-jit-dumps.md). 184 | - You can get profiling data: `dotnet run -c Release -- -p EP`. 185 | 186 | ## Scientific References 187 | 188 | - Wojciech Muła, Daniel Lemire, [Base64 encoding and decoding at almost the speed of a memory copy](https://arxiv.org/abs/1910.05109), Software: Practice and Experience 50 (2), 2020. 189 | - Wojciech Muła, Daniel Lemire, [Faster Base64 Encoding and Decoding using AVX2 Instructions](https://arxiv.org/abs/1704.00605), ACM Transactions on the Web 12 (3), 2018. 190 | 191 | ## References 192 | 193 | - [base64 encoding with simd-support](https://github.com/dotnet/runtime/issues/27433) 194 | - [gfoidl.Base64](https://github.com/gfoidl/Base64): original code that lead to the SIMD-based code in the runtime 195 | - [simdutf's base64 decode](https://github.com/simdutf/simdutf/blob/74126531454de9b06388cb2de78b18edbfcfbe3d/src/westmere/sse_base64.cpp#L337) 196 | - [WHATWG forgiving-base64 decode](https://infra.spec.whatwg.org/#forgiving-base64-decode) 197 | 198 | ## More reading 199 | 200 | - https://learn.microsoft.com/en-us/dotnet/standard/design-guidelines/ 201 | - https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/coding-conventions 202 | 203 | 204 | [^1]: The .NET runtime appear to have received some of its fast SIMD base64 functions from [gfoidl.Base64](https://github.com/gfoidl/Base64) who built on earlier work by Klomp, Muła and others. See [Faster Base64 Encoding and Decoding using AVX2 Instructions](https://arxiv.org/abs/1704.00605) for a review. 205 | -------------------------------------------------------------------------------- /SimdBase64.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31903.59 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBD}") = "benchmark", "benchmark\benchmark.csproj", "{6C7744C5-AF3E-446A-8475-A03001989AB9}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBD}") = "SimdBase64", "src\SimdBase64.csproj", "{14D65B6D-24CD-4616-AAEF-BDBEB5E390A1}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBD}") = "tests", "test\tests.csproj", "{BDAFE9A2-0CC2-4F72-919F-5D9A8D185EBF}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 21 | {6C7744C5-AF3E-446A-8475-A03001989AB9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 22 | {6C7744C5-AF3E-446A-8475-A03001989AB9}.Debug|Any CPU.Build.0 = Debug|Any CPU 23 | {6C7744C5-AF3E-446A-8475-A03001989AB9}.Release|Any CPU.ActiveCfg = Release|Any CPU 24 | {6C7744C5-AF3E-446A-8475-A03001989AB9}.Release|Any CPU.Build.0 = Release|Any CPU 25 | {14D65B6D-24CD-4616-AAEF-BDBEB5E390A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 26 | {14D65B6D-24CD-4616-AAEF-BDBEB5E390A1}.Debug|Any CPU.Build.0 = Debug|Any CPU 27 | {14D65B6D-24CD-4616-AAEF-BDBEB5E390A1}.Release|Any CPU.ActiveCfg = Release|Any CPU 28 | {14D65B6D-24CD-4616-AAEF-BDBEB5E390A1}.Release|Any CPU.Build.0 = Release|Any CPU 29 | {BDAFE9A2-0CC2-4F72-919F-5D9A8D185EBF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 30 | {BDAFE9A2-0CC2-4F72-919F-5D9A8D185EBF}.Debug|Any CPU.Build.0 = Debug|Any CPU 31 | {BDAFE9A2-0CC2-4F72-919F-5D9A8D185EBF}.Release|Any CPU.ActiveCfg = Release|Any CPU 32 | {BDAFE9A2-0CC2-4F72-919F-5D9A8D185EBF}.Release|Any CPU.Build.0 = Release|Any CPU 33 | EndGlobalSection 34 | EndGlobal 35 | -------------------------------------------------------------------------------- /benchmark/.github/workflows/dotnet.yml: -------------------------------------------------------------------------------- 1 | name: .NET 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | name: Build and test on ${{ matrix.os }} 8 | runs-on: ${{ matrix.os }} 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | os: [ubuntu-latest, windows-latest, macos-latest] 13 | steps: 14 | - uses: actions/checkout@v4 15 | 16 | - name: Setup .NET 8.0 17 | uses: actions/setup-dotnet@v4 18 | with: 19 | dotnet-version: '8.0.x' 20 | - name: Restore dependencies 21 | run: dotnet restore 22 | - name: Build 23 | run: dotnet build --no-restore 24 | - name: Unit tests 25 | run: dotnet test --verbosity m --no-build 26 | continue-on-error: false 27 | -------------------------------------------------------------------------------- /benchmark/Benchmark.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using BenchmarkDotNet.Running; 3 | using BenchmarkDotNet.Configs; 4 | using BenchmarkDotNet.Reports; 5 | using BenchmarkDotNet.Jobs; 6 | using System.Text; 7 | using BenchmarkDotNet.Columns; 8 | 9 | namespace SimdUnicodeBenchmarks 10 | { 11 | 12 | #pragma warning disable CA1515 13 | public class Speed : IColumn 14 | { 15 | static long GetDirectorySize(string folderPath) 16 | { 17 | long totalSize = 0; 18 | DirectoryInfo di = new DirectoryInfo(folderPath); 19 | 20 | foreach (FileInfo fi in di.EnumerateFiles("*.*", SearchOption.AllDirectories)) 21 | { 22 | totalSize += fi.Length; 23 | } 24 | 25 | return totalSize; 26 | } 27 | public string GetValue(Summary summary, BenchmarkCase benchmarkCase) 28 | { 29 | #pragma warning disable CA1062 30 | var ourReport = summary.Reports.First(x => x.BenchmarkCase.Equals(benchmarkCase)); 31 | var fileName = (string)benchmarkCase.Parameters["FileName"]; 32 | long length = 0; 33 | if (File.Exists(fileName)) 34 | { 35 | length = new System.IO.FileInfo(fileName).Length; 36 | } 37 | else if (Directory.Exists(fileName)) 38 | { 39 | length = GetDirectorySize(fileName); 40 | } 41 | if (ourReport.ResultStatistics is null) 42 | { 43 | return "N/A"; 44 | } 45 | var mean = ourReport.ResultStatistics.Mean; 46 | return $"{(length / ourReport.ResultStatistics.Mean):#####.00}"; 47 | } 48 | 49 | public string GetValue(Summary summary, BenchmarkCase benchmarkCase, SummaryStyle style) => GetValue(summary, benchmarkCase); 50 | public bool IsDefault(Summary summary, BenchmarkCase benchmarkCase) => false; 51 | public bool IsAvailable(Summary summary) => true; 52 | 53 | public string Id { get; } = nameof(Speed); 54 | public string ColumnName { get; } = "Speed (GB/s)"; 55 | public bool AlwaysShow { get; } = true; 56 | public ColumnCategory Category { get; } = ColumnCategory.Custom; 57 | #pragma warning disable CA1805 58 | public int PriorityInCategory { get; } = 0; 59 | #pragma warning disable CA1805 60 | public bool IsNumeric { get; } = false; 61 | public UnitType UnitType { get; } = UnitType.Dimensionless; 62 | public string Legend { get; } = "The speed in gigabytes per second"; 63 | } 64 | 65 | #pragma warning disable CA1515 66 | public class DataVolume : IColumn 67 | { 68 | static double GetDirectorySize(string folderPath) 69 | { 70 | double totalSize = 0; 71 | DirectoryInfo di = new DirectoryInfo(folderPath); 72 | long fileCount = di.EnumerateFiles("*.*", SearchOption.AllDirectories).Count(); 73 | 74 | foreach (FileInfo fi in di.EnumerateFiles("*.*", SearchOption.AllDirectories)) 75 | { 76 | totalSize += fi.Length; 77 | } 78 | 79 | return totalSize / fileCount; 80 | } 81 | public string GetValue(Summary summary, BenchmarkCase benchmarkCase) 82 | { 83 | #pragma warning disable CA1062 84 | var ourReport = summary.Reports.First(x => x.BenchmarkCase.Equals(benchmarkCase)); 85 | var fileName = (string)benchmarkCase.Parameters["FileName"]; 86 | double length = 0; 87 | if (File.Exists(fileName)) 88 | { 89 | FileInfo fi = new FileInfo(fileName); 90 | length = fi.Length; 91 | length /= File.ReadAllLines(fi.FullName).Length; 92 | } 93 | else if (Directory.Exists(fileName)) 94 | { 95 | length = GetDirectorySize(fileName); 96 | } 97 | if (ourReport.ResultStatistics is null) 98 | { 99 | return "N/A"; 100 | } 101 | return $"{length / 1000:#####.00}"; 102 | } 103 | 104 | public string GetValue(Summary summary, BenchmarkCase benchmarkCase, SummaryStyle style) => GetValue(summary, benchmarkCase); 105 | public bool IsDefault(Summary summary, BenchmarkCase benchmarkCase) => false; 106 | public bool IsAvailable(Summary summary) => true; 107 | 108 | public string Id { get; } = nameof(DataVolume); 109 | public string ColumnName { get; } = "kiB/input"; 110 | public bool AlwaysShow { get; } = true; 111 | public ColumnCategory Category { get; } = ColumnCategory.Custom; 112 | #pragma warning disable CA1805 113 | public int PriorityInCategory { get; } = 0; 114 | #pragma warning disable CA1805 115 | public bool IsNumeric { get; } = false; 116 | public UnitType UnitType { get; } = UnitType.Dimensionless; 117 | public string Legend { get; } = "The average data volume in kilobytes per input"; 118 | } 119 | 120 | 121 | [SimpleJob(launchCount: 1, warmupCount: 10, iterationCount: 20)] 122 | [Config(typeof(Config))] 123 | #pragma warning disable CA1515 124 | public class RealDataBenchmark 125 | { 126 | #pragma warning disable CA1812 127 | private sealed class Config : ManualConfig 128 | { 129 | public Config() 130 | { 131 | AddColumn(new DataVolume()); 132 | AddColumn(new Speed()); 133 | } 134 | } 135 | // Parameters and variables for real data 136 | [Params( 137 | @"data/email/", 138 | @"data/dns/swedenzonebase.txt" 139 | )] 140 | #pragma warning disable CA1051 141 | public string? FileName; 142 | #pragma warning disable CS8618 143 | public string[] FileContent; 144 | public int[] DecodedLengths; 145 | public byte[][] output; // precomputed byte outputs (with correct size) 146 | public byte[][] input; // precomputed byte inputs 147 | public char[][] input16; // precomputed char inputs 148 | 149 | 150 | public void RunRuntimeDecodingBenchmarkUTF16(string[] data, int[] lengths) 151 | { 152 | foreach (string s in FileContent) 153 | { 154 | Convert.FromBase64String(s); 155 | } 156 | } 157 | 158 | // Note: The runtime decoding uses advanced SIMD instructions, including AVX-512. 159 | // Thus on systems with > SSSE3 support, it might beat our SSE implementation. 160 | // See https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Helper/Base64DecoderHelper.cs 161 | public void RunRuntimeSIMDDecodingBenchmarkUTF8(string[] data, int[] lengths) 162 | { 163 | for (int i = 0; i < FileContent.Length; i++) 164 | { 165 | System.Buffers.Text.Base64.DecodeFromUtf8(input[i].AsSpan(), output[i].AsSpan(), out int consumed, out int written); 166 | if (written != lengths[i]) 167 | { 168 | Console.WriteLine($"Error: {written} != {lengths[i]}"); 169 | #pragma warning disable CA2201 170 | throw new Exception("Error"); 171 | } 172 | } 173 | } 174 | 175 | // Note: The runtime decoding uses advanced SIMD instructions, including AVX-512. 176 | // See https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Helper/Base64DecoderHelper.cs 177 | // Thus on systems with > SSSE3 support, it might beat our SSE implementation. 178 | public void RunRuntimeSIMDDecodingBenchmarkWithAllocUTF8(string[] data, int[] lengths) 179 | { 180 | for (int i = 0; i < FileContent.Length; i++) 181 | { 182 | byte[] outputdata = new byte[System.Buffers.Text.Base64.GetMaxDecodedFromUtf8Length(input[i].Length)]; 183 | System.Buffers.Text.Base64.DecodeFromUtf8(input[i].AsSpan(), outputdata.AsSpan(), out int consumed, out int written); 184 | if (written != lengths[i]) 185 | { 186 | Console.WriteLine($"Error: {written} != {lengths[i]}"); 187 | #pragma warning disable CA2201 188 | throw new Exception("Error"); 189 | } 190 | } 191 | } 192 | public unsafe void RunGfoidlDecodingBenchmarkUTF16(string[] data, int[] lengths) 193 | { 194 | // gfoidl does not appear to always succeed. Note that 195 | // the decoding was not integrated into the DOTNET runtime. 196 | for (int i = 0; i < FileContent.Length; i++) 197 | { 198 | string s = FileContent[i]; 199 | ReadOnlySpan span = s.ToCharArray(); 200 | Span dataout = output[i]; 201 | int consumed = 0; 202 | int written = 0; 203 | gfoidl.Base64.Base64.Default.Decode(span, dataout, out consumed, out written, true); 204 | if (written != lengths[i]) 205 | { 206 | Console.WriteLine($"Error: {written} != {lengths[i]}"); 207 | #pragma warning disable CA2201 208 | throw new Exception("Error"); 209 | } 210 | } 211 | } 212 | 213 | public unsafe void RunScalarDecodingBenchmarkUTF8(string[] data, int[] lengths) 214 | { 215 | for (int i = 0; i < FileContent.Length; i++) 216 | { 217 | byte[] base64 = input[i]; 218 | byte[] dataoutput = output[i]; 219 | int bytesConsumed = 0; 220 | int bytesWritten = 0; 221 | SimdBase64.Scalar.Base64.Base64WithWhiteSpaceToBinaryScalar(base64.AsSpan(), dataoutput, out bytesConsumed, out bytesWritten, false); 222 | if (bytesWritten != lengths[i]) 223 | { 224 | Console.WriteLine($"Error: {bytesWritten} != {lengths[i]}"); 225 | #pragma warning disable CA2201 226 | throw new Exception("Error"); 227 | } 228 | } 229 | } 230 | 231 | public unsafe void RunScalarDecodingBenchmarkUTF16(string[] data, int[] lengths) 232 | { 233 | for (int i = 0; i < FileContent.Length; i++) 234 | { 235 | string s = FileContent[i]; 236 | char[] base64 = input16[i]; 237 | byte[] dataoutput = output[i]; 238 | int bytesConsumed = 0; 239 | int bytesWritten = 0; 240 | SimdBase64.Scalar.Base64.Base64WithWhiteSpaceToBinaryScalar(base64.AsSpan(), dataoutput, out bytesConsumed, out bytesWritten, false); 241 | if (bytesWritten != lengths[i]) 242 | { 243 | Console.WriteLine($"Error: {bytesWritten} != {lengths[i]}"); 244 | #pragma warning disable CA2201 245 | throw new Exception("Error"); 246 | } 247 | } 248 | } 249 | 250 | public unsafe void RunSSEDecodingBenchmarkUTF8(string[] data, int[] lengths) 251 | { 252 | for (int i = 0; i < FileContent.Length; i++) 253 | { 254 | byte[] base64 = input[i]; 255 | byte[] dataoutput = output[i]; 256 | int bytesConsumed = 0; 257 | int bytesWritten = 0; 258 | SimdBase64.SSE.Base64.DecodeFromBase64SSE(base64.AsSpan(), dataoutput, out bytesConsumed, out bytesWritten, false); 259 | if (bytesWritten != lengths[i]) 260 | { 261 | Console.WriteLine($"Error: {bytesWritten} != {lengths[i]}"); 262 | #pragma warning disable CA2201 263 | throw new Exception("Error"); 264 | } 265 | } 266 | } 267 | 268 | public unsafe void RunSSEDecodingBenchmarkUTF16(string[] data, int[] lengths) 269 | { 270 | for (int i = 0; i < FileContent.Length; i++) 271 | { 272 | string s = FileContent[i]; 273 | ReadOnlySpan base64 = s.AsSpan(); 274 | byte[] dataoutput = output[i]; 275 | int bytesConsumed = 0; 276 | int bytesWritten = 0; 277 | SimdBase64.SSE.Base64.DecodeFromBase64SSE(base64, dataoutput, out bytesConsumed, out bytesWritten, false); 278 | if (bytesWritten != lengths[i]) 279 | { 280 | Console.WriteLine($"Error: {bytesWritten} != {lengths[i]}"); 281 | #pragma warning disable CA2201 282 | throw new Exception("Error"); 283 | } 284 | } 285 | } 286 | 287 | public unsafe void RunSSEDecodingBenchmarkWithAllocUTF8(string[] data, int[] lengths) 288 | { 289 | for (int i = 0; i < FileContent.Length; i++) 290 | { 291 | Span base64 = input[i].AsSpan(); 292 | byte[] dataoutput = new byte[SimdBase64.Base64.MaximalBinaryLengthFromBase64(base64)]; 293 | int bytesConsumed = 0; 294 | int bytesWritten = 0; 295 | SimdBase64.SSE.Base64.DecodeFromBase64SSE(base64, dataoutput, out bytesConsumed, out bytesWritten, false); 296 | if (bytesWritten != lengths[i]) 297 | { 298 | Console.WriteLine($"Error: {bytesWritten} != {lengths[i]}"); 299 | #pragma warning disable CA2201 300 | throw new Exception("Error"); 301 | } 302 | } 303 | } 304 | 305 | public unsafe void RunSSEDecodingBenchmarkWithAllocUTF16(string[] data, int[] lengths) 306 | { 307 | for (int i = 0; i < FileContent.Length; i++) 308 | { 309 | string s = FileContent[i]; 310 | Span base64 = input16[i].AsSpan(); 311 | byte[] dataoutput = new byte[SimdBase64.Base64.MaximalBinaryLengthFromBase64(base64)]; 312 | int bytesConsumed = 0; 313 | int bytesWritten = 0; 314 | SimdBase64.SSE.Base64.DecodeFromBase64SSE(base64, dataoutput, out bytesConsumed, out bytesWritten, false); 315 | if (bytesWritten != lengths[i]) 316 | { 317 | Console.WriteLine($"Error: {bytesWritten} != {lengths[i]}"); 318 | #pragma warning disable CA2201 319 | throw new Exception("Error"); 320 | } 321 | } 322 | } 323 | 324 | public unsafe void RunAVX2DecodingBenchmarkUTF8(string[] data, int[] lengths) 325 | { 326 | for (int i = 0; i < FileContent.Length; i++) 327 | { 328 | byte[] base64 = input[i]; 329 | byte[] dataoutput = output[i]; 330 | int bytesConsumed = 0; 331 | int bytesWritten = 0; 332 | SimdBase64.AVX2.Base64.DecodeFromBase64AVX2(base64.AsSpan(), dataoutput, out bytesConsumed, out bytesWritten, false); 333 | if (bytesWritten != lengths[i]) 334 | { 335 | Console.WriteLine($"Error: {bytesWritten} != {lengths[i]}"); 336 | #pragma warning disable CA2201 337 | throw new Exception("Error"); 338 | } 339 | } 340 | } 341 | 342 | 343 | public unsafe void RunOurDecodingBenchmarkUTF8(string[] data, int[] lengths) 344 | { 345 | for (int i = 0; i < FileContent.Length; i++) 346 | { 347 | byte[] base64 = input[i]; 348 | byte[] dataoutput = output[i]; 349 | int bytesConsumed = 0; 350 | int bytesWritten = 0; 351 | SimdBase64.Base64.DecodeFromBase64(base64.AsSpan(), dataoutput, out bytesConsumed, out bytesWritten, false); 352 | if (bytesWritten != lengths[i]) 353 | { 354 | Console.WriteLine($"Error: {bytesWritten} != {lengths[i]}"); 355 | #pragma warning disable CA2201 356 | throw new Exception("Error"); 357 | } 358 | } 359 | } 360 | 361 | public unsafe void RunAVX2DecodingBenchmarkUTF16(string[] data, int[] lengths) 362 | { 363 | for (int i = 0; i < FileContent.Length; i++) 364 | { 365 | string s = FileContent[i]; 366 | ReadOnlySpan base64 = s.AsSpan(); 367 | byte[] dataoutput = output[i]; 368 | int bytesConsumed = 0; 369 | int bytesWritten = 0; 370 | SimdBase64.AVX2.Base64.DecodeFromBase64AVX2(base64, dataoutput, out bytesConsumed, out bytesWritten, false); 371 | if (bytesWritten != lengths[i]) 372 | { 373 | Console.WriteLine($"Error: {bytesWritten} != {lengths[i]}"); 374 | #pragma warning disable CA2201 375 | throw new Exception("Error"); 376 | } 377 | } 378 | } 379 | public unsafe void RunOurDecodingBenchmarkUTF16(string[] data, int[] lengths) 380 | { 381 | for (int i = 0; i < FileContent.Length; i++) 382 | { 383 | string s = FileContent[i]; 384 | ReadOnlySpan base64 = s.AsSpan(); 385 | byte[] dataoutput = output[i]; 386 | int bytesConsumed = 0; 387 | int bytesWritten = 0; 388 | SimdBase64.Base64.DecodeFromBase64(base64, dataoutput, out bytesConsumed, out bytesWritten, false); 389 | if (bytesWritten != lengths[i]) 390 | { 391 | Console.WriteLine($"Error: {bytesWritten} != {lengths[i]}"); 392 | #pragma warning disable CA2201 393 | throw new Exception("Error"); 394 | } 395 | } 396 | } 397 | 398 | public unsafe void RunAVX2DecodingBenchmarkWithAllocUTF8(string[] data, int[] lengths) 399 | { 400 | for (int i = 0; i < FileContent.Length; i++) 401 | { 402 | Span base64 = input[i].AsSpan(); 403 | byte[] dataoutput = new byte[SimdBase64.Base64.MaximalBinaryLengthFromBase64(base64)]; 404 | int bytesConsumed = 0; 405 | int bytesWritten = 0; 406 | SimdBase64.AVX2.Base64.DecodeFromBase64AVX2(base64, dataoutput, out bytesConsumed, out bytesWritten, false); 407 | if (bytesWritten != lengths[i]) 408 | { 409 | Console.WriteLine($"Error: {bytesWritten} != {lengths[i]}"); 410 | #pragma warning disable CA2201 411 | throw new Exception("Error"); 412 | } 413 | } 414 | } 415 | 416 | public unsafe void RunAVX2DecodingBenchmarkWithAllocUTF16(string[] data, int[] lengths) 417 | { 418 | for (int i = 0; i < FileContent.Length; i++) 419 | { 420 | string s = FileContent[i]; 421 | Span base64 = input16[i].AsSpan(); 422 | byte[] dataoutput = new byte[SimdBase64.Base64.MaximalBinaryLengthFromBase64(base64)]; 423 | int bytesConsumed = 0; 424 | int bytesWritten = 0; 425 | SimdBase64.AVX2.Base64.DecodeFromBase64AVX2(base64, dataoutput, out bytesConsumed, out bytesWritten, false); 426 | if (bytesWritten != lengths[i]) 427 | { 428 | Console.WriteLine($"Error: {bytesWritten} != {lengths[i]}"); 429 | #pragma warning disable CA2201 430 | throw new Exception("Error"); 431 | } 432 | } 433 | } 434 | 435 | 436 | public unsafe void RunOurDecodingBenchmarkWithAllocUTF8(string[] data, int[] lengths) 437 | { 438 | for (int i = 0; i < FileContent.Length; i++) 439 | { 440 | Span base64 = input[i].AsSpan(); 441 | byte[] dataoutput = new byte[SimdBase64.Base64.MaximalBinaryLengthFromBase64(base64)]; 442 | int bytesConsumed = 0; 443 | int bytesWritten = 0; 444 | SimdBase64.Base64.DecodeFromBase64(base64, dataoutput, out bytesConsumed, out bytesWritten, false); 445 | if (bytesWritten != lengths[i]) 446 | { 447 | Console.WriteLine($"Error: {bytesWritten} != {lengths[i]}"); 448 | #pragma warning disable CA2201 449 | throw new Exception("Error"); 450 | } 451 | } 452 | } 453 | 454 | public unsafe void RunOurDecodingBenchmarkWithAllocUTF16(string[] data, int[] lengths) 455 | { 456 | for (int i = 0; i < FileContent.Length; i++) 457 | { 458 | string s = FileContent[i]; 459 | byte[] dataoutput = SimdBase64.Base64.FromBase64String(s); 460 | 461 | if (dataoutput.Length != lengths[i]) 462 | { 463 | Console.WriteLine($"Error: {dataoutput.Length} != {lengths[i]}"); 464 | #pragma warning disable CA2201 465 | throw new Exception("Error"); 466 | } 467 | } 468 | } 469 | 470 | public unsafe void RunARMDecodingBenchmarkUTF8(string[] data, int[] lengths) 471 | { 472 | for (int i = 0; i < FileContent.Length; i++) 473 | { 474 | byte[] base64 = input[i]; 475 | byte[] dataoutput = output[i]; 476 | int bytesConsumed = 0; 477 | int bytesWritten = 0; 478 | SimdBase64.Arm.Base64.DecodeFromBase64ARM(base64.AsSpan(), dataoutput, out bytesConsumed, out bytesWritten, false); 479 | if (bytesWritten != lengths[i]) 480 | { 481 | Console.WriteLine($"Error: {bytesWritten} != {lengths[i]}"); 482 | #pragma warning disable CA2201 483 | throw new Exception("Error"); 484 | } 485 | } 486 | } 487 | 488 | public unsafe void RunARMDecodingBenchmarkUTF16(string[] data, int[] lengths) 489 | { 490 | for (int i = 0; i < FileContent.Length; i++) 491 | { 492 | string s = FileContent[i]; 493 | ReadOnlySpan base64 = s.AsSpan(); 494 | byte[] dataoutput = output[i]; 495 | int bytesConsumed = 0; 496 | int bytesWritten = 0; 497 | SimdBase64.Arm.Base64.DecodeFromBase64ARM(base64, dataoutput, out bytesConsumed, out bytesWritten, false); 498 | if (bytesWritten != lengths[i]) 499 | { 500 | Console.WriteLine($"Error: {bytesWritten} != {lengths[i]}"); 501 | #pragma warning disable CA2201 502 | throw new Exception("Error"); 503 | } 504 | } 505 | } 506 | 507 | public unsafe void RunARMDecodingBenchmarkWithAllocUTF8(string[] data, int[] lengths) 508 | { 509 | for (int i = 0; i < FileContent.Length; i++) 510 | { 511 | Span base64 = input[i].AsSpan(); 512 | byte[] dataoutput = new byte[SimdBase64.Base64.MaximalBinaryLengthFromBase64(base64)]; 513 | int bytesConsumed = 0; 514 | int bytesWritten = 0; 515 | SimdBase64.Arm.Base64.DecodeFromBase64ARM(base64, dataoutput, out bytesConsumed, out bytesWritten, false); 516 | if (bytesWritten != lengths[i]) 517 | { 518 | Console.WriteLine($"Error: {bytesWritten} != {lengths[i]}"); 519 | #pragma warning disable CA2201 520 | throw new Exception("Error"); 521 | } 522 | } 523 | } 524 | 525 | public unsafe void RunARMDecodingBenchmarkWithAllocUTF16(string[] data, int[] lengths) 526 | { 527 | for (int i = 0; i < FileContent.Length; i++) 528 | { 529 | string s = FileContent[i]; 530 | Span base64 = input16[i].AsSpan(); 531 | byte[] dataoutput = new byte[SimdBase64.Base64.MaximalBinaryLengthFromBase64(base64)]; 532 | int bytesConsumed = 0; 533 | int bytesWritten = 0; 534 | SimdBase64.Arm.Base64.DecodeFromBase64ARM(base64, dataoutput, out bytesConsumed, out bytesWritten, false); 535 | if (bytesWritten != lengths[i]) 536 | { 537 | Console.WriteLine($"Error: {bytesWritten} != {lengths[i]}"); 538 | #pragma warning disable CA2201 539 | throw new Exception("Error"); 540 | } 541 | } 542 | } 543 | 544 | [GlobalSetup] 545 | public void Setup() 546 | { 547 | Console.WriteLine($"FileContent : {FileName}"); 548 | if (FileName == "data/dns/swedenzonebase.txt") 549 | { 550 | FileContent = File.ReadAllLines(FileName); 551 | DecodedLengths = new int[FileContent.Length]; 552 | output = new byte[FileContent.Length][]; 553 | input = new byte[FileContent.Length][]; 554 | input16 = new char[FileContent.Length][]; 555 | for (int i = 0; i < FileContent.Length; i++) 556 | { 557 | DecodedLengths[i] = Convert.FromBase64String(FileContent[i]).Length; 558 | output[i] = new byte[DecodedLengths[i]]; 559 | input[i] = Encoding.UTF8.GetBytes(FileContent[i]); 560 | input16[i] = FileContent[i].ToCharArray(); 561 | } 562 | } 563 | else if (FileName == "data/email/") 564 | { 565 | string[] fileNames = Directory.GetFiles(FileName); 566 | FileContent = new string[fileNames.Length]; 567 | DecodedLengths = new int[fileNames.Length]; 568 | output = new byte[FileContent.Length][]; 569 | input = new byte[FileContent.Length][]; 570 | input16 = new char[FileContent.Length][]; 571 | 572 | for (int i = 0; i < fileNames.Length; i++) 573 | { 574 | FileContent[i] = File.ReadAllText(fileNames[i]); 575 | DecodedLengths[i] = Convert.FromBase64String(FileContent[i]).Length; 576 | output[i] = new byte[DecodedLengths[i]]; 577 | input[i] = Encoding.UTF8.GetBytes(FileContent[i]); 578 | input16[i] = FileContent[i].ToCharArray(); 579 | } 580 | 581 | } 582 | else 583 | { 584 | FileContent = []; 585 | } 586 | 587 | } 588 | 589 | [Benchmark] 590 | [BenchmarkCategory("default", "runtime")] 591 | public unsafe void DotnetRuntimeSIMDBase64RealDataUTF8() 592 | { 593 | RunRuntimeSIMDDecodingBenchmarkUTF8(FileContent, DecodedLengths); 594 | } 595 | 596 | public unsafe void DotnetRuntimeSIMDBase64RealDataWithAllocUTF8() 597 | { 598 | RunRuntimeSIMDDecodingBenchmarkWithAllocUTF8(FileContent, DecodedLengths); 599 | } 600 | 601 | [Benchmark] 602 | [BenchmarkCategory("UTF16")] 603 | public unsafe void DotnetRuntimeBase64RealDataUTF16() 604 | { 605 | RunRuntimeDecodingBenchmarkUTF16(FileContent, DecodedLengths); 606 | } 607 | 608 | public unsafe void SSEDecodingRealDataUTF8() 609 | { 610 | RunSSEDecodingBenchmarkUTF8(FileContent, DecodedLengths); 611 | } 612 | 613 | public unsafe void SSEDecodingRealDataWithAllocUTF8() 614 | { 615 | RunSSEDecodingBenchmarkWithAllocUTF8(FileContent, DecodedLengths); 616 | } 617 | 618 | public unsafe void AVX2DecodingRealDataUTF8() 619 | { 620 | RunAVX2DecodingBenchmarkUTF8(FileContent, DecodedLengths); 621 | } 622 | 623 | [Benchmark] 624 | [BenchmarkCategory("default")] 625 | public unsafe void SimdBase64DecodingRealDataUTF8() 626 | { 627 | RunOurDecodingBenchmarkUTF8(FileContent, DecodedLengths); 628 | } 629 | 630 | [Benchmark] 631 | [BenchmarkCategory("UTF16")] 632 | public unsafe void SimdBase64DecodingRealDataWithAllocUTF16() 633 | { 634 | RunOurDecodingBenchmarkWithAllocUTF16(FileContent, DecodedLengths); 635 | } 636 | 637 | public unsafe void AVX2DecodingRealDataWithAllocUTF8() 638 | { 639 | RunAVX2DecodingBenchmarkWithAllocUTF8(FileContent, DecodedLengths); 640 | } 641 | 642 | public unsafe void ARMDecodingRealDataUTF8() 643 | { 644 | RunARMDecodingBenchmarkUTF8(FileContent, DecodedLengths); 645 | } 646 | 647 | public unsafe void ARMDecodingRealDataWithAllocUTF8() 648 | { 649 | RunARMDecodingBenchmarkWithAllocUTF8(FileContent, DecodedLengths); 650 | } 651 | 652 | public unsafe void ARMDecodingRealDataUTF16() 653 | { 654 | RunARMDecodingBenchmarkUTF16(FileContent, DecodedLengths); 655 | } 656 | 657 | public unsafe void SSEDecodingRealDataUTF16() 658 | { 659 | RunSSEDecodingBenchmarkUTF16(FileContent, DecodedLengths); 660 | } 661 | 662 | public unsafe void SSEDecodingRealDataWithAllocUTF16() 663 | { 664 | RunSSEDecodingBenchmarkWithAllocUTF16(FileContent, DecodedLengths); 665 | } 666 | 667 | public unsafe void AVX2DecodingRealDataUTF16() 668 | { 669 | RunAVX2DecodingBenchmarkUTF16(FileContent, DecodedLengths); 670 | } 671 | 672 | public unsafe void AVX2DecodingRealDataWithAllocUTF16() 673 | { 674 | RunAVX2DecodingBenchmarkWithAllocUTF16(FileContent, DecodedLengths); 675 | } 676 | 677 | } 678 | #pragma warning disable CA1515 679 | public class Program 680 | { 681 | static void Main(string[] args) 682 | { 683 | if (args.Length == 0) 684 | { 685 | args = new string[] { "--filter", "*", "--anyCategories", "default" }; 686 | } 687 | var job = Job.Default 688 | .WithWarmupCount(1) 689 | .WithMinIterationCount(2) 690 | .WithMaxIterationCount(10) 691 | .AsDefault(); 692 | 693 | BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args, DefaultConfig.Instance.AddJob(job).WithSummaryStyle(SummaryStyle.Default.WithMaxParameterColumnWidth(100))); 694 | } 695 | } 696 | 697 | } -------------------------------------------------------------------------------- /benchmark/benchmark.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net9.0 6 | enable 7 | enable 8 | true 9 | true 10 | AllEnabledByDefault 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | Always 26 | 27 | 28 | 29 | Always 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /benchmark/data/email/enron1.txt: -------------------------------------------------------------------------------- 1 | 0M8R4KGxGuEAAAAAAAAAAAAAAAAAAAAAPgADAP7/CQAGAAAAAAAAAAAAAAABAAAAHAAAAAAAAAAA 2 | EAAA/v///wAAAAD+////AAAAABsAAAD///////////////////////////////////////////// 3 | //////////////////////////////////////////////////////////////////////////// 4 | //////////////////////////////////////////////////////////////////////////// 5 | //////////////////////////////////////////////////////////////////////////// 6 | //////////////////////////////////////////////////////////////////////////// 7 | //////////////////////////////////////////////////////////////////////////// 8 | //////////////////////////////////////////////////////////////////////////// 9 | //////////////////////////////////////////////////////////////////////////8J 10 | CBAAAAYFAP4czQfBQAAABgEAAOEAAgCwBMEAAgAAAOIAAABcAHAABgAAanBhcmtzICAgICAgICAg 11 | ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg 12 | ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEIAAgCwBGEBAgAAAMABAAA9AQYA 13 | AQACAAMAnAACAA4AGQACAAAAEgACAAAAEwACAAAArwECAAAAvAECAAAAPQASAGgBLQCMN4UgOAAA 14 | AAAAAQBYAkAAAgAAAI0AAgAAACIAAgAAAA4AAgABALcBAgAAANoAAgAAADEAGgDIAAAA/3+QAQAA 15 | AAAAAAUBQQByAGkAYQBsADEAGgDIAAAA/3+QAQAAAAAAAAUBQQByAGkAYQBsADEAGgDIAAAA/3+Q 16 | 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 76 | AAAWAIA54kABAgYADAABABYAfgIKAAwAAgAbAACI00ABAgYADAADABsAfgIKAAwABAAVAAH4cEAG 77 | ABsADAAFABwAAAAAAICD6sAIAA0ABf8FAAEFAAUAAQIGAAwABgAcAH4CCgANAAAAFgCgOeJAAQIG 78 | AA0AAQAWAH4CCgANAAIAGwAAiNNAAQIGAA0AAwAbAH4CCgANAAQAFQAB+HBABgAbAA0ABQAcAAAA 79 | AACAg+rACAAOAAX/BQABBQAFAH4CCgAOAAAAFgDAOeJAAQIGAA4AAQAWAH4CCgAOAAIAGwAAiNNA 80 | AQIGAA4AAwAbAH4CCgAOAAQAFQAB+HBABgAbAA4ABQAcAAAAAACAg+rACAAPAAX/BQABBQAFAH4C 81 | CgAOAAYAHAAgXP9AfgIKAA8AAAAWAOA54kABAgYADwABABYAfgIKAA8AAgAbAACI00ABAgYADwAD 82 | ABsAfgIKAA8ABAAVAAGgcUAGABsADwAFABwAAAAAAACK68AIAA8ABf0FAAEFAAUAfgIKABAAAAAW 83 | AAA64kC+AAwAEAABABYAGwAbAAMAvgAKABAABQAcABwABgB+AgoAEQAAABYAIDriQL4ADAARAAEA 84 | FgAbABsAAwB+AgoAEgAAABYAQDriQL4ADAASAAEAFgAbABsAAwB+AgoAEwAAABYAYDriQL4ADAAT 85 | AAEAFgAbABsAAwB+AgoAFAAAABYAgDriQL4ADAAUAAEAFgAbABsAAwB+AgoAFQAAABYAoDriQL4A 86 | DAAVAAEAFgAbABsAAwB+AgoAFgAAABYAwDriQL4ADAAWAAEAFgAbABsAAwB+AgoAFwAAABYA4Dri 87 | QL4ADAAXAAEAFgAbABsAAwB+AgoAGAAAABYAADviQL4ADAAYAAEAFgAbABsAAwB+AgoAGQAAABYA 88 | IDviQL4ADAAZAAEAFgAbABsAAwB+AgoAGgAAABYAQDviQL4ADAAaAAEAFgAbABsAAwB+AgoAGwAA 89 | ABYAYDviQL4ADAAbAAEAFgAbABsAAwB+AgoAHAAAABYAgDviQL4ADAAcAAEAFgAbABsAAwB+AgoA 90 | HQAAABYAoDviQL4ADAAdAAEAFgAbABsAAwB+AgoAHgAAABYAwDviQL4ADAAeAAEAFgAbABsAAwB+ 91 | AgoAHwAAABYA4DviQL4ADAAfAAEAFgAbABsAAwB+AgoAIAAAABYAADziQL4ADAAgAAEAFgAbABsA 92 | AwB+AgoAIQAAABYAIDziQL4ADAAhAAEAFgAbABsAAwB+AgoAIgAAABYAQDziQL4ADAAiAAEAFgAb 93 | ABsAAwDXAEQAiQoAAGwCRgCkALcAbwBvAG8ARQCbAGcAZwBdAGsAXQAsAB4AHgAeAB4AHgAeAB4A 94 | HgAeAB4AHgAeAB4AHgAeAB4AHgA+AhIAtgYAAAAAQAAAAAAAAAAAAAAAHQAPAAMCAAMAAAABAAIA 95 | AgADA+8ABgAAADcAAAAKAAAACQgQAAAGEAD+HM0HwUAAAAYBAAALAhAAAAAAAAAAAAAAAAAAuBMA 96 | AA0AAgABAAwAAgBkAA8AAgABABEAAgAAABAACAD8qfHSTWJQP18AAgABACoAAgAAACsAAgAAAIIA 97 | AgABAIAACAAAAAAAAAAAACUCBAAAAP8AgQACAMEEFAAAABUAAACDAAIAAACEAAIAAAChACIAAAD/ 98 | AAEAAQABAAQAAAMDAwAAAAAAAOA/AAAAAAAA4D8AAFUAAgAIAAACDgAAAAAAAAAAAAAAAAAAAD4C 99 | EgC2AAAAAABAAAAAAAAAAAAAAAAdAA8AAwAAAAAAAAEAAAAAAAAA7wAGAAAANwAAAAoAAAAJCBAA 100 | AAYQAP4czQfBQAAABgEAAAsCEAAAAAAAAAAAAAAAAAC/FAAADQACAAEADAACAGQADwACAAEAEQAC 101 | AAAAEAAIAPyp8dJNYlA/XwACAAEAKgACAAAAKwACAAAAggACAAEAgAAIAAAAAAAAAAAAJQIEAAAA 102 | /wCBAAIAwQQUAAAAFQAAAIMAAgAAAIQAAgAAAKEAIgAAAP8AAQABAAEABAAAAAAAAAAAAAAA4D8A 103 | AAAAAADgPwAAVQACAAgAAAIOAAAAAAAAAAAAAAAAAAAAPgISALYAAAAAAEAAAAAAAAAAAAAAAB0A 104 | DwADAAAAAAAAAQAAAAAAAADvAAYAAAA3AAAACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 105 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 106 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 107 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 108 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/v8AAAUAAgAAAAAA 109 | AAAAAAAAAAAAAAAAAQAAAOCFn/L5T2gQq5EIACsns9kwAAAAoAAAAAcAAAABAAAAQAAAAAQAAABI 110 | AAAACAAAAFgAAAASAAAAaAAAAAwAAACAAAAADQAAAIwAAAATAAAAmAAAAAIAAADkBAAAHgAAAAcA 111 | AABqcGFya3MAAB4AAAAHAAAAanBhcmtzAAAeAAAAEAAAAE1pY3Jvc29mdCBFeGNlbABAAAAAAGVu 112 | qyvJwQFAAAAAAFw66CvJwQEDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 113 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 114 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 115 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 116 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 117 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 118 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 119 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 120 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 121 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 122 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 123 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 124 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 125 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 126 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 127 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 128 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 129 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 130 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 131 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 132 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 133 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 134 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 135 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 136 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 137 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 138 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 139 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 140 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 141 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 142 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 143 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 144 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 145 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 146 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 147 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 148 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 149 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 150 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 151 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 152 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 153 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 154 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 155 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 156 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 157 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 158 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 159 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 160 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 161 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 162 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 163 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 164 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 165 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 166 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 167 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 168 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 169 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 170 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 171 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 172 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 173 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 174 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 175 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 176 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 177 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 178 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 179 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 180 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7/AAAFAAIAAAAAAAAAAAAAAAAA 181 | AAAAAAEAAAAC1c3VnC4bEJOXCAArLPmuMAAAAOAAAAAJAAAAAQAAAFAAAAAPAAAAWAAAABcAAABs 182 | AAAACwAAAHQAAAAQAAAAfAAAABMAAACEAAAAFgAAAIwAAAANAAAAlAAAAAwAAAC9AAAAAgAAAOQE 183 | AAAeAAAACwAAAEVucm9uIENvcnAAMwMAAADtDgkACwAAAAAAAAALAAAAAAAAAAsAAAAAAAAACwAA 184 | AAAAAAAeEAAAAwAAAAcAAABTaGVldDEABwAAAFNoZWV0MgAHAAAAU2hlZXQzAAwQAAACAAAAHgAA 185 | AAsAAABXb3Jrc2hlZXRzAAMAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 186 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 187 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 188 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 189 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 190 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 191 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 192 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 193 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 194 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 195 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 196 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 197 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 198 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 199 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 200 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 201 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 202 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 203 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 204 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 205 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 206 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 207 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 208 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 209 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 210 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 211 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 212 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 213 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 214 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 215 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 216 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 217 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 218 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 219 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 220 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 221 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 222 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 223 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 224 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 225 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 226 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 227 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 228 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 229 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 230 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 231 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 232 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 233 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 234 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 235 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 236 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 237 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 238 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 239 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 240 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 241 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 242 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 243 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 244 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 245 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 246 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 247 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 248 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 249 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 250 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 251 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 252 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAgAAAAMAAAAEAAAABQAAAAYAAAAHAAAA 253 | CAAAAAkAAAAKAAAA/v///wwAAAANAAAADgAAAA8AAAAQAAAAEQAAABIAAAD+////FAAAABUAAAAW 254 | AAAAFwAAABgAAAAZAAAAGgAAAP7////9/////v////////////////////////////////////// 255 | //////////////////////////////////////////////////////////////////////////// 256 | //////////////////////////////////////////////////////////////////////////// 257 | //////////////////////////////////////////////////////////////////////////// 258 | //////////////////////////////////////////////////////////////////////////// 259 | //////////////////////////////////////////////////////////////////////////// 260 | //////////////////////////////////////////////////////////////////////////// 261 | /////////////////////////////////////1IAbwBvAHQAIABFAG4AdAByAHkAAAAAAAAAAAAA 262 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWAAUB//////////8CAAAAIAgCAAAA 263 | AADAAAAAAAAARgAAAAAAAAAAAAAAAAAAAAAAAAAA/v///wAAAAAAAAAAVwBvAHIAawBiAG8AbwBr 264 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABIAAgH///// 265 | //////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADhUAAAAAAAAF 266 | AFMAdQBtAG0AYQByAHkASQBuAGYAbwByAG0AYQB0AGkAbwBuAAAAAAAAAAAAAAAAAAAAAAAAAAAA 267 | AAAAAAAAKAACAQEAAAADAAAA/////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 268 | AAsAAAAAEAAAAAAAAAUARABvAGMAdQBtAGUAbgB0AFMAdQBtAG0AYQByAHkASQBuAGYAbwByAG0A 269 | YQB0AGkAbwBuAAAAAAAAAAAAAAA4AAIB////////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAA 270 | AAAAAAAAAAAAAAAAAAAAEwAAAAAQAAAAAAAA -------------------------------------------------------------------------------- /benchmark/data/email/enron3.txt: -------------------------------------------------------------------------------- 1 | 0M8R4KGxGuEAAAAAAAAAAAAAAAAAAAAAPgADAP7/CQAGAAAAAAAAAAAAAAABAAAALgAAAAAAAAAA 2 | EAAAMAAAAAEAAAD+////AAAAAC0AAAD///////////////////////////////////////////// 3 | //////////////////////////////////////////////////////////////////////////// 4 | //////////////////////////////////////////////////////////////////////////// 5 | //////////////////////////////////////////////////////////////////////////// 6 | //////////////////////////////////////////////////////////////////////////// 7 | //////////////////////////////////////////////////////////////////////////// 8 | //////////////////////////////////////////////////////////////////////////// 9 | ///////////////////////////////////////////////////////////////////////////s 10 | pcEARwAJBAAAABa/AAAAAAAAEAAAAAAABAAA3xUAAA4AYmpiao7ZjtkAAAAAAAAAAAAAAAAAAAAA 11 | AAAJBBYAtCIAAOyzAQDsswEAuxEAAAAAAAAjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//w8AAAAA 12 | AAAAAAD//w8AAAAAAAAAAAD//w8AAAAAAAAAAAAAAAAAAAAAAF0AAAAAAFgDAAAAAAAAWAMAAFgD 13 | AAAAAAAAWAMAAAAAAABYAwAAAAAAAFgDAAAAAAAAWAMAAEQAAAAAAAAAAAAAAJwDAAAAAAAAnAMA 14 | AAAAAACcAwAAAAAAAJwDAACAAAAAHAQAAAwAAAAoBAAAHAAAAJwDAAAAAAAAbhAAAC4BAABQBAAA 15 | AAAAAFAEAAAWAAAAZgQAAAAAAABmBAAAAAAAAGYEAAAAAAAAZgQAAAAAAABmBAAAAAAAAGYEAAAA 16 | AAAAMxAAAAIAAAA1EAAAAAAAADUQAAAAAAAANRAAAAAAAAA1EAAAAAAAADUQAAAAAAAANRAAACQA 17 | AACcEQAA9AEAAJATAACIAAAAWRAAABUAAAAAAAAAAAAAAAAAAAAAAAAAWAMAAAAAAABmBAAAAAAA 18 | AAAAAAAAAAAAAAAAAAAAAABmBAAAAAAAAGYEAAAAAAAAZgQAAAAAAABmBAAAAAAAAFkQAAAAAAAA 19 | ngQAAAAAAABYAwAAAAAAAFgDAAAAAAAAZgQAAAAAAAAAAAAAAAAAAGYEAAAAAAAAUAQAAAAAAACe 20 | BAAAAAAAAJ4EAAAAAAAAngQAAAAAAABmBAAAFgAAAFgDAAAAAAAAZgQAAAAAAABYAwAAAAAAAGYE 21 | AAAAAAAAMxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAMAAAAAAACcAwAAAAAAAFgDAAAAAAAAWAMA 22 | AAAAAABYAwAAAAAAAFgDAAAAAAAAZgQAAAAAAAAzEAAAAAAAAJ4EAAAqBgAAngQAAAAAAADICgAA 23 | /gAAAOgOAAAzAQAAWAMAAAAAAABYAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 24 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMxAAAAAAAABmBAAAAAAAAEQEAAAMAAAAsGGROqxY 25 | wQGcAwAAAAAAAJwDAAAAAAAAfAQAACIAAAAbEAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 26 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 27 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAT2N0 28 | b2JlciAxNiwgMjAwMQ0NVG86CVByb2dyYW0gYW5kIFBsYW5uaW5nIENvbW1pdHRlZQ0JRmluYW5j 29 | ZSBDb21taXR0ZWUNCUFCQSBCb2FyZCBvZiBHb3Zlcm5vcnMNDUZyb206CVNoZWlsYSBTLiBIb2xs 30 | aXMsIFNlY3Rpb24gQ2hhaXINCUFCQSBTZWN0aW9uIG9mIEVudmlyb25tZW50LCBFbmVyZ3ksIGFu 31 | ZCBSZXNvdXJjZXMNDVJlOglQcm9ncmFtIFBsYW4gZm9yIFNlY3Rpb24gUHVibGljIFNlcnZpY2Ug 32 | UHJvamVjdAkNDQ0NVGhlIFNlY3Rpb24gb2YgRW52aXJvbm1lbnQsIEVuZXJneSwgYW5kIFJlc291 33 | cmNlcyBzZWVrcyBwcm9ncmFtbWF0aWMgYXBwcm92YWwgYW5kIGFwcHJvdmFsIHRvIHNlZWsgb3V0 34 | c2lkZSBmdW5kcyB0byBkZXZlbG9wIGEgcHVibGljIHNlcnZpY2UgcHJvamVjdCBhbmQgbWF0ZXJp 35 | YWxzLiAgVGhpcyBwcm9ncmFtIHdpbGwgYmUgZnVuZGVkIHNvbGVseSB3aXRoIG91dHNpZGUgZnVu 36 | ZHMsIGFuZCB3aWxsIG5vdCByZXF1aXJlIEFCQSBHZW5lcmFsIFJldmVudWUgZnVuZGluZy4gVGhl 37 | IFNlY3Rpb24gaXMgd2lsbGluZyB0byBjb21taXQgaW4ta2luZCBzZXJ2aWNlcyB0byBjYXJyeSBv 38 | dXQgdGhlIHByb2dyYW0sIGlmIGl0IGlzIGFjY2VwdGVkIGFuZCBmdW5kZWQgYnkgdGhlIENvbW1p 39 | c3Npb24gZm9yIEVudmlyb25tZW50YWwgQ29vcGVyYXRpb24uIA0NRnVuZGluZyBTb3VyY2UNVGhl 40 | IENvbW1pc3Npb24gZm9yIEVudmlyb25tZW50YWwgQ29vcGVyYXRpb24gKENFQykgaGFzIG1hZGUg 41 | YSBncmFudCBhdmFpbGFibGUgZm9yIHVwIHRvICQyNSwwMDAgdG8gZGV2ZWxvcCBhIHB1YmxpYyBz 42 | ZXJ2aWNlIHByb2plY3QgYW5kIG1hdGVyaWFscy4gIFRoZSBzdWJqZWN0IG1hdHRlciBvZiB0aGUg 43 | Z3JhbnQgYXBwbGljYXRpb24gd2lsbCBiZSBkZXRlcm1pbmVkIGJhc2VkIHVwb24gdGhlIENFQ5Jz 44 | IGNhbGwgZm9yIHByb3Bvc2FscyByZWxlYXNlZCBpbiBsYXRlIERlY2VtYmVyIDIwMDEgb3IgZWFy 45 | bHkgSmFudWFyeSAyMDAyLg0NUHJvZ3JhbSBPdmVydmlldw1UaGUgQ29tbWlzc2lvbiBmb3IgRW52 46 | aXJvbm1lbnRhbCBDb29wZXJhdGlvbiAoQ0VDKSBpcyBhbiBpbnRlcm5hdGlvbmFsIG9yZ2FuaXph 47 | dGlvbiBjcmVhdGVkIGJ5IENhbmFkYSwgTWV4aWNvIGFuZCB0aGUgVW5pdGVkIFN0YXRlcyB1bmRl 48 | ciB0aGUgTm9ydGggQW1lcmljYW4gQWdyZWVtZW50IG9uIEVudmlyb25tZW50YWwgQ29vcGVyYXRp 49 | b24gKE5BQUVDKS4gVGhlIENFQyB3YXMgZXN0YWJsaXNoZWQgdG8gYWRkcmVzcyByZWdpb25hbCBl 50 | bnZpcm9ubWVudGFsIGNvbmNlcm5zLCBoZWxwIHByZXZlbnQgcG90ZW50aWFsIHRyYWRlIGFuZCBl 51 | bnZpcm9ubWVudGFsIGNvbmZsaWN0cywgYW5kIHRvIHByb21vdGUgdGhlIGVmZmVjdGl2ZSBlbmZv 52 | cmNlbWVudCBvZiBlbnZpcm9ubWVudGFsIGxhdy4gVGhlIEFncmVlbWVudCBjb21wbGVtZW50cyB0 53 | aGUgZW52aXJvbm1lbnRhbCBwcm92aXNpb25zIG9mIHRoZSBOb3J0aCBBbWVyaWNhbiBGcmVlIFRy 54 | YWRlIEFncmVlbWVudCAoTkFGVEEpLiBUaGUgQ0VDIGNyZWF0ZWQgdGhlIE5vcnRoIEFtZXJpY2Fu 55 | IEZ1bmQgZm9yIEVudmlyb25tZW50YWwgQ29vcGVyYXRpb24gKE5BRkVDKSBpbiAxOTk1IGFzIGEg 56 | bWVhbnMgdG8gZnVuZCBjb21tdW5pdHktYmFzZWQgcHJvamVjdHMgaW4gQ2FuYWRhLCBNZXhpY28g 57 | YW5kIHRoZSBVbml0ZWQgU3RhdGVzIHRoYXQgcHJvbW90ZSB0aGUgZ29hbHMgYW5kIG9iamVjdGl2 58 | ZXMgb2YgdGhlIENFQy4NDE5BRkVDIHN1cHBvcnRzIHByb2plY3RzIHRoYXQ6DWFyZSBjb21tdW5p 59 | dHktYmFzZWQgKGludm9sdmUgYSBjbGVhcmx5IGRlZmluZWQgY29tbXVuaXR5IG9mIHN0YWtlaG9s 60 | ZGVycyB3aG8gYWN0aXZlbHkgcGFydGljaXBhdGUgaW4gdGhlIGRlc2lnbiBhbmQgaW1wbGVtZW50 61 | YXRpb24gb2YgdGhlIHByb2plY3QpOyANcmVzcG9uZCB0byBhIHNwZWNpZmljIGlzc3VlIG9yIHBy 62 | b2JsZW0gYW5kIGxlYWQgdG8gY29uY3JldGUgcmVzdWx0czsgDXJlZmxlY3QgY29vcGVyYXRpdmUg 63 | YW5kIGVxdWl0YWJsZSBwYXJ0bmVyc2hpcHMgYmV0d2VlbiBvciBhbW9uZyBvcmdhbml6YXRpb25z 64 | IGZyb20gZGlmZmVyZW50IHNlY3RvcnMgYW5kL29yIGNvdW50cmllcyB3aXRoaW4gTm9ydGggQW1l 65 | cmljYTsgDW1lZXQgdGhlIG9iamVjdGl2ZXMgb2YgdGhlIENFQyAoYnkgY29tcGxlbWVudGluZyB0 66 | aGUgY3VycmVudCBDRUMgcHJvZ3JhbSk7IA1zdHJlbmd0aGVuIGFuZCBidWlsZCB0aGUgY2FwYWNp 67 | dGllcyBvZiBsb2NhbCBwZW9wbGUsIG9yZ2FuaXphdGlvbnMgYW5kIGluc3RpdHV0aW9uczsgDWVt 68 | cGhhc2l6ZSBzdXN0YWluYWJpbGl0eTsgbGluayBlbnZpcm9ubWVudGFsLCBzb2NpYWwgYW5kIGVj 69 | b25vbWljIGlzc3VlczsgYW5kIA1sZXZlcmFnZSBhZGRpdGlvbmFsIHN1cHBvcnQsIGJ1dCBhcmUg 70 | dW5saWtlbHkgdG8gb2J0YWluIGZ1bGwgZnVuZGluZyBmcm9tIG90aGVyIHNvdXJjZXMuDQ1BbiBl 71 | eGFtcGxlIG9mIGEgcG9zc2libGUgU2VjdGlvbiBwcm9qZWN0IHdvdWxkIGJlIHRvIGRldmVsb3Ag 72 | ZWR1Y2F0aW9uYWwgaW5zdHJ1Y3Rpb24gaW4gc2Nob29sIG1hdGVyaWFscyBzdWNoIGFzIHRoZSBT 73 | ZWN0aW9uJ3MgTWFyaW5lIFJlc291cmNlcyBDb21taXR0ZWUgbWF0ZXJpYWxzIGF0dGFjaGVkLiAg 74 | QWx0aG91Z2gsIHRoZXNlIG1hdGVyaWFscyB3ZXJlIG5vdCBkZXZlbG9wZWQgd2l0aCBDRUMgZ3Jh 75 | bnQgZnVuZGluZyBpbiBtaW5kLCBpdCB3b3VsZCBiZSB2ZXJ5IGVhc3kgdG8gYWRhcHQgdGhlIG1h 76 | dGVyaWFscyB0byBmb2N1cyBvbiB0aGUgQ0VDIGdvYWxzIG9mIGJ1aWxkaW5nIGNhcGFjaXR5IGlu 77 | IGNvbW11bml0aWVzLCB3aGlsZSBzdGlsbCBtZWV0aW5nIHRoZSBTZWN0aW9uIGFuZCBBQkEncyBn 78 | b2Fscy4NDUNFQyBncmFudCBtb25leSwgaWYgYXdhcmRlZCB0byB0aGUgU2VjdGlvbiwgY291bGQg 79 | YmUgdXNlZCBmb3IgZGV2ZWxvcG1lbnQsIHB1Ymxpc2hpbmcsIHZpZGVvIHN0cmVhbWluZywgYW5k 80 | L29yIGZvciBkaXN0cmlidXRpb24gb2YgdGhlc2UgcGFydGljdWxhciBtYXRlcmlhbHMgaW4gdGhl 81 | IEd1bGYgQ29hc3QgYXJlYSwgd2l0aCB0aGUgcG9zc2liaWxpdHkgb2YgYWRhcHRhdGlvbiBvZiB0 82 | aGUgbWF0ZXJpYWxzIHRvIGFkZHJlc3MgaXNzdWVzIGluIE1leGljbyBhbmQgQ2FuYWRhLiAgVGhp 83 | cyB0eXBlIG9mIHByb2plY3QgaXMgdmVyeSBzaW1pbGFyIHRvIHByb2plY3RzIHRoYXQgaGF2ZSBy 84 | ZWNlbnRseSBvYnRhaW5lZCBDRUMgZ3JhbnQgZnVuZGluZy4NDVRoZSBwcm9wb3NlZCBwcm9ncmFt 85 | IHdpbGwgbWVldCB0aGUgZm9sbG93aW5nIEFCQSBhbmQgU2VjdGlvbiBvZiBFbnZpcm9ubWVudCwg 86 | RW5lcmd5LCBhbmQgUmVzb3VyY2VzIGdvYWxzOg0NQUJBIEdvYWwgSVY6IFRvIGluY3JlYXNlIHB1 87 | YmxpYyB1bmRlcnN0YW5kaW5nIG9mIGFuZCByZXNwZWN0IGZvciB0aGUgbGF3LCB0aGUgbGVnYWwg 88 | cHJvY2VzcywgYW5kIHRoZSByb2xlIG9mIHRoZSBsZWdhbCBwcm9mZXNzaW9uOw1BQkEgR29hbCBJ 89 | WDogVG8gcHJvbW90ZSBmdWxsIGFuZCBlcXVhbCBwYXJ0aWNpcGF0aW9uIGluIHRoZSBsZWdhbCBw 90 | cm9mZXNzaW9uIGJ5IG1pbm9yaXRpZXMsIHdvbWVuIGFuZCBwZXJzb25zIHdpdGggZGlzYWJpbGl0 91 | aWVzLg1BQkEgR29hbCBYOiBUbyBwcmVzZXJ2ZSBhbmQgZW5oYW5jZSB0aGUgaWRlYWxzIG9mIHRo 92 | ZSBsZWdhbCBwcm9mZXNzaW9uIGFzIGEgY29tbW9uIGNhbGxpbmcgYW5kIGl0cyBkZWRpY2F0aW9u 93 | IHRvIHB1YmxpYyBzZXJ2aWNlLg0NVGhlIFNlY3Rpb24gb2YgRW52aXJvbm1lbnQsIEVuZXJneSwg 94 | YW5kIFJlc291cmNlcyB3aWxsIGRldmVsb3AgYW5kIGltcGxlbWVudCB0aGUgcHJvcG9zZWQgcHJv 95 | amVjdC4gVGhlIE5BRkVDIHN0YWZmIGFuZCBTZWxlY3Rpb24gQ29tbWl0dGVlIHJldmlldyBwcm9w 96 | b3NhbHMuIFRoZSBTZWxlY3Rpb24gQ29tbWl0dGVlLCB3aGljaCBjb25zaXN0cyBvZiB0d28gcmVw 97 | cmVzZW50YXRpdmVzIGZyb20gZWFjaCBjb3VudHJ5LCBtYWtlcyB0aGUgZmluYWwgZGVjaXNpb24g 98 | dG8gYXBwcm92ZSBvciBkZWNsaW5lIGEgcHJvcG9zYWwuIFRoZSBDRUMgd2lsbCBwcm92aWRlIHRo 99 | ZSBmdW5kaW5nIGZvciBkZXZlbG9wbWVudCBhbmQgaW1wbGVtZW50YXRpb24gb2YgdGhlIHByb2pl 100 | Y3QgaWYgYXBwcm92ZWQuIA0NSWYgdGhlIGdyYW50IGlzIGF3YXJkZWQgdGhlIHByb2dyYW0gd2ls 101 | bCBiZSBkZXZlbG9wZWQgYW5kIHN0YWZmZWQgYnkgU2VjdGlvbiB2b2x1bnRlZXIgbWVtYmVycyBh 102 | cyBjb29yZGluYXRlZCBieSB0aGUgU2VjdGlvbpJzIFB1YmxpYyBTZXJ2aWNlIFRhc2sgRm9yY2Ug 103 | d2l0aCBhc3Npc3RhbmNlIGZyb20gU2VjdGlvbiBzdGFmZi4NDVRoYW5rIHlvdSBmb3IgeW91ciBj 104 | b25zaWRlcmF0aW9uLg0NREJCL2RqDWF0dGFjaG1lbnQNDQxjYzoJU2VjdGlvbiBDb3VuY2lsDQlT 105 | YXVsIEEuIFdvbGZlLCBCb2FyZCBMaWFpc29uDQlEYW5hIEIuIEJyb3duLCBDaGFpciwgUHVibGlj 106 | IFNlcnZpY2UgVGFzayBGb3JjZQ0JVGVycnkgTC4gS3JhbWVyDQlDYW5keSBMLiBTaW1vbnMNCUgu 107 | IE1hcmlhIEVucmlnaHQNCURhbmEgSm9udXNhaXRpcwwNUGFnZSATIFBBR0UgFDIVDU9jdG9iZXIg 108 | MTYsIDIwMDENDQ0NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 109 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 110 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 111 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 112 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 113 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 114 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 115 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 116 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 117 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAnQYAAKwGAADe 118 | BwAA7wcAAMELAADCCwAAkg0AAKAQAAAcFAAAHRQAANoUAAD9FAAA/hQAABAVAAARFQAAuhUAALsV 119 | AADAFQAAwRUAAMcVAADIFQAAyRUAAMoVAADbFQAA3hUAAN8VAAD57/nv+ef53fnVAPnVANXQ1cm8 120 | s7yovMkA1QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 121 | 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 166 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 167 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 168 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 169 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 170 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 171 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASABoACgABAFsADwACAAAAAAAA 172 | ADAAAEDx/wIAMAAAAAYATgBvAHIAbQBhAGwAAAACAAAAEABDShYAT0oDAFFKAwBtSAkESAABQAEA 173 | AgBIAAAACQBIAGUAYQBkAGkAbgBnACAAMQAAABMAAQADJAMGJAFAJgANxgUAAQAAAQAPADYIgUNK 174 | FABPSgAAUUoAAAAAAAAAAAAAAAAAAAAAAAAAPABBQPL/oQA8AAAAFgBEAGUAZgBhAHUAbAB0ACAA 175 | UABhAHIAYQBnAHIAYQBwAGgAIABGAG8AbgB0AAAAAAAAAAAAAAAAADYAUEABAPIANgAAAAsAQgBv 176 | AGQAeQAgAFQAZQB4AHQAIAAyAAAADQAPAAMkAw+E0AIRhNACAAAAPAD+TwEAAgA8AAAAAgBIADIA 177 | AAAQABAABiQBE6RkABSkZABAJgIWADUIgUNKJABPSgAAUUoAAGgIAG5ICQQoAFVAogARASgAAAAJ 178 | AEgAeQBwAGUAcgBsAGkAbgBrAAAABgA+KgFCKgJIAENAAQAiAUgAAAAQAEIAbwBkAHkAIABUAGUA 179 | eAB0ACAASQBuAGQAZQBuAHQAAAANABIAAyQDD4TQAhGE0AIACABPSgQAUUoEAFAAUkABADIBUAAA 180 | ABIAQgBvAGQAeQAgAFQAZQB4AHQAIABJAG4AZABlAG4AdAAgADIAAAARABMAAyQDD4SgBQ3GBQAB 181 | AAABAAgAT0oEAFFKBAAqAEJAAQBCASoAAAAJAEIAbwBkAHkAIABUAGUAeAB0AAAABQAUAAMkAwAA 182 | AEAAU0ABAFIBQAAAABIAQgBvAGQAeQAgAFQAZQB4AHQAIABJAG4AZABlAG4AdAAgADMAAAAJABUA 183 | AyQDEYTQAgAAAEIAUUABAGIBQgAAAAsAQgBvAGQAeQAgAFQAZQB4AHQAIAAzAAAADQAWAAMkAw3G 184 | BQABAAABAAwAQ0oYAE9KAABRSgAALAAfQAEAcgEsAAAABgBIAGUAYQBkAGUAcgAAAA0AFwANxggA 185 | AuAQwCEBAgAAACwAIEABAIIBLAAAAAYARgBvAG8AdABlAHIAAAANABgADcYIAALgEMAhAQIAAAAm 186 | AClAogCRASYAAAALAFAAYQBnAGUAIABOAHUAbQBiAGUAcgAAAAAAAAAAAMEGAAASEQAAuhEAAN8R 187 | AAAIAAAiAAANAP////8IADAiAAANAP////8IAFoiAAANAP////8IAIciAAANAP////8AAAAAAAAA 188 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAA 189 | IgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAi 190 | AAAAJQAAAAAEAADfFQAADAAAAAAEAADfCgAAHBQAAN8VAAANAAAADwAAABAAAAAABAAA3xUAAA4A 191 | AAAFAAAADAAAAA4AAAAlAAAAEyE0/5WAAAAAALsRAADdEQAA4BEAAAcABwACAAAAAAAFEQAADxEA 192 | ALsRAADdEQAA4BEAAAcAGgAHAAcAAgD//xQAAAAEAGEAdwBxAGQAOwBDADoAXABXAEkATgBEAE8A 193 | VwBTAFwAVABFAE0AUABcAEEAdQB0AG8AUgBlAGMAbwB2AGUAcgB5ACAAcwBhAHYAZQAgAG8AZgAg 194 | ADIAMAAwADIAYwBlAGMAZwByAGEAbgB0AEEAQgBBAGEAcABwAC4AYQBzAGQABABhAHcAcQBkAFgA 195 | XABcAFMAVABKAEEATQBFAFMATgBXAFwAVgBPAEwAMQBcAEcAUgBPAFUAUABTAFwAQQBXAFEARABc 196 | AEQAQQBUAEEAXABXAFAARABBAFQAQQBcAEUAbgBmAG8AcgBjAGUAbQBlAG4AdAAgAFMAZQByAHYA 197 | aQBjAGUAcwBcAEEAQgBBAFwAMgAwADAAMgBjAGUAYwBnAHIAYQBuAHQAQQBCAEEAYQBwAHAALgBk 198 | AG8AYwAEAGEAdwBxAGQAWABcAFwAUwBUAEoAQQBNAEUAUwBOAFcAXABWAE8ATAAxAFwARwBSAE8A 199 | VQBQAFMAXABBAFcAUQBEAFwARABBAFQAQQBcAFcAUABEAEEAVABBAFwARQBuAGYAbwByAGMAZQBt 200 | AGUAbgB0ACAAUwBlAHIAdgBpAGMAZQBzAFwAQQBCAEEAXAAyADAAMAAyAGMAZQBjAGcAcgBhAG4A 201 | dABBAEIAQQBhAHAAcAAuAGQAbwBjAA8ARABhAG4AYQAgAEoAbwBuAHUAcwBhAGkAdABpAHMASgBc 202 | AFwANwA1ADAALQBGAFAAUwAyAFwAZABhAHQAYQBcAFUAUwBFAFIAUwBcAEwASQBCAE4AQQBUAFIA 203 | XABDAE8AVQBOAEMASQBMAFwAQgBvAGEAcgBkACAAbwBmACAARwBvAHYAXABHAHIAYQBuAHQAIABS 204 | AGUAcQB1AGUAcwB0ACAAMQAwAC0AMAAxAC4AZABvAGMADwBEAGEAbgBhACAASgBvAG4AdQBzAGEA 205 | aQB0AGkAcwA0AEMAOgBcAFQARQBNAFAAXABBAHUAdABvAFIAZQBjAG8AdgBlAHIAeQAgAHMAYQB2 206 | AGUAIABvAGYAIABHAHIAYQBuAHQAIABSAGUAcQB1AGUAcwB0ACAAMQAwAC0AMAAxAC4AYQBzAGQA 207 | DwBEAGEAbgBhACAASgBvAG4AdQBzAGEAaQB0AGkAcwA0AEMAOgBcAFQARQBNAFAAXABBAHUAdABv 208 | AFIAZQBjAG8AdgBlAHIAeQAgAHMAYQB2AGUAIABvAGYAIABHAHIAYQBuAHQAIABSAGUAcQB1AGUA 209 | cwB0ACAAMQAwAC0AMAAxAC4AYQBzAGQADwBEAGEAbgBhACAASgBvAG4AdQBzAGEAaQB0AGkAcwA0 210 | AEMAOgBcAFQARQBNAFAAXABBAHUAdABvAFIAZQBjAG8AdgBlAHIAeQAgAHMAYQB2AGUAIABvAGYA 211 | IABHAHIAYQBuAHQAIABSAGUAcQB1AGUAcwB0ACAAMQAwAC0AMAAxAC4AYQBzAGQADwBEAGEAbgBh 212 | ACAASgBvAG4AdQBzAGEAaQB0AGkAcwA0AEMAOgBcAFQARQBNAFAAXABBAHUAdABvAFIAZQBjAG8A 213 | dgBlAHIAeQAgAHMAYQB2AGUAIABvAGYAIABHAHIAYQBuAHQAIABSAGUAcQB1AGUAcwB0ACAAMQAw 214 | AC0AMAAxAC4AYQBzAGQADwBEAGEAbgBhACAASgBvAG4AdQBzAGEAaQB0AGkAcwA3AEMAOgBcAFQA 215 | RQBNAFAAXABBAHUAdABvAFIAZQBjAG8AdgBlAHIAeQAgAHMAYQB2AGUAIABvAGYAIABHAHIAYQBu 216 | AHQAIABSAGUAcQB1AGUAcwB0ACAAMQAwAC0AMQA2AC0AMAAxAC4AYQBzAGQADwBEAGEAbgBhACAA 217 | SgBvAG4AdQBzAGEAaQB0AGkAcwBNAFwAXAA3ADUAMAAtAEYAUABTADIAXABkAGEAdABhAFwAVQBT 218 | AEUAUgBTAFwATABJAEIATgBBAFQAUgBcAEMATwBVAE4AQwBJAEwAXABCAG8AYQByAGQAIABvAGYA 219 | IABHAG8AdgBcAEcAcgBhAG4AdAAgAFIAZQBxAHUAZQBzAHQAIAAxADAALQAxADYALQAwADEALgBk 220 | AG8AYwAJAP7//////////w//D/8P/w//D/8P/w//D/8PAQACAAAAAAAAAP8P/w//D/8P/w//D/8P 221 | /w//DwAAnQxhBQEACQT/DwAAAAAAAAAAAAAAAAAAAAABABZS7AlU//63/w//D/8P/w//D/8P/w// 222 | D/8PAQBNPacPAQAJBP8PAAAAAAAAAAAAAAAAAAAAAAEA+xCkIwEACQT/DwAAAAAAAAAAAAAAAAAA 223 | AAABAPI+fVoBAAkE/w8AAAAAAAAAAAAAAAAAAAAAAQAxFXRuBq/Efv8P/w//D/8P/w//D/8P/w// 224 | DwEADk0NeQEACQT/DwAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 225 | AAABACoAAQAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAgAAA+E0AIRhJj+AgAAAC4AAQAAAAAAAQAA 226 | AAAAAAAAAAAAAAAAAAAAAAgAAA+EoAURhJj+AgABAC4AAQAAAAAAAQAAAAAAAAAAAAAAAAAAAAAA 227 | AAgAAA+EcAgRhJj+AgACAC4AAQAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAgAAA+EQAsRhJj+AgAD 228 | AC4AAQAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAgAAA+EEA4RhJj+AgAEAC4AAQAAAAAAAQAAAAAA 229 | AAAAAAAAAAAAAAAAAAgAAA+E4BARhJj+AgAFAC4AAQAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAgA 230 | AA+EsBMRhJj+AgAGAC4AAQAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAgAAA+EgBYRhJj+AgAHAC4A 231 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAABcAAAAAAAAAAAAAAAAAAAAAAAAACxAA 232 | AA+EaAERhJj+FcYFAAFoAQZPSgEAUUoBAG8oAAEAt/AEAAAABAABAAAAAAAAAAAAAAAAAAAAAAAG 233 | EAAAD4SgBRGEMP0VxgUAAaAFBjYIAG8oAAIAAAAuAAEAAAAXAAAAAAAAAAAAAAAAAAAAAAAAAAsQ 234 | AAAPhGgBEYSY/hXGBQABaAEGT0oBAFFKAQBvKAABALfwAQAAABcAAAAAAAAAAAAAAAAAAAAAAAAA 235 | CxAAAA+EaAERhJj+FcYFAAFoAQZPSgEAUUoBAG8oAAEAt/ABAAAAFwAAAAAAAAAAAAAAAAAAAAAA 236 | AAALEAAAD4RoARGEmP4VxgUAAWgBBk9KAQBRSgEAbygAAQC38AMAAAAEAAEAAAAAAAAAAAAAAAAA 237 | AAAAAAMQAAAPhKAFEYQw/RXGBQABoAUGbygAAgAAAC4AAQAAABcAAAAAAAAAAAAAAAAAAAAAAAAA 238 | CxAAAA+EaAERhJj+FcYFAAFoAQZPSgEAUUoBAG8oAAEAt/AJAAAAAgAAAAAAAABgBAwFCAAAAP7/ 239 | //8AAAAApAQMBQEAAAAWUuwJAAAAAAAAAAAAAAAAMRV0bgAAAAAAAAAAAAAAAJ0MYQUAAAAAAAAA 240 | AAAAAAD7EKQjAAAAAAAAAAAAAAAATT2nDwAAAAAAAAAAAAAAAA5NDXkAAAAAAAAAAAAAAADyPn1a 241 | AAAAAAAAAAAAAAAA/////wEAAAAQAAAAAQAAABEAAAABAAAAEgAAAAEAAAATAAAAAQAAABQAAAAB 242 | AAAAFQAAAAEAAAAWAAAAAQAAABcAAAD/////sAQMBSAAAAAAAAAAF0AAAAAAAAAAAAAAAAAAAGgB 243 | AAALCAAAD4TQAhGEmP5PSgEAUUoBAG8oAAEAt/D///////////////////////////////////// 244 | //8JAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0ADgAEAAAAAAAAAAACog/8AAQAAAAAAAAAAAAAAAAAA 245 | AAAAAAACEAAAAAAAAADfEQAAgAAACABAAAAFAAAARxaQAQAAAgIGAwUEBQIDBIcCAAAAAAAAAAAA 246 | AAAAAACfAAAAAAAAAFQAaQBtAGUAcwAgAE4AZQB3ACAAUgBvAG0AYQBuAAAANRaQAQIABQUBAgEH 247 | BgIFBwAAAAAAAAAQAAAAAAAAAAAAAACAAAAAAFMAeQBtAGIAbwBsAAAAMyaQAQAAAgsGBAICAgIC 248 | BIcCAAAAAAAAAAAAAAAAAACfAAAAAAAAAEEAcgBpAGEAbAAAADcxkAEAAAAAAAAAAAAAAAADAAAA 249 | AAAAAAAAAAAAAAAAAQAAAAAAAABDAG8AdQByAGkAZQByAAAAPzWQAQAAAgcDCQICBQIEBIcCAAAA 250 | AAAAAAAAAAAAAACfAAAAAAAAAEMAbwB1AHIAaQBlAHIAIABOAGUAdwAAACIABADxCIgYAADQAgAA 251 | aAEAAAAAOZpapmqaWqZimlqmAwAPAAAAkAIAAJ4OAAABAAcAAAAEAAMQHwAAAAAAAAAAAAAAAQAB 252 | AAAAAQAAAAAAAAAhAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 253 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 254 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 255 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 256 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 257 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAClBsAHtAC0AIAAMjAA 258 | ABAAGQBkAAAAGQAAAPMRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 259 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAA//8SAAAAAAAAABgAQQBNAEUAUgBJAEMAQQBO 260 | ACAAQgBBAFIAIABBAFMAUwBPAEMASQBBAFQASQBPAE4AAAAAAAAACABDAG8AbgBuAG8AbABsAEEA 261 | DwBEAGEAbgBhACAASgBvAG4AdQBzAGEAaQB0AGkAcwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 262 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 263 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 264 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 265 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 266 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 267 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 268 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 269 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 270 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/v8AAAQAAgAAAAAAAAAAAAAAAAAAAAAAAQAAAOCF 271 | n/L5T2gQq5EIACsns9kwAAAAiAEAABEAAAABAAAAkAAAAAIAAACYAAAAAwAAALwAAAAEAAAAyAAA 272 | AAUAAADcAAAABwAAAOgAAAAIAAAA+AAAAAkAAAAQAQAAEgAAABwBAAAKAAAAOAEAAAsAAABEAQAA 273 | DAAAAFABAAANAAAAXAEAAA4AAABoAQAADwAAAHABAAAQAAAAeAEAABMAAACAAQAAAgAAAOQEAAAe 274 | AAAAGQAAAEFNRVJJQ0FOIEJBUiBBU1NPQ0lBVElPTgAAMAAeAAAAAQAAAABNRVIeAAAACQAAAENv 275 | bm5vbGxBAEJBUh4AAAABAAAAAG9ubh4AAAAHAAAATm9ybWFsAEEeAAAAEAAAAERhbmEgSm9udXNh 276 | aXRpcwAeAAAAAgAAADMAbmEeAAAAEwAAAE1pY3Jvc29mdCBXb3JkIDguMABBQAAAAAAacRgCAAAA 277 | QAAAAAD8fxerWMEBQAAAAADeRuylWMEBQAAAAAAsmjWsWMEBAwAAAAEAAAADAAAAkAIAAAMAAACe 278 | DgAAAwAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 279 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 280 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 281 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 282 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 283 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 284 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 285 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 286 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 287 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 288 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 289 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 290 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 291 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 292 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 293 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 294 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 295 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 296 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 297 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 298 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 299 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 300 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 301 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 302 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 303 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 304 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 305 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 306 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 307 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 308 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 309 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 310 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 311 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 312 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 313 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 314 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 315 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 316 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 317 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 318 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 319 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 320 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 321 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 322 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 323 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 324 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 325 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 326 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 327 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 328 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 329 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 330 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 331 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 332 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 333 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 334 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 335 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 336 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 337 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 338 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 339 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 340 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 341 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 342 | AAAAAAAAAAAAAAAAAAAAAAAAAP7/AAAEAAIAAAAAAAAAAAAAAAAAAAAAAAIAAAAC1c3VnC4bEJOX 343 | CAArLPmuRAAAAAXVzdWcLhsQk5cIACss+a5cAQAAGAEAAAwAAAABAAAAaAAAAA8AAABwAAAABQAA 344 | AJQAAAAGAAAAnAAAABEAAACkAAAAFwAAAKwAAAALAAAAtAAAABAAAAC8AAAAEwAAAMQAAAAWAAAA 345 | zAAAAA0AAADUAAAADAAAAPkAAAACAAAA5AQAAB4AAAAZAAAAQUJBIC0gTUlTLCBUZWNoIFNlcnZp 346 | Y2VzAABDAAMAAAAfAAAAAwAAAAcAAAADAAAA8xEAAAMAAADoEAgACwAAAAAAAAALAAAAAAAAAAsA 347 | AAAAAAAACwAAAAAAAAAeEAAAAQAAABkAAABBTUVSSUNBTiBCQVIgQVNTT0NJQVRJT04ADBAAAAIA 348 | AAAeAAAABgAAAFRpdGxlAAMAAAABAAAAAJgAAAADAAAAAAAAACAAAAABAAAANgAAAAIAAAA+AAAA 349 | AQAAAAIAAAAKAAAAX1BJRF9HVUlEAAIAAADkBAAAQQAAAE4AAAB7AEUAOQBEADUAMgA3ADcAMQAt 350 | AEQAMQAxAEEALQAxADEARAA0AC0AOQA0ADQANQAtADAAMABBADAAQwA5ADkAMwA3AEUARgA2AH0A 351 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 352 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 353 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 354 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 355 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 356 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 357 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 358 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 359 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 360 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 361 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 362 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 363 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 364 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 365 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 366 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 367 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 368 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 369 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 370 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 371 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 372 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 373 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 374 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 375 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 376 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 377 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 378 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 379 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 380 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 381 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 382 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 383 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 384 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 385 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 386 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 387 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 388 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 389 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 390 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 391 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 392 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 393 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 394 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 395 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 396 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 397 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 398 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 399 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 400 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 401 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 402 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 403 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 404 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 405 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 406 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 407 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 408 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 409 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 410 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 411 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 412 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 413 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 414 | AAAAAAAAAAAAAAABAAAAAgAAAAMAAAAEAAAABQAAAAYAAAAHAAAACAAAAAkAAAAKAAAACwAAAAwA 415 | AAANAAAADgAAAA8AAAAQAAAAEQAAAP7///8TAAAAFAAAABUAAAAWAAAAFwAAABgAAAAZAAAAGgAA 416 | ABsAAAAcAAAA/v///x4AAAAfAAAAIAAAACEAAAAiAAAAIwAAACQAAAD+////JgAAACcAAAAoAAAA 417 | KQAAACoAAAArAAAALAAAAP7////9////LwAAAP7////+/////v////////////////////////// 418 | //////////////////////////////////////////////////////////////////////////// 419 | //////////////////////////////////////////////////////////////////////////// 420 | //////////////////////////////////////////////////////////////////////////// 421 | //////////////////////////////////////////////////////////////////////////// 422 | //////////////////////////////////////////////////////////////////////////// 423 | /////////////1IAbwBvAHQAIABFAG4AdAByAHkAAAAAACwAAABOAAAALAAAADIAAAAcAAAAGgAA 424 | ACwAAAAyAAAATgAAADIAAAAWAAUB//////////8DAAAABgkCAAAAAADAAAAAAAAARgAAAACOXhlD 425 | pVjBAdA3pTqsWMEBMQAAAIAAAAAcAAAAMQBUAGEAYgBsAGUAAAAAACwAAAAyAAAAHAAAACwAAAAc 426 | AAAAGwAAADIAAAAhAAAAMgAAADIAAAAcAAAAJwAAAA4AAgH/////BQAAAP////8bAAAAEAAAAAAA 427 | AAAAAAAAEgAAAAwAAAABAAAAEgAAAAwAAAASAAAAGBQAABAAAABXAG8AcgBkAEQAbwBjAHUAbQBl 428 | AG4AdAAAAAAA3xMAAAEAAABVVYdAQZyHQJIEAAB8EwAARwAAAEwAAAAEAAAAGgACAQEAAAD///// 429 | /////9wAAABvAGYAIAB0AGgAZQAgAE4AbwByAHQAaAAgAEEAbQBlAAAAAAC0IgAAbgAgAAUAUwB1 430 | AG0AbQBhAHIAeQBJAG4AZgBvAHIAbQBhAHQAaQBvAG4AAAAgACgATgBBAEYAVABBACkALgAgAFQA 431 | aAAoAAIBAgAAAAQAAAD/////ZQBhAHQAZQBkACAAdABoAGUAAAAyAAAAIQAAABsAAAAcAAAAHQAA 432 | AAAQAAAcAAAABQBEAG8AYwB1AG0AZQBuAHQAUwB1AG0AbQBhAHIAeQBJAG4AZgBvAHIAbQBhAHQA 433 | aQBvAG4AAAAcAAAAOAAAADgAAgH///////////////89AAAAIgAAACwAAAAyAAAALAAAABwAAABI 434 | AAAAMgAAACIAAAAlAAAAABAAAE4AAAABAEMAbwBtAHAATwBiAGoAAAAAAEgAAABIAAAAOAAAAD0A 435 | AABIAAAAIQAAABkAAAAcAAAAPQAAADMAAAAsAAAAEgACAP///////////////xwAAAAsAAAAIQAA 436 | AC0AAAAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABqAAAALAAAABsAAAAQAAAAAAAAAAAAAAASAAAA 437 | DAAAAAEAAAASAAAADAAAAAEAAAAbAAAAEAAAAJIEAADvEwAAVAAAAPABAAAAAAAA//////////// 438 | ////AQAAAFVVh0BBnIdAkgQAAO8TAABGAAAATAAAAAQAAAAAAAAAAAAAAOwTAADIGQAA2AAAAE4A 439 | bwByAHQAaAAgAEEAbQBlAHIAaQBjAGEAbgAgAEYAdQBuAGQAIABmAG8AcgAgAEUAbgB2AGkAcgBv 440 | AAAAAAD///////////////9DAG8AbwBwAGUAcgBhAHQAaQBvAG4AIAAoAE4AQQBGAEUAQwApACAA 441 | aQBuACAAMQABAAAA/v////////////////////////////////////////////////////////// 442 | //////////////////////////////////////////////////////////////////////////// 443 | //////////////////////////////////////////////////////////////////////////// 444 | //////////////////////////////////////////////////////////////////////////// 445 | //////////////////////////////////////////////////////////////////////////// 446 | //////////////////////////////////////////////////////////////////////////// 447 | //////////////////////////////////////////////////////////////////////////// 448 | //////////////////////////////////////////////////////////////////////////// 449 | //////////////////////////////////////////////////////////////////////////// 450 | /////////wEA/v8DCgAA/////wYJAgAAAAAAwAAAAAAAAEYYAAAATWljcm9zb2Z0IFdvcmQgRG9j 451 | dW1lbnQACgAAAE1TV29yZERvYwAQAAAAV29yZC5Eb2N1bWVudC44APQ5snEAAAAAAAAAAAAAAAAA 452 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 453 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 454 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 455 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 456 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 457 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 458 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 459 | AAAAAAAA -------------------------------------------------------------------------------- /benchmark/data/email/enron5.txt: -------------------------------------------------------------------------------- 1 | VGhlIEZJUlNUIGlsbHVzdHJhdGVkIHZlcnNpb24gb2YgS2FtYSBTdXRyYSBpbiBJbnRlcm5ldDxi 2 | cj4NCjxhIGhyZWY9Imh0dHA6Ly93d3cudG9wNTU1Lm5ldC8iPmh0dHA6Ly93d3cudG9wNTU1Lm5l 3 | dC88L2E+PGJyPjxicj4NCg0KDQo8aHI+SWYgeW91IHdpc2ggdG8gdW5zdWJzY3JpYmUgcGxlYXNl 4 | IHNlbmQgYSBsZXR0ZXIgdG8gPGEgaHJlZj0ibWFpbHRvOnVuc3Via3NAdG9wNTU1Lm5ldCI+dW5z 5 | dWJrc0B0b3A1NTUubmV0PC9hPjxicj4NClN1YmplY3QgYW5kIHRleHQgYXJlIHVuaW1wb3J0YW50 6 | Lg0KPGhyPg0K -------------------------------------------------------------------------------- /benchmark/data/email/enron6.txt: -------------------------------------------------------------------------------- 1 | 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 31 | AFgCAgCLAgUAigKnh8kAAAAjANTQBBUAAAsACQABsAQAAAAAASAVANDMUGxlYXNlgHByb3ZpZGWA 32 | Zm9ygGVhY2iAb2aAeW91coBDYWxpZm9ybmlhgGdlbmVyYXRpbmeAdW5pdHM6zMzTBC0AABkAAbAE 33 | BwAsAQBYAgCwBIIsAQBgCZhYAgD//wGwBAIAAACcWAItANPwAwTw4DAMAAAAANwFDADgRW1pc3Np 34 | b25zgGxpbWl0YXRpb25zgGZvcoBlYWNogHVuaXSAeW91gG93boBvcoBjb250cm9sgChpbmNsdWRl 35 | gHVuaXSAbmFtZYBhbmSAbG9jYXRpb24pLtAEHwAAFQATAAF0CMQDBAABAtwFKCPcBSgjAiAfANDM 36 | 8AME8OAwDAAAAADcBQwA4Ehvd4BmYXKAaW50b4BlbWlzc2lvbnOAbGltaXRhdGlvbnOAaXOAZWFj 37 | aIB1bml0gGFzgG9mgDgvMzEvMDAu0AQfAAAVABMAAUwKnAUGAAEC3AUoI9wFKCMCIB8A0NRfEAAA 38 | BgACAAAAAAEQANTxAqgA8eARDAAAAADcBQwA4OARDAAAAAAIBwwA4PEDqADx1F8QAAAGAAMAAAAA 39 | ARAA1MzwAwTw4DAMAAAAANwFDADgRGlkgHlvdYBwdXJjaGFzZYBvcoBhdHRlbXB0gHRvgHB1cmNo 40 | YXNlgGVtaXNzaW9uc4BjcmVkaXRzgGlugHRoZYBwZXJpb2SAZnJvbYBNYXmAMSyAMjAwMNABFQAA 41 | CwAJAAEkDHQHCAABIBUA0HRocm91Z2iAQXVndXN0gDMxLIAyMDAwLtAEFwAADQALAALcBSgj3AUo 42 | IwIgFwDQzPADBPDgMAwAAAAA3AUMAOBEaWSAdGhlgGluY3JlYXNlZIBwcmljZYBvZoBlbWlzc2lv 43 | bnOAY3JlZGl0c4BjYXVzZYB5b3WAdG+Ac2h1dIBkb3dugG9ygHJlZHVjZYDxAp8A8W918QOfAPHx 44 | AqAA8XTxA6AA8fECnwDxcHV08QOfAPHxAqEA8YBmb3LxA6EA8fECnwDxgPEDnwDxYW550AEVAAAL 45 | AAkAAegOOAoLAAEgFQDQdW5pdHM/0AQXAAANAAsAAtwFKCPcBSgjAiAXANDM8AME8OAwDAAAAADc 46 | BQwA4FdoYXSA8QCjAPFp8QGjAPHxAqQA8Xdh8QOkAPFzgHlvdXKAYXZlcmFnZYBtb250aGx5gHBl 47 | coBwb3VuZIBjb3N0gG9mgGVtaXNzaW9uc4BjcmVkaXRz8QKiAPGAKPEDogDx8QKlAPFNYXmAhIBB 48 | dWfxA6UA8fEApgDx8QKlAPFzdPEDpQDx8QGmAPHxAqcA8XVzdIAxOTk5gGFuZNABFQAACwAJAAGs 49 | EfwMDgABIBUA0DIwMDAp8QOnAPE/0AQXAAANAAsAAtwFKCPcBSgjAiAXANDM8AME8OAwDAAAAADc 50 | BQwA4EFueYBhZGRpdGlvbmFsgGluZm9ybWF0aW9ugHlvdYBjYW6AcHJvdmlkZYBvboBlbnZpcm9u 51 | bWVudGFsgGNvc3RzgG9mgHJ1bm5pbmeAdW5pdHMu4BEMAAAAANAgDADg4BEMAEAAANAgDADg4BEM 52 | AEAAANAgDADg4BEMAEAAANAgDADg -------------------------------------------------------------------------------- /src/Base64.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Buffers; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.Intrinsics.Arm; 5 | using System.Runtime.Intrinsics.X86; 6 | 7 | 8 | namespace SimdBase64 9 | { 10 | public static class Base64 11 | { 12 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 13 | public static int MaximalBinaryLengthFromBase64(ReadOnlySpan input) 14 | { 15 | return Scalar.Base64.MaximalBinaryLengthFromBase64Scalar(input); 16 | } 17 | public static byte[] FromBase64String(string s) 18 | { 19 | ReadOnlySpan base64 = s.AsSpan(); 20 | byte[] newBytes = new byte[SimdBase64.Base64.MaximalBinaryLengthFromBase64(base64)]; 21 | int bytesConsumed = 0; 22 | int bytesWritten = 0; 23 | SimdBase64.Base64.DecodeFromBase64(base64, newBytes, out bytesConsumed, out bytesWritten, false); 24 | Array.Resize(ref newBytes, bytesWritten); 25 | return newBytes; 26 | } 27 | 28 | public unsafe static OperationStatus DecodeFromBase64(ReadOnlySpan source, Span dest, out int bytesConsumed, out int bytesWritten, bool isUrl = false) 29 | { 30 | 31 | if (AdvSimd.Arm64.IsSupported && BitConverter.IsLittleEndian) 32 | { 33 | return Arm.Base64.DecodeFromBase64ARM(source, dest, out bytesConsumed, out bytesWritten, isUrl); 34 | } 35 | // To be comleted, this may have to wait for .NET 10. 36 | //if (Vector512.IsHardwareAccelerated && Avx512Vbmi2.IsSupported) 37 | //{ 38 | //} 39 | if (Avx2.IsSupported && Popcnt.IsSupported && Bmi1.IsSupported) 40 | { 41 | return AVX2.Base64.DecodeFromBase64AVX2(source, dest, out bytesConsumed, out bytesWritten, isUrl); 42 | } 43 | if (Ssse3.IsSupported && Popcnt.IsSupported) 44 | { 45 | return SSE.Base64.DecodeFromBase64SSE(source, dest, out bytesConsumed, out bytesWritten, isUrl); 46 | } 47 | 48 | return Scalar.Base64.DecodeFromBase64Scalar(source, dest, out bytesConsumed, out bytesWritten, isUrl); 49 | 50 | } 51 | 52 | public unsafe static OperationStatus DecodeFromBase64(ReadOnlySpan source, Span dest, out int bytesConsumed, out int bytesWritten, bool isUrl = false) 53 | { 54 | 55 | if (AdvSimd.Arm64.IsSupported && BitConverter.IsLittleEndian) 56 | { 57 | return Arm.Base64.DecodeFromBase64ARM(source, dest, out bytesConsumed, out bytesWritten, isUrl); 58 | } 59 | // To be comleted, this may have to wait for .NET 10. 60 | //if (Vector512.IsHardwareAccelerated && Avx512Vbmi.IsSupported) 61 | //{ 62 | // return GetPointerToFirstInvalidByteAvx512(pInputBuffer, inputLength, out Utf16CodeUnitCountAdjustment, out ScalarCodeUnitCountAdjustment); 63 | //} 64 | if (Avx2.IsSupported && Popcnt.IsSupported && Bmi1.IsSupported) 65 | { 66 | return AVX2.Base64.DecodeFromBase64AVX2(source, dest, out bytesConsumed, out bytesWritten, isUrl); 67 | } 68 | if (Ssse3.IsSupported && Popcnt.IsSupported) 69 | { 70 | return SSE.Base64.DecodeFromBase64SSE(source, dest, out bytesConsumed, out bytesWritten, isUrl); 71 | } 72 | 73 | return Scalar.Base64.DecodeFromBase64Scalar(source, dest, out bytesConsumed, out bytesWritten, isUrl); 74 | 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/Base64Scalar.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | using System.Buffers; 5 | using System.Buffers.Binary; 6 | 7 | namespace SimdBase64 8 | { 9 | namespace Scalar 10 | { 11 | public static partial class Base64 12 | { 13 | public enum Endianness 14 | { 15 | LITTLE = 0, 16 | BIG = 1 17 | } 18 | 19 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 20 | public static bool MatchSystem(Endianness e) 21 | { 22 | return e == (BitConverter.IsLittleEndian ? Endianness.LITTLE : Endianness.BIG); 23 | } 24 | 25 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 26 | internal static bool IsAsciiWhiteSpace(byte c) 27 | { 28 | ReadOnlySpan table = new bool[] { 29 | false, false, false, false, false, false, false, false, false, true, true, false, true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}; 30 | return Unsafe.AddByteOffset(ref MemoryMarshal.GetReference(table), (nint)c); 31 | } 32 | 33 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 34 | internal static bool IsAsciiWhiteSpace(char c) 35 | { 36 | if (c > 127) 37 | { 38 | return false; 39 | } 40 | return IsAsciiWhiteSpace((byte)c); 41 | } 42 | 43 | 44 | [Flags] 45 | public enum Base64Options 46 | { 47 | None = 0, // Standard base64 format 48 | Url = 1 // Base64url format 49 | } 50 | 51 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 52 | public static int MaximalBinaryLengthFromBase64Scalar(ReadOnlySpan input) 53 | { 54 | // We follow https://infra.spec.whatwg.org/#forgiving-base64-decode 55 | int length = input.Length; 56 | if (length % 4 <= 1) 57 | { 58 | return length / 4 * 3; 59 | } 60 | // If we have a valid input, then the remainder must be 2 or 3 adding one or two extra bytes. 61 | return length / 4 * 3 + (length % 4) - 1; 62 | } 63 | 64 | public unsafe static OperationStatus DecodeFromBase64Scalar(ReadOnlySpan source, Span dest, out int bytesConsumed, out int bytesWritten, bool isUrl = false) 65 | { 66 | int length = source.Length; 67 | Span buffer = [0, 0, 0, 0]; 68 | 69 | fixed (byte* srcInit = source) 70 | fixed (byte* dstInit = dest) 71 | fixed (byte* bufferPtr = buffer) 72 | { 73 | byte* srcEnd = srcInit + length; 74 | byte* src = srcInit; 75 | byte* dst = dstInit; 76 | 77 | UInt32 x; 78 | uint triple; 79 | int idx; 80 | 81 | while (true) 82 | { 83 | // fastpath 84 | while (src + 4 <= srcEnd && 85 | (x = isUrl ? Base64Url.GetD(src) : Base64Default.GetD(src)) < 0x01FFFFFF) 86 | { 87 | if (MatchSystem(Endianness.BIG)) 88 | { 89 | x = BinaryPrimitives.ReverseEndianness(x); 90 | } 91 | 92 | *(uint*)dst = x;// optimization opportunity: copy 4 bytes 93 | dst += 3; 94 | src += 4; 95 | } 96 | idx = 0; 97 | 98 | // We need at least four characters. 99 | while (idx < 4 && src < srcEnd) 100 | { 101 | char c = (char)*src; 102 | byte code = isUrl ? Tables.GetToBase64UrlValue(c) : Tables.GetToBase64Value(c); 103 | bufferPtr[idx] = code; 104 | 105 | if (code <= 63) 106 | { 107 | idx++; 108 | } 109 | else if (code > 64) 110 | { 111 | bytesConsumed = (int)(src - srcInit); 112 | bytesWritten = (int)(dst - dstInit); 113 | 114 | return OperationStatus.InvalidData;// Found a character that cannot be part of a valid base64 string. 115 | } 116 | else 117 | { 118 | // We have a space or a newline. We ignore it. 119 | } 120 | src++; 121 | } 122 | 123 | // deals with remainder 124 | if (idx != 4) 125 | { 126 | if (idx == 2) // we just copy directly while converting 127 | { 128 | triple = ((uint)bufferPtr[0] << (3 * 6)) + ((uint)bufferPtr[1] << (2 * 6)); // the 2 last byte are shifted 18 and 12 bits respectively 129 | if (MatchSystem(Endianness.BIG)) 130 | { 131 | triple <<= 8; 132 | byte[] byteTriple = BitConverter.GetBytes(triple); 133 | dst[0] = byteTriple[0]; 134 | } 135 | else 136 | { 137 | triple = BinaryPrimitives.ReverseEndianness(triple); 138 | triple >>= 8; 139 | Buffer.MemoryCopy(&triple, dst, 1, 1); 140 | } 141 | dst += 1; 142 | } 143 | 144 | else if (idx == 3) 145 | { 146 | triple = ((uint)bufferPtr[0] << 3 * 6) + 147 | ((uint)bufferPtr[1] << 2 * 6) + 148 | ((uint)bufferPtr[2] << 1 * 6); 149 | if (MatchSystem(Endianness.BIG)) 150 | { 151 | triple <<= 8; 152 | Buffer.MemoryCopy(&triple, dst, 2, 2); 153 | } 154 | else 155 | { 156 | triple = BinaryPrimitives.ReverseEndianness(triple); 157 | triple >>= 8; 158 | Buffer.MemoryCopy(&triple, dst, 2, 2); 159 | } 160 | dst += 2; 161 | } 162 | 163 | else if (idx == 1) 164 | { 165 | bytesConsumed = (int)(src - srcInit); 166 | bytesWritten = (int)(dst - dstInit); 167 | return OperationStatus.NeedMoreData;// The base64 input terminates with a single character, excluding padding. 168 | } 169 | bytesConsumed = (int)(src - srcInit); 170 | bytesWritten = (int)(dst - dstInit); 171 | return OperationStatus.Done; 172 | } 173 | triple = 174 | ((uint)bufferPtr[0] << 3 * 6) + ((uint)bufferPtr[1] << 2 * 6) + 175 | ((uint)bufferPtr[2] << 1 * 6) + ((uint)bufferPtr[3] << 0 * 6); 176 | if (MatchSystem(Endianness.BIG)) 177 | { 178 | triple <<= 8; 179 | Buffer.MemoryCopy(&triple, dst, 3, 3); 180 | } 181 | else 182 | { 183 | triple = BinaryPrimitives.ReverseEndianness(triple); 184 | triple >>= 8; 185 | Buffer.MemoryCopy(&triple, dst, 3, 3); 186 | } 187 | dst += 3; 188 | } 189 | 190 | } 191 | } 192 | 193 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 194 | public static bool IsValidBase64Index(char b) 195 | { 196 | return b < 256; 197 | } 198 | 199 | public unsafe static OperationStatus DecodeFromBase64Scalar(ReadOnlySpan source, Span dest, out int bytesConsumed, out int bytesWritten, bool isUrl = false) 200 | { 201 | int length = source.Length; 202 | Span buffer = [0, 0, 0, 0]; 203 | 204 | fixed (char* srcInit = source) 205 | fixed (byte* dstInit = dest) 206 | fixed (byte* bufferPtr = buffer) 207 | { 208 | char* srcEnd = srcInit + length; 209 | char* src = srcInit; 210 | byte* dst = dstInit; 211 | 212 | UInt32 x; 213 | uint triple; 214 | int idx; 215 | while (true) 216 | { 217 | // fastpath 218 | while (src + 4 <= srcEnd && 219 | (x = isUrl ? Base64Url.GetD(src) : Base64Default.GetD(src)) < 0x01FFFFFF) 220 | { 221 | if (MatchSystem(Endianness.BIG)) 222 | { 223 | x = BinaryPrimitives.ReverseEndianness(x); 224 | } 225 | 226 | *(uint*)dst = x;// optimization opportunity: copy 4 bytes 227 | dst += 3; 228 | src += 4; 229 | } 230 | idx = 0; 231 | 232 | // We need at least four characters. 233 | while (idx < 4 && src < srcEnd) 234 | { 235 | char c = (char)*src; 236 | if (!IsValidBase64Index(c)) // Ensure c is a valid index 237 | { 238 | bytesConsumed = (int)(src - srcInit); 239 | bytesWritten = (int)(dst - dstInit); 240 | 241 | return OperationStatus.InvalidData; 242 | // Process code 243 | } 244 | byte code = isUrl ? Tables.GetToBase64UrlValue(c) : Tables.GetToBase64Value(c); 245 | // byte code = 1; 246 | buffer[idx] = code; 247 | 248 | if (code <= 63) 249 | { 250 | idx++; 251 | } 252 | else if (code > 64) 253 | { 254 | bytesConsumed = (int)(src - srcInit); 255 | bytesWritten = (int)(dst - dstInit); 256 | 257 | return OperationStatus.InvalidData;// Found a character that cannot be part of a valid base64 string. 258 | } 259 | else 260 | { 261 | // We have a space or a newline. We ignore it. 262 | } 263 | src++; 264 | } 265 | 266 | // deals with remainder 267 | if (idx != 4) 268 | { 269 | if (idx == 2) // we just copy directly while converting 270 | { 271 | triple = ((uint)bufferPtr[0] << (3 * 6)) + ((uint)bufferPtr[1] << (2 * 6)); // the 2 last byte are shifted 18 and 12 bits respectively 272 | if (MatchSystem(Endianness.BIG)) 273 | { 274 | triple <<= 8; 275 | byte[] byteTriple = BitConverter.GetBytes(triple); 276 | dst[0] = byteTriple[0]; 277 | } 278 | else 279 | { 280 | triple = BinaryPrimitives.ReverseEndianness(triple); 281 | triple >>= 8; 282 | Buffer.MemoryCopy(&triple, dst, 1, 1); 283 | } 284 | dst += 1; 285 | } 286 | 287 | else if (idx == 3) 288 | { 289 | triple = ((uint)bufferPtr[0] << 3 * 6) + 290 | ((uint)bufferPtr[1] << 2 * 6) + 291 | ((uint)bufferPtr[2] << 1 * 6); 292 | if (MatchSystem(Endianness.BIG)) 293 | { 294 | triple <<= 8; 295 | Buffer.MemoryCopy(&triple, dst, 2, 2); 296 | } 297 | else 298 | { 299 | triple = BinaryPrimitives.ReverseEndianness(triple); 300 | triple >>= 8; 301 | Buffer.MemoryCopy(&triple, dst, 2, 2); 302 | } 303 | dst += 2; 304 | } 305 | 306 | else if (idx == 1) 307 | { 308 | bytesConsumed = (int)(src - srcInit); 309 | bytesWritten = (int)(dst - dstInit); 310 | return OperationStatus.NeedMoreData;// The base64 input terminates with a single character, excluding padding. 311 | } 312 | bytesConsumed = (int)(src - srcInit); 313 | bytesWritten = (int)(dst - dstInit); 314 | return OperationStatus.Done; 315 | } 316 | triple = 317 | ((uint)bufferPtr[0] << 3 * 6) + ((uint)bufferPtr[1] << 2 * 6) + 318 | ((uint)bufferPtr[2] << 1 * 6) + ((uint)bufferPtr[3] << 0 * 6); 319 | if (MatchSystem(Endianness.BIG)) 320 | { 321 | triple <<= 8; 322 | Buffer.MemoryCopy(&triple, dst, 3, 3); 323 | } 324 | else 325 | { 326 | triple = BinaryPrimitives.ReverseEndianness(triple); 327 | triple >>= 8; 328 | Buffer.MemoryCopy(&triple, dst, 3, 3); 329 | } 330 | dst += 3; 331 | } 332 | 333 | } 334 | } 335 | 336 | 337 | public static OperationStatus Base64WithWhiteSpaceToBinaryScalar(ReadOnlySpan input, Span output, out int bytesConsumed, out int bytesWritten, bool isUrl = false) 338 | { 339 | int length = input.Length; 340 | int whiteSpaces = 0; 341 | while (length > 0 && IsAsciiWhiteSpace((char)input[length - 1])) 342 | { 343 | length--; 344 | whiteSpaces++; 345 | } 346 | int equallocation = length; // location of the first padding character if any 347 | int equalsigns = 0; 348 | if (length > 0 && input[length - 1] == '=') 349 | { 350 | length -= 1; 351 | equalsigns++; 352 | while (length > 0 && IsAsciiWhiteSpace((char)input[length - 1])) 353 | { 354 | length--; 355 | whiteSpaces++; 356 | } 357 | if (length > 0 && input[length - 1] == '=') 358 | { 359 | equalsigns++; 360 | length -= 1; 361 | } 362 | } 363 | if (length == 0) 364 | { 365 | if (equalsigns > 0) 366 | { 367 | bytesConsumed = equallocation; 368 | bytesWritten = 0; 369 | 370 | return OperationStatus.InvalidData; 371 | 372 | } 373 | bytesConsumed = 0 + whiteSpaces + equalsigns; 374 | bytesWritten = 0; 375 | return OperationStatus.Done; 376 | } 377 | ReadOnlySpan trimmedInput = input.Slice(0, length); 378 | OperationStatus r = Base64.DecodeFromBase64Scalar(trimmedInput, output, out bytesConsumed, out bytesWritten, isUrl); 379 | if (r == OperationStatus.Done) 380 | { 381 | if (equalsigns > 0) 382 | { 383 | // Additional checks 384 | if ((bytesWritten % 3 == 0) || (((bytesWritten % 3) + 1 + equalsigns) != 4)) 385 | { 386 | return OperationStatus.InvalidData; 387 | } 388 | } 389 | 390 | // Only increment bytesConsumed if decoding was successful 391 | bytesConsumed += equalsigns + whiteSpaces; 392 | } 393 | return r; 394 | } 395 | 396 | public static OperationStatus Base64WithWhiteSpaceToBinaryScalar(ReadOnlySpan input, Span output, out int bytesConsumed, out int bytesWritten, bool isUrl = false) 397 | { 398 | int length = input.Length; 399 | int whiteSpaces = 0; 400 | while (length > 0 && IsAsciiWhiteSpace((char)input[length - 1])) 401 | { 402 | length--; 403 | whiteSpaces++; 404 | } 405 | int equallocation = length; // location of the first padding character if any 406 | int equalsigns = 0; 407 | if (length > 0 && input[length - 1] == '=') 408 | { 409 | length -= 1; 410 | equalsigns++; 411 | while (length > 0 && IsAsciiWhiteSpace((char)input[length - 1])) 412 | { 413 | length--; 414 | whiteSpaces++; 415 | } 416 | if (length > 0 && input[length - 1] == '=') 417 | { 418 | equalsigns++; 419 | length -= 1; 420 | } 421 | } 422 | if (length == 0) 423 | { 424 | if (equalsigns > 0) 425 | { 426 | bytesConsumed = equallocation; 427 | bytesWritten = 0; 428 | 429 | return OperationStatus.InvalidData; 430 | 431 | } 432 | bytesConsumed = 0 + whiteSpaces + equalsigns; 433 | bytesWritten = 0; 434 | return OperationStatus.Done; 435 | } 436 | 437 | ReadOnlySpan trimmedInput = input.Slice(0, length); 438 | OperationStatus r = Base64.DecodeFromBase64Scalar(trimmedInput, output, out bytesConsumed, out bytesWritten, isUrl); 439 | if (r == OperationStatus.Done) 440 | { 441 | if (equalsigns > 0) 442 | { 443 | // Additional checks 444 | if ((bytesWritten % 3 == 0) || (((bytesWritten % 3) + 1 + equalsigns) != 4)) 445 | { 446 | return OperationStatus.InvalidData; 447 | } 448 | } 449 | 450 | // Only increment bytesConsumed if decoding was successful 451 | bytesConsumed += equalsigns + whiteSpaces; 452 | } 453 | return r; 454 | } 455 | 456 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 457 | public static int Base64LengthFromBinary(int length, bool isUrl = false) 458 | { 459 | if (isUrl) 460 | { 461 | return length / 3 * 4 + (length % 3 != 0 ? length % 3 + 1 : 0); 462 | } 463 | // Standard Base64 length calculation with padding to make the length a multiple of 4 464 | return (length + 2) / 3 * 4; 465 | } 466 | } 467 | } 468 | } 469 | 470 | -------------------------------------------------------------------------------- /src/Base64ScalarSafe.cs: -------------------------------------------------------------------------------- 1 | // These 'safe' methods are designed to be used when the output buffer is not large enough to hold the entire decoded data. 2 | // We only use them in our tests. 3 | using System; 4 | using System.Buffers; 5 | using System.Buffers.Binary; 6 | 7 | namespace SimdBase64 8 | { 9 | namespace Scalar 10 | { 11 | public static partial class Base64 12 | { 13 | // like DecodeFromBase64Scalar, but it will not write past the end of the ouput buffer. 14 | public unsafe static OperationStatus SafeDecodeFromBase64Scalar(ReadOnlySpan source, Span dest, out int bytesConsumed, out int bytesWritten, bool isUrl = false) 15 | { 16 | int length = source.Length; 17 | Span buffer = [0, 0, 0, 0]; 18 | 19 | // Define pointers within the fixed blocks 20 | fixed (byte* srcInit = source) 21 | fixed (byte* dstInit = dest) 22 | fixed (byte* bufferPtr = buffer) 23 | { 24 | byte* srcEnd = srcInit + length; 25 | byte* src = srcInit; 26 | byte* dst = dstInit; 27 | byte* dstEnd = dstInit + dest.Length; 28 | 29 | // Continue the implementation 30 | uint x; 31 | uint triple; 32 | int idx; 33 | // Should be 34 | // Span buffer = stackalloc byte[4]; 35 | 36 | while (true) 37 | { 38 | // fastpath 39 | while (src + 4 <= srcEnd && 40 | (x = isUrl ? Base64Url.GetD(src) : Base64Default.GetD(src)) < 0x01FFFFFF) 41 | { 42 | 43 | 44 | if (MatchSystem(Endianness.BIG)) 45 | { 46 | x = BinaryPrimitives.ReverseEndianness(x); 47 | } 48 | if (dst + 3 > dstEnd) 49 | { 50 | bytesConsumed = (int)(src - srcInit); 51 | bytesWritten = (int)(dst - dstInit); 52 | return OperationStatus.DestinationTooSmall; 53 | } 54 | Buffer.MemoryCopy(bufferPtr, dst, 3, 3); 55 | dst += 3; 56 | src += 4; 57 | } 58 | idx = 0; 59 | 60 | byte* srcCurrent = src; 61 | 62 | // We need at least four characters. 63 | while (idx < 4 && src < srcEnd) 64 | { 65 | char c = (char)*src; 66 | byte code = isUrl ? Tables.GetToBase64UrlValue(c) : Tables.GetToBase64Value(c); 67 | buffer[idx] = code; 68 | 69 | if (code <= 63) 70 | { 71 | idx++; 72 | } 73 | else if (code > 64) 74 | { 75 | bytesConsumed = (int)(src - srcInit); 76 | bytesWritten = (int)(dst - dstInit); 77 | return OperationStatus.InvalidData;// Found a character that cannot be part of a valid base64 string. 78 | } 79 | else 80 | { 81 | // We have a space or a newline. We ignore it. 82 | } 83 | src++; 84 | } 85 | 86 | // deals with remainder 87 | if (idx != 4) 88 | { 89 | if (idx == 2) // we just copy directly while converting 90 | { 91 | if (dst == dstEnd) 92 | { 93 | bytesConsumed = (int)(srcCurrent - srcInit); 94 | bytesWritten = (int)(dst - dstInit); 95 | return OperationStatus.DestinationTooSmall; 96 | } 97 | triple = ((uint)buffer[0] << (3 * 6)) + ((uint)buffer[1] << (2 * 6)); // the 2 last byte are shifted 18 and 12 bits respectively 98 | if (MatchSystem(Endianness.BIG)) 99 | { 100 | triple <<= 8; 101 | byte[] byteTriple = BitConverter.GetBytes(triple); 102 | dst[0] = byteTriple[0]; 103 | } 104 | else 105 | { 106 | triple = BinaryPrimitives.ReverseEndianness(triple); 107 | triple >>= 8; 108 | Buffer.MemoryCopy(&triple, dst, 1, 1); 109 | } 110 | dst += 1; 111 | } 112 | 113 | else if (idx == 3) // same story here 114 | { 115 | if (dst + 2 > dstEnd) 116 | { 117 | bytesConsumed = (int)(srcCurrent - srcInit); 118 | bytesWritten = (int)(dst - dstInit); 119 | return OperationStatus.DestinationTooSmall; 120 | } 121 | triple = ((uint)buffer[0] << 3 * 6) + 122 | ((uint)buffer[1] << 2 * 6) + 123 | ((uint)buffer[2] << 1 * 6); 124 | if (MatchSystem(Endianness.BIG)) 125 | { 126 | triple <<= 8; 127 | Buffer.MemoryCopy(&triple, dst, 2, 2); 128 | } 129 | else 130 | { 131 | triple = BinaryPrimitives.ReverseEndianness(triple); 132 | triple >>= 8; 133 | Buffer.MemoryCopy(&triple, dst, 2, 2); 134 | } 135 | dst += 2; 136 | } 137 | 138 | else if (idx == 1) 139 | { 140 | bytesConsumed = (int)(src - srcInit); 141 | bytesWritten = (int)(dst - dstInit); 142 | 143 | return OperationStatus.InvalidData;// The base64 input terminates with a single character, excluding padding. 144 | } 145 | bytesConsumed = (int)(src - srcInit); 146 | bytesWritten = (int)(dst - dstInit); 147 | return OperationStatus.Done;//SUCCESS 148 | } 149 | 150 | if (dst + 3 >= dstEnd) 151 | { 152 | bytesConsumed = (int)(srcCurrent - srcInit); 153 | bytesWritten = (int)(dst - dstInit); 154 | return OperationStatus.DestinationTooSmall; 155 | } 156 | triple = 157 | ((uint)(buffer[0]) << 3 * 6) + ((uint)(buffer[1]) << 2 * 6) + 158 | ((uint)(buffer[2]) << 1 * 6) + ((uint)(buffer[3]) << 0 * 6); 159 | if (MatchSystem(Endianness.BIG)) 160 | { 161 | triple <<= 8; 162 | Buffer.MemoryCopy(&triple, dst, 3, 3); 163 | } 164 | else 165 | { 166 | triple = BinaryPrimitives.ReverseEndianness(triple); 167 | triple >>= 8; 168 | Buffer.MemoryCopy(&triple, dst, 3, 3); 169 | } 170 | dst += 3; 171 | } 172 | 173 | } 174 | } 175 | 176 | // like DecodeFromBase64Scalar, but it will not write past the end of the ouput buffer. 177 | public unsafe static OperationStatus SafeDecodeFromBase64Scalar(ReadOnlySpan source, Span dest, out int bytesConsumed, out int bytesWritten, bool isUrl = false) 178 | { 179 | 180 | int length = source.Length; 181 | 182 | // Should be 183 | // Span buffer = stackalloc byte[4]; 184 | Span buffer = [0, 0, 0, 0]; 185 | // Define pointers within the fixed blocks 186 | fixed (char* srcInit = source) 187 | fixed (byte* dstInit = dest) 188 | fixed (byte* bufferPtr = buffer) 189 | 190 | { 191 | char* srcEnd = srcInit + length; 192 | char* src = srcInit; 193 | byte* dst = dstInit; 194 | byte* dstEnd = dstInit + dest.Length; 195 | 196 | // Continue the implementation 197 | uint x; 198 | uint triple; 199 | int idx; 200 | 201 | while (true) 202 | { 203 | // fastpath 204 | while (src + 4 <= srcEnd && 205 | (x = isUrl ? Base64Url.GetD(src) : Base64Default.GetD(src)) < 0x01FFFFFF) 206 | { 207 | if (MatchSystem(Endianness.BIG)) 208 | { 209 | x = BinaryPrimitives.ReverseEndianness(x); 210 | } 211 | if (dst + 3 > dstEnd) 212 | { 213 | bytesConsumed = (int)(src - srcInit); 214 | bytesWritten = (int)(dst - dstInit); 215 | return OperationStatus.DestinationTooSmall; 216 | } 217 | Buffer.MemoryCopy(bufferPtr, dst, 3, 3); 218 | dst += 3; 219 | src += 4; 220 | } 221 | idx = 0; 222 | 223 | char* srcCurrent = src; 224 | 225 | // We need at least four characters. 226 | while (idx < 4 && src < srcEnd) 227 | { 228 | char c = (char)*src; 229 | byte code = isUrl ? Tables.GetToBase64UrlValue(c) : Tables.GetToBase64Value(c); 230 | buffer[idx] = code; 231 | 232 | if (code <= 63) 233 | { 234 | idx++; 235 | } 236 | else if (code > 64) 237 | { 238 | bytesConsumed = (int)(src - srcInit); 239 | bytesWritten = (int)(dst - dstInit); 240 | return OperationStatus.InvalidData;// Found a character that cannot be part of a valid base64 string. 241 | } 242 | else 243 | { 244 | // We have a space or a newline. We ignore it. 245 | } 246 | src++; 247 | } 248 | 249 | // deals with remainder 250 | if (idx != 4) 251 | { 252 | if (idx == 2) // we just copy directly while converting 253 | { 254 | if (dst == dstEnd) 255 | { 256 | bytesConsumed = (int)(srcCurrent - srcInit); 257 | bytesWritten = (int)(dst - dstInit); 258 | return OperationStatus.DestinationTooSmall; 259 | } 260 | triple = ((uint)buffer[0] << (3 * 6)) + ((uint)buffer[1] << (2 * 6)); // the 2 last byte are shifted 18 and 12 bits respectively 261 | if (MatchSystem(Endianness.BIG)) 262 | { 263 | triple <<= 8; 264 | byte[] byteTriple = BitConverter.GetBytes(triple); 265 | dst[0] = byteTriple[0]; 266 | } 267 | else 268 | { 269 | triple = BinaryPrimitives.ReverseEndianness(triple); 270 | triple >>= 8; 271 | Buffer.MemoryCopy(&triple, dst, 1, 1); 272 | } 273 | dst += 1; 274 | } 275 | 276 | else if (idx == 3) // same story here 277 | { 278 | if (dst + 2 > dstEnd) 279 | { 280 | bytesConsumed = (int)(srcCurrent - srcInit); 281 | bytesWritten = (int)(dst - dstInit); 282 | return OperationStatus.DestinationTooSmall; 283 | } 284 | triple = ((uint)buffer[0] << 3 * 6) + 285 | ((uint)buffer[1] << 2 * 6) + 286 | ((uint)buffer[2] << 1 * 6); 287 | if (MatchSystem(Endianness.BIG)) 288 | { 289 | triple <<= 8; 290 | Buffer.MemoryCopy(&triple, dst, 2, 2); 291 | } 292 | else 293 | { 294 | triple = BinaryPrimitives.ReverseEndianness(triple); 295 | triple >>= 8; 296 | Buffer.MemoryCopy(&triple, dst, 2, 2); 297 | } 298 | dst += 2; 299 | } 300 | 301 | else if (idx == 1) 302 | { 303 | bytesConsumed = (int)(src - srcInit); 304 | bytesWritten = (int)(dst - dstInit); 305 | 306 | return OperationStatus.InvalidData;// The base64 input terminates with a single character, excluding padding. 307 | } 308 | bytesConsumed = (int)(src - srcInit); 309 | bytesWritten = (int)(dst - dstInit); 310 | return OperationStatus.Done;//SUCCESS 311 | } 312 | 313 | if (dst + 3 >= dstEnd) 314 | { 315 | bytesConsumed = (int)(srcCurrent - srcInit); 316 | bytesWritten = (int)(dst - dstInit); 317 | return OperationStatus.DestinationTooSmall; 318 | } 319 | triple = 320 | ((uint)(buffer[0]) << 3 * 6) + ((uint)(buffer[1]) << 2 * 6) + 321 | ((uint)(buffer[2]) << 1 * 6) + ((uint)(buffer[3]) << 0 * 6); 322 | if (MatchSystem(Endianness.BIG)) 323 | { 324 | triple <<= 8; 325 | Buffer.MemoryCopy(&triple, dst, 3, 3); 326 | } 327 | else 328 | { 329 | triple = BinaryPrimitives.ReverseEndianness(triple); 330 | triple >>= 8; 331 | Buffer.MemoryCopy(&triple, dst, 3, 3); 332 | } 333 | dst += 3; 334 | } 335 | 336 | } 337 | } 338 | 339 | 340 | public unsafe static OperationStatus SafeBase64ToBinaryWithWhiteSpace(ReadOnlySpan input, Span output, out int bytesConsumed, out int bytesWritten, bool isUrl = false) 341 | { 342 | // The implementation could be nicer, but we expect that most times, the user 343 | // will provide us with a buffer that is large enough. 344 | int maxLength = MaximalBinaryLengthFromBase64Scalar(input); 345 | 346 | if (output.Length >= maxLength) 347 | { 348 | // fast path 349 | OperationStatus fastPathResult = Base64.Base64WithWhiteSpaceToBinaryScalar(input, output, out bytesConsumed, out bytesWritten, isUrl); 350 | return fastPathResult; 351 | } 352 | // The output buffer is maybe too small. We will decode a truncated version of the input. 353 | int outlen3 = output.Length / 3 * 3; // round down to multiple of 3 354 | int safeInputLength = Base64LengthFromBinary(outlen3); 355 | OperationStatus r = DecodeFromBase64Scalar(input.Slice(0, Math.Max(0, safeInputLength)), output, out bytesConsumed, out bytesWritten, isUrl); // there might be a -1 error here 356 | if (r == OperationStatus.InvalidData) 357 | { 358 | return r; 359 | } 360 | int offset = (r == OperationStatus.NeedMoreData) ? 1 : 361 | ((bytesWritten % 3) == 0 ? 362 | 0 : (bytesWritten % 3) + 1); 363 | 364 | int outputIndex = bytesWritten - (bytesWritten % 3); 365 | int inputIndex = safeInputLength; 366 | int whiteSpaces = 0; 367 | // offset is a value that is no larger than 3. We backtrack 368 | // by up to offset characters + an undetermined number of 369 | // white space characters. It is expected that the next loop 370 | // runs at most 3 times + the number of white space characters 371 | // in between them, so we are not worried about performance. 372 | while (offset > 0 && inputIndex > 0) 373 | { 374 | char c = (char)input[--inputIndex]; 375 | if (IsAsciiWhiteSpace(c)) 376 | { 377 | // skipping 378 | } 379 | else 380 | { 381 | offset--; 382 | whiteSpaces++; 383 | } 384 | } 385 | ReadOnlySpan tailInput = input.Slice(inputIndex); 386 | int RemainingInputLength = tailInput.Length; 387 | while (RemainingInputLength > 0 && IsAsciiWhiteSpace((char)tailInput[RemainingInputLength - 1])) 388 | { 389 | RemainingInputLength--; 390 | } 391 | int paddingCharacts = 0; 392 | if (RemainingInputLength > 0 && tailInput[RemainingInputLength - 1] == '=') 393 | { 394 | RemainingInputLength--; 395 | paddingCharacts++; 396 | while (RemainingInputLength > 0 && IsAsciiWhiteSpace((char)tailInput[RemainingInputLength - 1])) 397 | { 398 | RemainingInputLength--; 399 | whiteSpaces++; 400 | } 401 | if (RemainingInputLength > 0 && tailInput[RemainingInputLength - 1] == '=') 402 | { 403 | RemainingInputLength--; 404 | paddingCharacts++; 405 | } 406 | } 407 | 408 | int tailBytesConsumed; 409 | int tailBytesWritten; 410 | 411 | Span remainingOut = output.Slice(Math.Min(output.Length, outputIndex)); 412 | r = SafeDecodeFromBase64Scalar(tailInput.Slice(0, RemainingInputLength), remainingOut, out tailBytesConsumed, out tailBytesWritten, isUrl); 413 | if (r == OperationStatus.Done && paddingCharacts > 0) 414 | { 415 | // additional checks: 416 | if ((remainingOut.Length % 3 == 0) || ((remainingOut.Length % 3) + 1 + paddingCharacts != 4)) 417 | { 418 | r = OperationStatus.InvalidData; 419 | } 420 | } 421 | 422 | 423 | if (r == OperationStatus.Done) 424 | { 425 | bytesConsumed += tailBytesConsumed + paddingCharacts + whiteSpaces; 426 | } 427 | else 428 | { 429 | bytesConsumed += tailBytesConsumed; 430 | } 431 | bytesWritten += tailBytesWritten; 432 | return r; 433 | } 434 | 435 | public unsafe static OperationStatus SafeBase64ToBinaryWithWhiteSpace(ReadOnlySpan input, Span output, out int bytesConsumed, out int bytesWritten, bool isUrl = false) 436 | { 437 | // The implementation could be nicer, but we expect that most times, the user 438 | // will provide us with a buffer that is large enough. 439 | int maxLength = MaximalBinaryLengthFromBase64Scalar(input); 440 | 441 | if (output.Length >= maxLength) 442 | { 443 | // fast path 444 | OperationStatus fastPathResult = Base64.Base64WithWhiteSpaceToBinaryScalar(input, output, out bytesConsumed, out bytesWritten, isUrl); 445 | return fastPathResult; 446 | } 447 | // The output buffer is maybe too small. We will decode a truncated version of the input. 448 | int outlen3 = output.Length / 3 * 3; // round down to multiple of 3 449 | int safeInputLength = Base64LengthFromBinary(outlen3); 450 | OperationStatus r = DecodeFromBase64Scalar(input.Slice(0, Math.Max(0, safeInputLength)), output, out bytesConsumed, out bytesWritten, isUrl); // there might be a -1 error here 451 | if (r == OperationStatus.InvalidData) 452 | { 453 | return r; 454 | } 455 | int offset = (r == OperationStatus.NeedMoreData) ? 1 : 456 | ((bytesWritten % 3) == 0 ? 457 | 0 : (bytesWritten % 3) + 1); 458 | 459 | int outputIndex = bytesWritten - (bytesWritten % 3); 460 | int inputIndex = safeInputLength; 461 | int whiteSpaces = 0; 462 | // offset is a value that is no larger than 3. We backtrack 463 | // by up to offset characters + an undetermined number of 464 | // white space characters. It is expected that the next loop 465 | // runs at most 3 times + the number of white space characters 466 | // in between them, so we are not worried about performance. 467 | while (offset > 0 && inputIndex > 0) 468 | { 469 | char c = (char)input[--inputIndex]; 470 | if (IsAsciiWhiteSpace(c)) 471 | { 472 | // skipping 473 | } 474 | else 475 | { 476 | offset--; 477 | whiteSpaces++; 478 | } 479 | } 480 | ReadOnlySpan tailInput = input.Slice(inputIndex); 481 | int RemainingInputLength = tailInput.Length; 482 | while (RemainingInputLength > 0 && IsAsciiWhiteSpace((char)tailInput[RemainingInputLength - 1])) 483 | { 484 | RemainingInputLength--; 485 | } 486 | int paddingCharacts = 0; 487 | if (RemainingInputLength > 0 && tailInput[RemainingInputLength - 1] == '=') 488 | { 489 | RemainingInputLength--; 490 | paddingCharacts++; 491 | while (RemainingInputLength > 0 && IsAsciiWhiteSpace((char)tailInput[RemainingInputLength - 1])) 492 | { 493 | RemainingInputLength--; 494 | whiteSpaces++; 495 | } 496 | if (RemainingInputLength > 0 && tailInput[RemainingInputLength - 1] == '=') 497 | { 498 | RemainingInputLength--; 499 | paddingCharacts++; 500 | } 501 | } 502 | 503 | int tailBytesConsumed; 504 | int tailBytesWritten; 505 | 506 | Span remainingOut = output.Slice(Math.Min(output.Length, outputIndex)); 507 | r = SafeDecodeFromBase64Scalar(tailInput.Slice(0, RemainingInputLength), remainingOut, out tailBytesConsumed, out tailBytesWritten, isUrl); 508 | if (r == OperationStatus.Done && paddingCharacts > 0) 509 | { 510 | // additional checks: 511 | if ((remainingOut.Length % 3 == 0) || ((remainingOut.Length % 3) + 1 + paddingCharacts != 4)) 512 | { 513 | r = OperationStatus.InvalidData; 514 | } 515 | } 516 | 517 | 518 | if (r == OperationStatus.Done) 519 | { 520 | bytesConsumed += tailBytesConsumed + paddingCharacts + whiteSpaces; 521 | } 522 | else 523 | { 524 | bytesConsumed += tailBytesConsumed; 525 | } 526 | bytesWritten += tailBytesWritten; 527 | return r; 528 | } 529 | 530 | } 531 | } 532 | } -------------------------------------------------------------------------------- /src/Base64Tables.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | namespace SimdBase64 6 | { 7 | public static class Base64Default 8 | { 9 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 10 | public static unsafe uint GetD(char* src) 11 | { 12 | if ((src[0] | src[1] | src[2] | src[3]) > 255) 13 | { 14 | return 0x01ffffff; 15 | } 16 | Span b = [(byte)src[0], (byte)src[1], (byte)src[2], (byte)src[3]]; 17 | fixed (byte* p = b) 18 | { 19 | return GetD(p); 20 | } 21 | } 22 | 23 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 24 | public static unsafe uint GetD(byte* src) 25 | { 26 | ReadOnlySpan d0 = new uint[256] { 27 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 28 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 29 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 30 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 31 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 32 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 33 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 34 | 0x01ffffff, 0x000000f8, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x000000fc, 35 | 0x000000d0, 0x000000d4, 0x000000d8, 0x000000dc, 0x000000e0, 0x000000e4, 36 | 0x000000e8, 0x000000ec, 0x000000f0, 0x000000f4, 0x01ffffff, 0x01ffffff, 37 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, 38 | 0x00000004, 0x00000008, 0x0000000c, 0x00000010, 0x00000014, 0x00000018, 39 | 0x0000001c, 0x00000020, 0x00000024, 0x00000028, 0x0000002c, 0x00000030, 40 | 0x00000034, 0x00000038, 0x0000003c, 0x00000040, 0x00000044, 0x00000048, 41 | 0x0000004c, 0x00000050, 0x00000054, 0x00000058, 0x0000005c, 0x00000060, 42 | 0x00000064, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 43 | 0x01ffffff, 0x00000068, 0x0000006c, 0x00000070, 0x00000074, 0x00000078, 44 | 0x0000007c, 0x00000080, 0x00000084, 0x00000088, 0x0000008c, 0x00000090, 45 | 0x00000094, 0x00000098, 0x0000009c, 0x000000a0, 0x000000a4, 0x000000a8, 46 | 0x000000ac, 0x000000b0, 0x000000b4, 0x000000b8, 0x000000bc, 0x000000c0, 47 | 0x000000c4, 0x000000c8, 0x000000cc, 0x01ffffff, 0x01ffffff, 0x01ffffff, 48 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 49 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 50 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 51 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 52 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 53 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 54 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 55 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 56 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 57 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 58 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 59 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 60 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 61 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 62 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 63 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 64 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 65 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 66 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 67 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 68 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 69 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; 70 | 71 | ReadOnlySpan d1 = new uint[256] { 72 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 73 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 74 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 75 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 76 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 77 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 78 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 79 | 0x01ffffff, 0x0000e003, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0000f003, 80 | 0x00004003, 0x00005003, 0x00006003, 0x00007003, 0x00008003, 0x00009003, 81 | 0x0000a003, 0x0000b003, 0x0000c003, 0x0000d003, 0x01ffffff, 0x01ffffff, 82 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, 83 | 0x00001000, 0x00002000, 0x00003000, 0x00004000, 0x00005000, 0x00006000, 84 | 0x00007000, 0x00008000, 0x00009000, 0x0000a000, 0x0000b000, 0x0000c000, 85 | 0x0000d000, 0x0000e000, 0x0000f000, 0x00000001, 0x00001001, 0x00002001, 86 | 0x00003001, 0x00004001, 0x00005001, 0x00006001, 0x00007001, 0x00008001, 87 | 0x00009001, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 88 | 0x01ffffff, 0x0000a001, 0x0000b001, 0x0000c001, 0x0000d001, 0x0000e001, 89 | 0x0000f001, 0x00000002, 0x00001002, 0x00002002, 0x00003002, 0x00004002, 90 | 0x00005002, 0x00006002, 0x00007002, 0x00008002, 0x00009002, 0x0000a002, 91 | 0x0000b002, 0x0000c002, 0x0000d002, 0x0000e002, 0x0000f002, 0x00000003, 92 | 0x00001003, 0x00002003, 0x00003003, 0x01ffffff, 0x01ffffff, 0x01ffffff, 93 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 94 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 95 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 96 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 97 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 98 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 99 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 100 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 101 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 102 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 103 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 104 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 105 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 106 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 107 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 108 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 109 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 110 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 111 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 112 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 113 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 114 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; 115 | 116 | ReadOnlySpan d2 = new uint[256] { 117 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 118 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 119 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 120 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 121 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 122 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 123 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 124 | 0x01ffffff, 0x00800f00, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00c00f00, 125 | 0x00000d00, 0x00400d00, 0x00800d00, 0x00c00d00, 0x00000e00, 0x00400e00, 126 | 0x00800e00, 0x00c00e00, 0x00000f00, 0x00400f00, 0x01ffffff, 0x01ffffff, 127 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, 128 | 0x00400000, 0x00800000, 0x00c00000, 0x00000100, 0x00400100, 0x00800100, 129 | 0x00c00100, 0x00000200, 0x00400200, 0x00800200, 0x00c00200, 0x00000300, 130 | 0x00400300, 0x00800300, 0x00c00300, 0x00000400, 0x00400400, 0x00800400, 131 | 0x00c00400, 0x00000500, 0x00400500, 0x00800500, 0x00c00500, 0x00000600, 132 | 0x00400600, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 133 | 0x01ffffff, 0x00800600, 0x00c00600, 0x00000700, 0x00400700, 0x00800700, 134 | 0x00c00700, 0x00000800, 0x00400800, 0x00800800, 0x00c00800, 0x00000900, 135 | 0x00400900, 0x00800900, 0x00c00900, 0x00000a00, 0x00400a00, 0x00800a00, 136 | 0x00c00a00, 0x00000b00, 0x00400b00, 0x00800b00, 0x00c00b00, 0x00000c00, 137 | 0x00400c00, 0x00800c00, 0x00c00c00, 0x01ffffff, 0x01ffffff, 0x01ffffff, 138 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 139 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 140 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 141 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 142 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 143 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 144 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 145 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 146 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 147 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 148 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 149 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 150 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 151 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 152 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 153 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 154 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 155 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 156 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 157 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 158 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 159 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; 160 | 161 | ReadOnlySpan d3 = new uint[256] { 162 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 163 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 164 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 165 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 166 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 167 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 168 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 169 | 0x01ffffff, 0x003e0000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x003f0000, 170 | 0x00340000, 0x00350000, 0x00360000, 0x00370000, 0x00380000, 0x00390000, 171 | 0x003a0000, 0x003b0000, 0x003c0000, 0x003d0000, 0x01ffffff, 0x01ffffff, 172 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, 173 | 0x00010000, 0x00020000, 0x00030000, 0x00040000, 0x00050000, 0x00060000, 174 | 0x00070000, 0x00080000, 0x00090000, 0x000a0000, 0x000b0000, 0x000c0000, 175 | 0x000d0000, 0x000e0000, 0x000f0000, 0x00100000, 0x00110000, 0x00120000, 176 | 0x00130000, 0x00140000, 0x00150000, 0x00160000, 0x00170000, 0x00180000, 177 | 0x00190000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 178 | 0x01ffffff, 0x001a0000, 0x001b0000, 0x001c0000, 0x001d0000, 0x001e0000, 179 | 0x001f0000, 0x00200000, 0x00210000, 0x00220000, 0x00230000, 0x00240000, 180 | 0x00250000, 0x00260000, 0x00270000, 0x00280000, 0x00290000, 0x002a0000, 181 | 0x002b0000, 0x002c0000, 0x002d0000, 0x002e0000, 0x002f0000, 0x00300000, 182 | 0x00310000, 0x00320000, 0x00330000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 183 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 184 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 185 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 186 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 187 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 188 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 189 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 190 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 191 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 192 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 193 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 194 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 195 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 196 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 197 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 198 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 199 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 200 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 201 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 202 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 203 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 204 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; 205 | 206 | ref uint tableRef0 = ref MemoryMarshal.GetReference(d0); 207 | ref uint tableRef1 = ref MemoryMarshal.GetReference(d1); 208 | ref uint tableRef2 = ref MemoryMarshal.GetReference(d2); 209 | ref uint tableRef3 = ref MemoryMarshal.GetReference(d3); 210 | return Unsafe.AddByteOffset(ref tableRef0, (nint)(src[0])) | Unsafe.AddByteOffset(ref tableRef1, (nint)(src[1])) | Unsafe.AddByteOffset(ref tableRef2, (nint)(src[2])) | Unsafe.AddByteOffset(ref tableRef3, (nint)(src[3])); 211 | } 212 | } 213 | 214 | public static class Base64Url 215 | { 216 | 217 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 218 | public static unsafe uint GetD(byte* src) 219 | { 220 | ReadOnlySpan d0 = new uint[256] { 221 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 222 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 223 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 224 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 225 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 226 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 227 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 228 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x000000f8, 0x01ffffff, 0x01ffffff, 229 | 0x000000d0, 0x000000d4, 0x000000d8, 0x000000dc, 0x000000e0, 0x000000e4, 230 | 0x000000e8, 0x000000ec, 0x000000f0, 0x000000f4, 0x01ffffff, 0x01ffffff, 231 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, 232 | 0x00000004, 0x00000008, 0x0000000c, 0x00000010, 0x00000014, 0x00000018, 233 | 0x0000001c, 0x00000020, 0x00000024, 0x00000028, 0x0000002c, 0x00000030, 234 | 0x00000034, 0x00000038, 0x0000003c, 0x00000040, 0x00000044, 0x00000048, 235 | 0x0000004c, 0x00000050, 0x00000054, 0x00000058, 0x0000005c, 0x00000060, 236 | 0x00000064, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x000000fc, 237 | 0x01ffffff, 0x00000068, 0x0000006c, 0x00000070, 0x00000074, 0x00000078, 238 | 0x0000007c, 0x00000080, 0x00000084, 0x00000088, 0x0000008c, 0x00000090, 239 | 0x00000094, 0x00000098, 0x0000009c, 0x000000a0, 0x000000a4, 0x000000a8, 240 | 0x000000ac, 0x000000b0, 0x000000b4, 0x000000b8, 0x000000bc, 0x000000c0, 241 | 0x000000c4, 0x000000c8, 0x000000cc, 0x01ffffff, 0x01ffffff, 0x01ffffff, 242 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 243 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 244 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 245 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 246 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 247 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 248 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 249 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 250 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 251 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 252 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 253 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 254 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 255 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 256 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 257 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 258 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 259 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 260 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 261 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 262 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 263 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; 264 | 265 | ReadOnlySpan d1 = new uint[256] { 266 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 267 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 268 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 269 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 270 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 271 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 272 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 273 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0000e003, 0x01ffffff, 0x01ffffff, 274 | 0x00004003, 0x00005003, 0x00006003, 0x00007003, 0x00008003, 0x00009003, 275 | 0x0000a003, 0x0000b003, 0x0000c003, 0x0000d003, 0x01ffffff, 0x01ffffff, 276 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, 277 | 0x00001000, 0x00002000, 0x00003000, 0x00004000, 0x00005000, 0x00006000, 278 | 0x00007000, 0x00008000, 0x00009000, 0x0000a000, 0x0000b000, 0x0000c000, 279 | 0x0000d000, 0x0000e000, 0x0000f000, 0x00000001, 0x00001001, 0x00002001, 280 | 0x00003001, 0x00004001, 0x00005001, 0x00006001, 0x00007001, 0x00008001, 281 | 0x00009001, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0000f003, 282 | 0x01ffffff, 0x0000a001, 0x0000b001, 0x0000c001, 0x0000d001, 0x0000e001, 283 | 0x0000f001, 0x00000002, 0x00001002, 0x00002002, 0x00003002, 0x00004002, 284 | 0x00005002, 0x00006002, 0x00007002, 0x00008002, 0x00009002, 0x0000a002, 285 | 0x0000b002, 0x0000c002, 0x0000d002, 0x0000e002, 0x0000f002, 0x00000003, 286 | 0x00001003, 0x00002003, 0x00003003, 0x01ffffff, 0x01ffffff, 0x01ffffff, 287 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 288 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 289 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 290 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 291 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 292 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 293 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 294 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 295 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 296 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 297 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 298 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 299 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 300 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 301 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 302 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 303 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 304 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 305 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 306 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 307 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 308 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; 309 | 310 | ReadOnlySpan d2 = new uint[256] { 311 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 312 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 313 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 314 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 315 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 316 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 317 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 318 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00800f00, 0x01ffffff, 0x01ffffff, 319 | 0x00000d00, 0x00400d00, 0x00800d00, 0x00c00d00, 0x00000e00, 0x00400e00, 320 | 0x00800e00, 0x00c00e00, 0x00000f00, 0x00400f00, 0x01ffffff, 0x01ffffff, 321 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, 322 | 0x00400000, 0x00800000, 0x00c00000, 0x00000100, 0x00400100, 0x00800100, 323 | 0x00c00100, 0x00000200, 0x00400200, 0x00800200, 0x00c00200, 0x00000300, 324 | 0x00400300, 0x00800300, 0x00c00300, 0x00000400, 0x00400400, 0x00800400, 325 | 0x00c00400, 0x00000500, 0x00400500, 0x00800500, 0x00c00500, 0x00000600, 326 | 0x00400600, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00c00f00, 327 | 0x01ffffff, 0x00800600, 0x00c00600, 0x00000700, 0x00400700, 0x00800700, 328 | 0x00c00700, 0x00000800, 0x00400800, 0x00800800, 0x00c00800, 0x00000900, 329 | 0x00400900, 0x00800900, 0x00c00900, 0x00000a00, 0x00400a00, 0x00800a00, 330 | 0x00c00a00, 0x00000b00, 0x00400b00, 0x00800b00, 0x00c00b00, 0x00000c00, 331 | 0x00400c00, 0x00800c00, 0x00c00c00, 0x01ffffff, 0x01ffffff, 0x01ffffff, 332 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 333 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 334 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 335 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 336 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 337 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 338 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 339 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 340 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 341 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 342 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 343 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 344 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 345 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 346 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 347 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 348 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 349 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 350 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 351 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 352 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 353 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; 354 | 355 | ReadOnlySpan d3 = new uint[256] { 356 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 357 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 358 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 359 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 360 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 361 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 362 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 363 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x003e0000, 0x01ffffff, 0x01ffffff, 364 | 0x00340000, 0x00350000, 0x00360000, 0x00370000, 0x00380000, 0x00390000, 365 | 0x003a0000, 0x003b0000, 0x003c0000, 0x003d0000, 0x01ffffff, 0x01ffffff, 366 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, 367 | 0x00010000, 0x00020000, 0x00030000, 0x00040000, 0x00050000, 0x00060000, 368 | 0x00070000, 0x00080000, 0x00090000, 0x000a0000, 0x000b0000, 0x000c0000, 369 | 0x000d0000, 0x000e0000, 0x000f0000, 0x00100000, 0x00110000, 0x00120000, 370 | 0x00130000, 0x00140000, 0x00150000, 0x00160000, 0x00170000, 0x00180000, 371 | 0x00190000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x003f0000, 372 | 0x01ffffff, 0x001a0000, 0x001b0000, 0x001c0000, 0x001d0000, 0x001e0000, 373 | 0x001f0000, 0x00200000, 0x00210000, 0x00220000, 0x00230000, 0x00240000, 374 | 0x00250000, 0x00260000, 0x00270000, 0x00280000, 0x00290000, 0x002a0000, 375 | 0x002b0000, 0x002c0000, 0x002d0000, 0x002e0000, 0x002f0000, 0x00300000, 376 | 0x00310000, 0x00320000, 0x00330000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 377 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 378 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 379 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 380 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 381 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 382 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 383 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 384 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 385 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 386 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 387 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 388 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 389 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 390 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 391 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 392 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 393 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 394 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 395 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 396 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 397 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 398 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; 399 | 400 | ref uint tableRef0 = ref MemoryMarshal.GetReference(d0); 401 | ref uint tableRef1 = ref MemoryMarshal.GetReference(d1); 402 | ref uint tableRef2 = ref MemoryMarshal.GetReference(d2); 403 | ref uint tableRef3 = ref MemoryMarshal.GetReference(d3); 404 | return Unsafe.AddByteOffset(ref tableRef0, (nint)(src[0])) | Unsafe.AddByteOffset(ref tableRef1, (nint)(src[1])) | Unsafe.AddByteOffset(ref tableRef2, (nint)(src[2])) | Unsafe.AddByteOffset(ref tableRef3, (nint)(src[3])); 405 | } 406 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 407 | public static unsafe uint GetD(char* src) 408 | { 409 | if ((src[0] | src[1] | src[2] | src[3]) > 255) 410 | { 411 | return 0x01ffffff; 412 | } 413 | Span b = [(byte)src[0], (byte)src[1], (byte)src[2], (byte)src[3]]; 414 | fixed (byte* p = b) 415 | { 416 | return GetD(p); 417 | } 418 | } 419 | } 420 | 421 | 422 | public static class Tables 423 | { 424 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 425 | public static ulong GetThintableEpi8(uint n) 426 | { 427 | ReadOnlySpan thintableEpi8 = new ulong[256] 428 | { 0x0706050403020100, 0x0007060504030201, 0x0007060504030200, 429 | 0x0000070605040302, 0x0007060504030100, 0x0000070605040301, 430 | 0x0000070605040300, 0x0000000706050403, 0x0007060504020100, 431 | 0x0000070605040201, 0x0000070605040200, 0x0000000706050402, 432 | 0x0000070605040100, 0x0000000706050401, 0x0000000706050400, 433 | 0x0000000007060504, 0x0007060503020100, 0x0000070605030201, 434 | 0x0000070605030200, 0x0000000706050302, 0x0000070605030100, 435 | 0x0000000706050301, 0x0000000706050300, 0x0000000007060503, 436 | 0x0000070605020100, 0x0000000706050201, 0x0000000706050200, 437 | 0x0000000007060502, 0x0000000706050100, 0x0000000007060501, 438 | 0x0000000007060500, 0x0000000000070605, 0x0007060403020100, 439 | 0x0000070604030201, 0x0000070604030200, 0x0000000706040302, 440 | 0x0000070604030100, 0x0000000706040301, 0x0000000706040300, 441 | 0x0000000007060403, 0x0000070604020100, 0x0000000706040201, 442 | 0x0000000706040200, 0x0000000007060402, 0x0000000706040100, 443 | 0x0000000007060401, 0x0000000007060400, 0x0000000000070604, 444 | 0x0000070603020100, 0x0000000706030201, 0x0000000706030200, 445 | 0x0000000007060302, 0x0000000706030100, 0x0000000007060301, 446 | 0x0000000007060300, 0x0000000000070603, 0x0000000706020100, 447 | 0x0000000007060201, 0x0000000007060200, 0x0000000000070602, 448 | 0x0000000007060100, 0x0000000000070601, 0x0000000000070600, 449 | 0x0000000000000706, 0x0007050403020100, 0x0000070504030201, 450 | 0x0000070504030200, 0x0000000705040302, 0x0000070504030100, 451 | 0x0000000705040301, 0x0000000705040300, 0x0000000007050403, 452 | 0x0000070504020100, 0x0000000705040201, 0x0000000705040200, 453 | 0x0000000007050402, 0x0000000705040100, 0x0000000007050401, 454 | 0x0000000007050400, 0x0000000000070504, 0x0000070503020100, 455 | 0x0000000705030201, 0x0000000705030200, 0x0000000007050302, 456 | 0x0000000705030100, 0x0000000007050301, 0x0000000007050300, 457 | 0x0000000000070503, 0x0000000705020100, 0x0000000007050201, 458 | 0x0000000007050200, 0x0000000000070502, 0x0000000007050100, 459 | 0x0000000000070501, 0x0000000000070500, 0x0000000000000705, 460 | 0x0000070403020100, 0x0000000704030201, 0x0000000704030200, 461 | 0x0000000007040302, 0x0000000704030100, 0x0000000007040301, 462 | 0x0000000007040300, 0x0000000000070403, 0x0000000704020100, 463 | 0x0000000007040201, 0x0000000007040200, 0x0000000000070402, 464 | 0x0000000007040100, 0x0000000000070401, 0x0000000000070400, 465 | 0x0000000000000704, 0x0000000703020100, 0x0000000007030201, 466 | 0x0000000007030200, 0x0000000000070302, 0x0000000007030100, 467 | 0x0000000000070301, 0x0000000000070300, 0x0000000000000703, 468 | 0x0000000007020100, 0x0000000000070201, 0x0000000000070200, 469 | 0x0000000000000702, 0x0000000000070100, 0x0000000000000701, 470 | 0x0000000000000700, 0x0000000000000007, 0x0006050403020100, 471 | 0x0000060504030201, 0x0000060504030200, 0x0000000605040302, 472 | 0x0000060504030100, 0x0000000605040301, 0x0000000605040300, 473 | 0x0000000006050403, 0x0000060504020100, 0x0000000605040201, 474 | 0x0000000605040200, 0x0000000006050402, 0x0000000605040100, 475 | 0x0000000006050401, 0x0000000006050400, 0x0000000000060504, 476 | 0x0000060503020100, 0x0000000605030201, 0x0000000605030200, 477 | 0x0000000006050302, 0x0000000605030100, 0x0000000006050301, 478 | 0x0000000006050300, 0x0000000000060503, 0x0000000605020100, 479 | 0x0000000006050201, 0x0000000006050200, 0x0000000000060502, 480 | 0x0000000006050100, 0x0000000000060501, 0x0000000000060500, 481 | 0x0000000000000605, 0x0000060403020100, 0x0000000604030201, 482 | 0x0000000604030200, 0x0000000006040302, 0x0000000604030100, 483 | 0x0000000006040301, 0x0000000006040300, 0x0000000000060403, 484 | 0x0000000604020100, 0x0000000006040201, 0x0000000006040200, 485 | 0x0000000000060402, 0x0000000006040100, 0x0000000000060401, 486 | 0x0000000000060400, 0x0000000000000604, 0x0000000603020100, 487 | 0x0000000006030201, 0x0000000006030200, 0x0000000000060302, 488 | 0x0000000006030100, 0x0000000000060301, 0x0000000000060300, 489 | 0x0000000000000603, 0x0000000006020100, 0x0000000000060201, 490 | 0x0000000000060200, 0x0000000000000602, 0x0000000000060100, 491 | 0x0000000000000601, 0x0000000000000600, 0x0000000000000006, 492 | 0x0000050403020100, 0x0000000504030201, 0x0000000504030200, 493 | 0x0000000005040302, 0x0000000504030100, 0x0000000005040301, 494 | 0x0000000005040300, 0x0000000000050403, 0x0000000504020100, 495 | 0x0000000005040201, 0x0000000005040200, 0x0000000000050402, 496 | 0x0000000005040100, 0x0000000000050401, 0x0000000000050400, 497 | 0x0000000000000504, 0x0000000503020100, 0x0000000005030201, 498 | 0x0000000005030200, 0x0000000000050302, 0x0000000005030100, 499 | 0x0000000000050301, 0x0000000000050300, 0x0000000000000503, 500 | 0x0000000005020100, 0x0000000000050201, 0x0000000000050200, 501 | 0x0000000000000502, 0x0000000000050100, 0x0000000000000501, 502 | 0x0000000000000500, 0x0000000000000005, 0x0000000403020100, 503 | 0x0000000004030201, 0x0000000004030200, 0x0000000000040302, 504 | 0x0000000004030100, 0x0000000000040301, 0x0000000000040300, 505 | 0x0000000000000403, 0x0000000004020100, 0x0000000000040201, 506 | 0x0000000000040200, 0x0000000000000402, 0x0000000000040100, 507 | 0x0000000000000401, 0x0000000000000400, 0x0000000000000004, 508 | 0x0000000003020100, 0x0000000000030201, 0x0000000000030200, 509 | 0x0000000000000302, 0x0000000000030100, 0x0000000000000301, 510 | 0x0000000000000300, 0x0000000000000003, 0x0000000000020100, 511 | 0x0000000000000201, 0x0000000000000200, 0x0000000000000002, 512 | 0x0000000000000100, 0x0000000000000001, 0x0000000000000000, 513 | 0x0000000000000000, 514 | }; 515 | ref ulong tableRef = ref MemoryMarshal.GetReference(thintableEpi8); 516 | return Unsafe.AddByteOffset(ref tableRef, (nint)n); 517 | } 518 | 519 | internal static readonly byte[] pshufbCombineTable = new byte[144] 520 | { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 521 | 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08, 522 | 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0x00, 0x01, 0x02, 0x03, 523 | 0x04, 0x05, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, 524 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 525 | 0x0f, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, 0x08, 0x09, 0x0a, 0x0b, 526 | 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x08, 527 | 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 528 | 0x00, 0x01, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, 529 | 0xff, 0xff, 0xff, 0xff, 0x00, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 530 | 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x08, 0x09, 0x0a, 0x0b, 531 | 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 532 | }; 533 | 534 | 535 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 536 | public static byte GetBitsSetTable256mul2(uint n) 537 | { 538 | ReadOnlySpan BitsSetTable256mul2 = new byte[256] 539 | { 0, 2, 2, 4, 2, 4, 4, 6, 2, 4, 4, 6, 4, 6, 6, 8, 2, 4, 4, 540 | 6, 4, 6, 6, 8, 4, 6, 6, 8, 6, 8, 8, 10, 2, 4, 4, 6, 4, 6, 541 | 6, 8, 4, 6, 6, 8, 6, 8, 8, 10, 4, 6, 6, 8, 6, 8, 8, 10, 6, 542 | 8, 8, 10, 8, 10, 10, 12, 2, 4, 4, 6, 4, 6, 6, 8, 4, 6, 6, 8, 543 | 6, 8, 8, 10, 4, 6, 6, 8, 6, 8, 8, 10, 6, 8, 8, 10, 8, 10, 10, 544 | 12, 4, 6, 6, 8, 6, 8, 8, 10, 6, 8, 8, 10, 8, 10, 10, 12, 6, 8, 545 | 8, 10, 8, 10, 10, 12, 8, 10, 10, 12, 10, 12, 12, 14, 2, 4, 4, 6, 4, 546 | 6, 6, 8, 4, 6, 6, 8, 6, 8, 8, 10, 4, 6, 6, 8, 6, 8, 8, 10, 547 | 6, 8, 8, 10, 8, 10, 10, 12, 4, 6, 6, 8, 6, 8, 8, 10, 6, 8, 8, 548 | 10, 8, 10, 10, 12, 6, 8, 8, 10, 8, 10, 10, 12, 8, 10, 10, 12, 10, 12, 549 | 12, 14, 4, 6, 6, 8, 6, 8, 8, 10, 6, 8, 8, 10, 8, 10, 10, 12, 6, 550 | 8, 8, 10, 8, 10, 10, 12, 8, 10, 10, 12, 10, 12, 12, 14, 6, 8, 8, 10, 551 | 8, 10, 10, 12, 8, 10, 10, 12, 10, 12, 12, 14, 8, 10, 10, 12, 10, 12, 12, 552 | 14, 10, 12, 12, 14, 12, 14, 14, 16}; 553 | ref byte tableRef = ref MemoryMarshal.GetReference(BitsSetTable256mul2); 554 | return Unsafe.AddByteOffset(ref tableRef, (nint)n); 555 | } 556 | 557 | 558 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 559 | public static byte GetToBase64Value(uint n) 560 | { 561 | ReadOnlySpan ToBase64Value = new byte[256] 562 | { 255, 255, 255, 255, 255, 255, 255, 255, 255, 64, 64, 255, 64, 64, 255, 563 | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 564 | 255, 255, 64, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, 565 | 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 566 | 255, 255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 567 | 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 568 | 25, 255, 255, 255, 255, 255, 255, 26, 27, 28, 29, 30, 31, 32, 33, 569 | 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 570 | 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 571 | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 572 | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 573 | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 574 | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 575 | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 576 | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 577 | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 578 | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 579 | 255}; 580 | ref byte tableRef = ref MemoryMarshal.GetReference(ToBase64Value); 581 | return Unsafe.AddByteOffset(ref tableRef, (nint)n); 582 | } 583 | 584 | 585 | 586 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 587 | public static byte GetToBase64UrlValue(uint n) 588 | { 589 | ReadOnlySpan toBase64UrlValue = new byte[256] { 590 | 255, 255, 255, 255, 255, 255, 255, 255, 255, 64, 64, 255, 64, 64, 255, 591 | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 592 | 255, 255, 64, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 593 | 62, 255, 255, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 594 | 255, 255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 595 | 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 596 | 25, 255, 255, 255, 255, 63, 255, 26, 27, 28, 29, 30, 31, 32, 33, 597 | 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 598 | 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 599 | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 600 | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 601 | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 602 | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 603 | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 604 | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 605 | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 606 | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 607 | 255}; 608 | ref byte tableRef = ref MemoryMarshal.GetReference(toBase64UrlValue); 609 | return Unsafe.AddByteOffset(ref tableRef, (nint)n); 610 | } 611 | 612 | } 613 | } 614 | 615 | -------------------------------------------------------------------------------- /src/SimdBase64.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Library 5 | net9.0 6 | enable 7 | 8 | true 9 | true 10 | AllEnabledByDefault 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/helpers.cs: -------------------------------------------------------------------------------- 1 | namespace SimdBase64 2 | { 3 | } 4 | 5 | -------------------------------------------------------------------------------- /test/TestHelpers.cs: -------------------------------------------------------------------------------- 1 | namespace tests; 2 | using System.Text; 3 | using System.Runtime.InteropServices; 4 | using System.Runtime.Intrinsics; 5 | using System.Runtime.Intrinsics.X86; 6 | using System.Runtime.Intrinsics.Arm; 7 | 8 | public partial class Base64DecodingTests 9 | { 10 | Random random = new Random(12345680); 11 | 12 | private static readonly char[] SpaceCharacters = { ' ', '\t', '\n', '\r', '\f' }; 13 | #pragma warning disable CA1002 14 | protected static void AddSpace(List list, Random random) 15 | { 16 | ArgumentNullException.ThrowIfNull(random); 17 | ArgumentNullException.ThrowIfNull(list); 18 | #pragma warning disable CA5394 // Do not use insecure randomness 19 | int index = random.Next(list.Count + 1); // Random index to insert at 20 | #pragma warning disable CA5394 // Do not use insecure randomness 21 | int charIndex = random.Next(SpaceCharacters.Length); // Random space character 22 | char spaceChar = SpaceCharacters[charIndex]; 23 | byte[] spaceBytes = Encoding.UTF8.GetBytes(new char[] { spaceChar }); 24 | list.Insert(index, spaceBytes[0]); 25 | } 26 | 27 | protected static void AddSpace(List list, Random random) 28 | { 29 | ArgumentNullException.ThrowIfNull(random); 30 | ArgumentNullException.ThrowIfNull(list); 31 | #pragma warning disable CA5394 // Do not use insecure randomness 32 | int index = random.Next(list.Count + 1); // Random index to insert at 33 | #pragma warning disable CA5394 // Do not use insecure randomness 34 | int charIndex = random.Next(SpaceCharacters.Length); // Random space character 35 | char spaceChar = SpaceCharacters[charIndex]; 36 | list.Insert(index, spaceChar); 37 | } 38 | 39 | public static (byte[] modifiedArray, int location) AddGarbage( 40 | byte[] inputArray, Random gen, int? specificLocation = null, byte? specificGarbage = null) 41 | { 42 | ArgumentNullException.ThrowIfNull(inputArray); 43 | ArgumentNullException.ThrowIfNull(gen); 44 | List v = new List(inputArray); 45 | 46 | int len = v.Count; 47 | int i; 48 | 49 | int equalSignIndex = v.FindIndex(c => c == '='); 50 | if (equalSignIndex != -1) 51 | { 52 | len = equalSignIndex; // Adjust the length to before the '=' 53 | } 54 | 55 | if (specificLocation.HasValue && specificLocation.Value < len) 56 | { 57 | i = specificLocation.Value; 58 | } 59 | else 60 | { 61 | i = gen.Next(len + 1); 62 | } 63 | 64 | byte c; 65 | if (specificGarbage.HasValue) 66 | { 67 | c = specificGarbage.Value; 68 | } 69 | else 70 | { 71 | do 72 | { 73 | c = (byte)gen.Next(256); 74 | } while (c == '=' || ToBase64Value[c] != 255); 75 | } 76 | 77 | v.Insert(i, c); 78 | 79 | byte[] modifiedArray = v.ToArray(); 80 | 81 | return (modifiedArray, i); 82 | } 83 | 84 | public static (char[] modifiedArray, int location) AddGarbage( 85 | char[] inputArray, Random gen, int? specificLocation = null, byte? specificGarbage = null) 86 | { 87 | ArgumentNullException.ThrowIfNull(inputArray); 88 | ArgumentNullException.ThrowIfNull(gen); 89 | List v = new List(inputArray); 90 | 91 | int len = v.Count; 92 | int i; 93 | 94 | int equalSignIndex = v.FindIndex(c => c == '='); 95 | if (equalSignIndex != -1) 96 | { 97 | len = equalSignIndex; // Adjust the length to before the '=' 98 | } 99 | 100 | if (specificLocation.HasValue && specificLocation.Value < len) 101 | { 102 | i = specificLocation.Value; 103 | } 104 | else 105 | { 106 | i = gen.Next(len + 1); 107 | } 108 | 109 | char c; 110 | 111 | 112 | do 113 | { 114 | c = (char)gen.Next(256); 115 | } while (c == '=' || ToBase64Value[c] != 255); 116 | 117 | v.Insert(i, c); 118 | 119 | char[] modifiedArray = v.ToArray(); 120 | 121 | return (modifiedArray, i); 122 | } 123 | 124 | 125 | [Flags] 126 | #pragma warning disable CA1515 127 | public enum TestSystemRequirements 128 | { 129 | None = 0, 130 | Arm64 = 1, 131 | X64Avx512 = 2, 132 | X64Avx2 = 4, 133 | X64Sse = 8, 134 | } 135 | 136 | protected sealed class FactOnSystemRequirementAttribute : FactAttribute 137 | { 138 | private TestSystemRequirements RequiredSystems; 139 | #pragma warning disable CA1019 140 | public FactOnSystemRequirementAttribute(TestSystemRequirements requiredSystems) 141 | { 142 | RequiredSystems = requiredSystems; 143 | 144 | if (!IsSystemSupported(requiredSystems)) 145 | { 146 | Skip = "Test is skipped due to not meeting system requirements."; 147 | } 148 | } 149 | 150 | private static bool IsSystemSupported(TestSystemRequirements requiredSystems) 151 | { 152 | switch (RuntimeInformation.ProcessArchitecture) 153 | { 154 | case Architecture.Arm64: 155 | return requiredSystems.HasFlag(TestSystemRequirements.Arm64) && AdvSimd.Arm64.IsSupported && BitConverter.IsLittleEndian; 156 | case Architecture.X64: 157 | return (requiredSystems.HasFlag(TestSystemRequirements.X64Avx512) && Vector512.IsHardwareAccelerated && System.Runtime.Intrinsics.X86.Avx512F.IsSupported) || 158 | (requiredSystems.HasFlag(TestSystemRequirements.X64Avx2) && System.Runtime.Intrinsics.X86.Avx2.IsSupported) || 159 | (requiredSystems.HasFlag(TestSystemRequirements.X64Sse) && System.Runtime.Intrinsics.X86.Ssse3.IsSupported && Popcnt.IsSupported); 160 | default: 161 | return false; 162 | } 163 | } 164 | } 165 | 166 | 167 | protected sealed class TestIfCondition : FactAttribute 168 | { 169 | #pragma warning disable CA1019 170 | public TestIfCondition(Func condition, string skipReason) 171 | { 172 | ArgumentNullException.ThrowIfNull(condition); 173 | // Only set the Skip property if the condition evaluates to false 174 | if (!condition.Invoke()) 175 | { 176 | Skip = skipReason; 177 | } 178 | } 179 | 180 | } 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | } 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | -------------------------------------------------------------------------------- /test/Usings.cs: -------------------------------------------------------------------------------- 1 | global using Xunit; -------------------------------------------------------------------------------- /test/tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net9.0 5 | enable 6 | enable 7 | 8 | false 9 | true 10 | true 11 | true 12 | AllEnabledByDefault 13 | 14 | 15 | 16 | 17 | 18 | 19 | runtime; build; native; contentfiles; analyzers; buildtransitive 20 | all 21 | 22 | 23 | runtime; build; native; contentfiles; analyzers; buildtransitive 24 | all 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | --------------------------------------------------------------------------------