diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 5189f04356..6d31e8c532 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -1,202 +1,196 @@ name: Build on: - push: - branches: - - master - tags: - - "v*" - pull_request: - branches: - - master + push: + branches: + - master + tags: + - "v*" + pull_request: + branches: + - master jobs: - Build: - strategy: - matrix: - options: - - os: ubuntu-latest - framework: net6.0 - sdk: 6.0.x - sdk-preview: true - runtime: -x64 - codecov: false - - os: macos-latest - framework: net6.0 - sdk: 6.0.x - sdk-preview: true - runtime: -x64 - codecov: false - - os: windows-latest - framework: net6.0 - sdk: 6.0.x - sdk-preview: true - runtime: -x64 - codecov: false - - os: ubuntu-latest - framework: net5.0 - runtime: -x64 - codecov: false - - os: macos-latest - framework: net5.0 - runtime: -x64 - codecov: false - - os: windows-latest - framework: net5.0 - runtime: -x64 - codecov: false - - os: ubuntu-latest - framework: netcoreapp3.1 - runtime: -x64 - codecov: true - - os: macos-latest - framework: netcoreapp3.1 - runtime: -x64 - codecov: false - - os: windows-latest - framework: netcoreapp3.1 - runtime: -x64 - codecov: false - - os: windows-latest - framework: netcoreapp2.1 - runtime: -x64 - codecov: false - - os: windows-latest - framework: net472 - runtime: -x64 - codecov: false - - os: windows-latest - framework: net472 - runtime: -x86 - codecov: false - - runs-on: ${{matrix.options.os}} - - steps: - - name: Git Config - shell: bash - run: | - git config --global core.autocrlf false - git config --global core.longpaths true - - - name: Git Checkout - uses: actions/checkout@v2 - with: - fetch-depth: 0 - submodules: recursive - - # See https://github.com/actions/checkout/issues/165#issuecomment-657673315 - - name: Git Create LFS FileList - run: git lfs ls-files -l | cut -d' ' -f1 | sort > .lfs-assets-id - - - name: Git Setup LFS Cache - uses: actions/cache@v2 - id: lfs-cache - with: - path: .git/lfs - key: ${{ runner.os }}-lfs-${{ hashFiles('.lfs-assets-id') }}-v1 - - - name: Git Pull LFS - run: git lfs pull - - - name: NuGet Install - uses: NuGet/setup-nuget@v1 - - - name: NuGet Setup Cache - uses: actions/cache@v2 - id: nuget-cache - with: - path: ~/.nuget - key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets') }} - restore-keys: ${{ runner.os }}-nuget- - - - name: DotNet Setup Preview - if: ${{ matrix.options.sdk-preview == true }} - uses: actions/setup-dotnet@v1 - with: - dotnet-version: ${{ matrix.options.sdk }} - include-prerelease: true - - - name: DotNet Build - if: ${{ matrix.options.sdk-preview != true }} - shell: pwsh - run: ./ci-build.ps1 "${{matrix.options.framework}}" - env: - SIXLABORS_TESTING: True - - - name: DotNet Build Preview - if: ${{ matrix.options.sdk-preview == true }} - shell: pwsh - run: ./ci-build.ps1 "${{matrix.options.framework}}" - env: - SIXLABORS_TESTING_PREVIEW: True - - - name: DotNet Test - if: ${{ matrix.options.sdk-preview != true }} - shell: pwsh - run: ./ci-test.ps1 "${{matrix.options.os}}" "${{matrix.options.framework}}" "${{matrix.options.runtime}}" "${{matrix.options.codecov}}" - env: - SIXLABORS_TESTING: True - XUNIT_PATH: .\tests\ImageSharp.Tests # Required for xunit - - - name: DotNet Test Preview - if: ${{ matrix.options.sdk-preview == true }} - shell: pwsh - run: ./ci-test.ps1 "${{matrix.options.os}}" "${{matrix.options.framework}}" "${{matrix.options.runtime}}" "${{matrix.options.codecov}}" - env: - SIXLABORS_TESTING_PREVIEW: True - XUNIT_PATH: .\tests\ImageSharp.Tests # Required for xunit - - - name: Export Failed Output - uses: actions/upload-artifact@v2 - if: failure() - with: - name: actual_output_${{ runner.os }}_${{ matrix.options.framework }}${{ matrix.options.runtime }}.zip - path: tests/Images/ActualOutput/ - - - name: Codecov Update - uses: codecov/codecov-action@v1 - if: matrix.options.codecov == true && startsWith(github.repository, 'SixLabors') - with: - flags: unittests - - Publish: - needs: [Build] - - runs-on: ubuntu-latest - - if: (github.event_name == 'push') - - steps: - - name: Git Config - shell: bash - run: | - git config --global core.autocrlf false - git config --global core.longpaths true - - - name: Git Checkout - uses: actions/checkout@v2 - with: - fetch-depth: 0 - submodules: recursive - - - name: NuGet Install - uses: NuGet/setup-nuget@v1 - - - name: NuGet Setup Cache - uses: actions/cache@v2 - id: nuget-cache - with: - path: ~/.nuget - key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets') }} - restore-keys: ${{ runner.os }}-nuget- - - - name: DotNet Pack - shell: pwsh - run: ./ci-pack.ps1 - - - name: MyGet Publish - shell: pwsh - run: | - dotnet nuget push .\artifacts\*.nupkg -k ${{secrets.MYGET_TOKEN}} -s https://www.myget.org/F/sixlabors/api/v2/package - dotnet nuget push .\artifacts\*.snupkg -k ${{secrets.MYGET_TOKEN}} -s https://www.myget.org/F/sixlabors/api/v3/index.json - # TODO: If github.ref starts with 'refs/tags' then it was tag push and we can optionally push out package to nuget.org + Build: + strategy: + matrix: + options: + - os: ubuntu-latest + framework: net6.0 + sdk: 6.0.x + sdk-preview: true + runtime: -x64 + codecov: false + - os: macos-latest + framework: net6.0 + sdk: 6.0.x + sdk-preview: true + runtime: -x64 + codecov: false + - os: windows-latest + framework: net6.0 + sdk: 6.0.x + sdk-preview: true + runtime: -x64 + codecov: false + - os: ubuntu-latest + framework: net5.0 + runtime: -x64 + codecov: false + - os: macos-latest + framework: net5.0 + runtime: -x64 + codecov: false + - os: windows-latest + framework: net5.0 + runtime: -x64 + codecov: false + - os: ubuntu-latest + framework: netcoreapp3.1 + runtime: -x64 + codecov: false + - os: macos-latest + framework: netcoreapp3.1 + runtime: -x64 + codecov: false + - os: windows-latest + framework: netcoreapp3.1 + runtime: -x64 + codecov: false + - os: windows-latest + framework: netcoreapp2.1 + runtime: -x64 + codecov: false + - os: windows-latest + framework: net472 + runtime: -x64 + codecov: false + - os: windows-latest + framework: net472 + runtime: -x86 + codecov: false + + runs-on: ${{matrix.options.os}} + + steps: + - name: Git Config + shell: bash + run: | + git config --global core.autocrlf false + git config --global core.longpaths true + + - name: Git Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + submodules: recursive + + # See https://github.com/actions/checkout/issues/165#issuecomment-657673315 + - name: Git Create LFS FileList + run: git lfs ls-files -l | cut -d' ' -f1 | sort > .lfs-assets-id + + - name: Git Setup LFS Cache + uses: actions/cache@v2 + id: lfs-cache + with: + path: .git/lfs + key: ${{ runner.os }}-lfs-${{ hashFiles('.lfs-assets-id') }}-v1 + + - name: Git Pull LFS + run: git lfs pull + + - name: NuGet Install + uses: NuGet/setup-nuget@v1 + + - name: NuGet Setup Cache + uses: actions/cache@v2 + id: nuget-cache + with: + path: ~/.nuget + key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets') }} + restore-keys: ${{ runner.os }}-nuget- + + - name: DotNet Setup Preview + if: ${{ matrix.options.sdk-preview == true }} + uses: actions/setup-dotnet@v1 + with: + dotnet-version: ${{ matrix.options.sdk }} + include-prerelease: true + + - name: DotNet Build + if: ${{ matrix.options.sdk-preview != true }} + shell: pwsh + run: ./ci-build.ps1 "${{matrix.options.framework}}" + env: + SIXLABORS_TESTING: True + + - name: DotNet Build Preview + if: ${{ matrix.options.sdk-preview == true }} + shell: pwsh + run: ./ci-build.ps1 "${{matrix.options.framework}}" + env: + SIXLABORS_TESTING_PREVIEW: True + + - name: DotNet Test + if: ${{ matrix.options.sdk-preview != true }} + shell: pwsh + run: ./ci-test.ps1 "${{matrix.options.os}}" "${{matrix.options.framework}}" "${{matrix.options.runtime}}" "${{matrix.options.codecov}}" + env: + SIXLABORS_TESTING: True + XUNIT_PATH: .\tests\ImageSharp.Tests # Required for xunit + + - name: DotNet Test Preview + if: ${{ matrix.options.sdk-preview == true }} + shell: pwsh + run: ./ci-test.ps1 "${{matrix.options.os}}" "${{matrix.options.framework}}" "${{matrix.options.runtime}}" "${{matrix.options.codecov}}" + env: + SIXLABORS_TESTING_PREVIEW: True + XUNIT_PATH: .\tests\ImageSharp.Tests # Required for xunit + + - name: Export Failed Output + uses: actions/upload-artifact@v2 + if: failure() + with: + name: actual_output_${{ runner.os }}_${{ matrix.options.framework }}${{ matrix.options.runtime }}.zip + path: tests/Images/ActualOutput/ + + Publish: + needs: [Build] + + runs-on: ubuntu-latest + + if: (github.event_name == 'push') + + steps: + - name: Git Config + shell: bash + run: | + git config --global core.autocrlf false + git config --global core.longpaths true + + - name: Git Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + submodules: recursive + + - name: NuGet Install + uses: NuGet/setup-nuget@v1 + + - name: NuGet Setup Cache + uses: actions/cache@v2 + id: nuget-cache + with: + path: ~/.nuget + key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets') }} + restore-keys: ${{ runner.os }}-nuget- + + - name: DotNet Pack + shell: pwsh + run: ./ci-pack.ps1 + + - name: MyGet Publish + shell: pwsh + run: | + dotnet nuget push .\artifacts\*.nupkg -k ${{secrets.MYGET_TOKEN}} -s https://www.myget.org/F/sixlabors/api/v2/package + dotnet nuget push .\artifacts\*.snupkg -k ${{secrets.MYGET_TOKEN}} -s https://www.myget.org/F/sixlabors/api/v3/index.json + # TODO: If github.ref starts with 'refs/tags' then it was tag push and we can optionally push out package to nuget.org diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml new file mode 100644 index 0000000000..2b14f2a4b7 --- /dev/null +++ b/.github/workflows/code-coverage.yml @@ -0,0 +1,81 @@ +name: CodeCoverage + +on: + schedule: + # 2AM every Tuesday/Thursday + - cron: "0 2 * * 2,4" +jobs: + Build: + strategy: + matrix: + options: + - os: ubuntu-latest + framework: netcoreapp3.1 + runtime: -x64 + codecov: true + + runs-on: ${{matrix.options.os}} + + steps: + - name: Git Config + shell: bash + run: | + git config --global core.autocrlf false + git config --global core.longpaths true + + - name: Git Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + submodules: recursive + + # See https://github.com/actions/checkout/issues/165#issuecomment-657673315 + - name: Git Create LFS FileList + run: git lfs ls-files -l | cut -d' ' -f1 | sort > .lfs-assets-id + + - name: Git Setup LFS Cache + uses: actions/cache@v2 + id: lfs-cache + with: + path: .git/lfs + key: ${{ runner.os }}-lfs-${{ hashFiles('.lfs-assets-id') }}-v1 + + - name: Git Pull LFS + run: git lfs pull + + - name: NuGet Install + uses: NuGet/setup-nuget@v1 + + - name: NuGet Setup Cache + uses: actions/cache@v2 + id: nuget-cache + with: + path: ~/.nuget + key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets') }} + restore-keys: ${{ runner.os }}-nuget- + + - name: DotNet Build + shell: pwsh + run: ./ci-build.ps1 "${{matrix.options.framework}}" + env: + SIXLABORS_TESTING: True + + - name: DotNet Test + shell: pwsh + run: ./ci-test.ps1 "${{matrix.options.os}}" "${{matrix.options.framework}}" "${{matrix.options.runtime}}" "${{matrix.options.codecov}}" + env: + SIXLABORS_TESTING: True + XUNIT_PATH: .\tests\ImageSharp.Tests # Required for xunit + + - name: Export Failed Output + uses: actions/upload-artifact@v2 + if: failure() + with: + name: actual_output_${{ runner.os }}_${{ matrix.options.framework }}${{ matrix.options.runtime }}.zip + path: tests/Images/ActualOutput/ + + - name: Codecov Update + uses: codecov/codecov-action@v1 + if: matrix.options.codecov == true && startsWith(github.repository, 'SixLabors') + with: + flags: unittests diff --git a/ImageSharp.sln b/ImageSharp.sln index f16f98ac59..17d293b434 100644 --- a/ImageSharp.sln +++ b/ImageSharp.sln @@ -551,6 +551,7 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{C0D7754B-5277-438E-ABEB-2BA34401B5A7}" ProjectSection(SolutionItems) = preProject .github\workflows\build-and-test.yml = .github\workflows\build-and-test.yml + .github\workflows\code-coverage.yml = .github\workflows\code-coverage.yml EndProjectSection EndProject Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "SharedInfrastructure", "shared-infrastructure\src\SharedInfrastructure\SharedInfrastructure.shproj", "{68A8CC40-6AED-4E96-B524-31B1158FDEEA}" diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs index ce5e5110b6..2ae3ae86bc 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs @@ -167,18 +167,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder int mcusPerLine = this.frame.McusPerLine; ref HuffmanScanBuffer buffer = ref this.scanBuffer; - // Pre-derive the huffman table to avoid in-loop checks. - for (int i = 0; i < this.componentsCount; i++) - { - int order = this.frame.ComponentOrder[i]; - JpegComponent component = this.components[order]; - - ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; - ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId]; - dcHuffmanTable.Configure(); - acHuffmanTable.Configure(); - } - for (int j = 0; j < mcusPerColumn; j++) { this.cancellationToken.ThrowIfCancellationRequested(); @@ -248,8 +236,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId]; - dcHuffmanTable.Configure(); - acHuffmanTable.Configure(); for (int j = 0; j < h; j++) { @@ -347,15 +333,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder int mcusPerLine = this.frame.McusPerLine; ref HuffmanScanBuffer buffer = ref this.scanBuffer; - // Pre-derive the huffman table to avoid in-loop checks. - for (int k = 0; k < this.componentsCount; k++) - { - int order = this.frame.ComponentOrder[k]; - JpegComponent component = this.components[order]; - ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; - dcHuffmanTable.Configure(); - } - for (int j = 0; j < mcusPerColumn; j++) { for (int i = 0; i < mcusPerLine; i++) @@ -416,7 +393,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder if (this.SpectralStart == 0) { ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; - dcHuffmanTable.Configure(); for (int j = 0; j < h; j++) { @@ -444,7 +420,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder else { ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId]; - acHuffmanTable.Configure(); for (int j = 0; j < h; j++) { @@ -752,11 +727,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// Table index. /// Code lengths. /// Code values. + /// The provided spare workspace memory, can be dirty. [MethodImpl(InliningOptions.ShortMethod)] - public void BuildHuffmanTable(int type, int index, ReadOnlySpan codeLengths, ReadOnlySpan values) + public void BuildHuffmanTable(int type, int index, ReadOnlySpan codeLengths, ReadOnlySpan values, Span workspace) { HuffmanTable[] tables = type == 0 ? this.dcHuffmanTables : this.acHuffmanTables; - tables[index] = new HuffmanTable(codeLengths, values); + tables[index] = new HuffmanTable(codeLengths, values, workspace); } } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs index f18c636278..bee5e0229b 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs @@ -13,12 +13,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder [StructLayout(LayoutKind.Sequential)] internal unsafe struct HuffmanTable { - private bool isConfigured; - /// - /// Derived from the DHT marker. Sizes[k] = # of symbols with codes of length k bits; Sizes[0] is unused. + /// Memory workspace buffer size used in ctor. /// - public fixed byte Sizes[17]; + public const int WorkspaceByteSize = 256 * sizeof(uint); /// /// Derived from the DHT marker. Contains the symbols, in order of incremental code length. @@ -58,51 +56,35 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// /// Initializes a new instance of the struct. /// - /// The code lengths - /// The huffman values - public HuffmanTable(ReadOnlySpan codeLengths, ReadOnlySpan values) + /// The code lengths. + /// The huffman values. + /// The provided spare workspace memory, can be dirty. + public HuffmanTable(ReadOnlySpan codeLengths, ReadOnlySpan values, Span workspace) { - this.isConfigured = false; - Unsafe.CopyBlockUnaligned(ref this.Sizes[0], ref MemoryMarshal.GetReference(codeLengths), (uint)codeLengths.Length); Unsafe.CopyBlockUnaligned(ref this.Values[0], ref MemoryMarshal.GetReference(values), (uint)values.Length); - } - - /// - /// Expands the HuffmanTable into its readable form. - /// - public void Configure() - { - if (this.isConfigured) - { - return; - } - Span huffSize = stackalloc char[257]; - Span huffCode = stackalloc uint[257]; - - // Figure C.1: make table of Huffman code length for each symbol + // Generate codes + uint code = 0; + int si = 1; int p = 0; - for (int j = 1; j <= 16; j++) + for (int i = 1; i <= 16; i++) { - int i = this.Sizes[j]; - while (i-- != 0) + int count = codeLengths[i]; + for (int j = 0; j < count; j++) { - huffSize[p++] = (char)j; + workspace[p++] = code; + code++; } - } - huffSize[p] = (char)0; - - // Figure C.2: generate the codes themselves - uint code = 0; - int si = huffSize[0]; - p = 0; - while (huffSize[p] != 0) - { - while (huffSize[p] == si) + // 'code' is now 1 more than the last code used for codelength 'si' + // in the valid worst possible case 'code' would have the least + // significant bit set to 1, e.g. 1111(0) +1 => 1111(1) + // but it must still fit in 'si' bits since no huffman code can be equal to all 1s + // if last code is all ones, e.g. 1111(1), then incrementing it by 1 would yield + // a new code which occupies one extra bit, e.g. 1111(1) +1 => (1)1111(0) + if (code >= (1 << si)) { - huffCode[p++] = code; - code++; + JpegThrowHelper.ThrowInvalidImageContentException("Bad huffman table."); } code <<= 1; @@ -113,11 +95,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder p = 0; for (int j = 1; j <= 16; j++) { - if (this.Sizes[j] != 0) + if (codeLengths[j] != 0) { - this.ValOffset[j] = p - (int)huffCode[p]; - p += this.Sizes[j]; - this.MaxCode[j] = huffCode[p - 1]; // Maximum code of length l + this.ValOffset[j] = p - (int)workspace[p]; + p += codeLengths[j]; + this.MaxCode[j] = workspace[p - 1]; // Maximum code of length l this.MaxCode[j] <<= JpegConstants.Huffman.RegisterSize - j; // Left justify this.MaxCode[j] |= (1ul << (JpegConstants.Huffman.RegisterSize - j)) - 1; } @@ -142,11 +124,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder for (int length = 1; length <= JpegConstants.Huffman.LookupBits; length++) { int jShift = JpegConstants.Huffman.LookupBits - length; - for (int i = 1; i <= this.Sizes[length]; i++, p++) + for (int i = 1; i <= codeLengths[length]; i++, p++) { // length = current code's length, p = its index in huffCode[] & Values[]. // Generate left-justified code followed by all possible bit sequences - int lookBits = (int)(huffCode[p] << jShift); + int lookBits = (int)(workspace[p] << jShift); for (int ctr = 1 << (JpegConstants.Huffman.LookupBits - length); ctr > 0; ctr--) { this.LookaheadSize[lookBits] = (byte)length; @@ -155,8 +137,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder } } } - - this.isConfigured = true; } } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 18f8dcc561..6c529f0c1f 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -846,6 +846,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg } } } + else + { + // If the profile is unknown skip over the rest of it. + stream.Skip(remaining); + } } /// @@ -1153,12 +1158,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// The remaining bytes in the segment block. private void ProcessDefineHuffmanTablesMarker(BufferedReadStream stream, int remaining) { - int length = remaining; + const int codeLengthsByteSize = 17; + const int codeValuesMaxByteSize = 256; + const int totalBufferSize = codeLengthsByteSize + codeValuesMaxByteSize + HuffmanTable.WorkspaceByteSize; - using (IMemoryOwner huffmanData = this.Configuration.MemoryAllocator.Allocate(256, AllocationOptions.Clean)) + int length = remaining; + using (IMemoryOwner buffer = this.Configuration.MemoryAllocator.Allocate(totalBufferSize)) { - Span huffmanDataSpan = huffmanData.GetSpan(); - ref byte huffmanDataRef = ref MemoryMarshal.GetReference(huffmanDataSpan); + Span bufferSpan = buffer.GetSpan(); + Span huffmanLegthsSpan = bufferSpan.Slice(0, codeLengthsByteSize); + Span huffmanValuesSpan = bufferSpan.Slice(codeLengthsByteSize, codeValuesMaxByteSize); + Span tableWorkspace = MemoryMarshal.Cast(bufferSpan.Slice(codeLengthsByteSize + codeValuesMaxByteSize)); + for (int i = 2; i < remaining;) { byte huffmanTableSpec = (byte)stream.ReadByte(); @@ -1168,49 +1179,40 @@ namespace SixLabors.ImageSharp.Formats.Jpeg // Types 0..1 DC..AC if (tableType > 1) { - JpegThrowHelper.ThrowInvalidImageContentException($"Bad huffman table type: {tableType}"); + JpegThrowHelper.ThrowInvalidImageContentException($"Bad huffman table type: {tableType}."); } // Max tables of each type if (tableIndex > 3) { - JpegThrowHelper.ThrowInvalidImageContentException($"Bad huffman table index: {tableIndex}"); + JpegThrowHelper.ThrowInvalidImageContentException($"Bad huffman table index: {tableIndex}."); } - stream.Read(huffmanDataSpan, 0, 16); + stream.Read(huffmanLegthsSpan, 1, 16); - using (IMemoryOwner codeLengths = this.Configuration.MemoryAllocator.Allocate(17, AllocationOptions.Clean)) + int codeLengthSum = 0; + for (int j = 1; j < 17; j++) { - Span codeLengthsSpan = codeLengths.GetSpan(); - ref byte codeLengthsRef = ref MemoryMarshal.GetReference(codeLengthsSpan); - int codeLengthSum = 0; - - for (int j = 1; j < 17; j++) - { - codeLengthSum += Unsafe.Add(ref codeLengthsRef, j) = Unsafe.Add(ref huffmanDataRef, j - 1); - } + codeLengthSum += huffmanLegthsSpan[j]; + } - length -= 17; + length -= 17; - if (codeLengthSum > 256 || codeLengthSum > length) - { - JpegThrowHelper.ThrowInvalidImageContentException("Huffman table has excessive length."); - } + if (codeLengthSum > 256 || codeLengthSum > length) + { + JpegThrowHelper.ThrowInvalidImageContentException("Huffman table has excessive length."); + } - using (IMemoryOwner huffmanValues = this.Configuration.MemoryAllocator.Allocate(256, AllocationOptions.Clean)) - { - Span huffmanValuesSpan = huffmanValues.GetSpan(); - stream.Read(huffmanValuesSpan, 0, codeLengthSum); + stream.Read(huffmanValuesSpan, 0, codeLengthSum); - i += 17 + codeLengthSum; + i += 17 + codeLengthSum; - this.scanDecoder.BuildHuffmanTable( - tableType, - tableIndex, - codeLengthsSpan, - huffmanValuesSpan); - } - } + this.scanDecoder.BuildHuffmanTable( + tableType, + tableIndex, + huffmanLegthsSpan, + huffmanValuesSpan.Slice(0, codeLengthSum), + tableWorkspace); } } }