Skip to content

Instantly share code, notes, and snippets.

@nesterenko-kv
Created November 2, 2022 20:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nesterenko-kv/25a9b046ce9fa6f4f046adf3063bf43d to your computer and use it in GitHub Desktop.
Save nesterenko-kv/25a9b046ce9fa6f4f046adf3063bf43d to your computer and use it in GitHub Desktop.
ObjectId review
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
};
}
}
}
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