diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Guid.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Guid.cs index 31b60eba9ea61c..33bece9919ebaa 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Guid.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Guid.cs @@ -28,29 +28,32 @@ public static partial class Utf8Formatter /// public static bool TryFormat(Guid value, Span destination, out int bytesWritten, StandardFormat format = default) { - int flags; - - switch (FormattingHelpers.GetSymbolOrDefault(format, 'D')) + int flags = format.Symbol; + if (flags is '\0' or 'D') { - case 'D': // nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn - flags = 36 + Guid.TryFormatFlags_UseDashes; - break; - - case 'B': // {nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn} - flags = 38 + Guid.TryFormatFlags_UseDashes + Guid.TryFormatFlags_CurlyBraces; - break; + // nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn + flags = 36 + Guid.TryFormatFlags_UseDashes; + } + else + { + switch (flags) + { + case 'N': // nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn + flags = 32; + break; - case 'P': // (nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn) - flags = 38 + Guid.TryFormatFlags_UseDashes + Guid.TryFormatFlags_Parens; - break; + case 'B': // {nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn} + flags = 38 + Guid.TryFormatFlags_UseDashes + Guid.TryFormatFlags_CurlyBraces; + break; - case 'N': // nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn - flags = 32; - break; + case 'P': // (nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn) + flags = 38 + Guid.TryFormatFlags_UseDashes + Guid.TryFormatFlags_Parens; + break; - default: - ThrowHelper.ThrowFormatException_BadFormatSpecifier(); - goto case 'D'; // unreachable + default: + ThrowHelper.ThrowFormatException_BadFormatSpecifier(); + break; // unreachable + } } return value.TryFormatCore(destination, out bytesWritten, flags); diff --git a/src/libraries/System.Private.CoreLib/src/System/Guid.cs b/src/libraries/System.Private.CoreLib/src/System/Guid.cs index 270da4ab7d0b09..37f656e03e976c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Guid.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Guid.cs @@ -1196,21 +1196,18 @@ private static unsafe int HexsToChars(TChar* guidChars, int a, int b) whe } // Returns the guid in "registry" format. - public override string ToString() => ToString("d", null); - - public string ToString([StringSyntax(StringSyntaxAttribute.GuidFormat)] string? format) - { - return ToString(format, null); - } + public override string ToString() => ToString(null); // IFormattable interface // We currently ignore provider - public string ToString([StringSyntax(StringSyntaxAttribute.GuidFormat)] string? format, IFormatProvider? provider) + public string ToString([StringSyntax(StringSyntaxAttribute.GuidFormat)] string? format, IFormatProvider? provider) => ToString(format); + + public string ToString([StringSyntax(StringSyntaxAttribute.GuidFormat)] string? format) { - int guidSize; + int flags; if (string.IsNullOrEmpty(format)) { - guidSize = 36; + flags = 36 + TryFormatFlags_UseDashes; } else { @@ -1223,32 +1220,34 @@ public string ToString([StringSyntax(StringSyntaxAttribute.GuidFormat)] string? switch (format[0] | 0x20) { case 'd': - guidSize = 36; + flags = 36 + TryFormatFlags_UseDashes; break; case 'n': - guidSize = 32; + flags = 32; break; - case 'b' or 'p': - guidSize = 38; + case 'b': + flags = 38 + TryFormatFlags_UseDashes + TryFormatFlags_CurlyBraces; break; - case 'x': - guidSize = 68; + case 'p': + flags = 38 + TryFormatFlags_UseDashes + TryFormatFlags_Parens; break; + case 'x': return ToStringX(); + default: - guidSize = 0; + flags = 0; ThrowBadGuidFormatSpecification(); break; - }; + } } - string guidString = string.FastAllocateString(guidSize); + string guidString = string.FastAllocateString((byte)flags); - bool result = TryFormatCore(new Span(ref guidString.GetRawStringData(), guidString.Length), out int bytesWritten, format); - Debug.Assert(result && bytesWritten == guidString.Length, "Formatting guid should have succeeded."); + bool result = TryFormatCore(new Span(ref guidString.GetRawStringData(), (byte)flags), out int charsWritten, flags); + Debug.Assert(result && charsWritten == guidString.Length, "Formatting guid should have succeeded."); return guidString; } @@ -1322,7 +1321,9 @@ private bool TryFormatCore(Span destination, out int charsWritten, return TryFormatCore(destination, out charsWritten, flags); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] // only used from two callers + // (Vectorized) implementation for D, N, P and B formats: + // [{|(]dddddddd[-]dddd[-]dddd[-]dddd[-]dddddddddddd[}|)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal unsafe bool TryFormatCore(Span destination, out int charsWritten, int flags) where TChar : unmanaged, IUtfChar { // The low byte of flags contains the required length. @@ -1335,81 +1336,92 @@ internal unsafe bool TryFormatCore(Span destination, out int chars charsWritten = (byte)flags; flags >>= 8; - fixed (TChar* guidChars = &MemoryMarshal.GetReference(destination)) + ref TChar d = ref MemoryMarshal.GetReference(destination); + + // The low byte of flags now contains the opening brace char (if any) + if ((byte)flags != 0) + { + d = TChar.CastFrom((byte)flags); + d = ref Unsafe.Add(ref d, 1); + } + flags >>= 8; + + if ((Ssse3.IsSupported || AdvSimd.Arm64.IsSupported) && BitConverter.IsLittleEndian) { - TChar* p = guidChars; + (Vector128 vecX, Vector128 vecY, Vector128 vecZ) = FormatGuidVector128Utf8(this, flags < 0 /* dash */); - // The low byte of flags now contains the opening brace char (if any) - if ((byte)flags != 0) + if (typeof(TChar) == typeof(byte)) { - *p++ = TChar.CastFrom((byte)flags); + ref byte pChar = ref Unsafe.As(ref d); + if (flags < 0 /* dash */) + { + // We need to merge these vectors in this order: + // xxxxxxxxxxxxxxxx + // yyyyyyyyyyyyyyyy + // zzzzzzzzzzzzzzzz + vecX.StoreUnsafe(ref pChar); + vecY.StoreUnsafe(ref pChar, 20); + vecZ.StoreUnsafe(ref pChar, 8); + d = ref Unsafe.Add(ref d, 36); + } + else + { + // xxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyy + vecX.StoreUnsafe(ref pChar); + vecY.StoreUnsafe(ref pChar, 16); + d = ref Unsafe.Add(ref d, 32); + } } - flags >>= 8; - - if ((Ssse3.IsSupported || AdvSimd.Arm64.IsSupported) && BitConverter.IsLittleEndian) + else { - // Vectorized implementation for D, N, P and B formats: - // [{|(]dddddddd[-]dddd[-]dddd[-]dddd[-]dddddddddddd[}|)] - (Vector128 vecX, Vector128 vecY, Vector128 vecZ) = FormatGuidVector128Utf8(this, flags < 0 /* dash */); - - if (typeof(TChar) == typeof(byte)) + ref ushort pChar = ref Unsafe.As(ref d); + if (flags < 0 /* dash */) { - byte* pChar = (byte*)p; - if (flags < 0 /* dash */) + // We need to merge these vectors in this order: + // xxxxxxxxxxxxxxxx + // yyyyyyyyyyyyyyyy + // zzzzzzzzzzzzzzzz + Vector128.WidenLower(vecX).StoreUnsafe(ref pChar); + if (Vector256.IsHardwareAccelerated) { - // We need to merge these vectors in this order: - // xxxxxxxxxxxxxxxx - // yyyyyyyyyyyyyyyy - // zzzzzzzzzzzzzzzz - vecX.Store(pChar); - vecY.Store(pChar + 20); - vecZ.Store(pChar + 8); - p += 36; + Vector256.WidenLower(Vector128.ToVector256Unsafe(vecY)).StoreUnsafe(ref pChar, 20); + Vector256.WidenLower(Vector128.ToVector256Unsafe(vecZ)).StoreUnsafe(ref pChar, 8); } else { - // xxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyy - vecX.Store(pChar); - vecY.Store(pChar + 16); - p += 32; + Vector128.WidenLower(vecY).StoreUnsafe(ref pChar, 20); + Vector128.WidenUpper(vecY).StoreUnsafe(ref pChar, 28); + Vector128.WidenLower(vecZ).StoreUnsafe(ref pChar, 8); + Vector128.WidenUpper(vecZ).StoreUnsafe(ref pChar, 16); } + d = ref Unsafe.Add(ref d, 36); } else { - // Expand to UTF-16 - (Vector128 x0, Vector128 x1) = Vector128.Widen(vecX); - (Vector128 y0, Vector128 y1) = Vector128.Widen(vecY); - ushort* pChar = (ushort*)p; - if (flags < 0 /* dash */) + // xxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyy + if (Vector256.IsHardwareAccelerated) { - (Vector128 z0, Vector128 z1) = Vector128.Widen(vecZ); - - // We need to merge these vectors in this order: - // xxxxxxxxxxxxxxxx - // yyyyyyyyyyyyyyyy - // zzzzzzzzzzzzzzzz - x0.Store(pChar); - y0.Store(pChar + 20); - y1.Store(pChar + 28); - z0.Store(pChar + 8); // overlaps x1 - z1.Store(pChar + 16); - p += 36; + Vector256.WidenLower(Vector128.ToVector256Unsafe(vecX)).StoreUnsafe(ref pChar); + Vector256.WidenLower(Vector128.ToVector256Unsafe(vecY)).StoreUnsafe(ref pChar, 16); } else { - // xxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyy - x0.Store(pChar); - x1.Store(pChar + 8); - y0.Store(pChar + 16); - y1.Store(pChar + 24); - p += 32; + Vector128.WidenLower(vecX).StoreUnsafe(ref pChar); + Vector128.WidenUpper(vecX).StoreUnsafe(ref pChar, 8); + Vector128.WidenLower(vecY).StoreUnsafe(ref pChar, 16); + Vector128.WidenUpper(vecY).StoreUnsafe(ref pChar, 24); } + d = ref Unsafe.Add(ref d, 32); } } - else + } + else + { + fixed (TChar* guidChars = &d) { - // Non-vectorized fallback for D, N, P and B formats: - // [{|(]dddddddd[-]dddd[-]dddd[-]dddd[-]dddddddddddd[}|)] + TChar* p = guidChars; + // Non-vectorized fallback for: + // dddddddd[-]dddd[-]dddd[-]dddd[-]dddddddddddd p += HexsToChars(p, _a >> 24, _a >> 16); p += HexsToChars(p, _a >> 8, _a); if (flags < 0 /* dash */) @@ -1434,20 +1446,28 @@ internal unsafe bool TryFormatCore(Span destination, out int chars p += HexsToChars(p, _f, _g); p += HexsToChars(p, _h, _i); p += HexsToChars(p, _j, _k); + d = ref *p; } + } - // The low byte of flags now contains the closing brace char (if any) - if ((byte)flags != 0) - { - *p = TChar.CastFrom((byte)flags); - } - - Debug.Assert(p == guidChars + charsWritten - ((byte)flags != 0 ? 1 : 0)); + // The low byte of flags now contains the closing brace char (if any) + if ((byte)flags != 0) + { + d = TChar.CastFrom((byte)flags); } + Debug.Assert(Unsafe.AreSame(in d, in Unsafe.Add(ref destination[0], charsWritten - ((byte)flags != 0 ? 1 : 0)))); return true; } + private string ToStringX() + { + string guidString = string.FastAllocateString(68); + bool result = TryFormatX(new Span(ref guidString.GetRawStringData(), guidString.Length), out int charsWritten); + Debug.Assert(result && charsWritten == guidString.Length, "Formatting guid should have succeeded."); + return guidString; + } + private bool TryFormatX(Span dest, out int charsWritten) where TChar : unmanaged, IUtfChar { if (dest.Length < 68) @@ -1543,14 +1563,7 @@ private static (Vector128, Vector128, Vector128) FormatGuidVec // yyyyyyyyyyyyyyyy // zzzzzzzzzzzzzzzz // - // Vector "x" - just one dash, shift all elements after it. - Vector128 vecX = Vector128.Shuffle(hexLow, - Vector128.Create(0x706050403020100, 0xD0CFF0B0A0908FF).AsByte()); - - // Vector "y" - same here. - Vector128 vecY = Vector128.Shuffle(hexHigh, - Vector128.Create(0x7060504FF030201, 0xF0E0D0C0B0A0908).AsByte()); - + // "x" is hexLow and "y" is hexHigh. // Vector "z" - we need to merge some elements of hexLow with hexHigh and add 4 dashes. Vector128 vecZ; Vector128 dashesMask = Vector128.Create(0x00002D000000002D, 0x2D000000002D0000).AsByte(); @@ -1567,14 +1580,12 @@ private static (Vector128, Vector128, Vector128) FormatGuidVec } else { - Vector128 mid1 = Vector128.Shuffle(hexLow, - Vector128.Create(0x0D0CFF0B0A0908FF, 0xFFFFFFFFFFFF0F0E).AsByte()); - Vector128 mid2 = Vector128.Shuffle(hexHigh, - Vector128.Create(0xFFFFFFFFFFFFFFFF, 0xFF03020100FFFFFF).AsByte()); - vecZ = (mid1 | mid2 | dashesMask); + vecZ = Vector128.Shuffle(Ssse3.AlignRight(hexHigh, hexLow, 8), + Vector128.Create(0x0504FF03020100FF, 0xFF0B0A0908FF0706).AsByte()); + vecZ |= dashesMask; } - return (vecX, vecY, vecZ); + return (hexLow, hexHigh, vecZ); } // N format - no dashes.