Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 51 additions & 51 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -463,57 +463,57 @@ Job=DefaultJob
| GenerateNonMono | Guid *(5) | 47.1776 ns | 0.0932 ns | - | - |
| GenerateNonMono | GuidV7 *(3,5) | 77.4890 ns | 0.2267 ns | - | - |

| FromByteArray | ByteAetherUlid | 0.7978 ns | 0.0060 ns | - | - |
| FromByteArray | NetUlid | 1.4778 ns | 0.0053 ns | - | - |
| FromByteArray | Ulid | 1.1777 ns | 0.0042 ns | - | - |
| FromByteArray | NUlid | 1.1467 ns | 0.0077 ns | - | - |
| FromByteArray | Guid | 1.0420 ns | 0.0055 ns | - | - |

| FromGuid | ByteAetherUlid | 0.8428 ns | 0.0140 ns | - | - |
| FromGuid | NetUlid | 1.7816 ns | 0.0219 ns | - | - |
| FromGuid | Ulid | 2.0271 ns | 0.0274 ns | - | - |
| FromGuid | NUlid | 1.0718 ns | 0.0353 ns | - | - |

| FromString | ByteAetherUlid | 15.2981 ns | 0.0964 ns | - | - |
| FromString | NetUlid | 28.0353 ns | 0.3071 ns | - | - |
| FromString | Ulid | 17.4969 ns | 0.0463 ns | - | - |
| FromString | NUlid | 55.3277 ns | 0.1974 ns | 0.0086 | 72 B |
| FromString | Guid | 22.0493 ns | 0.1599 ns | - | - |

| ToByteArray | ByteAetherUlid | 4.6643 ns | 0.0933 ns | 0.0048 | 40 B |
| ToByteArray | AsByteSpan *(6) | 0.7740 ns | 0.0041 ns | - | - |
| ToByteArray | NetUlid | 9.5123 ns | 0.1098 ns | 0.0048 | 40 B |
| ToByteArray | Ulid | 4.6310 ns | 0.0918 ns | 0.0048 | 40 B |
| ToByteArray | NUlid | 8.6575 ns | 0.1394 ns | 0.0048 | 40 B |

| ToGuid | ByteAetherUlid | 0.8021 ns | 0.0073 ns | - | - |
| ToGuid | NetUlid | 10.2952 ns | 0.0195 ns | - | - |
| ToGuid | Ulid | 1.2186 ns | 0.0057 ns | - | - |
| ToGuid | NUlid | 0.7696 ns | 0.0039 ns | - | - |

| ToString | ByteAetherUlid | 21.584 ns | 0.2609 ns | 0.0095 | 80 B |
| ToString | NetUlid | 27.315 ns | 0.3311 ns | 0.0095 | 80 B |
| ToString | Ulid | 23.141 ns | 0.3722 ns | 0.0095 | 80 B |
| ToString | NUlid | 27.861 ns | 0.2217 ns | 0.0095 | 80 B |
| ToString | Guid | 9.113 ns | 0.1121 ns | 0.0115 | 96 B |

| CompareTo | ByteAetherUlid | 1.2997 ns | 0.0105 ns | - | - |
| CompareTo | NetUlid | 4.6796 ns | 0.0222 ns | - | - |
| CompareTo | Ulid | 6.7590 ns | 0.0167 ns | - | - |
| CompareTo | NUlid | 9.0615 ns | 0.0564 ns | - | - |
| CompareTo | Guid | 4.7613 ns | 0.0244 ns | - | - |

| Equals | ByteAetherUlid | 1.0668 ns | 0.0065 ns | - | - |
| Equals | NetUlid | 1.9436 ns | 0.0050 ns | - | - |
| Equals | Ulid | 1.0576 ns | 0.0055 ns | - | - |
| Equals | NUlid | 1.0485 ns | 0.0040 ns | - | - |
| Equals | Guid | 1.1008 ns | 0.0073 ns | - | - |

| GetHashCode | ByteAetherUlid | 0.9074 ns | 0.0057 ns | - | - |
| GetHashCode | NetUlid | 8.8425 ns | 0.0366 ns | - | - |
| GetHashCode | Ulid | 0.9066 ns | 0.0051 ns | - | - |
| GetHashCode | NUlid | 6.9351 ns | 0.0229 ns | - | - |
| GetHashCode | Guid | 0.9417 ns | 0.0072 ns | - | - |
| FromByteArray | ByteAetherUlid | 0.8044 ns | 0.0070 ns | - | - |
| FromByteArray | NetUlid | 1.4115 ns | 0.0111 ns | - | - |
| FromByteArray | Ulid | 1.1808 ns | 0.0080 ns | - | - |
| FromByteArray | NUlid | 1.1515 ns | 0.0063 ns | - | - |
| FromByteArray | Guid | 1.0440 ns | 0.0059 ns | - | - |

| FromGuid | ByteAetherUlid | 0.8255 ns | 0.0057 ns | - | - |
| FromGuid | NetUlid | 2.0028 ns | 0.0373 ns | - | - |
| FromGuid | Ulid | 1.9495 ns | 0.0116 ns | - | - |
| FromGuid | NUlid | 0.9240 ns | 0.0183 ns | - | - |

| FromString | ByteAetherUlid | 14.4357 ns | 0.0392 ns | - | - |
| FromString | NetUlid | 27.2159 ns | 0.0690 ns | - | - |
| FromString | Ulid | 16.9972 ns | 0.0311 ns | - | - |
| FromString | NUlid | 53.9897 ns | 0.1649 ns | 0.0086 | 72 B |
| FromString | Guid | 21.8639 ns | 0.1228 ns | - | - |

| ToByteArray | ByteAetherUlid | 4.7470 ns | 0.1274 ns | 0.0048 | 40 B |
| ToByteArray | AsByteSpan *(6) | 0.7736 ns | 0.0051 ns | - | - |
| ToByteArray | NetUlid | 9.4871 ns | 0.1054 ns | 0.0048 | 40 B |
| ToByteArray | Ulid | 4.7189 ns | 0.1066 ns | 0.0048 | 40 B |
| ToByteArray | NUlid | 8.7597 ns | 0.1460 ns | 0.0048 | 40 B |

| ToGuid | ByteAetherUlid | 0.8170 ns | 0.0070 ns | - | - |
| ToGuid | NetUlid | 10.3155 ns | 0.0230 ns | - | - |
| ToGuid | Ulid | 0.9731 ns | 0.0074 ns | - | - |
| ToGuid | NUlid | 0.7636 ns | 0.0061 ns | - | - |

| ToString | ByteAetherUlid | 21.5166 ns | 0.3377 ns | 0.0095 | 80 B |
| ToString | NetUlid | 27.3102 ns | 0.3004 ns | 0.0095 | 80 B |
| ToString | Ulid | 23.4614 ns | 0.2211 ns | 0.0095 | 80 B |
| ToString | NUlid | 29.4123 ns | 0.2632 ns | 0.0095 | 80 B |
| ToString | Guid | 10.2546 ns | 0.2493 ns | 0.0115 | 96 B |

| CompareTo | ByteAetherUlid | 1.4082 ns | 0.0071 ns | - | - |
| CompareTo | NetUlid | 4.4499 ns | 0.0303 ns | - | - |
| CompareTo | Ulid | 6.6206 ns | 0.0352 ns | - | - |
| CompareTo | NUlid | 9.2860 ns | 0.0495 ns | - | - |
| CompareTo | Guid | 4.8326 ns | 0.0210 ns | - | - |

| Equals | ByteAetherUlid | 1.0720 ns | 0.0135 ns | - | - |
| Equals | NetUlid | 1.9610 ns | 0.0155 ns | - | - |
| Equals | Ulid | 1.0432 ns | 0.0052 ns | - | - |
| Equals | NUlid | 1.0552 ns | 0.0103 ns | - | - |
| Equals | Guid | 1.1030 ns | 0.0107 ns | - | - |

| GetHashCode | ByteAetherUlid | 0.9185 ns | 0.0053 ns | - | - |
| GetHashCode | NetUlid | 8.8601 ns | 0.0250 ns | - | - |
| GetHashCode | Ulid | 0.9362 ns | 0.0059 ns | - | - |
| GetHashCode | NUlid | 6.9396 ns | 0.0387 ns | - | - |
| GetHashCode | Guid | 0.9082 ns | 0.0049 ns | - | - |
```

Existing competitive libraries exhibit various deviations from the official ULID specification or present drawbacks:
Expand Down
2 changes: 1 addition & 1 deletion src/ByteAether.Ulid.Tests/Ulid.Boundaries.Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public void MinValue_ShouldBeDefault()

// Assert
Assert.Equal(default, ulid);
Assert.Equal(emptyBytes, ulid.AsByteSpan());
Assert.Equal(emptyBytes, ulid.ToByteArray());
}

[Fact]
Expand Down
28 changes: 19 additions & 9 deletions src/ByteAether.Ulid/Ulid.Comparable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ namespace ByteAether.Ulid;
/// <param name="right">The second ULID to compare.</param>
/// <returns>True if the value of the left ULID is less than the value of the right ULID; otherwise, false.</returns>
#if NETCOREAPP3_0_OR_GREATER
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
#else
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
public static bool operator <(Ulid left, Ulid right)
=> left.CompareTo(right) < 0;
Expand All @@ -29,7 +31,9 @@ namespace ByteAether.Ulid;
/// <param name="right">The second ULID to compare.</param>
/// <returns>True if the value of the left ULID is less than or equal to the value of the right ULID; otherwise, false.</returns>
#if NETCOREAPP3_0_OR_GREATER
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
#else
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
public static bool operator <=(Ulid left, Ulid right)
=> left.CompareTo(right) <= 0;
Expand All @@ -41,7 +45,9 @@ namespace ByteAether.Ulid;
/// <param name="right">The second ULID to compare.</param>
/// <returns>True if the value of the left ULID is greater than the value of the right ULID; otherwise, false.</returns>
#if NETCOREAPP3_0_OR_GREATER
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
#else
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
public static bool operator >(Ulid left, Ulid right)
=> left.CompareTo(right) > 0;
Expand All @@ -53,14 +59,18 @@ namespace ByteAether.Ulid;
/// <param name="right">The second ULID to compare.</param>
/// <returns>True if the value of the left ULID is greater than or equal to the value of the right ULID; otherwise, false.</returns>
#if NETCOREAPP3_0_OR_GREATER
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
#else
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
public static bool operator >=(Ulid left, Ulid right)
=> left.CompareTo(right) >= 0;

/// <inheritdoc/>
#if NETCOREAPP3_0_OR_GREATER
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
#else
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
public int CompareTo(object? obj)
{
Expand All @@ -79,15 +89,15 @@ public int CompareTo(object? obj)

/// <inheritdoc/>
#if NETCOREAPP3_0_OR_GREATER
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
#else
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
public int CompareTo(Ulid other)
=> CompareToCore(this, other);

#if NETCOREAPP3_0_OR_GREATER
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
#else
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
#endif
private static int CompareToCore(in Ulid left, in Ulid right)
{
Expand Down
27 changes: 21 additions & 6 deletions src/ByteAether.Ulid/Ulid.Equatable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,19 @@ namespace ByteAether.Ulid;
#endif
{
/// <inheritdoc />
#if NETCOREAPP3_0_OR_GREATER
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
#else
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
public int GetHashCode(Ulid ulid) => ulid.GetHashCode();

/// <inheritdoc />
#if NETCOREAPP3_0_OR_GREATER
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
#else
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
public bool Equals(Ulid x, Ulid y) => EqualsCore(x, y);

/// <inheritdoc/>
Expand All @@ -38,14 +48,18 @@ public override int GetHashCode()

/// <inheritdoc/>
#if NETCOREAPP3_0_OR_GREATER
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
#else
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
public bool Equals(Ulid other)
=> EqualsCore(this, other);

/// <inheritdoc/>
#if NETCOREAPP3_0_OR_GREATER
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
#else
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
public override bool Equals([NotNullWhen(true)] object? obj)
=> obj is Ulid ulid && EqualsCore(this, ulid);
Expand All @@ -56,8 +70,11 @@ public override bool Equals([NotNullWhen(true)] object? obj)
/// <param name="left">The first ULID to compare.</param>
/// <param name="right">The second ULID to compare.</param>
/// <returns>True if the value of the left ULID is equal to the value of the right ULID; otherwise, false.</returns>

#if NETCOREAPP3_0_OR_GREATER
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
#else
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
public static bool operator ==(Ulid left, Ulid right)
=> EqualsCore(left, right);
Expand All @@ -75,9 +92,7 @@ public override bool Equals([NotNullWhen(true)] object? obj)
[SkipLocalsInit]
#endif
#if NETCOREAPP3_0_OR_GREATER
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
#else
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
#endif
private static bool EqualsCore(in Ulid left, in Ulid right)
{
Expand Down
50 changes: 16 additions & 34 deletions src/ByteAether.Ulid/Ulid.Guid.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace ByteAether.Ulid;

#if NET8_0_OR_GREATER
// We need to target netstandard2.1, so keep using ref for MemoryMarshal.Write
// CS9191: The 'ref' modifier for argument 2 corresponding to 'in' parameter is equivalent to 'in'. Consider using 'in' instead.
// CS9191: The 'ref' modifier for argument 2 corresponding to the 'in' parameter is equivalent to 'in'. Consider using 'in' instead.
#pragma warning disable CS9191
#endif

Expand All @@ -29,14 +29,9 @@ public readonly partial struct Ulid
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
public static Ulid New(Guid guid)
{
if (BitConverter.IsLittleEndian)
{
return Shuffle<Guid, Ulid>(ref guid);
}

return Unsafe.As<Guid, Ulid>(ref guid);
}
=> BitConverter.IsLittleEndian
? Shuffle<Guid, Ulid>(ref guid)
: Unsafe.As<Guid, Ulid>(ref guid);

/// <summary>
/// Converts the ULID to a GUID.
Expand All @@ -51,14 +46,9 @@ public static Ulid New(Guid guid)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
public Guid ToGuid()
{
if (BitConverter.IsLittleEndian)
{
return Shuffle<Ulid, Guid>(ref Unsafe.AsRef(in this));
}

return Unsafe.As<Ulid, Guid>(ref Unsafe.AsRef(in this));
}
=> BitConverter.IsLittleEndian
? Shuffle<Ulid, Guid>(ref Unsafe.AsRef(in this))
: Unsafe.As<Ulid, Guid>(ref Unsafe.AsRef(in this));

/// <summary>
/// Implicitly converts a ULID to a GUID.
Expand Down Expand Up @@ -87,12 +77,6 @@ public Guid ToGuid()
#if NETCOREAPP
private static readonly Vector128<byte> _shuffleMask
= Vector128.Create((byte)3, 2, 1, 0, 5, 4, 7, 6, 8, 9, 10, 11, 12, 13, 14, 15);

private static readonly bool _isAccelerated =
#if NET7_0_OR_GREATER
Vector128.IsHardwareAccelerated ||
#endif
Ssse3.IsSupported;
#endif

// HACK: We assume the layout of a Guid is the following:
Expand All @@ -109,19 +93,17 @@ private static readonly Vector128<byte> _shuffleMask
#endif
private static TOut Shuffle<TIn, TOut>(ref TIn bytes)
{
#if NETCOREAPP
if (_isAccelerated)
#if NET7_0_OR_GREATER
if (Vector128.IsHardwareAccelerated)
{
var vector = Unsafe.As<TIn, Vector128<byte>>(ref bytes);
vector = Vector128.Shuffle(vector, _shuffleMask);
return Unsafe.As<Vector128<byte>, TOut>(ref vector);
}
#elif NETCOREAPP3_0_OR_GREATER
if (Ssse3.IsSupported)
{
var vector = Unsafe.As<TIn, Vector128<byte>>(ref bytes);

#if NET7_0_OR_GREATER
if (Vector128.IsHardwareAccelerated)
{
vector = Vector128.Shuffle(vector, _shuffleMask);
return Unsafe.As<Vector128<byte>, TOut>(ref vector);
}
#endif

vector = Ssse3.Shuffle(vector, _shuffleMask);
return Unsafe.As<Vector128<byte>, TOut>(ref vector);
}
Expand Down
Loading