mirror of https://github.com/SixLabors/ImageSharp
2 changed files with 179 additions and 0 deletions
@ -0,0 +1,165 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Buffers.Binary; |
|||
using System.Diagnostics; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jxl.IO; |
|||
|
|||
/// <summary>
|
|||
/// Represents a bitstream reader.
|
|||
/// </summary>
|
|||
internal sealed class JxlBitReader(ReadOnlyMemory<byte> bytes) |
|||
{ |
|||
private ulong buffer; |
|||
private uint bufferRemainingBits; |
|||
private int pointer; |
|||
private bool endOfStream; |
|||
|
|||
/// <summary>
|
|||
/// Fetches a new buffer.
|
|||
/// </summary>
|
|||
private void RefillCore() |
|||
{ |
|||
ReadOnlySpan<byte> samplesSpan = bytes.Span; |
|||
|
|||
int remaining = samplesSpan.Length - this.pointer; |
|||
if (remaining <= 0) |
|||
{ |
|||
// we don't have any more data... mark an end of stream
|
|||
this.buffer = 0; |
|||
this.bufferRemainingBits = 0; |
|||
this.endOfStream = true; |
|||
return; |
|||
} |
|||
|
|||
if (remaining >= 8) |
|||
{ |
|||
this.buffer = BinaryPrimitives.ReadUInt64LittleEndian(samplesSpan[this.pointer..]); |
|||
this.bufferRemainingBits = 64u; |
|||
this.pointer += 8; |
|||
} |
|||
else |
|||
{ |
|||
ulong value = 0; |
|||
for (int i = 0; i < remaining; i++) |
|||
{ |
|||
value |= (ulong)samplesSpan[this.pointer + i] << (8 * i); |
|||
} |
|||
|
|||
this.buffer = value; |
|||
this.bufferRemainingBits = (uint)(remaining * 8); |
|||
this.pointer += remaining; |
|||
} |
|||
} |
|||
|
|||
private void MaybeRefill() |
|||
{ |
|||
if (this.bufferRemainingBits <= 0) |
|||
{ |
|||
this.RefillCore(); |
|||
} |
|||
} |
|||
|
|||
private ulong ReadBits64Core(uint n, bool peek = false) |
|||
{ |
|||
Debug.Assert(n <= 64, "Too many bits to pack into ulong"); |
|||
this.MaybeRefill(); |
|||
|
|||
if (this.endOfStream) |
|||
{ |
|||
JxlThrowHelper.ThrowEndOfStream(); |
|||
} |
|||
|
|||
if (n <= this.bufferRemainingBits) |
|||
{ |
|||
ulong result = this.buffer & ((1UL << (int)n) - 1); |
|||
|
|||
if (!peek) |
|||
{ |
|||
this.buffer >>= (int)n; |
|||
this.bufferRemainingBits -= n; |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
else |
|||
{ |
|||
uint bitsFromCurrent = this.bufferRemainingBits; |
|||
ulong part = this.buffer & ((1UL << (int)bitsFromCurrent) - 1); |
|||
|
|||
this.buffer >>= (int)bitsFromCurrent; |
|||
this.bufferRemainingBits = 0; |
|||
|
|||
this.RefillCore(); |
|||
|
|||
uint bitsFromNext = n - bitsFromCurrent; |
|||
ulong nextPart = this.buffer & ((1UL << (int)bitsFromNext) - 1); |
|||
|
|||
if (!peek) |
|||
{ |
|||
this.buffer >>= (int)bitsFromNext; |
|||
this.bufferRemainingBits -= bitsFromNext; |
|||
} |
|||
|
|||
return part | (nextPart << (int)bitsFromCurrent); |
|||
} |
|||
} |
|||
|
|||
private uint ReadBits32Core(uint n, bool peek = false) |
|||
{ |
|||
Debug.Assert(n <= 32, "Too many bits to pack into uint"); |
|||
this.MaybeRefill(); |
|||
|
|||
if (this.endOfStream) |
|||
{ |
|||
JxlThrowHelper.ThrowEndOfStream(); |
|||
} |
|||
|
|||
if (n <= this.bufferRemainingBits) |
|||
{ |
|||
uint result = (uint)(this.buffer & ((1UL << (int)n) - 1)); |
|||
|
|||
if (!peek) |
|||
{ |
|||
this.buffer >>= (int)n; |
|||
this.bufferRemainingBits -= n; |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
else |
|||
{ |
|||
uint bitsFromCurrent = this.bufferRemainingBits; |
|||
uint part = (uint)(this.buffer & ((1UL << (int)bitsFromCurrent) - 1)); |
|||
|
|||
this.buffer >>= (int)bitsFromCurrent; |
|||
this.bufferRemainingBits = 0; |
|||
|
|||
this.RefillCore(); |
|||
|
|||
uint bitsFromNext = n - bitsFromCurrent; |
|||
uint nextPart = (uint)(this.buffer & ((1UL << (int)bitsFromNext) - 1)); |
|||
|
|||
if (!peek) |
|||
{ |
|||
this.buffer >>= (int)bitsFromNext; |
|||
this.bufferRemainingBits -= bitsFromNext; |
|||
} |
|||
|
|||
return part | (nextPart << (int)bitsFromCurrent); |
|||
} |
|||
} |
|||
|
|||
public uint ReadBits32(uint bits) => this.ReadBits32Core(bits, peek: false); |
|||
|
|||
public uint PeekBits32(uint bits) => this.ReadBits32Core(bits, peek: true); |
|||
|
|||
public void SkipBits32(uint bits) => _ = this.ReadBits32(bits); |
|||
|
|||
public ulong ReadBits64(uint bits) => this.ReadBits64Core(bits, peek: false); |
|||
|
|||
public ulong PeekBits64(uint bits) => this.ReadBits64Core(bits, peek: true); |
|||
|
|||
public void SkipBits64(uint bits) => _ = this.ReadBits64(bits); |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Diagnostics.CodeAnalysis; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jxl; |
|||
|
|||
internal static class JxlThrowHelper |
|||
{ |
|||
private static readonly EndOfStreamException EndOfStream = new(); |
|||
|
|||
[DoesNotReturn] |
|||
public static void ThrowEndOfStream() => throw EndOfStream; |
|||
} |
|||
Loading…
Reference in new issue