Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 31 additions & 35 deletions src/GreenDonut/src/GreenDonut.Data/Internal/ExpressionHasher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ public ExpressionHasher()
_initialSize = _buffer.Length;
}

internal int BufferSize => _buffer.Length;
internal int InitialBufferSize => _initialSize;

public ExpressionHasher Add(Expression expression)
{
Visit(expression);
Expand All @@ -50,8 +53,7 @@ public ExpressionHasher Add<T>(SortDefinition<T> sortDefinition)

public ExpressionHasher Add(char c)
{
Append(c);
Append(';');
Append(c, ';');
return this;
}

Expand Down Expand Up @@ -211,12 +213,10 @@ private void Append(int i)
{
int written;

while (!Utf8Formatter.TryFormat(i, _buffer.AsSpan()[_start..], out written))
var span = _buffer.AsSpan()[_start..];
while (!Utf8Formatter.TryFormat(i, span, out written))
{
var newBuffer = ArrayPool<byte>.Shared.Rent(_buffer.Length * 2);
_buffer.AsSpan()[.._start].CopyTo(newBuffer);
ArrayPool<byte>.Shared.Return(_buffer);
_buffer = newBuffer;
span = ExpandRollingBufferCapacity(_start);
}

_start += written;
Expand Down Expand Up @@ -262,23 +262,17 @@ private void Append(char s)
{
if (_start == _buffer.Length)
{
var newBuffer = ArrayPool<byte>.Shared.Rent(_buffer.Length * 2);
_buffer.AsSpan().CopyTo(newBuffer);
ArrayPool<byte>.Shared.Return(_buffer);
_buffer = newBuffer;
ExpandBufferCapacity();
}

_buffer[_start++] = (byte)s;
}

private void Append(char a, char b)
{
if (_start + 1 == _buffer.Length)
if (_start + 1 >= _buffer.Length)
{
var newBuffer = ArrayPool<byte>.Shared.Rent(_buffer.Length * 2);
_buffer.AsSpan().CopyTo(newBuffer);
ArrayPool<byte>.Shared.Return(_buffer);
_buffer = newBuffer;
ExpandBufferCapacity();
}

_buffer[_start++] = (byte)a;
Expand All @@ -293,11 +287,7 @@ private void Append(string s)

while (!Encoding.UTF8.TryGetBytes(chars, span, out written))
{
var newBuffer = ArrayPool<byte>.Shared.Rent(_buffer.Length * 2);
_buffer.AsSpan()[.._start].CopyTo(newBuffer);
ArrayPool<byte>.Shared.Return(_buffer);
_buffer = newBuffer;
span = _buffer.AsSpan()[_start..];
span = ExpandRollingBufferCapacity(_start);
}

_start += written;
Expand All @@ -310,11 +300,7 @@ private void Append(ReadOnlySpan<char> s)

while (!Encoding.UTF8.TryGetBytes(s, span, out written))
{
var newBuffer = ArrayPool<byte>.Shared.Rent(_buffer.Length * 2);
_buffer.AsSpan()[.._start].CopyTo(newBuffer);
ArrayPool<byte>.Shared.Return(_buffer);
_buffer = newBuffer;
span = _buffer.AsSpan()[_start..];
span = ExpandRollingBufferCapacity(_start);
}

_start += written;
Expand All @@ -324,10 +310,7 @@ private void Append(byte b)
{
if (_start == _buffer.Length)
{
var newBuffer = ArrayPool<byte>.Shared.Rent(_buffer.Length * 2);
_buffer.AsSpan().CopyTo(newBuffer);
ArrayPool<byte>.Shared.Return(_buffer);
_buffer = newBuffer;
ExpandBufferCapacity();
}

_buffer[_start++] = b;
Expand All @@ -339,13 +322,26 @@ private void Append(ReadOnlySpan<byte> span)

while (!span.TryCopyTo(bufferSpan))
{
var newBuffer = ArrayPool<byte>.Shared.Rent(_buffer.Length * 2);
_buffer.AsSpan()[.._start].CopyTo(newBuffer);
ArrayPool<byte>.Shared.Return(_buffer);
_buffer = newBuffer;
bufferSpan = _buffer.AsSpan()[_start..];
bufferSpan = ExpandRollingBufferCapacity(_start);
}

_start += span.Length;
}

private void ExpandBufferCapacity()
{
var newBuffer = ArrayPool<byte>.Shared.Rent(_buffer.Length * 2);
_buffer.AsSpan().CopyTo(newBuffer);
ArrayPool<byte>.Shared.Return(_buffer);
_buffer = newBuffer;
}

private Span<byte> ExpandRollingBufferCapacity(int bufferIndex)
{
var newBuffer = ArrayPool<byte>.Shared.Rent(_buffer.Length * 2);
_buffer.AsSpan()[..bufferIndex].CopyTo(newBuffer);
ArrayPool<byte>.Shared.Return(_buffer);
_buffer = newBuffer;
return _buffer.AsSpan()[bufferIndex..];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,114 @@ public static void Simple_Predicate()
Assert.Equal("76892c282824bfdfe59e135a298c926e", hash);
}

[Fact]
public static void BufferResize_Add_Char()
{
// arrange
var hasher = new ExpressionHasher();
var initialBufferSize = hasher.InitialBufferSize;

// act
for (var i = 0; i < initialBufferSize / 2; i++)
{
hasher.Add('a');
}

Assert.Equal(initialBufferSize, hasher.BufferSize);
hasher.Add('b');

// assert
Assert.Equal(initialBufferSize * 2, hasher.BufferSize);
}

[Fact]
public static void BufferResize_Add_ReadOnlyCharSpan()
{
// arrange
var hasher = new ExpressionHasher();
var initialBufferSize = hasher.InitialBufferSize;

// act
hasher.Add(new string('a', initialBufferSize + 1));

// assert
Assert.Equal(initialBufferSize * 2, hasher.BufferSize);
}

[Fact]
public static void BufferResize_Add_ReadOnlyByteSpan()
{
// arrange
var hasher = new ExpressionHasher();
var initialBufferSize = hasher.InitialBufferSize;

// act
hasher.Add(Enumerable.Range(0, initialBufferSize + 1).Select(_ => (byte)1).ToArray());

// assert
Assert.Equal(initialBufferSize * 2, hasher.BufferSize);
}

[Fact]
public static void BufferResize_Add_Expression()
{
// arrange
var hasher = new ExpressionHasher();
var initialBufferSize = hasher.InitialBufferSize;
var expression = Expression.Lambda<Func<Entity1>>(Expression.Constant(new Entity1())); // translated to 8 bytes

// act
var length = 0;
while (length < initialBufferSize + 1)
{
hasher.Add(expression);
length += 8;
}

// assert
Assert.Equal(initialBufferSize * 2, hasher.BufferSize);
}

[Fact]
public static void BufferResize_Add_QueryContext()
{
// arrange
var hasher = new ExpressionHasher();
var initialBufferSize = hasher.InitialBufferSize;
var queryContext = new QueryContext<Entity1>(x => x); // translated to 32 bytes

// act
var length = 0;
while (length < initialBufferSize + 1)
{
hasher.Add(queryContext);
length += 32;
}

// assert
Assert.Equal(initialBufferSize * 2, hasher.BufferSize);
}

[Fact]
public static void BufferResize_Add_SortDefinition()
{
// arrange
var hasher = new ExpressionHasher();
var initialBufferSize = hasher.InitialBufferSize;
var sortDefinition = new SortDefinition<Entity1>().AddAscending(x => x.Name); // translated to 102 bytes

// act
var length = 0;
while (length < initialBufferSize + 1)
{
hasher.Add(sortDefinition);
length += 102;
}

// assert
Assert.Equal(initialBufferSize * 2, hasher.BufferSize);
}

public class Entity1
{
public string Name { get; set; } = null!;
Expand Down
Loading