diff --git a/X10D.Tests/src/Text/RuneTests.cs b/X10D.Tests/src/Text/RuneTests.cs
index c8181b1..6382fab 100644
--- a/X10D.Tests/src/Text/RuneTests.cs
+++ b/X10D.Tests/src/Text/RuneTests.cs
@@ -45,6 +45,38 @@ public class RuneTests
Assert.AreEqual("a", repeated);
}
+ [TestMethod]
+ public void RepeatCodepoint_0000_007F_ShouldCorrect()
+ {
+ string repeated = new Rune(69).Repeat(16);
+ Assert.AreEqual(16, repeated.Length);
+ Assert.AreEqual("EEEEEEEEEEEEEEEE", repeated);
+ }
+
+ [TestMethod]
+ public void RepeatCodepoint_0080_07FF_ShouldCorrect()
+ {
+ string repeated = new Rune(192).Repeat(8);
+ Assert.AreEqual(8, repeated.Length);
+ Assert.AreEqual("ÀÀÀÀÀÀÀÀ", repeated);
+ }
+
+ [TestMethod]
+ public void RepeatCodepoint_0800_FFFF_ShouldCorrect()
+ {
+ string repeated = new Rune(0x0800).Repeat(5);
+ Assert.AreEqual(5, repeated.Length);
+ Assert.AreEqual("ࠀࠀࠀࠀࠀ", repeated);
+ }
+
+ [TestMethod]
+ public void RepeatCodepointBeyondU10000ShouldCorrect()
+ {
+ string repeated = new Rune('\uD800', '\uDC00').Repeat(6);
+ Assert.AreEqual(12, repeated.Length);
+ Assert.AreEqual("𐀀𐀀𐀀𐀀𐀀𐀀", repeated);
+ }
+
[TestMethod]
public void RepeatZeroCountShouldBeEmpty()
{
diff --git a/X10D/Resource.Designer.cs b/X10D/Resource.Designer.cs
index 02e56e9..9f56c01 100644
--- a/X10D/Resource.Designer.cs
+++ b/X10D/Resource.Designer.cs
@@ -19,7 +19,7 @@ namespace X10D {
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resource {
@@ -77,5 +77,14 @@ namespace X10D {
return ResourceManager.GetString("EnumParseNotEnumException", resourceCulture);
}
}
+
+ ///
+ /// Looks up a localized string similar to Rune.Utf8SequenceLength returns value outside range 1 to 4 (inclusive), which is unexpected according to the official documentation..
+ ///
+ internal static string RuneUtf8SequenceLengthUnexpectedValue {
+ get {
+ return ResourceManager.GetString("RuneUtf8SequenceLengthUnexpectedValue", resourceCulture);
+ }
+ }
}
}
diff --git a/X10D/Resource.resx b/X10D/Resource.resx
index 3a48c16..7ac15f8 100644
--- a/X10D/Resource.resx
+++ b/X10D/Resource.resx
@@ -123,4 +123,7 @@
Type provided must be an Enum.
+
+ Rune.Utf8SequenceLength returns value outside range 1 to 4 (inclusive), which is unexpected according to the official documentation.
+
\ No newline at end of file
diff --git a/X10D/src/Core/IntrinsicExtensions.cs b/X10D/src/Core/IntrinsicExtensions.cs
index 1128360..bcd8cfd 100644
--- a/X10D/src/Core/IntrinsicExtensions.cs
+++ b/X10D/src/Core/IntrinsicExtensions.cs
@@ -1,6 +1,9 @@
#if NETCOREAPP3_0_OR_GREATER
+using System.Diagnostics.Contracts;
+using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
namespace X10D.Core;
@@ -10,6 +13,154 @@ namespace X10D.Core;
///
public static class IntrinsicExtensions
{
- // Got nothing for now.
+ ///
+ ///
+ /// Correcting of into 0 and 1 depend on their boolean truthiness.
+ ///
+ /// Operation:
+ ///
+ /// for (int i = 0; i < 8; i++) {
+ /// dest[i] = vector[i] == 0 ? 0 : 1;
+ /// }
+ ///
+ ///
+ /// Vector of byte to correct.
+ ///
+ /// A of which remapped back to 0 and 1 based on boolean truthiness.
+ ///
+ [Pure]
+ [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
+ public static Vector64 CorrectBoolean(this Vector64 vector)
+ {
+ // TODO: AdvSimd implementation.
+ // TODO: WasmSimd implementation. (?)
+
+ var output = IntrinsicUtility.GetUninitializedVector64();
+
+ for (int i = 0; i < Vector64.Count; i++)
+ {
+ ref var writeElement = ref Unsafe.Add(ref Unsafe.As, byte>(ref output), i);
+#if NET7_0_OR_GREATER
+ writeElement = vector[i] == 0 ? (byte)0 : (byte)1;
+#else
+ var element = Unsafe.Add(ref Unsafe.As, byte>(ref vector), i);
+ writeElement = element == 0 ? (byte)0 : (byte)1;
+#endif
+ }
+
+ return output;
+ }
+
+ ///
+ ///
+ /// Correcting of into 0 and 1 depend on their boolean truthiness.
+ ///
+ /// Operation:
+ ///
+ /// for (int i = 0; i < 16; i++) {
+ /// dest[i] = vector[i] == 0 ? 0 : 1;
+ /// }
+ ///
+ ///
+ /// Vector of byte to correct.
+ ///
+ /// A of which remapped back to 0 and 1 based on boolean truthiness.
+ ///
+ [Pure]
+ [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
+ public static Vector128 CorrectBoolean(this Vector128 vector)
+ {
+ if (Sse2.IsSupported)
+ {
+ var cmp = Sse2.CompareEqual(vector, Vector128.Zero);
+ var result = Sse2.AndNot(cmp, Vector128.Create((byte)1));
+
+ return result;
+ }
+
+ // TODO: AdvSimd implementation.
+ // TODO: WasmSimd implementation.
+
+ var output = IntrinsicUtility.GetUninitializedVector128();
+
+ for (int i = 0; i < Vector128.Count; i++)
+ {
+ Unsafe.Add(ref Unsafe.As, byte>(ref output), i) =
+ Unsafe.Add(ref Unsafe.As, byte>(ref vector), i) == 0 ? (byte)0 : (byte)1;
+ }
+
+ return output;
+ }
+
+ ///
+ ///
+ /// Correcting of into 0 and 1 depend on their boolean truthiness.
+ ///
+ /// Operation:
+ ///
+ /// for (int i = 0; i < 32; i++) {
+ /// dest[i] = vector[i] == 0 ? 0 : 1;
+ /// }
+ ///
+ ///
+ /// Vector of byte to correct.
+ ///
+ /// A of which remapped back to 0 and 1 based on boolean truthiness.
+ ///
+ [Pure]
+ [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
+ public static Vector256 CorrectBoolean(this Vector256 vector)
+ {
+ if (Avx2.IsSupported)
+ {
+ var cmp = Avx2.CompareEqual(vector, Vector256.Zero);
+ var result = Avx2.AndNot(cmp, Vector256.Create((byte)1));
+
+ return result;
+ }
+
+ var output = IntrinsicUtility.GetUninitializedVector256();
+
+ for (int i = 0; i < Vector256.Count; i++)
+ {
+ Unsafe.Add(ref Unsafe.As, byte>(ref output), i) =
+ Unsafe.Add(ref Unsafe.As, byte>(ref vector), i) == 0 ? (byte)0 : (byte)1;
+ }
+
+ return output;
+ }
+
+ ///
+ ///
+ /// Reverse position of 2 64-bit unsigned integer.
+ ///
+ /// Operation:
+ ///
+ /// dest[1] = vector[0];
+ /// dest[0] = vector[1];
+ ///
+ ///
+ /// Input vector.
+ ///
+ /// A of with elements the same as input vector except their positions
+ /// (or indices) are reversed.
+ ///
+ [Pure]
+ [CLSCompliant(false)]
+ [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
+ public static Vector128 ReverseElements(this Vector128 vector)
+ {
+ if (Sse2.IsSupported)
+ {
+ return Sse2.Shuffle(vector.AsDouble(), vector.AsDouble(), 0b01).AsUInt64();
+ }
+
+ Vector128 output = IntrinsicUtility.GetUninitializedVector128();
+
+ Unsafe.As, ulong>(ref output) = Unsafe.Add(ref Unsafe.As, ulong>(ref vector), 1);
+ Unsafe.Add(ref Unsafe.As, ulong>(ref output), 1) = Unsafe.As, ulong>(ref vector);
+
+ return output;
+ }
}
#endif
diff --git a/X10D/src/Core/IntrinsicUtility.cs b/X10D/src/Core/IntrinsicUtility.cs
index 07dd852..b94edd1 100644
--- a/X10D/src/Core/IntrinsicUtility.cs
+++ b/X10D/src/Core/IntrinsicUtility.cs
@@ -14,149 +14,14 @@ namespace X10D.Core;
public static class IntrinsicUtility
{
// NOTE:
- // ANY METHOD THAT OPERATE ON ANYTHING THAT ISN'T FLOAT IS NOT SSE COMPATIBLE, MUST BE SSE2 AND BEYOND VERSION
+ // ANY METHOD THAT OPERATE ON ANYTHING THAT ISN'T FLOAT IS NOT SSE COMPATIBLE, MUST BE SSE2 AND BEYONDS
// FOR API CONSISTENCY.
///
- ///
- /// Correcting of into 0 and 1 depend on their boolean truthiness.
- ///
- ///
Operation (raw):
- ///
- /// for (int i = 0; i < 8; i++) {
- /// dest[i] = ~(vector[i] == 0 ? 0xFF : 0x00) & 1;
- /// }
- ///
- ///
Operation (simplified):
- ///
- /// for (int i = 0; i < 8; i++) {
- /// dest[i] = vector[i] == 0 ? 0 : 1;
- /// }
- ///
- ///
- /// Vector of byte to correct.
- ///
- /// A of which remapped back to 0 and 1 based on boolean truthiness.
- ///
- [Pure]
- [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
- public static Vector64 CorrectBoolean(Vector64 vector)
- {
- // TODO: AdvSimd implementation.
- // TODO: WasmSimd implementation. (?)
-
- var output = GetUninitializedVector64();
-
- for (int i = 0; i < Vector64.Count; i++)
- {
- ref var writeElement = ref Unsafe.Add(ref Unsafe.As, byte>(ref output), i);
-#if NET7_0_OR_GREATER
- writeElement = vector[i] == 0 ? (byte)0 : (byte)1;
-#else
- var element = Unsafe.Add(ref Unsafe.As, byte>(ref vector), i);
- writeElement = element == 0 ? (byte)0 : (byte)1;
-#endif
- }
-
- return output;
- }
-
- ///
- ///
- /// Correcting of into 0 and 1 depend on their boolean truthiness.
- ///
- ///
Operation (raw):
- ///
- /// for (int i = 0; i < 16; i++) {
- /// dest[i] = ~(vector[i] == 0 ? 0xFF : 0x00) & 1;
- /// }
- ///
- ///
Operation (simplified):
- ///
- /// for (int i = 0; i < 16; i++) {
- /// dest[i] = vector[i] == 0 ? 0 : 1;
- /// }
- ///
- ///
- /// Vector of byte to correct.
- ///
- /// A of which remapped back to 0 and 1 based on boolean truthiness.
- ///
- [Pure]
- [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
- public static Vector128 CorrectBoolean(Vector128 vector)
- {
- if (Sse2.IsSupported)
- {
- var cmp = Sse2.CompareEqual(vector, Vector128.Zero);
- var result = Sse2.AndNot(cmp, Vector128.Create((byte)1));
-
- return result;
- }
-
- // TODO: AdvSimd implementation.
- // TODO: WasmSimd implementation.
-
- var output = GetUninitializedVector128();
-
- for (int i = 0; i < Vector128.Count; i++)
- {
- Unsafe.Add(ref Unsafe.As, byte>(ref output), i) =
- Unsafe.Add(ref Unsafe.As, byte>(ref vector), i) == 0 ? (byte)0 : (byte)1;
- }
-
- return output;
- }
-
- ///
- ///
- /// Correcting of into 0 and 1 depend on their boolean truthiness.
- ///
- ///
Operation (raw):
- ///
- /// for (int i = 0; i < 16; i++) {
- /// dest[i] = ~(vector[i] == 0 ? 0xFF : 0x00) & 1;
- /// }
- ///
- ///
Operation (simplified):
- ///
- /// for (int i = 0; i < 16; i++) {
- /// dest[i] = vector[i] == 0 ? 0 : 1;
- /// }
- ///
- ///
- /// Vector of byte to correct.
- ///
- /// A of which remapped back to 0 and 1 based on boolean truthiness.
- ///
- [Pure]
- [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
- public static Vector256 CorrectBoolean(Vector256 vector)
- {
- if (Avx2.IsSupported)
- {
- var cmp = Avx2.CompareEqual(vector, Vector256.Zero);
- var result = Avx2.AndNot(cmp, Vector256.Create((byte)1));
-
- return result;
- }
-
- var output = GetUninitializedVector256();
-
- for (int i = 0; i < Vector256.Count; i++)
- {
- Unsafe.Add(ref Unsafe.As, byte>(ref output), i) =
- Unsafe.Add(ref Unsafe.As, byte>(ref vector), i) == 0 ? (byte)0 : (byte)1;
- }
-
- return output;
- }
-
- ///
- ///
+ ///
/// Multiply packed 64-bit unsigned integer elements in a and b and truncate the results to 64-bit integer.
- ///
- ///
Operation:
+ ///
+ /// Operation:
///
/// dest[0] = lhs[0] * rhs[0];
/// dest[1] = lhs[1] * rhs[1];
@@ -203,10 +68,10 @@ public static class IntrinsicUtility
}
///
- ///
+ ///
/// Multiply packed 64-bit unsigned integer elements in a and b and truncate the results to 64-bit integer.
- ///
- ///
Operation:
+ ///
+ /// Operation:
///
/// dest[0] = lhs[0] * rhs[0];
/// dest[1] = lhs[1] * rhs[1];
@@ -252,10 +117,10 @@ public static class IntrinsicUtility
}
///
- ///
+ ///
/// Multiply packed 64-bit signed integer elements in a and b and truncate the results to 64-bit integer.
- ///
- ///
Operation:
+ ///
+ /// Operation:
///
/// dest[0] = lhs[0] * rhs[0];
/// dest[1] = lhs[1] * rhs[1];
@@ -274,10 +139,10 @@ public static class IntrinsicUtility
}
///
- ///
+ ///
/// Multiply packed 64-bit signed integer elements in a and b and truncate the results to 64-bit integer.
- ///
- ///
Operation:
+ ///
+ /// Operation:
///
/// dest[0] = lhs[0] * rhs[0];
/// dest[1] = lhs[1] * rhs[1];
@@ -298,11 +163,11 @@ public static class IntrinsicUtility
}
///
- ///
+ ///
/// Horizontally apply OR operation on adjacent pairs of single-precision (32-bit) floating-point elements in lhs and
/// rhs.
- ///
- ///
Operation:
+ ///
+ /// Operation:
///
/// dest[0] = lhs[0] | lhs[1];
/// dest[1] = lhs[2] | lhs[3];
@@ -353,10 +218,10 @@ public static class IntrinsicUtility
}
///
- ///
+ ///
/// Horizontally apply OR operation on adjacent pairs of 32-bit integer elements in lhs and rhs.
- ///
- ///
Operation:
+ ///
+ /// Operation:
///
/// dest[0] = lhs[0] | lhs[1];
/// dest[1] = lhs[2] | lhs[3];
@@ -378,10 +243,10 @@ public static class IntrinsicUtility
}
///
- ///
+ ///
/// Horizontally apply OR operation on adjacent pairs of 32-bit unsigned integer elements in lhs and rhs.
- ///
- ///
Operation:
+ ///
+ /// Operation:
///
/// dest[0] = lhs[0] | lhs[1];
/// dest[1] = lhs[2] | lhs[3];
@@ -403,41 +268,9 @@ public static class IntrinsicUtility
return HorizontalOr(lhs.AsSingle(), rhs.AsSingle()).AsUInt32();
}
- ///
- ///
Reverse position of 2 64-bit unsigned integer.
- ///
Operation:
- ///
- /// ulong tmp = vector[0];
- /// vector[0] = vector[1];
- /// vector[1] = tmp;
- ///
- ///
- /// Input vector.
- ///
- /// A of with elements the same as input vector except their positions
- /// (or indices) are reversed.
- ///
- [Pure]
- [CLSCompliant(false)]
- [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
- public static Vector128 ReverseElements(Vector128 vector)
- {
- if (Sse2.IsSupported)
- {
- return Sse2.Shuffle(vector.AsDouble(), vector.AsDouble(), 0b01).AsUInt64();
- }
-
- Vector128 output = GetUninitializedVector128();
-
- Unsafe.As, ulong>(ref output) = Unsafe.Add(ref Unsafe.As, ulong>(ref vector), 1);
- Unsafe.Add(ref Unsafe.As, ulong>(ref output), 1) = Unsafe.As, ulong>(ref vector);
-
- return output;
- }
-
// Helper methods
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
- private static Vector64 GetUninitializedVector64() where T : struct
+ internal static Vector64 GetUninitializedVector64() where T : struct
{
#if NET6_0_OR_GREATER
Unsafe.SkipInit(out Vector64 output);
@@ -448,7 +281,7 @@ public static class IntrinsicUtility
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
- private static Vector128 GetUninitializedVector128() where T : struct
+ internal static Vector128 GetUninitializedVector128() where T : struct
{
#if NET6_0_OR_GREATER
Unsafe.SkipInit(out Vector128 output);
@@ -459,7 +292,7 @@ public static class IntrinsicUtility
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
- private static Vector256 GetUninitializedVector256() where T : struct
+ internal static Vector256 GetUninitializedVector256() where T : struct
{
#if NET6_0_OR_GREATER
Unsafe.SkipInit(out Vector256 output);
diff --git a/X10D/src/Core/SpanExtensions.cs b/X10D/src/Core/SpanExtensions.cs
index e4ecf07..77ecede 100644
--- a/X10D/src/Core/SpanExtensions.cs
+++ b/X10D/src/Core/SpanExtensions.cs
@@ -173,9 +173,8 @@ public static class SpanExtensions
if (Sse2.IsSupported)
{
var load = Sse2.LoadScalarVector128((ulong*)pSource).AsByte();
- var correct = IntrinsicUtility.CorrectBoolean(load);
- return unchecked((byte)(IntegerPackingMagic * correct.AsUInt64().GetElement(0) >> 56));
+ return unchecked((byte)(IntegerPackingMagic * load.CorrectBoolean().AsUInt64().GetElement(0) >> 56));
}
// Probably should remove this piece of code because it is untested, but I see no reason why it should fail
@@ -185,14 +184,11 @@ public static class SpanExtensions
{
// Hasn't been tested since March 6th 2023 (Reason: Unavailable hardware).
var load = AdvSimd.LoadVector64((byte*)pSource);
- var correct = IntrinsicUtility.CorrectBoolean(load);
- return unchecked((byte)(IntegerPackingMagic * correct.AsUInt64().GetElement(0) >> 56));
- }
- else
- {
- goto default;
+ return unchecked((byte)(IntegerPackingMagic * load.CorrectBoolean().AsUInt64().GetElement(0) >> 56));
}
+
+ goto default;
}
#endif
default:
@@ -252,10 +248,10 @@ public static class SpanExtensions
{
fixed (bool* pSource = source)
{
- var load = Sse2.LoadVector128((byte*)pSource);
- var correct = IntrinsicUtility.CorrectBoolean(load).AsUInt64();
- var multiply = IntrinsicUtility.Multiply(IntegerPackingMagicV128, correct);
- var shift = Sse2.ShiftRightLogical(multiply, 56);
+ Vector128 load = Sse2.LoadVector128((byte*)pSource);
+ Vector128 correct = load.CorrectBoolean().AsUInt64();
+ Vector128 multiply = IntrinsicUtility.Multiply(IntegerPackingMagicV128, correct);
+ Vector128 shift = Sse2.ShiftRightLogical(multiply, 56);
return (short)(shift.GetElement(0) | (shift.GetElement(1) << 8));
}
@@ -319,52 +315,52 @@ public static class SpanExtensions
{
if (Avx2.IsSupported)
{
- var load = Avx.LoadVector256((byte*)pSource);
- var correct = IntrinsicUtility.CorrectBoolean(load).AsUInt64();
+ Vector256 load = Avx.LoadVector256((byte*)pSource);
+ Vector256 correct = load.CorrectBoolean().AsUInt64();
- var multiply = IntrinsicUtility.Multiply(IntegerPackingMagicV256, correct);
- var shift = Avx2.ShiftRightLogical(multiply, 56);
+ Vector256 multiply = IntrinsicUtility.Multiply(IntegerPackingMagicV256, correct);
+ Vector256 shift = Avx2.ShiftRightLogical(multiply, 56);
shift = Avx2.ShiftLeftLogicalVariable(shift, Vector256.Create(0UL, 8, 16, 24));
- var p1 = Avx2.Permute4x64(shift, 0b10_11_00_01);
- var or1 = Avx2.Or(shift, p1);
- var p2 = Avx2.Permute4x64(or1, 0b00_00_10_10);
- var or2 = Avx2.Or(or1, p2);
+ Vector256 p1 = Avx2.Permute4x64(shift, 0b10_11_00_01);
+ Vector256 or1 = Avx2.Or(shift, p1);
+ Vector256 p2 = Avx2.Permute4x64(or1, 0b00_00_10_10);
+ Vector256 or2 = Avx2.Or(or1, p2);
return (int)or2.GetElement(0);
}
if (Sse2.IsSupported)
{
- var load = Sse2.LoadVector128((byte*)pSource);
- var correct = IntrinsicUtility.CorrectBoolean(load).AsUInt64();
+ Vector128 load = Sse2.LoadVector128((byte*)pSource);
+ Vector128 correct = load.CorrectBoolean().AsUInt64();
- var multiply = IntrinsicUtility.Multiply(IntegerPackingMagicV128, correct);
- var shift1 = Sse2.ShiftRightLogical(multiply, 56);
+ Vector128 multiply = IntrinsicUtility.Multiply(IntegerPackingMagicV128, correct);
+ Vector128 shift1 = Sse2.ShiftRightLogical(multiply, 56);
shift1 = Sse2.ShiftLeftLogical(shift1, Vector128.Create(0UL, 8UL));
load = Sse2.LoadVector128((byte*)(pSource + 16));
- correct = IntrinsicUtility.CorrectBoolean(load).AsUInt64();
+ correct = load.CorrectBoolean().AsUInt64();
multiply = IntrinsicUtility.Multiply(IntegerPackingMagicV128, correct);
- var shift2 = Sse2.ShiftRightLogical(multiply, 56);
+ Vector128 shift2 = Sse2.ShiftRightLogical(multiply, 56);
shift2 = Sse2.ShiftLeftLogical(shift2, Vector128.Create(16UL, 24UL));
- var or1 = Sse2.Or(shift1, shift2);
- var or2 = Sse2.Or(or1, IntrinsicUtility.ReverseElements(or1));
+ Vector128 or1 = Sse2.Or(shift1, shift2);
+ Vector128 or2 = Sse2.Or(or1, or1.ReverseElements());
return (int)or2.GetElement(0);
}
if (AdvSimd.IsSupported)
{
// Hasn't been tested since March 6th 2023 (Reason: Unavailable hardware).
- var vector1 = IntrinsicUtility.CorrectBoolean(AdvSimd.LoadVector128((byte*)pSource)).AsUInt64();
- var vector2 = IntrinsicUtility.CorrectBoolean(AdvSimd.LoadVector128((byte*)(pSource + 16))).AsUInt64();
+ Vector128 vector1 = AdvSimd.LoadVector128((byte*)pSource).CorrectBoolean().AsUInt64();
+ Vector128 vector2 = AdvSimd.LoadVector128((byte*)(pSource + 16)).CorrectBoolean().AsUInt64();
- var calc1 = AdvSimd.ShiftRightLogical(IntrinsicUtility.Multiply(IntegerPackingMagicV128, vector1), 56);
- var calc2 = AdvSimd.ShiftRightLogical(IntrinsicUtility.Multiply(IntegerPackingMagicV128, vector2), 56);
+ Vector128 calc1 = AdvSimd.ShiftRightLogical(IntrinsicUtility.Multiply(IntegerPackingMagicV128, vector1), 56);
+ Vector128 calc2 = AdvSimd.ShiftRightLogical(IntrinsicUtility.Multiply(IntegerPackingMagicV128, vector2), 56);
- var shift1 = AdvSimd.ShiftLogical(calc1, Vector128.Create(0, 8));
- var shift2 = AdvSimd.ShiftLogical(calc2, Vector128.Create(16, 24));
+ Vector128 shift1 = AdvSimd.ShiftLogical(calc1, Vector128.Create(0, 8));
+ Vector128 shift2 = AdvSimd.ShiftLogical(calc2, Vector128.Create(16, 24));
return (int)(shift1.GetElement(0) | shift1.GetElement(1) | shift2.GetElement(0) | shift2.GetElement(1));
}
diff --git a/X10D/src/Text/RuneExtensions.cs b/X10D/src/Text/RuneExtensions.cs
index 7bb1684..0089e83 100644
--- a/X10D/src/Text/RuneExtensions.cs
+++ b/X10D/src/Text/RuneExtensions.cs
@@ -1,5 +1,5 @@
#if NETCOREAPP3_0_OR_GREATER
-using System;
+using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@@ -47,40 +47,58 @@ public static class RuneExtensions
}
// Helpful documentation: https://en.wikipedia.org/wiki/UTF-8
+ // This probably gonna break interning but whatever.
switch (value.Utf8SequenceLength)
{
case 1:
{
- Unsafe.SkipInit(out byte bytes);
- value.EncodeToUtf8(MemoryMarshal.CreateSpan(ref bytes, 1));
-
+ // Codepoint 0 to 0x00FF can be directly turn into char value without any conversion.
return new string((char)value.Value, count);
}
+ // Codepoint 0x0080 to 0x07FF takes 2 UTF-8 bytes, and it can be represented by 1 UTF-16 character (.NET runtime use
+ // UTF-16 encoding).
+ // Source: https://stackoverflow.com/questions/63905684
case 2:
+ // Codepoint 0x0800 to 0xFFFF takes 3 UTF-8 bytes, and can also be represented by 1 UTF-16 character.
+ case 3:
{
- Span bytes = stackalloc byte[2];
- value.EncodeToUtf8(bytes);
+ // Codepoint 0x0080 to 0x07FF convert into 1 .NET character string, directly use string constructor.
+ unsafe
+ {
+ Span bytes = stackalloc byte[value.Utf8SequenceLength];
+ value.EncodeToUtf8(bytes);
- return new string(Encoding.UTF8.GetString(bytes)[0], count);
+ char character;
+ Encoding.UTF8.GetChars(bytes, new Span(&character, 1));
+
+ return new string(character, count);
+ }
+ }
+
+ // Codepoint 0x10000 and beyond will takes **only** 2 UTF-16 character.
+ case 4:
+ {
+ return string.Create(count * 2, value, (span, rune) =>
+ {
+ unsafe {
+ Span bytes = stackalloc byte[4];
+ value.EncodeToUtf8(bytes);
+
+ int characters; // 2 characters, fit inside 1 32-bit integer.
+ Encoding.UTF8.GetChars(bytes, new Span(&characters, 2));
+
+ MemoryMarshal.Cast(span).Fill(characters);
+ }
+ });
}
default:
- {
- int utf8SequenceLength = value.Utf8SequenceLength;
- Span utf8 = stackalloc byte[utf8SequenceLength];
- value.EncodeToUtf8(utf8);
-
- // Limit to maximum 1024 bytes stack allocation (Rune.Utf8SequenceLength return value in range of [1; 4])
- Span buffer = count <= 256 ? stackalloc byte[utf8.Length * count] : new byte[utf8.Length * count];
-
- for (var index = 0; index < count; index++)
- {
- utf8.CopyTo(buffer.Slice(index * utf8.Length, utf8.Length));
- }
-
- return Encoding.UTF8.GetString(buffer);
- }
+#if NET7_0_OR_GREATER
+ throw new UnreachableException(Resource.RuneUtf8SequenceLengthUnexpectedValue);
+#else
+ throw new InvalidOperationException(Resource.RuneUtf8SequenceLengthUnexpectedValue);
+#endif
}
}
}