mirror of
https://github.com/chylex/Brotli-Builder.git
synced 2025-05-11 02:34:13 +02:00
Add RingBufferFast & optimize construction
This commit is contained in:
parent
b29c044263
commit
440fa7fd16
BrotliLib
UnitTests/Collections
@ -17,8 +17,8 @@ namespace BrotliLib.Brotli{
|
||||
|
||||
public BrotliFileParameters Parameters { get; }
|
||||
|
||||
public RingBuffer<byte> LiteralBuffer { get; }
|
||||
public RingBuffer<int> DistanceBuffer { get; }
|
||||
public RingBufferFast<byte> LiteralBuffer { get; }
|
||||
public RingBufferFast<int> DistanceBuffer { get; }
|
||||
|
||||
private IBrotliOutput outputState;
|
||||
|
||||
@ -28,8 +28,8 @@ namespace BrotliLib.Brotli{
|
||||
this.Parameters = parameters;
|
||||
this.outputState = outputState;
|
||||
|
||||
this.LiteralBuffer = new RingBuffer<byte>(0, 0);
|
||||
this.DistanceBuffer = new RingBuffer<int>(16, 15, 11, 4);
|
||||
this.LiteralBuffer = RingBufferFast<byte>.From(0, 0);
|
||||
this.DistanceBuffer = RingBufferFast<int>.From(16, 15, 11, 4);
|
||||
}
|
||||
|
||||
public BrotliGlobalState(BrotliFileParameters parameters) : this(parameters, new BrotliOutputWindowed(parameters.WindowSize)){}
|
||||
@ -38,8 +38,8 @@ namespace BrotliLib.Brotli{
|
||||
this.Parameters = original.Parameters;
|
||||
this.outputState = original.outputState.Clone();
|
||||
|
||||
this.LiteralBuffer = new RingBuffer<byte>(original.LiteralBuffer);
|
||||
this.DistanceBuffer = new RingBuffer<int>(original.DistanceBuffer);
|
||||
this.LiteralBuffer = new RingBufferFast<byte>(original.LiteralBuffer);
|
||||
this.DistanceBuffer = new RingBufferFast<int>(original.DistanceBuffer);
|
||||
}
|
||||
|
||||
public BrotliGlobalState Clone(){
|
||||
|
@ -14,11 +14,11 @@ namespace BrotliLib.Brotli.Utils{
|
||||
private byte Code1Value => (byte)((1 + last.Front) % count);
|
||||
|
||||
private readonly int count;
|
||||
private readonly RingBuffer<byte> last;
|
||||
private readonly RingBufferFast<byte> last;
|
||||
|
||||
public BlockTypeTracker(int count){
|
||||
this.count = count;
|
||||
this.last = new RingBuffer<byte>(1, 0);
|
||||
this.last = RingBufferFast<byte>.From(1, 0);
|
||||
}
|
||||
|
||||
public List<BlockTypeCode> FindCodes(byte value){
|
||||
|
@ -5,6 +5,14 @@ namespace BrotliLib.Collections{
|
||||
/// A ring buffer implemented as a fixed-size queue.
|
||||
/// </summary>
|
||||
public sealed class RingBuffer<T>{
|
||||
/// <summary>
|
||||
/// Initializes a new ring buffer, with the values and size of the provided <paramref name="values"/>.
|
||||
/// </summary>
|
||||
/// <param name="values">Initial values placed into the buffer. Element at [0] is at the back of the queue.</param>
|
||||
public static RingBuffer<T> From(params T[] values){
|
||||
return new RingBuffer<T>(values);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Amount of elements in the buffer.
|
||||
/// </summary>
|
||||
@ -41,7 +49,7 @@ namespace BrotliLib.Collections{
|
||||
/// Initializes a new ring buffer, with the values and size of the provided <paramref name="values"/>.
|
||||
/// </summary>
|
||||
/// <param name="values">Initial values placed into the buffer. Element at [0] is at the back of the queue.</param>
|
||||
public RingBuffer(params T[] values){
|
||||
public RingBuffer(T[] values){
|
||||
if (values.Length == 0){
|
||||
throw new ArgumentException("Ring buffer size cannot be zero.");
|
||||
}
|
||||
|
84
BrotliLib/Collections/RingBufferFast.cs
Normal file
84
BrotliLib/Collections/RingBufferFast.cs
Normal file
@ -0,0 +1,84 @@
|
||||
using System;
|
||||
using BrotliLib.Numbers;
|
||||
|
||||
namespace BrotliLib.Collections{
|
||||
/// <summary>
|
||||
/// A ring buffer implemented as a fixed-size queue. Uses bit mask to wrap around, meaning it requires a power-of-two amount of elements.
|
||||
/// </summary>
|
||||
public sealed class RingBufferFast<T>{
|
||||
/// <summary>
|
||||
/// Initializes a new ring buffer, with the values and size of the provided <paramref name="values"/>.
|
||||
/// </summary>
|
||||
/// <param name="values">Initial values placed into the buffer. Element at [0] is at the back of the queue.</param>
|
||||
public static RingBufferFast<T> From(params T[] values){
|
||||
return new RingBufferFast<T>(values);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Amount of elements in the buffer.
|
||||
/// </summary>
|
||||
public int Length => mask + 1;
|
||||
|
||||
/// <summary>
|
||||
/// Element at index [<see cref="Length"/> - 1].
|
||||
/// </summary>
|
||||
public T Front => this[mask];
|
||||
|
||||
/// <summary>
|
||||
/// Element at index [0].
|
||||
/// </summary>
|
||||
public T Back => this[0];
|
||||
|
||||
/// <summary>
|
||||
/// Element at the provided <paramref name="index"/>.
|
||||
/// </summary>
|
||||
/// <exception cref="IndexOutOfRangeException">Thrown when the provided <paramref name="index"/> is negative.</exception>
|
||||
public T this[int index]{
|
||||
get{
|
||||
if (index < 0){
|
||||
throw new IndexOutOfRangeException("Ring buffer index cannot be negative.");
|
||||
}
|
||||
|
||||
return values[(index + accessOffset) & mask];
|
||||
}
|
||||
}
|
||||
|
||||
private readonly T[] values;
|
||||
private readonly int mask;
|
||||
private int accessOffset;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new ring buffer, with the values and size of the provided <paramref name="values"/>.
|
||||
/// </summary>
|
||||
/// <param name="values">Initial values placed into the buffer. Element at [0] is at the back of the queue.</param>
|
||||
public RingBufferFast(T[] values){
|
||||
int length = values.Length;
|
||||
|
||||
if (length == 0){
|
||||
throw new ArgumentException("Ring buffer size cannot be zero.");
|
||||
}
|
||||
else if (length != 1 << Log2.Floor(length)){
|
||||
throw new ArgumentException("Fast ring buffer size must be a power of 2.");
|
||||
}
|
||||
|
||||
this.values = values;
|
||||
this.mask = length - 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new ring buffer, with a shallow copy of the contents of the provided <paramref name="original"/>.
|
||||
/// </summary>
|
||||
public RingBufferFast(RingBufferFast<T> original){
|
||||
this.values = (T[])original.values.Clone();
|
||||
this.mask = original.mask;
|
||||
this.accessOffset = original.accessOffset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pushes a new value to the front of the queue, removing the value at the back.
|
||||
/// </summary>
|
||||
public void Push(T value){
|
||||
values[accessOffset++ & mask] = value;
|
||||
}
|
||||
}
|
||||
}
|
@ -8,7 +8,7 @@ open BrotliLib.Collections
|
||||
module InitializationAndAccess =
|
||||
[<Fact>]
|
||||
let ``constructing with one value initializes the buffer correctly`` () =
|
||||
let buffer = RingBuffer(10)
|
||||
let buffer = RingBuffer.From(10)
|
||||
|
||||
Assert.Equal(1, buffer.Length)
|
||||
Assert.Equal(10, buffer.Front)
|
||||
@ -20,7 +20,7 @@ module InitializationAndAccess =
|
||||
|
||||
[<Fact>]
|
||||
let ``constructing with two values initializes the buffer correctly`` () =
|
||||
let buffer = RingBuffer(10, 20)
|
||||
let buffer = RingBuffer.From(10, 20)
|
||||
|
||||
Assert.Equal(2, buffer.Length)
|
||||
Assert.Equal(20, buffer.Front)
|
||||
@ -32,7 +32,7 @@ module InitializationAndAccess =
|
||||
|
||||
[<Fact>]
|
||||
let ``constructing with three values initializes the buffer correctly`` () =
|
||||
let buffer = RingBuffer(10, 20, 30)
|
||||
let buffer = RingBuffer.From(10, 20, 30)
|
||||
|
||||
Assert.Equal(3, buffer.Length)
|
||||
Assert.Equal(30, buffer.Front)
|
||||
@ -44,25 +44,25 @@ module InitializationAndAccess =
|
||||
|
||||
[<Fact>]
|
||||
let ``constructing with no parameters throws exception`` () =
|
||||
Assert.Throws<ArgumentException>(fun () -> RingBuffer() |> ignore)
|
||||
Assert.Throws<ArgumentException>(fun () -> RingBuffer.From() |> ignore)
|
||||
|
||||
[<Fact>]
|
||||
let ``accessing a negative index throws exception`` () =
|
||||
let buffer = RingBuffer(10, 20, 30)
|
||||
let buffer = RingBuffer.From(10, 20, 30)
|
||||
Assert.Throws<IndexOutOfRangeException>(fun () -> buffer.[-1] |> ignore)
|
||||
|
||||
|
||||
module Mutability =
|
||||
[<Fact>]
|
||||
let ``pushing to a buffer of size 1 replaces the stored value`` () =
|
||||
let buffer = RingBuffer(10)
|
||||
let buffer = RingBuffer.From(10)
|
||||
|
||||
buffer.Push(90)
|
||||
Assert.Equal(90, buffer.Front)
|
||||
|
||||
[<Fact>]
|
||||
let ``pushing to a buffer of size 2 inserts the value and shifts the rest`` () =
|
||||
let buffer = RingBuffer(10, 20)
|
||||
let buffer = RingBuffer.From(10, 20)
|
||||
|
||||
buffer.Push(90)
|
||||
Assert.Equal(90, buffer.Front)
|
||||
@ -74,7 +74,7 @@ module Mutability =
|
||||
|
||||
[<Fact>]
|
||||
let ``pushing to a buffer of size 3 inserts the value and shifts the rest`` () =
|
||||
let buffer = RingBuffer(10, 20, 30)
|
||||
let buffer = RingBuffer.From(10, 20, 30)
|
||||
|
||||
buffer.Push(90)
|
||||
Assert.Equal(90, buffer.Front)
|
||||
@ -88,7 +88,7 @@ module Mutability =
|
||||
|
||||
[<Fact>]
|
||||
let ``accessing a negative index after pushing throws exception`` () =
|
||||
let buffer = RingBuffer(10, 20, 30)
|
||||
let buffer = RingBuffer.From(10, 20, 30)
|
||||
|
||||
buffer.Push(90)
|
||||
buffer.Push(80)
|
||||
|
Loading…
Reference in New Issue
Block a user