diff --git a/api_list.include.md b/api_list.include.md index 4aabfb95..0b3be03c 100644 --- a/api_list.include.md +++ b/api_list.include.md @@ -1125,6 +1125,7 @@ * `StringBuilder Insert(int, ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.text.stringbuilder.insert?view=net-11.0#system-text-stringbuilder-insert(system-int32-system-readonlyspan((system-char)))) * `StringBuilder Replace(ReadOnlySpan, ReadOnlySpan, int, int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.text.stringbuilder.replace?view=net-11.0#system-text-stringbuilder-replace(system-char-system-char-system-int32-system-int32)) * `StringBuilder Replace(ReadOnlySpan, ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.text.stringbuilder.replace?view=net-11.0#system-text-stringbuilder-replace(system-readonlyspan((system-char))-system-readonlyspan((system-char)))) + * `StringBuilder MoveChunks(StringBuilder)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.text.stringbuilder.movechunks?view=net-11.0) #### Task diff --git a/src/Consume/Consume.cs b/src/Consume/Consume.cs index 54afd335..d839bc64 100644 --- a/src/Consume/Consume.cs +++ b/src/Consume/Consume.cs @@ -1526,6 +1526,7 @@ void DefaultInterpolatedStringHandler_Methods() void StringBuilder_Methods() { var builder = new StringBuilder("value"); + var moved = StringBuilder.MoveChunks(builder); #if FeatureMemory builder.Append("suffix".AsSpan()); var targetSpan = new Span(new char[1]); diff --git a/src/Polyfill/StringBuilderPolyfill.cs b/src/Polyfill/StringBuilderPolyfill.cs new file mode 100644 index 00000000..860747ce --- /dev/null +++ b/src/Polyfill/StringBuilderPolyfill.cs @@ -0,0 +1,63 @@ +#if !NET12_0_OR_GREATER + +namespace Polyfills; + +using System; +using System.Reflection; +using System.Text; + +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.text.stringbuilder.movechunks?view=net-11.0 + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + + return destination; + } + } + + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} + +#endif diff --git a/src/Split/net10.0/StringBuilderPolyfill.cs b/src/Split/net10.0/StringBuilderPolyfill.cs new file mode 100644 index 00000000..cddb85db --- /dev/null +++ b/src/Split/net10.0/StringBuilderPolyfill.cs @@ -0,0 +1,54 @@ +// +#pragma warning disable +#if !NET12_0_OR_GREATER +namespace Polyfills; +using System; +using System.Reflection; +using System.Text; +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + return destination; + } + } + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} +#endif diff --git a/src/Split/net11.0/StringBuilderPolyfill.cs b/src/Split/net11.0/StringBuilderPolyfill.cs new file mode 100644 index 00000000..cddb85db --- /dev/null +++ b/src/Split/net11.0/StringBuilderPolyfill.cs @@ -0,0 +1,54 @@ +// +#pragma warning disable +#if !NET12_0_OR_GREATER +namespace Polyfills; +using System; +using System.Reflection; +using System.Text; +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + return destination; + } + } + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} +#endif diff --git a/src/Split/net461/StringBuilderPolyfill.cs b/src/Split/net461/StringBuilderPolyfill.cs new file mode 100644 index 00000000..cddb85db --- /dev/null +++ b/src/Split/net461/StringBuilderPolyfill.cs @@ -0,0 +1,54 @@ +// +#pragma warning disable +#if !NET12_0_OR_GREATER +namespace Polyfills; +using System; +using System.Reflection; +using System.Text; +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + return destination; + } + } + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} +#endif diff --git a/src/Split/net462/StringBuilderPolyfill.cs b/src/Split/net462/StringBuilderPolyfill.cs new file mode 100644 index 00000000..cddb85db --- /dev/null +++ b/src/Split/net462/StringBuilderPolyfill.cs @@ -0,0 +1,54 @@ +// +#pragma warning disable +#if !NET12_0_OR_GREATER +namespace Polyfills; +using System; +using System.Reflection; +using System.Text; +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + return destination; + } + } + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} +#endif diff --git a/src/Split/net47/StringBuilderPolyfill.cs b/src/Split/net47/StringBuilderPolyfill.cs new file mode 100644 index 00000000..cddb85db --- /dev/null +++ b/src/Split/net47/StringBuilderPolyfill.cs @@ -0,0 +1,54 @@ +// +#pragma warning disable +#if !NET12_0_OR_GREATER +namespace Polyfills; +using System; +using System.Reflection; +using System.Text; +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + return destination; + } + } + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} +#endif diff --git a/src/Split/net471/StringBuilderPolyfill.cs b/src/Split/net471/StringBuilderPolyfill.cs new file mode 100644 index 00000000..cddb85db --- /dev/null +++ b/src/Split/net471/StringBuilderPolyfill.cs @@ -0,0 +1,54 @@ +// +#pragma warning disable +#if !NET12_0_OR_GREATER +namespace Polyfills; +using System; +using System.Reflection; +using System.Text; +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + return destination; + } + } + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} +#endif diff --git a/src/Split/net472/StringBuilderPolyfill.cs b/src/Split/net472/StringBuilderPolyfill.cs new file mode 100644 index 00000000..cddb85db --- /dev/null +++ b/src/Split/net472/StringBuilderPolyfill.cs @@ -0,0 +1,54 @@ +// +#pragma warning disable +#if !NET12_0_OR_GREATER +namespace Polyfills; +using System; +using System.Reflection; +using System.Text; +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + return destination; + } + } + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} +#endif diff --git a/src/Split/net48/StringBuilderPolyfill.cs b/src/Split/net48/StringBuilderPolyfill.cs new file mode 100644 index 00000000..cddb85db --- /dev/null +++ b/src/Split/net48/StringBuilderPolyfill.cs @@ -0,0 +1,54 @@ +// +#pragma warning disable +#if !NET12_0_OR_GREATER +namespace Polyfills; +using System; +using System.Reflection; +using System.Text; +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + return destination; + } + } + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} +#endif diff --git a/src/Split/net481/StringBuilderPolyfill.cs b/src/Split/net481/StringBuilderPolyfill.cs new file mode 100644 index 00000000..cddb85db --- /dev/null +++ b/src/Split/net481/StringBuilderPolyfill.cs @@ -0,0 +1,54 @@ +// +#pragma warning disable +#if !NET12_0_OR_GREATER +namespace Polyfills; +using System; +using System.Reflection; +using System.Text; +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + return destination; + } + } + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} +#endif diff --git a/src/Split/net5.0/StringBuilderPolyfill.cs b/src/Split/net5.0/StringBuilderPolyfill.cs new file mode 100644 index 00000000..cddb85db --- /dev/null +++ b/src/Split/net5.0/StringBuilderPolyfill.cs @@ -0,0 +1,54 @@ +// +#pragma warning disable +#if !NET12_0_OR_GREATER +namespace Polyfills; +using System; +using System.Reflection; +using System.Text; +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + return destination; + } + } + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} +#endif diff --git a/src/Split/net6.0/StringBuilderPolyfill.cs b/src/Split/net6.0/StringBuilderPolyfill.cs new file mode 100644 index 00000000..cddb85db --- /dev/null +++ b/src/Split/net6.0/StringBuilderPolyfill.cs @@ -0,0 +1,54 @@ +// +#pragma warning disable +#if !NET12_0_OR_GREATER +namespace Polyfills; +using System; +using System.Reflection; +using System.Text; +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + return destination; + } + } + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} +#endif diff --git a/src/Split/net7.0/StringBuilderPolyfill.cs b/src/Split/net7.0/StringBuilderPolyfill.cs new file mode 100644 index 00000000..cddb85db --- /dev/null +++ b/src/Split/net7.0/StringBuilderPolyfill.cs @@ -0,0 +1,54 @@ +// +#pragma warning disable +#if !NET12_0_OR_GREATER +namespace Polyfills; +using System; +using System.Reflection; +using System.Text; +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + return destination; + } + } + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} +#endif diff --git a/src/Split/net8.0/StringBuilderPolyfill.cs b/src/Split/net8.0/StringBuilderPolyfill.cs new file mode 100644 index 00000000..cddb85db --- /dev/null +++ b/src/Split/net8.0/StringBuilderPolyfill.cs @@ -0,0 +1,54 @@ +// +#pragma warning disable +#if !NET12_0_OR_GREATER +namespace Polyfills; +using System; +using System.Reflection; +using System.Text; +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + return destination; + } + } + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} +#endif diff --git a/src/Split/net9.0/StringBuilderPolyfill.cs b/src/Split/net9.0/StringBuilderPolyfill.cs new file mode 100644 index 00000000..cddb85db --- /dev/null +++ b/src/Split/net9.0/StringBuilderPolyfill.cs @@ -0,0 +1,54 @@ +// +#pragma warning disable +#if !NET12_0_OR_GREATER +namespace Polyfills; +using System; +using System.Reflection; +using System.Text; +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + return destination; + } + } + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} +#endif diff --git a/src/Split/netcoreapp2.0/StringBuilderPolyfill.cs b/src/Split/netcoreapp2.0/StringBuilderPolyfill.cs new file mode 100644 index 00000000..cddb85db --- /dev/null +++ b/src/Split/netcoreapp2.0/StringBuilderPolyfill.cs @@ -0,0 +1,54 @@ +// +#pragma warning disable +#if !NET12_0_OR_GREATER +namespace Polyfills; +using System; +using System.Reflection; +using System.Text; +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + return destination; + } + } + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} +#endif diff --git a/src/Split/netcoreapp2.1/StringBuilderPolyfill.cs b/src/Split/netcoreapp2.1/StringBuilderPolyfill.cs new file mode 100644 index 00000000..cddb85db --- /dev/null +++ b/src/Split/netcoreapp2.1/StringBuilderPolyfill.cs @@ -0,0 +1,54 @@ +// +#pragma warning disable +#if !NET12_0_OR_GREATER +namespace Polyfills; +using System; +using System.Reflection; +using System.Text; +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + return destination; + } + } + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} +#endif diff --git a/src/Split/netcoreapp2.2/StringBuilderPolyfill.cs b/src/Split/netcoreapp2.2/StringBuilderPolyfill.cs new file mode 100644 index 00000000..cddb85db --- /dev/null +++ b/src/Split/netcoreapp2.2/StringBuilderPolyfill.cs @@ -0,0 +1,54 @@ +// +#pragma warning disable +#if !NET12_0_OR_GREATER +namespace Polyfills; +using System; +using System.Reflection; +using System.Text; +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + return destination; + } + } + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} +#endif diff --git a/src/Split/netcoreapp3.0/StringBuilderPolyfill.cs b/src/Split/netcoreapp3.0/StringBuilderPolyfill.cs new file mode 100644 index 00000000..cddb85db --- /dev/null +++ b/src/Split/netcoreapp3.0/StringBuilderPolyfill.cs @@ -0,0 +1,54 @@ +// +#pragma warning disable +#if !NET12_0_OR_GREATER +namespace Polyfills; +using System; +using System.Reflection; +using System.Text; +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + return destination; + } + } + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} +#endif diff --git a/src/Split/netcoreapp3.1/StringBuilderPolyfill.cs b/src/Split/netcoreapp3.1/StringBuilderPolyfill.cs new file mode 100644 index 00000000..cddb85db --- /dev/null +++ b/src/Split/netcoreapp3.1/StringBuilderPolyfill.cs @@ -0,0 +1,54 @@ +// +#pragma warning disable +#if !NET12_0_OR_GREATER +namespace Polyfills; +using System; +using System.Reflection; +using System.Text; +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + return destination; + } + } + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} +#endif diff --git a/src/Split/netstandard2.0/StringBuilderPolyfill.cs b/src/Split/netstandard2.0/StringBuilderPolyfill.cs new file mode 100644 index 00000000..cddb85db --- /dev/null +++ b/src/Split/netstandard2.0/StringBuilderPolyfill.cs @@ -0,0 +1,54 @@ +// +#pragma warning disable +#if !NET12_0_OR_GREATER +namespace Polyfills; +using System; +using System.Reflection; +using System.Text; +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + return destination; + } + } + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} +#endif diff --git a/src/Split/netstandard2.1/StringBuilderPolyfill.cs b/src/Split/netstandard2.1/StringBuilderPolyfill.cs new file mode 100644 index 00000000..cddb85db --- /dev/null +++ b/src/Split/netstandard2.1/StringBuilderPolyfill.cs @@ -0,0 +1,54 @@ +// +#pragma warning disable +#if !NET12_0_OR_GREATER +namespace Polyfills; +using System; +using System.Reflection; +using System.Text; +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + return destination; + } + } + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} +#endif diff --git a/src/Split/uap10.0/StringBuilderPolyfill.cs b/src/Split/uap10.0/StringBuilderPolyfill.cs new file mode 100644 index 00000000..cddb85db --- /dev/null +++ b/src/Split/uap10.0/StringBuilderPolyfill.cs @@ -0,0 +1,54 @@ +// +#pragma warning disable +#if !NET12_0_OR_GREATER +namespace Polyfills; +using System; +using System.Reflection; +using System.Text; +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + return destination; + } + } + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} +#endif diff --git a/src/Tests/PolyfillTests_StringBuilder.cs b/src/Tests/PolyfillTests_StringBuilder.cs index 800b8a7b..ad42616d 100644 --- a/src/Tests/PolyfillTests_StringBuilder.cs +++ b/src/Tests/PolyfillTests_StringBuilder.cs @@ -90,6 +90,29 @@ public async Task AppendLineWithFormat() await Assert.That(builder.ToString()).IsEqualTo("value10" + Environment.NewLine); } + [Test] + public async Task MoveChunks() + { + var source = new StringBuilder("hello", 10); + var maxCapacity = source.MaxCapacity; + + var destination = StringBuilder.MoveChunks(source); + + await Assert.That(destination.ToString()).IsEqualTo("hello"); + await Assert.That(destination.MaxCapacity).IsEqualTo(maxCapacity); + + await Assert.That(source.Length).IsEqualTo(0); + await Assert.That(source.Capacity).IsEqualTo(0); + await Assert.That(source.MaxCapacity).IsEqualTo(maxCapacity); + + source.Append('x'); + await Assert.That(source.ToString()).IsEqualTo("x"); + } + + [Test] + public async Task MoveChunks_Null_Throws() => + await Assert.That(() => StringBuilder.MoveChunks(null!)).Throws(); + [Test] public async Task StringBuilder_Insert_ReadOnlySpan() {