获取使用大端格式 C# 进行编码的指南

问题描述 投票:0回答:3

我有一个不寻常的情况,我有一个使用二进制(16)主键的现有 MySQL 数据库,这些是现有 api 中使用的 UUID 的基础。

我的问题是,我现在想添加一个用 dotnet core 编写的替换 api,并且我遇到了编码问题,已在 here

进行了解释

具体来说,dotnet 中的 Guid 结构使用混合字节序格式,生成与现有 api 不同的字符串。由于显而易见的原因,这是不可接受的。

所以我的问题是:有没有一种优雅的方法来强制 Guid 结构完全使用大端格式进行编码?

如果没有,我可以写一个糟糕的 hack,但我想我应该首先检查 SO 社区的集体智慧!

c# mysql .net-core guid asp.net-core-webapi
3个回答
3
投票

不;据我所知,没有内置的方法可以实现这一点。是的,

Guid
目前只能称之为“疯狂字节序”实现。您需要获取
Guid
排序的位(通过
unsafe
Guid.ToByteArray
),然后手动排序它们,找出要反转的块 - 这不是一个简单的
Array.Reverse()
。所以:恐怕非常手动。我建议使用类似的指南

00010203-0405-0607-0809-0a0b0c0d0e0f

调试它;这给了你(我怀疑你知道):

03-02-01-00-05-04-07-06-08-09-0A-0B-0C-0D-0E-0F

所以:

  • 反转4
  • 反转2
  • 反转2
  • 直8

1
投票

截至 2021 年 2023 年,仍然没有内置方法可以在 C# 中将 System.Guid

 转换为 MySQL 兼容的大端字符串。

这是我们在工作中遇到这个确切的 C# 混合字节序 Guid 问题时提出的扩展:

原版

public static string ToStringBigEndian(this Guid guid) { // allocate enough bytes to store Guid ASCII string Span<byte> result = stackalloc byte[36]; // set all bytes to 0xFF (to be able to distinguish them from real data) result.Fill(0xFF); // get bytes from guid Span<byte> buffer = stackalloc byte[16]; _ = guid.TryWriteBytes(buffer); int skip = 0; // iterate over guid bytes for (int i = 0; i < buffer.Length; i++) { // indices 4, 6, 8 and 10 will contain a '-' delimiter character in the Guid string. // --> leave space for those delimiters if (i is 4 or 6 or 8 or 10) { skip++; } // stretch high and low bytes of every single byte into two bytes (skipping '-' delimiter characters) result[(2 * i) + skip] = (byte)(buffer[i] >> 0x4); result[(2 * i) + 1 + skip] = (byte)(buffer[i] & 0x0Fu); } // iterate over precomputed byte array. // values 0x0 to 0xF are final hex values, but must be mapped to ASCII characters. // value 0xFF is to be mapped to '-' delimiter character. for (int i = 0; i < result.Length; i++) { // map bytes to ASCII values (a-f will be lowercase) ref byte b = ref result[i]; b = b switch { 0xFF => 0x2D, // Map 0xFF to '-' character < 0xA => (byte)(b + 0x30u), // Map 0x0 - 0x9 to '0' - '9' _ => (byte)(b + 0x57u) // Map 0xA - 0xF to 'a' - 'f' }; } // get string from ASCII encoded guid byte array return Encoding.ASCII.GetString(result); }
它有点长,但除了它返回的大端字符串之外,它不进行堆分配,因此保证速度很快:)

2023 年更新:更快的版本

更少的分支 => 更少的分支错误预测 => 更少的管道停顿 => 更快。

public static string ToStringBigEndian(this Guid guid) { // allocate enough bytes to store Guid ASCII string Span<byte> result = stackalloc byte[36]; // get bytes from guid Span<byte> buffer = stackalloc byte[16]; _ = guid.TryWriteBytes(buffer); int skip = 0; // iterate over guid bytes for (int i = 0; i < buffer.Length; i++) { // indices 4, 6, 8 and 10 will contain a '-' delimiter character in the Guid string. // --> leave space for those delimiters // we can check if i is even and i / 2 is >= 2 and <= 5 to determine if we are at one of those indices // 0xF...F if i is odd and 0x0...0 if i is even int isOddMask = -(i & 1); // 0xF...F if i / 2 is < 2 and 0x0...0 if i / 2 is >= 2 int less2Mask = ((i >> 1) - 2) >> 31; // 0xF...F if i / 2 is > 5 and 0x0...0 if i / 2 is <= 5 int greater5Mask = ~(((i >> 1) - 6) >> 31); // 0xF...F if i is even and 2 <= i / 2 <= 5 otherwise 0x0...0 int skipIndexMask = ~(isOddMask | less2Mask | greater5Mask); // skipIndexMask will be 0xFFFFFFFF for indices 4, 6, 8 and 10 and 0x00000000 for all other indices // --> skip those indices skip += 1 & skipIndexMask; result[(2 * i) + skip] = ToHexCharBranchless(buffer[i] >>> 0x4); result[(2 * i) + skip + 1] = ToHexCharBranchless(buffer[i] & 0x0F); } // add dashes const byte dash = (byte)'-'; result[8] = result[13] = result[18] = result[23] = dash; // get string from ASCII encoded guid byte array return Encoding.ASCII.GetString(result); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static byte ToHexCharBranchless(int b) => // b + 0x30 for [0-9] if 0 <= b <= 9 and b + 0x30 + 0x27 for [a-f] if 10 <= b <= 15 (byte)(b + 0x30 + (0x27 & ~((b - 0xA) >> 31)));
基准测试结果表明性能提高了约 30%:


0
投票
转发自

为什么 Guid.ToByteArray() 以这种方式对字节进行排序?

using System; namespace rm.Extensions; /// <summary> /// Guid extensions. /// </summary> public static class GuidExtension { /// <summary> /// Returns a 16-element byte array that contains the value of this instance /// matching its string representation (endian-agnostic). /// <para></para> /// See https://stackoverflow.com/questions/9195551/why-does-guid-tobytearray-order-the-bytes-the-way-it-does /// <remarks> /// <para></para> /// Note: The byte[] returned by <see cref="ToByteArrayMatchingStringRepresentation(Guid)"/> will not yield /// the original Guid with <see cref="Guid(byte[])"/> ctor. /// </remarks> /// </summary> public static byte[] ToByteArrayMatchingStringRepresentation(this Guid guid) { var bytes = guid.ToByteArray(); TweakOrderOfGuidBytesToMatchStringRepresentation(bytes); return bytes; } /// <summary> /// Initializes a new instance of the <see cref="Guid"></see> structure by using the specified array of bytes /// matching its string representation (endian-agnostic). /// <para></para> /// See https://stackoverflow.com/questions/9195551/why-does-guid-tobytearray-order-the-bytes-the-way-it-does /// <remarks> /// <para></para> /// Note: The Guid returned by <see cref="ToGuidMatchingStringRepresentation(byte[])"/> will not yield /// the original byte[] with <see cref="Guid.ToByteArray()"/>. /// </remarks> /// </summary> public static Guid ToGuidMatchingStringRepresentation(this byte[] bytes) { _ = bytes ?? throw new ArgumentNullException(nameof(bytes)); if (bytes.Length != 16) { throw new ArgumentException("Length should be 16.", nameof(bytes)); } TweakOrderOfGuidBytesToMatchStringRepresentation(bytes); return new Guid(bytes); } private static void TweakOrderOfGuidBytesToMatchStringRepresentation(byte[] guidBytes) { if (BitConverter.IsLittleEndian) { Array.Reverse(guidBytes, 0, 4); Array.Reverse(guidBytes, 4, 2); Array.Reverse(guidBytes, 6, 2); } } }
来源:

https://github.com/rmandvikar/csharp-extensions/blob/dev/src/rm.Extensions/GuidExtension.cs

© www.soinside.com 2019 - 2024. All rights reserved.