Skip to content

Latest commit

History

63 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

TypeId (Spec v0.3.0)

NuGet Version

High-performance C# implementation of TypeId.

Here's an example of a TypeID of type user:

 user_2x4y6z8a0b1c2d3e4f5g6h7j8k
└──┘ └────────────────────────┘
type uuid suffix (base32)

Why another library?

This implementation is comparable or faster (sometimes 3x faster) in all common scenarios than other .NET implementations. It also allocates up to 5x less memory, reducing GC pressure.

See the Benchmarks wiki for more details.

Why should you care?

You may think that generating, parsing, or serializing a single TypeId is very fast regardless of the implementation. To some degree, that's true. But small inefficiencies accumulate quickly, and they are very hard to spot in a large system. Most likely, there are millions of IDs parsed and serialized across your whole application daily. There is no single place with "slow" performance to spot in the profiler, so it's very hard to notice these inefficiencies.

GC is another important factor. If every small library generates tons of short-lived objects for no reason, the GC will trigger much more frequently, impacting your whole application. The tricky part? There is nothing you can do about it because the memory is allocated inside 3rd party code.

There is no reason to use inefficient building blocks in your application. With this library, you get the same high-level, easy-to-use API. You don't have to deal with "weird" performance-oriented approaches. However, I plan to expose additional performance-oriented APIs in the near future for those who need them.

Installation

Install from NuGet: https://www.nuget.org/packages/FastIDs.TypeId

Usage

The library exposes two types optimized for slightly different use-cases. For better understanding refer to TypeId vs TypeIdDecoded section in wiki.

This readme covers the basic operation you need to know to use the library.

Import

usingFastIDs.TypeId;

Creating a TypeId

TypeId can be generated using static methods of TypeId and TypeIdDecoded classes. Both have the same API and will create an instance of TypeIdDecoded struct. Examples in this section only use TypeId for simplicity.

Generate new ID:

vartypeIdDecoded=TypeId.New("prefix");

It's also possible to create a TypeID without a prefix by passing the empty string:

vartypeIdDecodedWithoutPrefix=TypeId.New("");

Note: If the prefix is empty, the separator _ is omitted in the string representation.

Create TypeId from existing UUIDv7:

GuiduuidV7=newGuid("01890a5d-ac96-774b-bcce-b302099a8057");vartypeIdDecoded=TypeId.FromUuidV7("prefix",uuidV7);

Both TypeId.New(prefix) and TypeId.FromUuidV7(prefix, guid) validate provided prefix. You can skip this validation by using overloads with bool validateType parameter set to false. The best case to do so is when you're 100% sure your prefix is correct and you want to squeeze extra bits of performance.

vartypeIdDecoded=TypeId.New("prefix",false)// skips validation and creates a valid TypeId instance.varinvalidTypeIdDecoded=TypeId.New("123",false)// doesn't throw FormatException despite invalid type providedvarshouldThrowIdDecoded=TypeId.New("123")// throws FormatException

Conversion between TypeId and TypeIdDecoded

Convert TypeIdDecoded to TypeId:

TypeIdtypeId=typeIdDecoded.Encode();

Convert TypeId to TypeIdDecoded:

TypeIdDecodedtypeIdDecoded=typeId.Decode();

TypeId serialization to string

All the following examples assume that there is a typeId variable created this way:

TypeIdtypeId=TypeId.FromUuidV7("type",newGuid("01890a5d-ac96-774b-bcce-b302099a8057")).Encode();

Get string representation

stringtypeIdString=typeId.ToString();// returns "type_01h455vb4pex5vsknk084sn02q" (without quotes)

It's possible to get only type:

ReadOnlySpan<char>typeSpan=typeId.Type;stringtype=typeSpan.ToString();// both are "type" (without quotes)

It's also possible to get only the suffix:

ReadOnlySpan<char>suffixSpan=typeId.Suffix;stringsuffix=suffixSpan.ToString();// both are "01h455vb4pex5vsknk084sn02q" (without quotes)

Note: if TypeID doesn't have a type (i.e. type is an empty string), values returned from Suffix and ToString() are equal.

vartypeId=TypeId.FromUuidV7("",newGuid("01890a5d-ac96-774b-bcce-b302099a8057")).Encode();Console.WriteLine($"{typeId.Suffix.ToString()} == {typeId.ToString()}");// prints: "01h455vb4pex5vsknk084sn02q == 01h455vb4pex5vsknk084sn02q" (without quotes)

TypeIdDecoded serialization to string

All the following examples assume that there is a typeIdDecoded variable created this way:

TypeIdDecodedtypeIdDecoded=TypeId.FromUuidV7("type",newGuid("01890a5d-ac96-774b-bcce-b302099a8057"));

Get string representation

stringtypeIdString=typeIdDecoded.ToString();// returns "type_01h455vb4pex5vsknk084sn02q" (without quotes)

It's possible to get only type:

stringtype=typeIdDecoded.Type;// returns "type" (without quotes)

It's also possible to get only the suffix:

stringsuffix=typeIdDecoded.GetSuffix();// returns "01h455vb4pex5vsknk084sn02q" (without quotes)Span<char>suffixSpan=stackallocchar[26];intcharsWritten=typeIdDecoded.GetSuffix(suffixSpan);// `charsWritten` is 26, and `suffixSpan` contains "01h455vb4pex5vsknk084sn02q" (without quotes)

Note: if TypeID doesn't have a type (i.e. type is an empty string), values returned from GetSuffix() and ToString() are equal.

vartypeIdDecoded=TypeId.FromUuidV7("",newGuid("01890a5d-ac96-774b-bcce-b302099a8057"));Console.WriteLine($"{typeIdDecoded.GetSuffix()} == {typeIdDecoded.ToString()}");// prints: "01h455vb4pex5vsknk084sn02q == 01h455vb4pex5vsknk084sn02q" (without quotes)

Parsing

String representation can only be parsed into TypeId.

Parse existing string representation to the TypeId instance:

TypeIdtypeId=TypeId.Parse("type_01h455vb4pex5vsknk084sn02q");

The Parse(string input) method will throw a FormatException in case of incorrect format of the passed value. Use the TryParse(string input, out TypeId result) method to avoid throwing the exception:

if(TypeId.TryParse("type_01h455vb4pex5vsknk084sn02q",outTypeIdtypeId))// TypeId is successfully parsed here.
else
// Unable to parse TypeId from the provided string.

Match type

Both TypeId and TypeIdDecoded have the same API for checking if the type equals the provided value.

boolisSameType=typeId.HasType("your_type");// also has overload for ReadOnlySpan<char>

Equality

Both TypeId and TypeIdDecoded structs implement the IEquatable<T> interface with all its benefits:

  • typeId.Equals(other) or typeId == other to check if IDs are same.
  • !typeId.Equals(other) or typeId != other to check if IDs are different.
  • Use TypeId as a key in Dictionary or HashSet.

UUIDv7 component operations

TypeIdDecoded provides the API for accessing the UUIDv7 component of the TypeID.

Get Guid:

Guiduuidv7=typeIdDecoded.Id;

Get the creation timestamp (part of the UUIDv7 component):

DateTimeOffsettimestamp=typeIdDecoded.GetTimestamp();

Json Serialization

NuGet packages are available for working with JSON.

You can use the extension method ConfigureForTypeId on the JsonSerializerSettings type for Json.Net or on the JsonSerializerOptions type for System.Text.Json to automatically serialize a TypeId or a TypeIdDecoded to a string.

If you are using SwashBuckle, you will need to configure your service as follows:

builder.Services.AddSwaggerGen(c =>{c.SwaggerDoc("v1",newOpenApiInfo{Title="api",Version="v1"});c.MapType(typeof(TypeId),()=>newOpenApiSchema{Type="string",Example=newOpenApiString("prefix_01h93ech7jf5ktdwg6ye383x34")});c.MapType(typeof(TypeIdDecoded),()=>newOpenApiSchema{Type="string",Example=newOpenApiString("prefix_01h93ech7jf5ktdwg6ye383x34")});});

About

Performance-oriented TypeId C# library

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages