aboutsummaryrefslogtreecommitdiff
path: root/lib/Utils/src/Extensions
diff options
context:
space:
mode:
authorLibravatar vnugent <public@vaughnnugent.com>2023-09-22 12:29:32 -0400
committerLibravatar vnugent <public@vaughnnugent.com>2023-09-22 12:29:32 -0400
commit04c8fa8017a04da05cd8e1d104cfcd0a9aba17f5 (patch)
tree771243c218e880346fc7d826fa25ac2d639b01be /lib/Utils/src/Extensions
parent0b4e18b9a7d8e0aea23aef7efd3707674f223b2b (diff)
Structure refactor & unused feature pruning, more http2 prep
Diffstat (limited to 'lib/Utils/src/Extensions')
-rw-r--r--lib/Utils/src/Extensions/StringExtensions.cs117
1 files changed, 89 insertions, 28 deletions
diff --git a/lib/Utils/src/Extensions/StringExtensions.cs b/lib/Utils/src/Extensions/StringExtensions.cs
index 09d6517..f211b73 100644
--- a/lib/Utils/src/Extensions/StringExtensions.cs
+++ b/lib/Utils/src/Extensions/StringExtensions.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2022 Vaughn Nugent
+* Copyright (c) 2023 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Utils
@@ -32,6 +32,10 @@ using VNLib.Utils.Memory;
namespace VNLib.Utils.Extensions
{
+ /// <summary>
+ /// Delegate for a stateless span action
+ /// </summary>
+ /// <param name="line">The line of data to process</param>
public delegate void StatelessSpanAction(ReadOnlySpan<char> line);
/// <summary>
@@ -51,6 +55,7 @@ namespace VNLib.Utils.Extensions
{
Split(value, splitter.AsSpan(), output, options);
}
+
/// <summary>
/// Split a string based on split value and insert into the specified list
/// </summary>
@@ -66,6 +71,7 @@ namespace VNLib.Utils.Extensions
//Call the split function on the span
Split(value, cs, output, options);
}
+
/// <summary>
/// Split a string based on split value and insert into the specified list
/// </summary>
@@ -79,6 +85,7 @@ namespace VNLib.Utils.Extensions
{
Split(value.AsSpan(), splitter, output, options);
}
+
/// <summary>
/// Split a string based on split value and insert into the specified list
/// </summary>
@@ -88,13 +95,14 @@ namespace VNLib.Utils.Extensions
/// <param name="options">String split options</param>
/// <exception cref="ArgumentNullException"></exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void Split<T>(this in ReadOnlySpan<char> value, char splitter, T output, StringSplitOptions options) where T : ICollection<string>
+ public static void Split<T>(this ReadOnlySpan<char> value, char splitter, T output, StringSplitOptions options) where T : ICollection<string>
{
//Create span from char pointer
ReadOnlySpan<char> cs = MemoryMarshal.CreateReadOnlySpan(ref splitter, 1);
//Call the split function on the span
- Split(in value, cs, output, options);
+ Split(value, cs, output, options);
}
+
/// <summary>
/// Split a <see cref="ReadOnlySpan{T}"/> based on split value and insert into the specified list
/// </summary>
@@ -104,13 +112,15 @@ namespace VNLib.Utils.Extensions
/// <param name="options">String split options</param>
/// <exception cref="ArgumentNullException"></exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void Split<T>(this in ReadOnlySpan<char> value, ReadOnlySpan<char> splitter, T output, StringSplitOptions options) where T : ICollection<string>
+ public static void Split<T>(this ReadOnlySpan<char> value, ReadOnlySpan<char> splitter, T output, StringSplitOptions options) where T : ICollection<string>
{
//Create a local function that adds the split strings to the list
static void SplitFound(ReadOnlySpan<char> split, T output) => output.Add(split.ToString());
+
//Invoke the split function with the local callback method
- Split(in value, splitter, options, SplitFound, output);
+ Split(value, splitter, options, SplitFound, output);
}
+
/// <summary>
/// Split a <see cref="ReadOnlySpan{T}"/> based on split value and pass it to the split delegate handler
/// </summary>
@@ -120,11 +130,13 @@ namespace VNLib.Utils.Extensions
/// <param name="splitCb">The action to invoke when a split segment has been found</param>
/// <param name="state">The state to pass to the callback handler</param>
/// <exception cref="ArgumentNullException"></exception>
- public static void Split<T>(this in ReadOnlySpan<char> value, ReadOnlySpan<char> splitter, StringSplitOptions options, ReadOnlySpanAction<char, T> splitCb, T state)
+ public static void Split<T>(this ReadOnlySpan<char> value, ReadOnlySpan<char> splitter, StringSplitOptions options, ReadOnlySpanAction<char, T> splitCb, T state)
{
_ = splitCb ?? throw new ArgumentNullException(nameof(splitCb));
+
//Get span over string
ForwardOnlyReader<char> reader = new(value);
+
//No string options
if (options == 0)
{
@@ -132,41 +144,49 @@ namespace VNLib.Utils.Extensions
{
//Find index of the splitter
int start = reader.Window.IndexOf(splitter);
+
//guard
if (start == -1)
{
break;
}
+
//Trim and add it regardless of length
splitCb(reader.Window[..start], state);
+
//shift window
reader.Advance(start + splitter.Length);
} while (true);
+
//Trim remaining and add it regardless of length
splitCb(reader.Window, state);
}
//Trim but do not remove empties
- else if ((options & StringSplitOptions.RemoveEmptyEntries) == 0)
+ else if ((options & StringSplitOptions.TrimEntries) == StringSplitOptions.TrimEntries)
{
do
{
//Find index of the splitter
int start = reader.Window.IndexOf(splitter);
+
//guard
if (start == -1)
{
break;
}
+
//Trim and add it regardless of length
splitCb(reader.Window[..start].Trim(), state);
+
//shift window
reader.Advance(start + splitter.Length);
} while (true);
+
//Trim remaining and add it regardless of length
splitCb(reader.Window.Trim(), state);
}
//Remove empty entires but do not trim them
- else if ((options & StringSplitOptions.TrimEntries) == 0)
+ else if ((options & StringSplitOptions.RemoveEmptyEntries) == StringSplitOptions.RemoveEmptyEntries)
{
//Get data before splitter and trim it
ReadOnlySpan<char> data;
@@ -186,9 +206,11 @@ namespace VNLib.Utils.Extensions
{
splitCb(data, state);
}
- //shift window
- reader.Advance(start + splitter.Length);
+
+ reader.Advance(start + splitter.Length);
+
} while (true);
+
//Add if not empty
if (reader.WindowSize > 0)
{
@@ -204,23 +226,29 @@ namespace VNLib.Utils.Extensions
{
//Find index of the splitter
int start = reader.Window.IndexOf(splitter);
+
//guard
if (start == -1)
{
break;
}
+
//Get data before splitter and trim it
data = reader.Window[..start].Trim();
+
//If its not empty, then add it to the list
if (!data.IsEmpty)
{
splitCb(data, state);
}
- //shift window
+
reader.Advance(start + splitter.Length);
+
} while (true);
+
//Trim remaining
data = reader.Window.Trim();
+
//Add if not empty
if (!data.IsEmpty)
{
@@ -238,13 +266,14 @@ namespace VNLib.Utils.Extensions
/// <param name="splitCb">The action to invoke when a split segment has been found</param>
/// <param name="state"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void Split<T>(this in ReadOnlySpan<char> value, char splitter, StringSplitOptions options, ReadOnlySpanAction<char, T> splitCb, T state)
+ public static void Split<T>(this ReadOnlySpan<char> value, char splitter, StringSplitOptions options, ReadOnlySpanAction<char, T> splitCb, T state)
{
//Alloc a span for char
ReadOnlySpan<char> cs = MemoryMarshal.CreateReadOnlySpan(ref splitter, 1);
//Call the split function on the span
- Split(in value, cs, options, splitCb, state);
+ Split(value, cs, options, splitCb, state);
}
+
/// <summary>
/// Split a <see cref="ReadOnlySpan{T}"/> based on split value and pass it to the split delegate handler
/// </summary>
@@ -254,13 +283,14 @@ namespace VNLib.Utils.Extensions
/// <param name="splitCb">The action to invoke when a split segment has been found</param>
/// <exception cref="ArgumentNullException"></exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void Split(this in ReadOnlySpan<char> value, ReadOnlySpan<char> splitter, StringSplitOptions options, StatelessSpanAction splitCb)
+ public static void Split(this ReadOnlySpan<char> value, ReadOnlySpan<char> splitter, StringSplitOptions options, StatelessSpanAction splitCb)
{
//Create a SpanSplitDelegate with the non-typed delegate as the state argument
static void ssplitcb(ReadOnlySpan<char> param, StatelessSpanAction callback) => callback(param);
//Call split with the new callback delegate
- Split(in value, splitter, options, ssplitcb, splitCb);
+ Split(value, splitter, options, ssplitcb, splitCb);
}
+
/// <summary>
/// Split a <see cref="ReadOnlySpan{T}"/> based on split value and pass it to the split delegate handler
/// </summary>
@@ -270,12 +300,12 @@ namespace VNLib.Utils.Extensions
/// <param name="splitCb">The action to invoke when a split segment has been found</param>
/// <exception cref="ArgumentNullException"></exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void Split(this in ReadOnlySpan<char> value, char splitter, StringSplitOptions options, StatelessSpanAction splitCb)
+ public static void Split(this ReadOnlySpan<char> value, char splitter, StringSplitOptions options, StatelessSpanAction splitCb)
{
//Create a SpanSplitDelegate with the non-typed delegate as the state argument
static void ssplitcb(ReadOnlySpan<char> param, StatelessSpanAction callback) => callback(param);
//Call split with the new callback delegate
- Split(in value, splitter, options, ssplitcb, splitCb);
+ Split(value, splitter, options, ssplitcb, splitCb);
}
/// <summary>
@@ -285,11 +315,12 @@ namespace VNLib.Utils.Extensions
/// <param name="search">Sequence to search for within the current sequence</param>
/// <returns>the index of the end of the sequenc</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static int EndOf(this in ReadOnlySpan<char> data, ReadOnlySpan<char> search)
+ public static int EndOf(this ReadOnlySpan<char> data, ReadOnlySpan<char> search)
{
int index = data.IndexOf(search);
return index > -1 ? index + search.Length : -1;
}
+
/// <summary>
/// Gets the index of the end of the found character
/// </summary>
@@ -297,15 +328,18 @@ namespace VNLib.Utils.Extensions
/// <param name="search">Character to search for within the current sequence</param>
/// <returns>the index of the end of the sequence</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static int EndOf(this in ReadOnlySpan<char> data, char search)
+ public static int EndOf(this ReadOnlySpan<char> data, char search)
{
int index = data.IndexOf(search);
return index > -1 ? index + 1 : -1;
}
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int IndexOf(this in Memory<byte> data, byte search) => data.Span.IndexOf(search);
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int IndexOf(this in Memory<byte> data, ReadOnlySpan<byte> search) => data.Span.IndexOf(search);
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int IndexOf(this in Memory<byte> data, ReadOnlyMemory<byte> search) => IndexOf(data, search.Span);
@@ -317,13 +351,14 @@ namespace VNLib.Utils.Extensions
/// <param name="search">The delimiting character</param>
/// <returns>The segment of data before the search character, or the entire segment if not found</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static ReadOnlySpan<char> SliceBeforeParam(this in ReadOnlySpan<char> data, char search)
+ public static ReadOnlySpan<char> SliceBeforeParam(this ReadOnlySpan<char> data, char search)
{
//Find the index of the specified data
int index = data.IndexOf(search);
//Return the slice of data before the index, or an empty span if it was not found
return index > -1 ? data[..index] : data;
}
+
/// <summary>
/// Slices the current span from the begining of the segment to the first occurrance of the specified character sequence.
/// If the character sequence is not found, the entire segment is returned
@@ -332,13 +367,14 @@ namespace VNLib.Utils.Extensions
/// <param name="search">The delimiting character sequence</param>
/// <returns>The segment of data before the search character, or the entire <paramref name="data"/> if the seach sequence is not found</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static ReadOnlySpan<char> SliceBeforeParam(this in ReadOnlySpan<char> data, ReadOnlySpan<char> search)
+ public static ReadOnlySpan<char> SliceBeforeParam(this ReadOnlySpan<char> data, ReadOnlySpan<char> search)
{
//Find the index of the specified data
int index = data.IndexOf(search);
//Return the slice of data before the index, or an empty span if it was not found
return index > -1 ? data[..index] : data;
}
+
/// <summary>
/// Gets the remaining segment of data after the specified search character or <see cref="ReadOnlySpan{T}.Empty"/>
/// if the search character is not found within the current segment
@@ -347,13 +383,15 @@ namespace VNLib.Utils.Extensions
/// <param name="search">The character to search for within the segment</param>
/// <returns>The segment of data after the search character or <see cref="ReadOnlySpan{T}.Empty"/> if not found</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static ReadOnlySpan<char> SliceAfterParam(this in ReadOnlySpan<char> data, char search)
+ public static ReadOnlySpan<char> SliceAfterParam(this ReadOnlySpan<char> data, char search)
{
//Find the index of the specified data
- int index = EndOf(in data, search);
+ int index = EndOf(data, search);
+
//Return the slice of data after the index, or an empty span if it was not found
return index > -1 ? data[index..] : ReadOnlySpan<char>.Empty;
}
+
/// <summary>
/// Gets the remaining segment of data after the specified search sequence or <see cref="ReadOnlySpan{T}.Empty"/>
/// if the search sequence is not found within the current segment
@@ -362,42 +400,53 @@ namespace VNLib.Utils.Extensions
/// <param name="search">The sequence to search for within the segment</param>
/// <returns>The segment of data after the search sequence or <see cref="ReadOnlySpan{T}.Empty"/> if not found</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static ReadOnlySpan<char> SliceAfterParam(this in ReadOnlySpan<char> data, ReadOnlySpan<char> search)
+ public static ReadOnlySpan<char> SliceAfterParam(this ReadOnlySpan<char> data, ReadOnlySpan<char> search)
{
//Find the index of the specified data
int index = EndOf(data, search);
+
//Return the slice of data after the index, or an empty span if it was not found
return index > -1 ? data[index..] : ReadOnlySpan<char>.Empty;
}
+
/// <summary>
/// Trims any leading or trailing <c>'\r'|'\n'|' '</c>(whitespace) characters from the segment
/// </summary>
/// <returns>The trimmed segment</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static ReadOnlySpan<char> TrimCRLF(this in ReadOnlySpan<char> data)
+ public static ReadOnlySpan<char> TrimCRLF(this ReadOnlySpan<char> data)
{
int start = 0, end = data.Length;
+
//trim leading \r\n chars
while(start < end)
{
char t = data[start];
+
//If character \r or \n slice it off
- if (t != '\r' && t != '\n' && t != ' ') {
+ if (t != '\r' && t != '\n' && t != ' ')
+ {
break;
}
+
//Shift
start++;
}
+
//remove trailing crlf characters
while (end > start)
{
char t = data[end - 1];
+
//If character \r or \n slice it off
- if (t != '\r' && t != '\n' && t != ' ') {
+ if (t != '\r' && t != '\n' && t != ' ')
+ {
break;
}
+
end--;
}
+
return data[start..end];
}
@@ -408,7 +457,7 @@ namespace VNLib.Utils.Extensions
/// <param name="search">The sequence to search for</param>
/// <param name="replace">The sequence to write in the place of the search parameter</param>
/// <exception cref="OutOfMemoryException"></exception>
- public static int Replace(this ref Span<char> buffer, ReadOnlySpan<char> search, ReadOnlySpan<char> replace)
+ public static int Replace(this Span<char> buffer, ReadOnlySpan<char> search, ReadOnlySpan<char> replace)
{
ForwardOnlyWriter<char> writer = new (buffer);
writer.Replace(search, replace);
@@ -425,20 +474,25 @@ namespace VNLib.Utils.Extensions
public static void Replace(this ref ForwardOnlyWriter<char> writer, ReadOnlySpan<char> search, ReadOnlySpan<char> replace)
{
Span<char> buffer = writer.AsSpan();
+
//If the search and replacment parameters are the same length
if (search.Length == replace.Length)
{
buffer.ReplaceInPlace(search, replace);
return;
}
+
//Search and replace are not the same length
int searchLen = search.Length, start = buffer.IndexOf(search);
+
if(start == -1)
{
return;
}
+
//Replacment might be empty
writer.Reset();
+
do
{
//Append the data before the split character
@@ -449,10 +503,13 @@ namespace VNLib.Utils.Extensions
buffer = buffer[(start + searchLen)..];
//search for next index
start = buffer.IndexOf(search);
+
} while (start > -1);
+
//Write remaining data
writer.Append(replace);
}
+
/// <summary>
/// Replaces very ocurrance of character sequence within a buffer with another sequence of the same length
/// </summary>
@@ -466,13 +523,17 @@ namespace VNLib.Utils.Extensions
{
throw new ArgumentException("Search parameter and replacment parameter must be the same length");
}
+
int start = buffer.IndexOf(search);
+
while(start > -1)
{
//Shift the buffer to the begining of the search parameter
buffer = buffer[start..];
+
//Overwite the search parameter
replace.CopyTo(buffer);
+
//Search for next index of the search character
start = buffer.IndexOf(search);
}