Created
November 2, 2022 20:00
-
-
Save nesterenko-kv/25a9b046ce9fa6f4f046adf3063bf43d to your computer and use it in GitHub Desktop.
ObjectId review
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Runtime.InteropServices; | |
namespace WishDish.Primitives.Internal; | |
internal static unsafe class InternalHexTables | |
{ | |
internal const ushort MaximalChar = 103; | |
internal static readonly uint* TableToHex; | |
internal static readonly byte* TableFromHexToBytes; | |
static InternalHexTables() | |
{ | |
TableToHex = (uint*) Marshal.AllocHGlobal(sizeof(uint) * 256).ToPointer(); | |
for (var i = 0; i < 256; i++) | |
{ | |
var chars = Convert.ToString(i, toBase: 16).PadLeft(totalWidth: 2, paddingChar: '0'); | |
TableToHex[i] = ((uint) chars[1] << 16) | chars[0]; | |
} | |
TableFromHexToBytes = (byte*) Marshal.AllocHGlobal(103).ToPointer(); | |
for (var i = 0; i < 103; i++) | |
{ | |
TableFromHexToBytes[i] = (char)i switch | |
{ | |
'0' => 0x0, | |
'1' => 0x1, | |
'2' => 0x2, | |
'3' => 0x3, | |
'4' => 0x4, | |
'5' => 0x5, | |
'6' => 0x6, | |
'7' => 0x7, | |
'8' => 0x8, | |
'9' => 0x9, | |
'a' => 0xa, | |
'A' => 0xa, | |
'b' => 0xb, | |
'B' => 0xb, | |
'c' => 0xc, | |
'C' => 0xc, | |
'd' => 0xd, | |
'D' => 0xd, | |
'e' => 0xe, | |
'E' => 0xe, | |
'f' => 0xf, | |
'F' => 0xf, | |
_ => byte.MaxValue | |
}; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.ComponentModel; | |
using System.Diagnostics.CodeAnalysis; | |
using System.Runtime.CompilerServices; | |
using System.Runtime.InteropServices; | |
using System.Text.Json.Serialization; | |
using WishDish.Primitives.Internal; | |
namespace WishDish.Primitives; | |
/* | |
* BSON ObjectID is a 12-byte value consisting of: | |
* - a 4-byte timestamp (seconds since epoch) | |
* - a 3-byte machine id | |
* - a 2-byte process id | |
* - a 3-byte counter | |
* | |
* 0123 456 78 91011 | |
* time machine pid inc | |
*/ | |
[StructLayout(LayoutKind.Explicit, Pack = 1)] | |
[TypeConverter(typeof(ObjectIdTypeConverter))] | |
[JsonConverter(typeof(SystemTextJsonObjectIdJsonConverter))] | |
[SuppressMessage("ReSharper", "RedundantNameQualifier")] | |
// ReSharper disable once StructCanBeMadeReadOnly | |
public unsafe struct ObjectId : | |
#if NETCOREAPP3_1 || NET5_0 || NET6_0_OR_GREATER | |
ISpanFormattable, | |
#endif | |
IComparable, IComparable<ObjectId>, IEquatable<ObjectId> | |
{ | |
[FieldOffset(0)] | |
private readonly byte _byte0; | |
[FieldOffset(1)] | |
private readonly byte _byte1; | |
[FieldOffset(2)] | |
private readonly byte _byte2; | |
[FieldOffset(3)] | |
private readonly byte _byte3; | |
[FieldOffset(4)] | |
private readonly byte _byte4; | |
[FieldOffset(5)] | |
private readonly byte _byte5; | |
[FieldOffset(6)] | |
private readonly byte _byte6; | |
[FieldOffset(7)] | |
private readonly byte _byte7; | |
[FieldOffset(8)] | |
private readonly byte _byte8; | |
[FieldOffset(9)] | |
private readonly byte _byte9; | |
[FieldOffset(10)] | |
private readonly byte _byte10; | |
[FieldOffset(11)] | |
private readonly byte _byte11; | |
[FieldOffset(0)] | |
private readonly int _int0; | |
[FieldOffset(4)] | |
private readonly int _int4; | |
[FieldOffset(8)] | |
private readonly int _int8; | |
/// <summary> | |
/// A read-only instance of the <see cref="ObjectId" /> structure whose value is all zeros. | |
/// </summary> | |
// ReSharper disable once RedundantDefaultMemberInitializer | |
// ReSharper disable once MemberCanBePrivate.Global | |
public static readonly ObjectId Empty = new(); | |
/// <summary> | |
/// Gets the machine. | |
/// </summary> | |
// ReSharper disable once UnusedMember.Global | |
public int Machine => (_byte4 << 16) + (_byte5 << 8) + _byte6 /*(_int4 >> 8) & 0xffffff */; | |
/// <summary> | |
/// Gets the PID. | |
/// </summary> | |
// ReSharper disable once UnusedMember.Global | |
public short Pid => (short) ((_byte7 << 8) + _byte8) /*(short)(((_int4 << 8) & 0xff00) | ((_int8 >> 24) & 0x00ff))*/; | |
/// <summary> | |
/// Gets the increment. | |
/// </summary> | |
// ReSharper disable once UnusedMember.Global | |
public int Increment => (_byte9 << 16) + (_byte10 << 8) + _byte11 /*_int8 & 0xffffff */; | |
/// <summary> | |
/// Gets the timestamp. | |
/// </summary> | |
// ReSharper disable once ConvertToAutoPropertyWhenPossible | |
// ReSharper disable once MemberCanBePrivate.Global | |
// ReSharper disable once UnusedMember.Global | |
public int Timestamp => (_byte0 << 24) + (_byte1 << 16) + (_byte2 << 8) + _byte3 /*_int0*/; | |
/// <summary> | |
/// Gets the creation time (derived from the timestamp). | |
/// </summary> | |
// ReSharper disable once UnusedMember.Global | |
public DateTime CreationTime => DateTime.UnixEpoch.AddSeconds((uint) Timestamp); | |
static ObjectId() | |
{ | |
TableToHex = InternalHexTables.TableToHex; | |
TableFromHexToBytes = InternalHexTables.TableFromHexToBytes; | |
} | |
private const ushort MaximalChar = InternalHexTables.MaximalChar; | |
private static readonly uint* TableToHex; | |
private static readonly byte* TableFromHexToBytes; | |
/// <summary> | |
/// Initializes a new instance of the <see cref="ObjectId" /> structure by using the specified array of bytes. | |
/// </summary> | |
/// <param name="bytes">A 12-element byte array containing values with which to initialize the <see cref="ObjectId" />.</param> | |
/// <exception cref="ArgumentNullException"><paramref name="bytes" /> is <see langword="null" />.</exception> | |
/// <exception cref="ArgumentException"><paramref name="bytes" /> is not 12 bytes long.</exception> | |
// ReSharper disable once UnusedMember.Global | |
public ObjectId( | |
byte[] bytes | |
) | |
{ | |
ArgumentNullException.ThrowIfNull(bytes); | |
if (bytes.Length != 12) | |
{ | |
throw new ArgumentException("Byte array for ObjectId must be exactly 12 bytes long.", nameof(bytes)); | |
} | |
this = Unsafe.ReadUnaligned<ObjectId>(ref MemoryMarshal.GetReference(new ReadOnlySpan<byte>(bytes))); | |
} | |
/// <summary> | |
/// Initializes a new instance of the <see cref="ObjectId" /> structure by using the specified byte pointer. | |
/// </summary> | |
/// <param name="bytes">A byte pointer containing bytes which used to initialize the <see cref="ObjectId" />.</param> | |
// ReSharper disable once UnusedMember.Global | |
public ObjectId( | |
byte* bytes | |
) | |
{ | |
this = Unsafe.ReadUnaligned<ObjectId>(bytes); | |
} | |
/// <summary> | |
/// Initializes a new instance of the <see cref="ObjectId" /> structure by using the value represented by the specified | |
/// read-only span of | |
/// bytes. | |
/// </summary> | |
/// <param name="bytes"> | |
/// A read-only span containing the bytes representing the <see cref="ObjectId" />. The span must be | |
/// exactly 12 bytes long. | |
/// </param> | |
/// <exception cref="ArgumentException"><paramref name="bytes" /> is not 12 bytes long.</exception> | |
// ReSharper disable once UnusedMember.Global | |
public ObjectId( | |
ReadOnlySpan<byte> bytes | |
) | |
{ | |
if (bytes.Length != 12) | |
{ | |
throw new ArgumentException("Byte array for ObjectId must be exactly 12 bytes long.", nameof(bytes)); | |
} | |
this = Unsafe.ReadUnaligned<ObjectId>(ref MemoryMarshal.GetReference(bytes)); | |
} | |
/// <summary> | |
/// Returns a 12-element byte array that contains the value of this instance. | |
/// </summary> | |
/// <returns>A 12-element byte array.</returns> | |
// ReSharper disable once UnusedMember.Global | |
public byte[] ToByteArray() | |
{ | |
var result = new byte[12]; | |
Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(new Span<byte>(result)), this); | |
return result; | |
} | |
/// <summary> | |
/// Tries to write the current <see cref="ObjectId" /> instance into a span of bytes. | |
/// </summary> | |
/// <param name="destination"> | |
/// When this method returns <see langword="true" />, the <see cref="ObjectId" /> as a span of | |
/// bytes. | |
/// </param> | |
/// <returns> | |
/// <see langword="true" /> if the <see cref="ObjectId" /> is successfully written to the specified span; | |
/// <see langword="false" /> | |
/// otherwise. | |
/// </returns> | |
// ReSharper disable once UnusedMember.Global | |
public bool TryWriteBytes(Span<byte> destination) | |
{ | |
if (Unsafe.SizeOf<ObjectId>() > destination.Length) | |
{ | |
return false; | |
} | |
Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(destination), this); | |
return true; | |
} | |
/// <summary> | |
/// Compares this instance to a specified object or <see cref="ObjectId" /> and returns an indication of their relative | |
/// values. | |
/// </summary> | |
/// <param name="obj">An object to compare, or <see langword="null" />.</param> | |
/// <returns>A signed number indicating the relative values of this instance and <paramref name="obj" />.</returns> | |
/// <exception cref="ArgumentException"><paramref name="obj" /> must be of type <see cref="ObjectId" />.</exception> | |
[SuppressMessage("ReSharper", "MergeCastWithTypeCheck")] | |
[SuppressMessage("ReSharper", "UseNegatedPatternInIsExpression")] | |
public int CompareTo( | |
object? obj | |
) | |
{ | |
if (obj == null) | |
{ | |
return 1; | |
} | |
#pragma warning disable IDE0083 // Use pattern matching | |
if (!(obj is ObjectId)) | |
{ | |
throw new ArgumentException("Object must be of type ObjectId.", nameof(obj)); | |
} | |
#pragma warning restore IDE0083 // Use pattern matching | |
var other = (ObjectId) obj; | |
if (other._byte0 != _byte0) | |
{ | |
return _byte0 < other._byte0 ? -1 : 1; | |
} | |
if (other._byte1 != _byte1) | |
{ | |
return _byte1 < other._byte1 ? -1 : 1; | |
} | |
if (other._byte2 != _byte2) | |
{ | |
return _byte2 < other._byte2 ? -1 : 1; | |
} | |
if (other._byte3 != _byte3) | |
{ | |
return _byte3 < other._byte3 ? -1 : 1; | |
} | |
if (other._byte4 != _byte4) | |
{ | |
return _byte4 < other._byte4 ? -1 : 1; | |
} | |
if (other._byte5 != _byte5) | |
{ | |
return _byte5 < other._byte5 ? -1 : 1; | |
} | |
if (other._byte6 != _byte6) | |
{ | |
return _byte6 < other._byte6 ? -1 : 1; | |
} | |
if (other._byte7 != _byte7) | |
{ | |
return _byte7 < other._byte7 ? -1 : 1; | |
} | |
if (other._byte8 != _byte8) | |
{ | |
return _byte8 < other._byte8 ? -1 : 1; | |
} | |
if (other._byte9 != _byte9) | |
{ | |
return _byte9 < other._byte9 ? -1 : 1; | |
} | |
if (other._byte10 != _byte10) | |
{ | |
return _byte10 < other._byte10 ? -1 : 1; | |
} | |
if (other._byte11 != _byte11) | |
{ | |
return _byte11 < other._byte11 ? -1 : 1; | |
} | |
return 0; | |
} | |
/// <summary> | |
/// Compares this instance to a specified <see cref="ObjectId" /> object and returns an indication of their relative | |
/// values. | |
/// </summary> | |
/// <param name="other">An <see cref="ObjectId" /> object to compare to this instance.</param> | |
/// <returns>A signed number indicating the relative values of this instance and <paramref name="other" />.</returns> | |
public int CompareTo( | |
ObjectId other | |
) | |
{ | |
if (other._byte0 != _byte0) | |
{ | |
return _byte0 < other._byte0 ? -1 : 1; | |
} | |
if (other._byte1 != _byte1) | |
{ | |
return _byte1 < other._byte1 ? -1 : 1; | |
} | |
if (other._byte2 != _byte2) | |
{ | |
return _byte2 < other._byte2 ? -1 : 1; | |
} | |
if (other._byte3 != _byte3) | |
{ | |
return _byte3 < other._byte3 ? -1 : 1; | |
} | |
if (other._byte4 != _byte4) | |
{ | |
return _byte4 < other._byte4 ? -1 : 1; | |
} | |
if (other._byte5 != _byte5) | |
{ | |
return _byte5 < other._byte5 ? -1 : 1; | |
} | |
if (other._byte6 != _byte6) | |
{ | |
return _byte6 < other._byte6 ? -1 : 1; | |
} | |
if (other._byte7 != _byte7) | |
{ | |
return _byte7 < other._byte7 ? -1 : 1; | |
} | |
if (other._byte8 != _byte8) | |
{ | |
return _byte8 < other._byte8 ? -1 : 1; | |
} | |
if (other._byte9 != _byte9) | |
{ | |
return _byte9 < other._byte9 ? -1 : 1; | |
} | |
if (other._byte10 != _byte10) | |
{ | |
return _byte10 < other._byte10 ? -1 : 1; | |
} | |
if (other._byte11 != _byte11) | |
{ | |
return _byte11 < other._byte11 ? -1 : 1; | |
} | |
return 0; | |
} | |
/// <summary> | |
/// Returns a value that indicates whether two instances of <see cref="ObjectId" /> represent the same value. | |
/// </summary> | |
/// <param name="obj">The object to compare with this instance.</param> | |
/// <returns> | |
/// <see langword="true" /> if <paramref name="obj" /> is <see cref="ObjectId" /> that has the same value as this | |
/// instance; otherwise, | |
/// <see langword="false" />. | |
/// </returns> | |
[SuppressMessage("ReSharper", "MergeSequentialChecks")] | |
[SuppressMessage("ReSharper", "RedundantIfElseBlock")] | |
// Do not change that code syntax (do not merge checks, do not remove else) - perf critical | |
public override bool Equals( | |
object? obj | |
) | |
{ | |
if (obj is ObjectId other) | |
{ | |
return _int0 == other._int0 && _int4 == other._int4 && _int8 == other._int8; | |
} | |
return false; | |
} | |
/// <summary> | |
/// Returns a value indicating whether this instance and a specified <see cref="ObjectId" /> object represent the same | |
/// value. | |
/// </summary> | |
/// <param name="other">An object to compare to this instance.</param> | |
/// <returns> | |
/// <see langword="true" /> if <paramref name="other" /> is equal to this instance; otherwise, | |
/// <see langword="false" />. | |
/// </returns> | |
public bool Equals( | |
ObjectId other | |
) | |
{ | |
return _int0 == other._int0 && _int4 == other._int4 && _int8 == other._int8; | |
} | |
/// <summary> | |
/// Returns the hash code for this instance. | |
/// </summary> | |
/// <returns>The hash code for this instance.</returns> | |
public override int GetHashCode() | |
{ | |
return _int0 ^ _int4 ^ _int8; | |
} | |
/// <summary> | |
/// Indicates whether the values of two specified <see cref="ObjectId" /> objects are equal. | |
/// </summary> | |
/// <param name="left">The first object to compare.</param> | |
/// <param name="right">The second object to compare.</param> | |
/// <returns> | |
/// <see langword="true" /> if <paramref name="left" /> and <paramref name="right" /> are equal; otherwise, | |
/// <see langword="false" />. | |
/// </returns> | |
public static bool operator ==( | |
ObjectId left, | |
ObjectId right | |
) | |
{ | |
return left._int0 == right._int0 && left._int4 == right._int4 && left._int8 == right._int8; | |
} | |
/// <summary> | |
/// Indicates whether the values of two specified <see cref="ObjectId" /> objects are not equal. | |
/// </summary> | |
/// <param name="left">The first object to compare.</param> | |
/// <param name="right">The second object to compare.</param> | |
/// <returns> | |
/// <see langword="true" /> if <paramref name="left" /> and <paramref name="right" /> are not equal; otherwise, | |
/// <see langword="false" />. | |
/// </returns> | |
public static bool operator !=( | |
ObjectId left, | |
ObjectId right | |
) | |
{ | |
return left._int0 != right._int0 || left._int4 != right._int4 || left._int8 != right._int8; | |
} | |
#if NETCOREAPP3_1 || NET5_0 || NET6_0_OR_GREATER | |
/// <summary> | |
/// Tries to format the value of the current instance into the provided span of characters. | |
/// </summary> | |
/// <param name="destination"> | |
/// When this method returns <see langword="true" />, the <see cref="ObjectId" /> as a span of | |
/// characters. | |
/// </param> | |
/// <param name="charsWritten"> | |
/// When this method returns <see langword="true" />, the number of characters written in | |
/// <paramref name="destination" />. | |
/// </param> | |
/// <param name="format"> | |
/// A read-only span containing the character representing one of the following specifiers that indicates how to format | |
/// the value of this <see cref="ObjectId" />. The format parameter can be "N". If format is <see langword="null" /> or | |
/// an empty string (""), "N" is used. | |
/// </param> | |
/// <param name="provider"> | |
/// An optional object that supplies culture-specific formatting information for | |
/// <paramref name="destination" />. | |
/// </param> | |
/// <returns><see langword="true" /> if the formatting operation was successful; <see langword="false" /> otherwise.</returns> | |
bool ISpanFormattable.TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format, IFormatProvider? provider) | |
{ | |
if (format.Length == 0) | |
{ | |
format = "N"; | |
} | |
if (format.Length != 1) | |
{ | |
charsWritten = 0; | |
return false; | |
} | |
switch ((char) (format[0] | 0x20)) | |
{ | |
case 'n': | |
{ | |
if (destination.Length < 24) | |
{ | |
charsWritten = 0; | |
return false; | |
} | |
fixed (char* objectIdChars = &destination.GetPinnableReference()) | |
{ | |
FormatN(objectIdChars); | |
} | |
charsWritten = 24; | |
return true; | |
} | |
default: | |
{ | |
charsWritten = 0; | |
return false; | |
} | |
} | |
} | |
#endif | |
/// <summary> | |
/// Tries to format the value of the current instance into the provided span of characters. | |
/// </summary> | |
/// <param name="destination">When this method returns <see langword="true" />, the <see cref="ObjectId" /> as a span of characters.</param> | |
/// <param name="charsWritten"> | |
/// When this method returns <see langword="true" />, the number of characters written in | |
/// <paramref name="destination" />. | |
/// </param> | |
/// <param name="format"> | |
/// A read-only span containing the character representing one of the following specifiers that indicates how to format | |
/// the value of this <see cref="ObjectId" />. The format parameter can be "N". If format is <see langword="null" /> or | |
/// an empty string (""), "N" is used. | |
/// </param> | |
/// <returns><see langword="true" /> if the formatting operation was successful; <see langword="false" /> otherwise.</returns> | |
// ReSharper disable once OutParameterValueIsAlwaysDiscarded.Global | |
public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default) | |
{ | |
if (format.Length == 0) | |
{ | |
#if NETCOREAPP3_1 || NET5_0 || NET6_0_OR_GREATER | |
format = "N"; | |
#endif | |
#if NETSTANDARD2_0 | |
format = "N".AsSpan(); | |
#endif | |
} | |
if (format.Length != 1) | |
{ | |
charsWritten = 0; | |
return false; | |
} | |
switch ((char)(format[0] | 0x20)) | |
{ | |
case 'n': | |
{ | |
if (destination.Length < 24) | |
{ | |
charsWritten = 0; | |
return false; | |
} | |
fixed (char* objectIdChars = &destination.GetPinnableReference()) | |
{ | |
FormatN(objectIdChars); | |
} | |
charsWritten = 24; | |
return true; | |
} | |
default: | |
{ | |
charsWritten = 0; | |
return false; | |
} | |
} | |
} | |
// ReSharper disable once CommentTypo | |
/// <summary> | |
/// Returns a string representation of the value of this instance. | |
/// </summary> | |
/// <returns> | |
/// The value of this <see cref="ObjectId" />, formatted by using the "N" format specifier as follows: | |
/// xxxxxxxxxxxxxxxxxxxxxxxx | |
/// </returns> | |
public override string ToString() | |
{ | |
return ToString("N", formatProvider: null); | |
} | |
/// <summary> | |
/// Returns a string representation of the value of this <see cref="ObjectId" /> instance, according to the provided | |
/// format specifier. | |
/// </summary> | |
/// <param name="format"> | |
/// A single format specifier that indicates how to format the value of this <see cref="ObjectId" />. The format | |
/// parameter can | |
/// be "N". If format is <see langword="null" /> or an empty string (""), "N" is used. | |
/// </param> | |
/// <returns> | |
/// The value of this <see cref="ObjectId" />, represented as a series of lowercase hexadecimal digits in the | |
/// specified format. | |
/// </returns> | |
// ReSharper disable once UnusedMember.Global | |
public string ToString(string? format) | |
{ | |
// ReSharper disable once IntroduceOptionalParameters.Global | |
return ToString(format, formatProvider: null); | |
} | |
public string ToString(string? format, IFormatProvider? formatProvider) | |
{ | |
format ??= "N"; | |
if (string.IsNullOrEmpty(format)) | |
{ | |
format = "N"; | |
} | |
if (format.Length != 1) | |
{ | |
throw new FormatException( | |
"Format string can be only \"N\", \"n\"." | |
); | |
} | |
switch ((char) (format[0] | 0x20)) | |
{ | |
case 'n': | |
{ | |
var objectIdString = new string(c: '\0', count: 24); | |
#if NETCOREAPP3_1 || NET5_0 || NET6_0_OR_GREATER | |
fixed (char* objectIdChars = &objectIdString.GetPinnableReference()) | |
#endif | |
#if NETSTANDARD2_0 | |
fixed (char* objectIdChars = objectIdString) | |
#endif | |
{ | |
FormatN(objectIdChars); | |
} | |
return objectIdString; | |
} | |
default: | |
throw new FormatException( | |
"Format string can be only \"N\", \"n\"." | |
); | |
} | |
} | |
#if NETCOREAPP3_1 || NET5_0 || NET6_0_OR_GREATER | |
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] | |
#endif | |
#if NETSTANDARD2_0 | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
#endif | |
private void FormatN(char* dest) | |
{ | |
// ReSharper disable once CommentTypo | |
// dddddddddddddddddddddddd | |
var destUints = (uint*) dest; | |
destUints[0] = TableToHex[_byte0]; | |
destUints[1] = TableToHex[_byte1]; | |
destUints[2] = TableToHex[_byte2]; | |
destUints[3] = TableToHex[_byte3]; | |
destUints[4] = TableToHex[_byte4]; | |
destUints[5] = TableToHex[_byte5]; | |
destUints[6] = TableToHex[_byte6]; | |
destUints[7] = TableToHex[_byte7]; | |
destUints[8] = TableToHex[_byte8]; | |
destUints[9] = TableToHex[_byte9]; | |
destUints[10] = TableToHex[_byte10]; | |
destUints[11] = TableToHex[_byte11]; | |
} | |
/// <summary> | |
/// Initializes a new instance of the <see cref="ObjectId" /> structure by using the value represented by the specified | |
/// string. | |
/// </summary> | |
/// <param name="input">A string that contains a ObjectId.</param> | |
/// <exception cref="ArgumentNullException"><paramref name="input" /> is <see langword="null" />.</exception> | |
public ObjectId(string input) | |
{ | |
ArgumentNullException.ThrowIfNull(input); | |
var result = new ObjectId(); | |
var resultPtr = (byte*) &result; | |
#if NETCOREAPP3_1 || NET5_0 || NET6_0_OR_GREATER | |
fixed (char* objectIdStringPtr = &input.GetPinnableReference()) | |
#endif | |
#if NETSTANDARD2_0 | |
fixed (char* objectIdStringPtr = input) | |
#endif | |
{ | |
ParseWithExceptions(new ReadOnlySpan<char>(objectIdStringPtr, input.Length), objectIdStringPtr, resultPtr); | |
} | |
this = result; | |
} | |
/// <summary> | |
/// Initializes a new instance of the <see cref="ObjectId" /> structure by using the value represented by the specified | |
/// read-only span of | |
/// characters. | |
/// </summary> | |
/// <param name="input">A read-only span of characters that contains a ObjectId.</param> | |
/// <exception cref="FormatException"> | |
/// <paramref name="input" /> is empty or contains unrecognized <see cref="ObjectId" /> | |
/// format. | |
/// </exception> | |
// ReSharper disable once UnusedMember.Global | |
public ObjectId(ReadOnlySpan<char> input) | |
{ | |
if (input.IsEmpty) | |
{ | |
throw new FormatException("Unrecognized ObjectId format."); | |
} | |
var result = new ObjectId(); | |
var resultPtr = (byte*) &result; | |
fixed (char* objectIdStringPtr = &input.GetPinnableReference()) | |
{ | |
ParseWithExceptions(input, objectIdStringPtr, resultPtr); | |
} | |
this = result; | |
} | |
/// <summary> | |
/// Converts the string representation of a ObjectId to the equivalent <see cref="ObjectId" /> structure. | |
/// </summary> | |
/// <param name="input">The string to convert.</param> | |
/// <returns>A structure that contains the value that was parsed.</returns> | |
/// <exception cref="ArgumentNullException"><paramref name="input" /> is <see langword="null" />.</exception> | |
// ReSharper disable once UnusedMember.Global | |
public static ObjectId Parse( | |
string input | |
) | |
{ | |
ArgumentNullException.ThrowIfNull(input); | |
var result = new ObjectId(); | |
var resultPtr = (byte*) &result; | |
#if NETCOREAPP3_1 || NET5_0 || NET6_0_OR_GREATER | |
fixed (char* objectIdStringPtr = &input.GetPinnableReference()) | |
#endif | |
#if NETSTANDARD2_0 | |
fixed (char* objectIdStringPtr = input) | |
#endif | |
{ | |
ParseWithExceptions(new ReadOnlySpan<char>(objectIdStringPtr, input.Length), objectIdStringPtr, resultPtr); | |
} | |
return result; | |
} | |
/// <summary> | |
/// Converts a read-only character span that represents a ObjectId to the equivalent <see cref="ObjectId" /> structure. | |
/// </summary> | |
/// <param name="input">A read-only span containing the bytes representing a <see cref="ObjectId" />.</param> | |
/// <returns>A structure that contains the value that was parsed.</returns> | |
/// <exception cref="FormatException"><paramref name="input" /> is not in a recognized format.</exception> | |
// ReSharper disable once UnusedMember.Global | |
public static ObjectId Parse(ReadOnlySpan<char> input) | |
{ | |
if (input.IsEmpty) | |
{ | |
throw new FormatException("Unrecognized ObjectId format."); | |
} | |
var result = new ObjectId(); | |
var resultPtr = (byte*) &result; | |
fixed (char* objectIdStringPtr = &input.GetPinnableReference()) | |
{ | |
ParseWithExceptions(input, objectIdStringPtr, resultPtr); | |
} | |
return result; | |
} | |
/// <summary> | |
/// Converts the string representation of a <see cref="ObjectId" /> to the equivalent <see cref="ObjectId" /> | |
/// structure, provided that the string | |
/// is in the specified format. | |
/// </summary> | |
/// <param name="input">The <see cref="ObjectId" /> to convert.</param> | |
/// <param name="format"> | |
/// One of the following specifiers that indicates the exact format to use when interpreting <paramref name="input" />: | |
/// "N". | |
/// </param> | |
/// <returns>A structure that contains the value that was parsed.</returns> | |
/// <exception cref="ArgumentNullException"> | |
/// <paramref name="input" /> or <paramref name="format" /> is | |
/// <see langword="null" />. | |
/// </exception> | |
/// <exception cref="FormatException"> | |
/// <paramref name="input" /> is not in the format specified by | |
/// <paramref name="format" />. | |
/// </exception> | |
// ReSharper disable once UnusedMember.Global | |
public static ObjectId ParseExact(string input, string format) | |
{ | |
ArgumentNullException.ThrowIfNull(input); | |
ArgumentNullException.ThrowIfNull(format); | |
var result = new ObjectId(); | |
var resultPtr = (byte*) &result; | |
switch ((char) (format[0] | 0x20)) | |
{ | |
case 'n': | |
{ | |
#if NETCOREAPP3_1 || NET5_0 || NET6_0_OR_GREATER | |
fixed (char* objectIdStringPtr = &input.GetPinnableReference()) | |
#endif | |
#if NETSTANDARD2_0 | |
fixed (char* objectIdStringPtr = input) | |
#endif | |
{ | |
ParseWithExceptionsN((uint) input.Length, objectIdStringPtr, resultPtr); | |
} | |
return result; | |
} | |
default: | |
{ | |
throw new FormatException( | |
"Format string can be only \"N\", \"n\"." | |
); | |
} | |
} | |
} | |
/// <summary> | |
/// Converts the character span representation of a <see cref="ObjectId" /> to the equivalent <see cref="ObjectId" /> | |
/// structure, provided that the | |
/// string is in the specified format. | |
/// </summary> | |
/// <param name="input">A read-only span containing the characters representing the <see cref="ObjectId" /> to convert.</param> | |
/// <param name="format"> | |
/// A read-only span of characters representing one of the following specifiers that indicates the exact format to use | |
/// when interpreting <paramref name="input" />: "N". | |
/// </param> | |
/// <returns>A structure that contains the value that was parsed.</returns> | |
/// <exception cref="FormatException"> | |
/// <paramref name="input" /> is not in the format specified by | |
/// <paramref name="format" />. | |
/// </exception> | |
// ReSharper disable once UnusedMember.Global | |
public static ObjectId ParseExact(ReadOnlySpan<char> input, ReadOnlySpan<char> format) | |
{ | |
if (input.IsEmpty) | |
{ | |
throw new FormatException("Unrecognized ObjectId format."); | |
} | |
if (format.Length != 1) | |
{ | |
throw new FormatException( | |
"Format string can be only \"N\", \"n\"." | |
); | |
} | |
var result = new ObjectId(); | |
var resultPtr = (byte*) &result; | |
switch ((char) (format[0] | 0x20)) | |
{ | |
case 'n': | |
{ | |
fixed (char* objectIdStringPtr = &input.GetPinnableReference()) | |
{ | |
ParseWithExceptionsN((uint) input.Length, objectIdStringPtr, resultPtr); | |
} | |
return result; | |
} | |
default: | |
{ | |
throw new FormatException( | |
"Format string can be only \"N\", \"n\"." | |
); | |
} | |
} | |
} | |
/// <summary> | |
/// Converts the string representation of a ObjectId to the equivalent <see cref="ObjectId" /> structure. | |
/// </summary> | |
/// <param name="input">A string containing the ObjectId to convert.</param> | |
/// <param name="output"> | |
/// A <see cref="ObjectId" /> instance to contain the parsed value. If the method returns <see langword="true" />, | |
/// <paramref name="output" /> contains a valid <see cref="ObjectId" />. If the method returns <see langword="false" /> | |
/// , | |
/// <paramref name="output" /> equals <see cref="Empty" />. | |
/// </param> | |
/// <returns><see langword="true" /> if the parse operation was successful; otherwise, <see langword="false" />.</returns> | |
// ReSharper disable once UnusedMember.Global | |
public static bool TryParse( | |
string? input, | |
out ObjectId output | |
) | |
{ | |
if (input == null) | |
{ | |
output = default; | |
return false; | |
} | |
var result = new ObjectId(); | |
var resultPtr = (byte*) &result; | |
#if NETCOREAPP3_1 || NET5_0 || NET6_0_OR_GREATER | |
fixed (char* objectIdStringPtr = &input.GetPinnableReference()) | |
#endif | |
#if NETSTANDARD2_0 | |
fixed (char* objectIdStringPtr = input) | |
#endif | |
{ | |
if (ParseWithoutExceptions(input.AsSpan(), objectIdStringPtr, resultPtr)) | |
{ | |
output = result; | |
return true; | |
} | |
} | |
output = default; | |
return false; | |
} | |
/// <summary> | |
/// Converts the specified read-only span of characters containing the representation of a ObjectId to the equivalent | |
/// <see cref="ObjectId" /> | |
/// structure. | |
/// </summary> | |
/// <param name="input">A span containing the characters representing the ObjectId to convert.</param> | |
/// <param name="output"> | |
/// A <see cref="ObjectId" /> instance to contain the parsed value. If the method returns <see langword="true" />, | |
/// <paramref name="output" /> contains a valid <see cref="ObjectId" />. If the method returns <see langword="false" /> | |
/// , | |
/// <paramref name="output" /> equals <see cref="Empty" />. | |
/// </param> | |
/// <returns><see langword="true" /> if the parse operation was successful; otherwise, <see langword="false" />.</returns> | |
// ReSharper disable once UnusedMember.Global | |
public static bool TryParse( | |
ReadOnlySpan<char> input, | |
out ObjectId output | |
) | |
{ | |
if (input.IsEmpty) | |
{ | |
output = default; | |
return false; | |
} | |
var result = new ObjectId(); | |
var resultPtr = (byte*) &result; | |
fixed (char* objectIdStringPtr = &input.GetPinnableReference()) | |
{ | |
if (ParseWithoutExceptions(input, objectIdStringPtr, resultPtr)) | |
{ | |
output = result; | |
return true; | |
} | |
} | |
output = default; | |
return false; | |
} | |
/// <summary> | |
/// Converts the specified read-only span bytes of UTF-8 characters containing the representation of a ObjectId to the | |
/// equivalent | |
/// <see cref="ObjectId" /> structure. | |
/// </summary> | |
/// <param name="objectIdUtf8String">A span containing the bytes of UTF-8 characters representing the ObjectId to convert.</param> | |
/// <param name="output"> | |
/// A <see cref="ObjectId" /> instance to contain the parsed value. If the method returns <see langword="true" />, | |
/// <paramref name="output" /> contains a valid <see cref="ObjectId" />. If the method returns <see langword="false" /> | |
/// , | |
/// <paramref name="output" /> equals <see cref="Empty" />. | |
/// </param> | |
/// <returns><see langword="true" /> if the parse operation was successful; otherwise, <see langword="false" />.</returns> | |
// ReSharper disable once UnusedMember.Global | |
public static bool TryParse(ReadOnlySpan<byte> objectIdUtf8String, out ObjectId output) | |
{ | |
if (objectIdUtf8String.IsEmpty) | |
{ | |
output = default; | |
return false; | |
} | |
var result = new ObjectId(); | |
var resultPtr = (byte*) &result; | |
fixed (byte* objectIdUtf8StringPtr = &objectIdUtf8String.GetPinnableReference()) | |
{ | |
if (ParseWithoutExceptionsUtf8(objectIdUtf8String, objectIdUtf8StringPtr, resultPtr)) | |
{ | |
output = result; | |
return true; | |
} | |
} | |
output = default; | |
return false; | |
} | |
/// <summary> | |
/// Converts the string representation of a ObjectId to the equivalent <see cref="ObjectId" /> structure, provided that | |
/// the string is in the | |
/// specified format. | |
/// </summary> | |
/// <param name="input">The ObjectId to convert.</param> | |
/// <param name="format"> | |
/// One of the following specifiers that indicates the exact format to use when interpreting <paramref name="input" />: | |
/// "N". | |
/// </param> | |
/// <param name="output"> | |
/// A <see cref="ObjectId" /> instance to contain the parsed value. If the method returns <see langword="true" />, | |
/// <paramref name="output" /> contains a valid <see cref="ObjectId" />. If the method returns <see langword="false" /> | |
/// , | |
/// <paramref name="output" /> equals <see cref="Empty" />. | |
/// </param> | |
/// <returns><see langword="true" /> if the parse operation was successful; otherwise, <see langword="false" />.</returns> | |
// ReSharper disable once UnusedMember.Global | |
public static bool TryParseExact(string? input, string? format, out ObjectId output) | |
{ | |
if (input == null || format?.Length != 1) | |
{ | |
output = default; | |
return false; | |
} | |
var result = new ObjectId(); | |
var resultPtr = (byte*) &result; | |
var parsed = false; | |
switch ((char) (format[0] | 0x20)) | |
{ | |
case 'n': | |
{ | |
#if NETCOREAPP3_1 || NET5_0 || NET6_0_OR_GREATER | |
fixed (char* objectIdStringPtr = &input.GetPinnableReference()) | |
#endif | |
#if NETSTANDARD2_0 | |
fixed (char* objectIdStringPtr = input) | |
#endif | |
{ | |
parsed = ParseWithoutExceptionsN((uint) input.Length, objectIdStringPtr, resultPtr); | |
} | |
break; | |
} | |
} | |
if (parsed) | |
{ | |
output = result; | |
return true; | |
} | |
output = default; | |
return false; | |
} | |
/// <summary> | |
/// Converts span of characters representing the ObjectId to the equivalent <see cref="ObjectId" /> structure, provided | |
/// that the string is in the | |
/// specified format. | |
/// </summary> | |
/// <param name="input">A read-only span containing the characters representing the ObjectId to convert.</param> | |
/// <param name="format"> | |
/// A read-only span containing a character representing one of the following specifiers that indicates the exact | |
/// format | |
/// to use when interpreting <paramref name="input" />: "N". | |
/// </param> | |
/// <param name="output"> | |
/// A <see cref="ObjectId" /> instance to contain the parsed value. If the method returns <see langword="true" />, | |
/// <paramref name="output" /> contains a valid <see cref="ObjectId" />. If the method returns <see langword="false" /> | |
/// , | |
/// <paramref name="output" /> equals <see cref="Empty" />. | |
/// </param> | |
/// <returns><see langword="true" /> if the parse operation was successful; otherwise, <see langword="false" />.</returns> | |
// ReSharper disable once UnusedMember.Global | |
public static bool TryParseExact(ReadOnlySpan<char> input, ReadOnlySpan<char> format, out ObjectId output) | |
{ | |
if (format.Length != 1) | |
{ | |
output = default; | |
return false; | |
} | |
var result = new ObjectId(); | |
var resultPtr = (byte*) &result; | |
var parsed = false; | |
switch ((char) (format[0] | 0x20)) | |
{ | |
case 'n': | |
{ | |
fixed (char* objectIdStringPtr = &input.GetPinnableReference()) | |
{ | |
parsed = ParseWithoutExceptionsN((uint) input.Length, objectIdStringPtr, resultPtr); | |
} | |
break; | |
} | |
} | |
if (parsed) | |
{ | |
output = result; | |
return true; | |
} | |
output = default; | |
return false; | |
} | |
private static bool ParseWithoutExceptions(ReadOnlySpan<char> objectIdString, char* objectIdStringPtr, byte* resultPtr) | |
{ | |
var length = (uint) objectIdString.Length; | |
return length != 0u && ParseWithoutExceptionsN(length, objectIdStringPtr, resultPtr); | |
} | |
private static bool ParseWithoutExceptionsN(uint objectIdStringLength, char* objectIdStringPtr, byte* resultPtr) | |
{ | |
return objectIdStringLength == 24u && TryParsePtrN(objectIdStringPtr, resultPtr); | |
} | |
#if NETCOREAPP3_1 || NET5_0 || NET6_0_OR_GREATER | |
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] | |
[SuppressMessage("ReSharper", "InvertIf")] | |
#endif | |
#if NETSTANDARD2_0 | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
#endif | |
private static bool TryParsePtrN( | |
char* value, | |
byte* resultPtr | |
) | |
{ | |
// e.g. "54759eb3c090d83494e2d804" | |
byte hi; | |
byte lo; | |
// 0 byte | |
if (value[0] < MaximalChar | |
&& (hi = TableFromHexToBytes[value[0]]) != 0xFF | |
&& value[1] < MaximalChar | |
&& (lo = TableFromHexToBytes[value[1]]) != 0xFF) | |
{ | |
resultPtr[0] = (byte) ((byte) (hi << 4) | lo); | |
// 1 byte | |
if (value[2] < MaximalChar | |
&& (hi = TableFromHexToBytes[value[2]]) != 0xFF | |
&& value[3] < MaximalChar | |
&& (lo = TableFromHexToBytes[value[3]]) != 0xFF) | |
{ | |
resultPtr[1] = (byte) ((byte) (hi << 4) | lo); | |
// 2 byte | |
if (value[4] < MaximalChar | |
&& (hi = TableFromHexToBytes[value[4]]) != 0xFF | |
&& value[5] < MaximalChar | |
&& (lo = TableFromHexToBytes[value[5]]) != 0xFF) | |
{ | |
resultPtr[2] = (byte) ((byte) (hi << 4) | lo); | |
// 3 byte | |
if (value[6] < MaximalChar | |
&& (hi = TableFromHexToBytes[value[6]]) != 0xFF | |
&& value[7] < MaximalChar | |
&& (lo = TableFromHexToBytes[value[7]]) != 0xFF) | |
{ | |
resultPtr[3] = (byte) ((byte) (hi << 4) | lo); | |
// 4 byte | |
if (value[8] < MaximalChar | |
&& (hi = TableFromHexToBytes[value[8]]) != 0xFF | |
&& value[9] < MaximalChar | |
&& (lo = TableFromHexToBytes[value[9]]) != 0xFF) | |
{ | |
resultPtr[4] = (byte) ((byte) (hi << 4) | lo); | |
// 5 byte | |
if (value[10] < MaximalChar | |
&& (hi = TableFromHexToBytes[value[10]]) != 0xFF | |
&& value[11] < MaximalChar | |
&& (lo = TableFromHexToBytes[value[11]]) != 0xFF) | |
{ | |
resultPtr[5] = (byte) ((byte) (hi << 4) | lo); | |
// 6 byte | |
if (value[12] < MaximalChar | |
&& (hi = TableFromHexToBytes[value[12]]) != 0xFF | |
&& value[13] < MaximalChar | |
&& (lo = TableFromHexToBytes[value[13]]) != 0xFF) | |
{ | |
resultPtr[6] = (byte) ((byte) (hi << 4) | lo); | |
// 7 byte | |
if (value[14] < MaximalChar | |
&& (hi = TableFromHexToBytes[value[14]]) != 0xFF | |
&& value[15] < MaximalChar | |
&& (lo = TableFromHexToBytes[value[15]]) != 0xFF) | |
{ | |
resultPtr[7] = (byte) ((byte) (hi << 4) | lo); | |
// 8 byte | |
if (value[16] < MaximalChar | |
&& (hi = TableFromHexToBytes[value[16]]) != 0xFF | |
&& value[17] < MaximalChar | |
&& (lo = TableFromHexToBytes[value[17]]) != 0xFF) | |
{ | |
resultPtr[8] = (byte) ((byte) (hi << 4) | lo); | |
// 9 byte | |
if (value[18] < MaximalChar | |
&& (hi = TableFromHexToBytes[value[18]]) != 0xFF | |
&& value[19] < MaximalChar | |
&& (lo = TableFromHexToBytes[value[19]]) != 0xFF) | |
{ | |
resultPtr[9] = (byte) ((byte) (hi << 4) | lo); | |
// 10 byte | |
if (value[20] < MaximalChar | |
&& (hi = TableFromHexToBytes[value[20]]) != 0xFF | |
&& value[21] < MaximalChar | |
&& (lo = TableFromHexToBytes[value[21]]) != 0xFF) | |
{ | |
resultPtr[10] = (byte) ((byte) (hi << 4) | lo); | |
// 11 byte | |
if (value[22] < MaximalChar | |
&& (hi = TableFromHexToBytes[value[22]]) != 0xFF | |
&& value[23] < MaximalChar | |
&& (lo = TableFromHexToBytes[value[23]]) != 0xFF) | |
{ | |
resultPtr[11] = | |
(byte) ((byte) (hi << 4) | lo); | |
return true; | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
return false; | |
} | |
#if NETCOREAPP3_1 || NET5_0 || NET6_0_OR_GREATER | |
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] | |
#endif | |
#if NETSTANDARD2_0 | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
#endif | |
private static bool TryParsePtrNUtf8(byte* value, byte* resultPtr) | |
{ | |
// e.g. "54759eb3c090d83494e2d804" | |
byte hi; | |
byte lo; | |
// 0 byte | |
if (value[0] < MaximalChar | |
&& (hi = TableFromHexToBytes[value[0]]) != 0xFF | |
&& value[1] < MaximalChar | |
&& (lo = TableFromHexToBytes[value[1]]) != 0xFF) | |
{ | |
resultPtr[0] = (byte) ((byte) (hi << 4) | lo); | |
// 1 byte | |
if (value[2] < MaximalChar | |
&& (hi = TableFromHexToBytes[value[2]]) != 0xFF | |
&& value[3] < MaximalChar | |
&& (lo = TableFromHexToBytes[value[3]]) != 0xFF) | |
{ | |
resultPtr[1] = (byte) ((byte) (hi << 4) | lo); | |
// 2 byte | |
if (value[4] < MaximalChar | |
&& (hi = TableFromHexToBytes[value[4]]) != 0xFF | |
&& value[5] < MaximalChar | |
&& (lo = TableFromHexToBytes[value[5]]) != 0xFF) | |
{ | |
resultPtr[2] = (byte) ((byte) (hi << 4) | lo); | |
// 3 byte | |
if (value[6] < MaximalChar | |
&& (hi = TableFromHexToBytes[value[6]]) != 0xFF | |
&& value[7] < MaximalChar | |
&& (lo = TableFromHexToBytes[value[7]]) != 0xFF) | |
{ | |
resultPtr[3] = (byte) ((byte) (hi << 4) | lo); | |
// 4 byte | |
if (value[8] < MaximalChar | |
&& (hi = TableFromHexToBytes[value[8]]) != 0xFF | |
&& value[9] < MaximalChar | |
&& (lo = TableFromHexToBytes[value[9]]) != 0xFF) | |
{ | |
resultPtr[4] = (byte) ((byte) (hi << 4) | lo); | |
// 5 byte | |
if (value[10] < MaximalChar | |
&& (hi = TableFromHexToBytes[value[10]]) != 0xFF | |
&& value[11] < MaximalChar | |
&& (lo = TableFromHexToBytes[value[11]]) != 0xFF) | |
{ | |
resultPtr[5] = (byte) ((byte) (hi << 4) | lo); | |
// 6 byte | |
if (value[12] < MaximalChar | |
&& (hi = TableFromHexToBytes[value[12]]) != 0xFF | |
&& value[13] < MaximalChar | |
&& (lo = TableFromHexToBytes[value[13]]) != 0xFF) | |
{ | |
resultPtr[6] = (byte) ((byte) (hi << 4) | lo); | |
// 7 byte | |
if (value[14] < MaximalChar | |
&& (hi = TableFromHexToBytes[value[14]]) != 0xFF | |
&& value[15] < MaximalChar | |
&& (lo = TableFromHexToBytes[value[15]]) != 0xFF) | |
{ | |
resultPtr[7] = (byte) ((byte) (hi << 4) | lo); | |
// 8 byte | |
if (value[16] < MaximalChar | |
&& (hi = TableFromHexToBytes[value[16]]) != 0xFF | |
&& value[17] < MaximalChar | |
&& (lo = TableFromHexToBytes[value[17]]) != 0xFF) | |
{ | |
resultPtr[8] = (byte) ((byte) (hi << 4) | lo); | |
// 9 byte | |
if (value[18] < MaximalChar | |
&& (hi = TableFromHexToBytes[value[18]]) != 0xFF | |
&& value[19] < MaximalChar | |
&& (lo = TableFromHexToBytes[value[19]]) != 0xFF) | |
{ | |
resultPtr[9] = (byte) ((byte) (hi << 4) | lo); | |
// 10 byte | |
if (value[20] < MaximalChar | |
&& (hi = TableFromHexToBytes[value[20]]) != 0xFF | |
&& value[21] < MaximalChar | |
&& (lo = TableFromHexToBytes[value[21]]) != 0xFF) | |
{ | |
resultPtr[10] = (byte) ((byte) (hi << 4) | lo); | |
// 11 byte | |
if (value[22] < MaximalChar | |
&& (hi = TableFromHexToBytes[value[22]]) != 0xFF | |
&& value[23] < MaximalChar | |
&& (lo = TableFromHexToBytes[value[23]]) != 0xFF) | |
{ | |
resultPtr[11] = (byte) ((byte) (hi << 4) | lo); | |
return true; | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
return false; | |
} | |
private static bool ParseWithoutExceptionsUtf8(ReadOnlySpan<byte> objectIdUtf8String, byte* objectIdUtf8StringPtr, byte* resultPtr) | |
{ | |
var length = (uint) objectIdUtf8String.Length; | |
return ParseWithoutExceptionsNUtf8(length, objectIdUtf8StringPtr, resultPtr); | |
} | |
private static bool ParseWithoutExceptionsNUtf8(uint objectIdStringLength, byte* objectIdUtf8StringPtr, byte* resultPtr) | |
{ | |
return objectIdStringLength == 24u && TryParsePtrNUtf8(objectIdUtf8StringPtr, resultPtr); | |
} | |
private static void ParseWithExceptions( | |
ReadOnlySpan<char> objectIdString, | |
char* objectIdStringPtr, | |
byte* resultPtr | |
) | |
{ | |
var length = (uint) objectIdString.Length; | |
if (length == 0u) | |
{ | |
throw new FormatException("Unrecognized ObjectId format."); | |
} | |
ParseWithExceptionsN(length, objectIdStringPtr, resultPtr); | |
} | |
[SuppressMessage("ReSharper", "ParameterOnlyUsedForPreconditionCheck.Local")] | |
private static void ParseWithExceptionsN(uint objectIdStringLength, char* objectIdStringPtr, byte* resultPtr) | |
{ | |
if (objectIdStringLength != 24u) | |
{ | |
// ReSharper disable once StringLiteralTypo | |
throw new FormatException("ObjectId should contain only 24 digits xxxxxxxxxxxxxxxxxxxxxxxx."); | |
} | |
if (!TryParsePtrN(objectIdStringPtr, resultPtr)) | |
{ | |
throw new FormatException("ObjectId string should only contain hexadecimal characters."); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment