Skip to content

Commit

Permalink
Ferature/UUID version 8 (#22)
Browse files Browse the repository at this point in the history
* Adding V8
* Fixing batch generation issues
* Adding UUID Max
* Unified slice for bytes to uuids
* Adding or extending tests
  • Loading branch information
DaanV2 authored Feb 3, 2024
1 parent 5103cf3 commit 4deda09
Show file tree
Hide file tree
Showing 18 changed files with 232 additions and 44 deletions.
6 changes: 6 additions & 0 deletions Library/Enumerators/Version.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,10 @@ public enum Version {
V4 = 0b0100_0000,
/// <summary>The <see cref="UUID"/> version that hashes (SHA1) given data into <see cref="UUID"/></summary>
V5 = 0b0101_0000,
/// <summary>The <see cref="UUID"/> version that is reordered Gregorian time-based UUID specified in this document.</summary></summary>

Check warning on line 15 in Library/Enumerators/Version.cs

View workflow job for this annotation

GitHub Actions / 🛠️ Build & 📋 Test on windows-2019

XML comment has badly formed XML -- 'End tag was not expected at this location.'

Check warning on line 15 in Library/Enumerators/Version.cs

View workflow job for this annotation

GitHub Actions / 🛠️ Build & 📋 Test on windows-2019

XML comment has badly formed XML -- 'End tag was not expected at this location.'

Check warning on line 15 in Library/Enumerators/Version.cs

View workflow job for this annotation

GitHub Actions / 🛠️ Build & 📋 Test on windows-latest

XML comment has badly formed XML -- 'End tag was not expected at this location.'

Check warning on line 15 in Library/Enumerators/Version.cs

View workflow job for this annotation

GitHub Actions / 🛠️ Build & 📋 Test on windows-latest

XML comment has badly formed XML -- 'End tag was not expected at this location.'

Check warning on line 15 in Library/Enumerators/Version.cs

View workflow job for this annotation

GitHub Actions / 🛠️ Build & 📋 Test on ubuntu-latest

XML comment has badly formed XML -- 'End tag was not expected at this location.'

Check warning on line 15 in Library/Enumerators/Version.cs

View workflow job for this annotation

GitHub Actions / 🛠️ Build & 📋 Test on ubuntu-latest

XML comment has badly formed XML -- 'End tag was not expected at this location.'

Check warning on line 15 in Library/Enumerators/Version.cs

View workflow job for this annotation

GitHub Actions / 🛠️ Build & 📋 Test on macos-latest

XML comment has badly formed XML -- 'End tag was not expected at this location.'

Check warning on line 15 in Library/Enumerators/Version.cs

View workflow job for this annotation

GitHub Actions / 🛠️ Build & 📋 Test on macos-latest

XML comment has badly formed XML -- 'End tag was not expected at this location.'

Check warning on line 15 in Library/Enumerators/Version.cs

View workflow job for this annotation

GitHub Actions / 🛠️ Build

XML comment has badly formed XML -- 'End tag was not expected at this location.'

Check warning on line 15 in Library/Enumerators/Version.cs

View workflow job for this annotation

GitHub Actions / 🛠️ Build

XML comment has badly formed XML -- 'End tag was not expected at this location.'
V6 = 0b0110_0000,
/// <summary>The <see cref="UUID"/> version that exposes Unix Epoch time-based UUID specified in this document.</summary>
V7 = 0b0111_0000,
/// <summary>The <see cref="UUID"/> version that allows for custom data of 122 bits of data</summary>
V8 = 0b1000_0000,
}
41 changes: 41 additions & 0 deletions Library/Static Classes/Convert/Convert - Batch.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;

namespace DaanV2.UUID;

public static partial class Convert {
/// <summary>Slices the given bytes into UUIDs</summary>
/// <param name="bytes">The bytes to slice</param>
/// <param name="version">The version to stamp in the UUID</param>
/// <param name="variant">The variant to stamp in the UUID</param>
/// <returns>A collection of <see cref="UUID"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static UUID[] Slice(ReadOnlySpan<Byte> bytes, Version version, Variant variant) {
Vector128<Byte> mask = Format.VersionVariantMaskNot(version, variant);
Vector128<Byte> overlay = Format.VersionVariantOverlayer(version, variant);

return Slice(bytes, mask, overlay);
}

/// <summary>Slices the given bytes into UUIDs</summary>
/// <param name="bytes">The bytes to slice</param>
/// <param name="mask">The mask to apply</param>
/// <param name="overlay">The overlay to add to the <see cref="UUID"/> usually version and variant</param>
/// <returns>A collection of <see cref="UUID"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static UUID[] Slice(ReadOnlySpan<Byte> bytes, Vector128<Byte> mask, Vector128<Byte> overlay) {
Int32 count = bytes.Length;
Int32 amount = count / Format.UUID_BYTE_LENGTH;
Int32 max = count - Format.UUID_BYTE_LENGTH;
var uuids = new UUID[amount];

Int32 J = 0;
for (Int32 I = 0; I <= max; I += Format.UUID_BYTE_LENGTH) {
var data = Vector128.Create(bytes.Slice(I, Format.UUID_BYTE_LENGTH));
Vector128<Byte> uuid = Format.StampVersion(mask, overlay, data);
uuids[J++] = new UUID(uuid);
}

return uuids;
}
}
28 changes: 16 additions & 12 deletions Library/Static Classes/Format/Format - Create.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics;

namespace DaanV2.UUID;

Expand Down Expand Up @@ -28,15 +27,20 @@ public static Vector128<Byte> Create(Version version, Variant variant, Vector128
return StampVersion(mask, overlay, data);
}

/// <summary>Uses the given mask and overlay to insert the version and variant onto the data</summary>
/// <param name="versionMask">The mask of the versions and variants</param>
/// <param name="versionOverlay">The overlay of the versions and variants</param>
/// <param name="data">The data to stamp the version and variant onto</param>
/// <returns>Returns a <see cref="Vector128{T}"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static Vector128<Byte> StampVersion(Vector128<Byte> versionMask, Vector128<Byte> versionOverlay, Vector128<Byte> data) {
var result = Vector128.BitwiseAnd(data, versionMask);
result = Vector128.BitwiseOr(result, versionOverlay);
return result;
/// <summary>Creates a layout of the data along with version and variant, the provided data will be placed around the version and variant</summary>
/// <param name="version">The version to set</param>
/// <param name="variant">The variant to set</param>
/// <param name="dataA">48 bits of data, top 16 will be removed</param>
/// <param name="dataB">12 bits of data, top 4 will be removed</param>
/// <param name="dataC">62 bits of data, top 2 will be removed</param>
/// <returns></returns>
public static Vector128<Byte> Create(Version version, Variant variant, UInt64 dataA, UInt16 dataB, UInt64 dataC) {
// 48 bits of data in the top, 16 bits will be removed
UInt64 upper = dataA << 16;
// 12 bits in the lower, 4 bits in the middle for the version.
upper |= dataB;

// 62 bits of data in the lower, 2 bits in the top for the variant.
return Create(version, variant, upper, dataC);
}
}
25 changes: 25 additions & 0 deletions Library/Static Classes/Format/Format - Stamp.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;

namespace DaanV2.UUID;

public static partial class Format {
/// <summary>Uses the given mask and overlay to insert the version and variant onto the data</summary>
/// <param name="versionMask">The mask of the versions and variants</param>
/// <param name="versionOverlay">The overlay of the versions and variants</param>
/// <param name="data">The data to stamp the version and variant onto</param>
/// <returns>Returns a <see cref="Vector128{T}"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static Vector128<Byte> StampVersion(Vector128<Byte> versionMask, Vector128<Byte> versionOverlay, ReadOnlySpan<Byte> data) {
var temp = Vector128.Create(data);
return StampVersion(versionMask, versionOverlay, temp);
}

/// <inheritdoc cref="StampVersion(Vector128{Byte},Vector128{Byte},ReadOnlySpan{Byte})"/>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static Vector128<Byte> StampVersion(Vector128<Byte> versionMask, Vector128<Byte> versionOverlay, Vector128<Byte> data) {
var result = Vector128.BitwiseAnd(data, versionMask);
result = Vector128.BitwiseOr(result, versionOverlay);
return result;
}
}
16 changes: 2 additions & 14 deletions Library/Static Classes/V3/V3 - Batch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,6 @@ public static UUID[] Batch(Int32 amount, String source, Encoding encoding) {
return Batch(amount, bytes);
}

/// <inheritdoc cref="Batch(Int32, ReadOnlySpan{Byte})"/>
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
public static UUID[] Batch(Byte[] source) {
return Batch(source.AsSpan());
}

/// <inheritdoc cref="Batch(Int32, ReadOnlySpan{Byte})"/>
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
public static UUID[] Batch(Int32 amount, Byte[] source) {
return Batch(amount, source.AsSpan());
}

/// <inheritdoc cref="Batch(Int32, ReadOnlySpan{Byte})"/>
[MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.AggressiveInlining)]
public static UUID[] Batch(ReadOnlySpan<Byte> source) {
Expand All @@ -57,9 +45,9 @@ public static UUID[] Batch(ReadOnlySpan<Byte> source) {
[MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.AggressiveInlining)]
public static UUID[] Batch(Int32 amount, ReadOnlySpan<Byte> source) {
Int32 step = source.Length / amount;
Int32 max = source.Length - step;
Int32 max = (source.Length - step + 1);

Span<Byte> hash = stackalloc Byte[MD5.HashSizeInBytes];
Span<Byte> hash = stackalloc Byte[Math.Max(MD5.HashSizeInBytes, UUID.BYTE_LENGTH)];

var uuids = new UUID[amount];
Int32 J = 0;
Expand Down
14 changes: 1 addition & 13 deletions Library/Static Classes/V4/V4 - Batch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,7 @@ public static UUID[] Batch(Int32 amount, Random rnd) {
/// <returns>A collection of <see cref="UUID"/></returns>
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
public static UUID[] Batch(ReadOnlySpan<Byte> bytes) {
Int32 count = bytes.Length;
Int32 amount = count / Format.UUID_BYTE_LENGTH;
Int32 max = count - Format.UUID_BYTE_LENGTH;
var uuids = new UUID[amount];

Int32 J = 0;
for (Int32 I = 0; I <= max; I += Format.UUID_BYTE_LENGTH) {
var data = Vector128.Create(bytes.Slice(I, Format.UUID_BYTE_LENGTH));
Vector128<Byte> uuid = Format.StampVersion(_VersionMask, _VersionOverlay, data);
uuids[J++] = new UUID(uuid);
}

return uuids;
return Convert.Slice(bytes, _VersionMask, _VersionOverlay);
}

/// <summary>Uses a user specified function to generate more <see cref="UUID"/></summary>
Expand Down
2 changes: 1 addition & 1 deletion Library/Static Classes/V5/V5 - Batch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public static UUID[] Batch(Int32 amount, Byte[] source) {
[MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.AggressiveInlining)]
public static UUID[] Batch(Int32 amount, ReadOnlySpan<Byte> source) {
Int32 step = source.Length / amount;
Int32 max = source.Length - step;
Int32 max = (source.Length - step) + 1;

Span<Byte> hash = stackalloc Byte[SHA1.HashSizeInBytes];

Expand Down
13 changes: 13 additions & 0 deletions Library/Static Classes/V8/V8 - Batch.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System.Runtime.CompilerServices;

namespace DaanV2.UUID;

public static partial class V8 {
/// <summary>Turns the entire bytes collection into UUIDs</summary>
/// <param name="bytes">The byte to chunk into <see cref="UUID"/></param>
/// <returns>A collection of <see cref="UUID"/></returns>
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
public static UUID[] Batch(ReadOnlySpan<Byte> bytes) {
return Convert.Slice(bytes, _VersionMask, _VersionOverlay);
}
}
13 changes: 13 additions & 0 deletions Library/Static Classes/V8/V8 - Const.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System.Runtime.Intrinsics;
using System.Security.Cryptography;

namespace DaanV2.UUID;
public static partial class V8 {
/// <inheritdoc cref="V1.Version"/>
public const Version Version = DaanV2.UUID.Version.V8;
/// <inheritdoc cref="V1.Variant"/>
public const Variant Variant = DaanV2.UUID.Variant.V1;

private static readonly Vector128<Byte> _VersionMask = Format.VersionVariantMaskNot(V8.Version, V8.Variant);
private static readonly Vector128<Byte> _VersionOverlay = Format.VersionVariantOverlayer(V8.Version, V8.Variant);
}
26 changes: 26 additions & 0 deletions Library/Static Classes/V8/V8 - Extract.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System.Runtime.Intrinsics;

namespace DaanV2.UUID;

public static partial class V8 {
/// <summary>Extracts the three given data set that UUID 8 holds</summary>
/// <param name="uuid">The UUID to extract the data from</param>
/// <returns>48 bits of data, 12 bits of data, and 62 bits data</returns>
public static (UInt64 bits48, UInt16 bits12, UInt64 bits62) Extract(UUID uuid) {
Vector128<UInt64> d = uuid._Data.AsUInt64();

UInt64 dataA = d.GetElement(0);
UInt64 dataC = d.GetElement(1);
// Lower 12 bits of the first 64 bits
UInt16 dataB = (UInt16)(dataA & 0b1111_1111_1111);

// Remove the lower 16 bits, so we have 48 bits of data
dataA >>= 16;

// Remove top 2 bits, so we have 62 bits of data
const UInt64 mask = 0b11u << 62;
dataC &= mask;

return (dataA, dataB, dataC);
}
}
29 changes: 29 additions & 0 deletions Library/Static Classes/V8/V8 - Generate.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;

namespace DaanV2.UUID;




public static partial class V8 {
/// <summary>Creates a <see cref="UUID"/> of the data around the version and variant, the provided data will be placed around the version and variant</summary>
/// <param name="dataA">48 bits of data, top 16 will be removed</param>
/// <param name="dataB">12 bits of data, top 4 will be removed</param>
/// <param name="dataC">62 bits of data, top 2 will be removed</param>
/// <returns>A new <see cref="UUID"/></returns>
public static UUID Generate(UInt64 dataA, UInt16 dataB, UInt64 dataC) {
Vector128<Byte> u = Format.Create(V8.Version, V8.Variant, dataA, dataB, dataC);
return new UUID(u);
}


/// <summary>Generates a <see cref="UUID"/> from the given data, will override the necessary bits to write the version and variant</summary>
/// <param name="source">The data to create a <see cref="UUID"/> from</param>
/// <returns>A new <see cref="UUID"/></returns>
[MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.AggressiveInlining)]
public static UUID Generate(ReadOnlySpan<Byte> source) {
Vector128<Byte> u = Format.Create(V8.Version, V8.Variant, source);
return new UUID(u);
}
}
11 changes: 11 additions & 0 deletions Library/Static Classes/V8/V8.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DaanV2.UUID;

/// <summary>The <see cref="UUID"/> version that is based on given data of 122 bits</summary>
public static partial class V8 {
}
5 changes: 4 additions & 1 deletion Library/Structures/UUID/UUID - Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ public readonly partial struct UUID {
/// <summary>The amount of bits in a UUID</summary>
public const Int32 BITS_AMOUNT = Format.UUID_BITS;

/// <summary>00000000-0000-0000-0000-000000000000</summary>
/// <summary>The minimum or null <see cref="UUID"/> 00000000-0000-0000-0000-000000000000</summary>
public static readonly UUID Zero = new(Vector128<Byte>.Zero);

/// <summary>The maximum or filled <see cref="UUID"/> FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF</summary>
public static readonly UUID Max = new(~Vector128<Byte>.Zero);
}
10 changes: 9 additions & 1 deletion Tests/NillTest/NillTest.cs → Tests/ConstantTest/NillTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,20 @@

namespace Tests;

public partial class ZeroTest {
public partial class ConstantTest {
[Fact(DisplayName = "UUID.Zero is really zero")]
public void ZeroIsReallyZero() {
UUID nil = UUID.Zero;
String str = nil.ToString();

Assert.True(str == "00000000-0000-0000-0000-000000000000", "Nill UUID is not an proper nill uuid");
}

[Fact(DisplayName = "UUID.Max is really max")]
public void MaxIsReallyMax() {
UUID max = UUID.Max;
String str = max.ToString();

Assert.True(str == "ffffffff-ffff-ffff-ffff-ffffffffffff", "Max UUID is not an proper max uuid");
}
}
2 changes: 2 additions & 0 deletions Tests/Generation/V3Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ public void TestBatchUnique(Int32 Amount) {
Assert.Equal(Amount, UUIDs.Length);

for (Int32 i = 0; i < UUIDs.Length; i++) {
Utility.ValidateUUID(UUIDs[i], V3.Version, V3.Variant);

for (Int32 j = 0; j < UUIDs.Length; j++) {
if (i == j) {
continue;
Expand Down
4 changes: 2 additions & 2 deletions Tests/Generation/V4Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,10 @@ public void TestUUIDStream() {
Byte[] data = new Byte[32];
Random.Shared.NextBytes(data);

UUID expected = DaanV2.UUID.V4.Generate(data);
UUID expected = V4.Generate(data);

using (var stream = new MemoryStream(data)) {
UUID actual = DaanV2.UUID.V4.Generate(stream);
UUID actual = V4.Generate(stream);

Assert.Equal(expected, actual);
}
Expand Down
2 changes: 2 additions & 0 deletions Tests/Generation/V5Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ public void TestBatchUnique(Int32 Amount) {
Assert.Equal(Amount, UUIDs.Length);

for (Int32 i = 0; i < UUIDs.Length; i++) {
Utility.ValidateUUID(UUIDs[i], V5.Version, V5.Variant);

for (Int32 j = 0; j < UUIDs.Length; j++) {
if (i == j) {
continue;
Expand Down
29 changes: 29 additions & 0 deletions Tests/Generation/V8Tests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using DaanV2.UUID;

namespace Tests.Generation;
public sealed partial class V8Tests {
[Theory(DisplayName = "When batch generating, will always return unique UUIDs")]
[InlineData(10)]
[InlineData(100)]
[InlineData(1000)]
public void TestBatchUnique(Int32 Amount) {
Int32 byteCount = Amount * UUID.BYTE_LENGTH;
Byte[] data = new Byte[byteCount];
Random.Shared.NextBytes(data);

UUID[] UUIDs = V8.Batch(data);
Assert.Equal(Amount, UUIDs.Length);

for (Int32 i = 0; i < UUIDs.Length; i++) {
Utility.ValidateUUID(UUIDs[i], V8.Version, V8.Variant);

for (Int32 j = 0; j < UUIDs.Length; j++) {
if (i == j) {
continue;
}

Assert.NotEqual(UUIDs[i], UUIDs[j]);
}
}
}
}

0 comments on commit 4deda09

Please sign in to comment.