MemoryTemplate<T>
Represents generic template for buffer rendering.
using DotNext.Runtime.CompilerServices;
using System;
using System.Buffers;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
namespace DotNext.Buffers
{
[StructLayout(LayoutKind.Auto)]
[System.Runtime.CompilerServices.NullableContext(1)]
[System.Runtime.CompilerServices.Nullable(0)]
public readonly struct MemoryTemplate<[System.Runtime.CompilerServices.Nullable(0)] T> where T : IEquatable<T>
{
private sealed class Placeholder
{
internal readonly int Offset;
internal Placeholder Next;
internal Placeholder(int offset)
{
Offset = offset;
}
}
[StructLayout(LayoutKind.Auto)]
private readonly struct BufferConsumer<TWriter> : IReadOnlySpanConsumer<T>, ISupplier<ReadOnlyMemory<T>, CancellationToken, ValueTask>, IFunctional<Func<ReadOnlyMemory<T>, CancellationToken, ValueTask>>, IConsumer<int>, IFunctional<Action<int>> where TWriter : class, IBufferWriter<T>
{
private readonly TWriter buffer;
private readonly Action<int, TWriter> rewriter;
internal BufferConsumer(TWriter buffer, Action<int, TWriter> rewriter)
{
this.buffer = buffer;
this.rewriter = rewriter;
}
void IReadOnlySpanConsumer<T>.Invoke(ReadOnlySpan<T> input)
{
BuffersExtensions.Write<T>((IBufferWriter<T>)buffer, input);
}
void IConsumer<int>.Invoke(int index)
{
rewriter(index, buffer);
}
}
[StructLayout(LayoutKind.Auto)]
private readonly struct DelegatingConsumer<TArg> : IReadOnlySpanConsumer<T>, ISupplier<ReadOnlyMemory<T>, CancellationToken, ValueTask>, IFunctional<Func<ReadOnlyMemory<T>, CancellationToken, ValueTask>>, IConsumer<int>, IFunctional<Action<int>>
{
private readonly ReadOnlySpanAction<T, TArg> output;
private readonly Action<int, TArg> rewriter;
private readonly TArg state;
internal DelegatingConsumer(ReadOnlySpanAction<T, TArg> output, Action<int, TArg> rewriter, TArg state)
{
this.output = output;
this.rewriter = rewriter;
this.state = state;
}
void IReadOnlySpanConsumer<T>.Invoke(ReadOnlySpan<T> input)
{
output(input, state);
}
void IConsumer<int>.Invoke(int index)
{
rewriter(index, state);
}
}
private readonly ReadOnlyMemory<T> template;
private readonly Placeholder firstOccurence;
private readonly int placeholderLength;
[System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})]
public ReadOnlyMemory<T> Value {
[return: System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})]
get {
return template;
}
}
public MemoryTemplate([System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})] ReadOnlyMemory<T> template, [LifetimeAnnotation(false, true)] [System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})] ReadOnlySpan<T> placeholder)
{
this.template = template;
if (placeholder.IsEmpty || placeholder.Length > template.Length) {
placeholderLength = 0;
firstOccurence = null;
} else {
placeholderLength = placeholder.Length;
firstOccurence = BuildPlaceholdersChain(template.Span, placeholder);
}
}
private static Placeholder BuildPlaceholdersChain([LifetimeAnnotation(false, true)] ReadOnlySpan<T> source, [LifetimeAnnotation(false, true)] ReadOnlySpan<T> placeholder)
{
Placeholder head = null;
Placeholder tail = null;
int num = 0;
while (num < source.Length - placeholder.Length + 1) {
if (MemoryExtensions.SequenceEqual<T>(source.Slice(num, placeholder.Length), placeholder)) {
CreateNode(ref head, ref tail, num);
num += placeholder.Length;
} else
num++;
}
return head;
}
private static void CreateNode(ref Placeholder head, ref Placeholder tail, int offset)
{
if (head == null || tail == null)
head = (tail = new Placeholder(offset));
else {
Placeholder placeholder = tail;
tail = (placeholder.Next = new Placeholder(offset));
}
}
public void Render<TConsumer>(TConsumer consumer) where TConsumer : IReadOnlySpanConsumer<T>, IConsumer<int>
{
ReadOnlySpan<T> span = this.template.Span;
Placeholder placeholder = this.firstOccurence;
int offset = 0;
int num = 0;
int num2 = 0;
bool isPlaceholder;
while (this.MoveNext(ref offset, ref placeholder, out isPlaceholder)) {
if (isPlaceholder)
((IConsumer<int>)consumer).Invoke(num2++);
else
((IReadOnlySpanConsumer<T>)consumer).Invoke(span.Slice(num, offset - num));
num = offset;
}
}
public void Render<TWriter>(TWriter output, Action<int, TWriter> rewriter) where TWriter : class, IBufferWriter<T>
{
if (output == null)
throw new ArgumentNullException("output");
if (rewriter == null)
throw new ArgumentNullException("rewriter");
Render(new BufferConsumer<TWriter>(output, rewriter));
}
public void Render<[System.Runtime.CompilerServices.Nullable(2)] TArg>(TArg arg, Action<int, TArg> rewriter, ReadOnlySpanAction<T, TArg> output)
{
Render(new DelegatingConsumer<TArg>(output, rewriter, arg));
}
private bool MoveNext(ref int offset, ref Placeholder placeholder, out bool isPlaceholder)
{
if (offset >= template.Length) {
isPlaceholder = false;
return false;
}
if (placeholder == null) {
isPlaceholder = false;
offset = template.Length;
} else if (placeholder.Offset == offset) {
isPlaceholder = true;
offset += placeholderLength;
placeholder = placeholder.Next;
} else {
offset = placeholder.Offset;
isPlaceholder = false;
}
return true;
}
}
}