aboutsummaryrefslogtreecommitdiff
path: root/lib/Utils
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Utils')
-rw-r--r--lib/Utils/src/Extensions/MemoryExtensions.cs45
-rw-r--r--lib/Utils/src/Extensions/StringExtensions.cs6
-rw-r--r--lib/Utils/src/Memory/ForwardOnlyWriter.cs108
-rw-r--r--lib/Utils/src/Memory/MemoryUtil.CopyUtilCore.cs5
4 files changed, 129 insertions, 35 deletions
diff --git a/lib/Utils/src/Extensions/MemoryExtensions.cs b/lib/Utils/src/Extensions/MemoryExtensions.cs
index 65d90a0..083c7cf 100644
--- a/lib/Utils/src/Extensions/MemoryExtensions.cs
+++ b/lib/Utils/src/Extensions/MemoryExtensions.cs
@@ -578,6 +578,27 @@ namespace VNLib.Utils.Extensions
#region VnBufferWriter
/// <summary>
+ /// Appends the string value by copying it to the internal buffer
+ /// </summary>
+ /// <param name="buffer"></param>
+ /// <param name="value">The string value to append to the buffer</param>
+ /// <exception cref="ArgumentOutOfRangeException"></exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void Append(this ref ForwardOnlyWriter<char> buffer, string? value)
+ => buffer.Append(value.AsSpan());
+
+ /// <summary>
+ /// Appends the string value by copying it to the internal buffer
+ /// when the string is known to be very short.
+ /// </summary>
+ /// <param name="buffer"></param>
+ /// <param name="value">The string value to append to the buffer</param>
+ /// <exception cref="ArgumentOutOfRangeException"></exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void AppendSmall(this ref ForwardOnlyWriter<char> buffer, string? value)
+ => buffer.AppendSmall(value.AsSpan());
+
+ /// <summary>
/// Formats and appends a value type to the writer with proper endianess
/// </summary>
/// <param name="buffer"></param>
@@ -636,12 +657,20 @@ namespace VNLib.Utils.Extensions
/// <param name="formatProvider"></param>
/// <exception cref="ArgumentOutOfRangeException"></exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void Append<T>(this ref ForwardOnlyWriter<char> buffer, T value, ReadOnlySpan<char> format = default, IFormatProvider? formatProvider = default) where T : ISpanFormattable
+ public static void Append<T>(
+ this ref ForwardOnlyWriter<char> buffer,
+ T value,
+ ReadOnlySpan<char> format = default,
+ IFormatProvider? formatProvider = default
+ ) where T : ISpanFormattable
{
//Format value and write to buffer
if (!value.TryFormat(buffer.Remaining, out int charsWritten, format, formatProvider))
{
- throw new ArgumentOutOfRangeException(nameof(buffer), "The value could not be formatted and appended to the buffer, because there is not enough available space");
+ throw new ArgumentOutOfRangeException(
+ nameof(buffer),
+ "The value could not be formatted and appended to the buffer, because there is not enough available space"
+ );
}
//Update written posiion
buffer.Advance(charsWritten);
@@ -657,12 +686,20 @@ namespace VNLib.Utils.Extensions
/// <exception cref="OutOfMemoryException"></exception>
/// <exception cref="ArgumentOutOfRangeException"></exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void Append<T>(this ref ForwardOnlyMemoryWriter<char> buffer, T value, ReadOnlySpan<char> format = default, IFormatProvider? formatProvider = default) where T : ISpanFormattable
+ public static void Append<T>(
+ this ref ForwardOnlyMemoryWriter<char> buffer,
+ T value,
+ ReadOnlySpan<char> format = default,
+ IFormatProvider? formatProvider = default
+ ) where T : ISpanFormattable
{
//Format value and write to buffer
if (!value.TryFormat(buffer.Remaining.Span, out int charsWritten, format, formatProvider))
{
- throw new ArgumentOutOfRangeException(nameof(buffer), "The value could not be formatted and appended to the buffer, because there is not enough available space");
+ throw new ArgumentOutOfRangeException(
+ nameof(buffer),
+ "The value could not be formatted and appended to the buffer, because there is not enough available space"
+ );
}
//Update written posiion
buffer.Advance(charsWritten);
diff --git a/lib/Utils/src/Extensions/StringExtensions.cs b/lib/Utils/src/Extensions/StringExtensions.cs
index c71d5a0..e9bbfbd 100644
--- a/lib/Utils/src/Extensions/StringExtensions.cs
+++ b/lib/Utils/src/Extensions/StringExtensions.cs
@@ -460,7 +460,7 @@ namespace VNLib.Utils.Extensions
public static int Replace(this Span<char> buffer, ReadOnlySpan<char> search, ReadOnlySpan<char> replace)
{
ForwardOnlyWriter<char> writer = new (buffer);
- writer.Replace(search, replace);
+ Replace(ref writer, search, replace);
return writer.Written;
}
@@ -496,9 +496,9 @@ namespace VNLib.Utils.Extensions
do
{
//Append the data before the search chars
- writer2.Append(buffer[..start]);
+ writer2.Append<char>(buffer[..start]);
//Append the replacment
- writer2.Append(replace);
+ writer2.Append<char>(replace);
//Shift buffer to the end of the
buffer = buffer[(start + searchLen)..];
//search for next index beyond current index
diff --git a/lib/Utils/src/Memory/ForwardOnlyWriter.cs b/lib/Utils/src/Memory/ForwardOnlyWriter.cs
index d3c33a2..55e3b11 100644
--- a/lib/Utils/src/Memory/ForwardOnlyWriter.cs
+++ b/lib/Utils/src/Memory/ForwardOnlyWriter.cs
@@ -23,6 +23,7 @@
*/
using System;
+using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
namespace VNLib.Utils.Memory
@@ -30,12 +31,22 @@ namespace VNLib.Utils.Memory
/// <summary>
/// Provides a stack based buffer writer
/// </summary>
- public ref struct ForwardOnlyWriter<T>
+ /// <remarks>
+ /// Creates a new <see cref="ForwardOnlyWriter{T}"/> assigning the specified buffer
+ /// at the specified offset
+ /// </remarks>
+ /// <param name="buffer">The buffer to write data to</param>
+ /// <param name="offset">The offset to begin the writer at</param>
+ [method: MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ref struct ForwardOnlyWriter<T>(Span<T> buffer, int offset)
{
+ //Cache reference to the first value
+ private readonly ref T _basePtr = ref MemoryMarshal.GetReference(buffer);
+
/// <summary>
/// The buffer for writing output data to
/// </summary>
- public readonly Span<T> Buffer { get; }
+ public readonly Span<T> Buffer { get; } = buffer[offset..];
/// <summary>
/// The number of characters written to the buffer
@@ -57,43 +68,79 @@ namespace VNLib.Utils.Memory
/// </summary>
/// <param name="buffer">The buffer to write data to</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public ForwardOnlyWriter(Span<T> buffer)
- {
- Buffer = buffer;
- Written = 0;
- }
+ public ForwardOnlyWriter(Span<T> buffer): this(buffer, 0)
+ { }
/// <summary>
- /// Creates a new <see cref="ForwardOnlyWriter{T}"/> assigning the specified buffer
- /// at the specified offset
+ /// Returns a compiled string from the characters written to the buffer
/// </summary>
- /// <param name="buffer">The buffer to write data to</param>
- /// <param name="offset">The offset to begin the writer at</param>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public ForwardOnlyWriter(Span<T> buffer, int offset)
+ /// <returns>A string of the characters written to the buffer</returns>
+ public readonly override string ToString() => Buffer[..Written].ToString();
+
+ /// <summary>
+ /// Appends a sequence to the buffer
+ /// </summary>
+ /// <param name="data">The data sequence to append to the buffer</param>
+ /// <exception cref="ArgumentOutOfRangeException"></exception>
+ public void Append<TClass>(ReadOnlySpan<T> data) where TClass : class, T
{
- Buffer = buffer[offset..];
- Written = 0;
+ //Make sure the current window is large enough to buffer the new string
+ ArgumentOutOfRangeException.ThrowIfGreaterThan(data.Length, RemainingSize, nameof(Remaining));
+
+ //write data to window
+ data.CopyTo(Remaining);
+
+ //update char position
+ Written += data.Length;
}
/// <summary>
- /// Returns a compiled string from the characters written to the buffer
+ /// Appends a sequence to the buffer of a value type by copying source
+ /// memory to internal buffer memory
/// </summary>
- /// <returns>A string of the characters written to the buffer</returns>
- public readonly override string ToString() => Buffer[..Written].ToString();
+ /// <typeparam name="TStruct"></typeparam>
+ /// <param name="data">The data sequence to append to the buffer</param>
+ /// <exception cref="ArgumentOutOfRangeException"></exception>
+ public void Append<TStruct>(ReadOnlySpan<TStruct> data) where TStruct : struct, T
+ {
+ //Make sure the current window is large enough to buffer the new string
+ ArgumentOutOfRangeException.ThrowIfGreaterThan(data.Length, RemainingSize, nameof(Remaining));
+
+ //write data to window
+ MemoryUtil.Memmove(
+ in MemoryMarshal.GetReference(data),
+ 0,
+ ref Unsafe.As<T, TStruct>(ref _basePtr), //Reinterpret the ref to the local scope type,
+ (nuint)Written,
+ (nuint)data.Length
+ );
+
+ //update char position
+ Written += data.Length;
+ }
/// <summary>
- /// Appends a sequence to the buffer
+ /// Appends a sequence to the buffer of a value type by copying source
+ /// memory to internal buffer memory, when the buffer size is known to be
+ /// smaller than <see cref="ushort.MaxValue"/>.
/// </summary>
- /// <param name="data">The data to append to the buffer</param>
+ /// <typeparam name="TStruct"></typeparam>
+ /// <param name="data">The data sequence to append to the buffer</param>
/// <exception cref="ArgumentOutOfRangeException"></exception>
- public void Append(ReadOnlySpan<T> data)
+ public void AppendSmall<TStruct>(ReadOnlySpan<TStruct> data) where TStruct : struct, T
{
//Make sure the current window is large enough to buffer the new string
ArgumentOutOfRangeException.ThrowIfGreaterThan(data.Length, RemainingSize, nameof(Remaining));
- Span<T> window = Buffer[Written..];
+
//write data to window
- data.CopyTo(window);
+ MemoryUtil.SmallMemmove(
+ in MemoryMarshal.GetReference(data),
+ 0,
+ ref Unsafe.As<T, TStruct>(ref _basePtr), //Reinterpret the ref to the local scope type,
+ (nuint)Written,
+ (ushort)data.Length
+ );
+
//update char position
Written += data.Length;
}
@@ -103,12 +150,21 @@ namespace VNLib.Utils.Memory
/// </summary>
/// <param name="c">The item to append to the buffer</param>
/// <exception cref="ArgumentOutOfRangeException"></exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Append(T c)
{
//Make sure the current window is large enough to buffer the new string
ArgumentOutOfRangeException.ThrowIfZero(RemainingSize);
- //Write data to buffer and increment the buffer position
- Buffer[Written++] = c;
+
+ /*
+ * Calc pointer to last written position.
+ * Written points to the address directly after the last written element
+ */
+
+ ref T offset = ref Unsafe.Add(ref _basePtr, Written);
+ offset = c;
+
+ Written++;
}
/// <summary>
@@ -116,6 +172,7 @@ namespace VNLib.Utils.Memory
/// </summary>
/// <param name="count">The number of elements to advance the writer by</param>
/// <exception cref="ArgumentOutOfRangeException"></exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Advance(int count)
{
ArgumentOutOfRangeException.ThrowIfGreaterThan(count, RemainingSize, nameof(Remaining));
@@ -126,6 +183,7 @@ namespace VNLib.Utils.Memory
/// Resets the writer by setting the <see cref="Written"/>
/// property to 0.
/// </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Reset() => Written = 0;
}
}
diff --git a/lib/Utils/src/Memory/MemoryUtil.CopyUtilCore.cs b/lib/Utils/src/Memory/MemoryUtil.CopyUtilCore.cs
index f196597..9decef7 100644
--- a/lib/Utils/src/Memory/MemoryUtil.CopyUtilCore.cs
+++ b/lib/Utils/src/Memory/MemoryUtil.CopyUtilCore.cs
@@ -126,7 +126,7 @@ namespace VNLib.Utils.Memory
Debug.Assert(!Unsafe.IsNullRef(in srcByte), "Null source reference passed to MemmoveByRef");
Debug.Assert(!Unsafe.IsNullRef(in dstByte), "Null destination reference passed to MemmoveByRef");
- //Check for 64bit copy
+ //Check for 64bit copy (should get optimized away when sizeof(nuint == uint) aka 32bit platforms)
if(byteCount > uint.MaxValue)
{
//We need a 64bit copy strategy
@@ -135,7 +135,6 @@ namespace VNLib.Utils.Memory
//Must be supported
if(_avxCopy.Features != CopyFeatures.NotSupported)
{
- //Copy
_avxCopy.Memmove(in srcByte, ref dstByte, byteCount);
return;
}
@@ -144,7 +143,6 @@ namespace VNLib.Utils.Memory
//try reflected memove incase it supports 64bit blocks
if(_reflectedMemmove.Features != CopyFeatures.NotSupported)
{
- //Copy
_reflectedMemmove.Memmove(in srcByte, ref dstByte, byteCount);
return;
}
@@ -223,6 +221,7 @@ namespace VNLib.Utils.Memory
public CopyFeatures Features => CopyFeatures.None;
///<inheritdoc/>
+ [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public void Memmove(ref readonly byte src, ref byte dst, nuint byteCount)
{
Debug.Assert(byteCount < uint.MaxValue, "Byte count must be less than uint.MaxValue and flags assumed 64bit blocks were supported");