An implementation of the segment.io ksuid, a K-sortable unique identifier, but as a struct and with 0 allocations.
The KSUID is sortable lexically (ToString()) and as an object.
There are two other dotnet implementations of KSUID that are popular, Ksuid.Net and DotKsuid. However, I have a use case where I need to create a lot of KSUIDs in succession and have those be lexically sortable based on the order in which they were created.
The GO implementation from segment.io has a next
method that takes a ksuid and returns the next one.
I modeled my implementation off of this so that one can call Ksuid.NextKsuid()
in succession and
each result will be sortable based on the order in which they were created.
I also exposed a Ksuid.RandomKsuid()
that gives a Ksuid with the timestamp and a random payload.
Besides being a struct and no-alloc, the data is stored as uint timestamp
, ulong a
, ulong b
instead of as a timestamp and/or byte array. In doing so, the bytes for the timestamp are no longer
switched for endianness as they are in the other implementations. Thus, I can't guarantee that
bytes made by other libraries would be compatible here.
Performance is pretty strong, coming in at about 10x the speed of Ksuid.Net and just a bit slower
DotKsuid. The only allocations made are when calling ToString()
.
I suspect that part of the reason for DotKsuid coming in slightly faster is that
this implementation has to copy the 3 parts of the payload instead of passing the
reference to the byte array.
I tried to keep the API close to the dotnet Guid API.
var ksuid = Ksuid.NextKsuid()
// Creates a new Ksuid that is sortable from the last one created
var ksuid = Ksuid.RandomKsuid()
// Creates an "untracked" ksuid with the current timestamp and a random payload.
Not guaranteed to be sortable from the last, except by timestamp.
var ksuid = Ksuid.FromTimestamp(DateTime | uint)
// Similar to RandomKsuid, but with a given timestamp
var ksuid = Ksuid.FromBytes()
var bytes = ksuid.GetBytes()
var timestampUtc = ksuid.TimestampUtc
Ksuid implements IComparable and IEquatable, and has the ==
and !=
operators.
ksuida.CompareTo(ksuidb)
ksuida.Equals(ksuidb)
ksuida == ksuidb
I did not make a serializer for this Ksuid.
Also be sure that when sorting the strings in dotnet,
that the sorting is case sensitive. I had to add do ....OrderBy(string => string, StringComparison.Ordinal)
to get the text to be sorted correctly, because the default comparer is case-insensitive.
You can see this in the unit tests.