diff options
Diffstat (limited to 'lib/Utils/src')
-rw-r--r-- | lib/Utils/src/Extensions/MemoryExtensions.cs | 45 | ||||
-rw-r--r-- | lib/Utils/src/Extensions/StringExtensions.cs | 6 | ||||
-rw-r--r-- | lib/Utils/src/Memory/ForwardOnlyWriter.cs | 108 | ||||
-rw-r--r-- | lib/Utils/src/Memory/MemoryUtil.CopyUtilCore.cs | 5 |
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"); |