Skip to main content

Class EntityIdFormat

Namespace: AdCodicem.ValueObjects.Identifiers
Assembly: AdCodicem.ValueObjects.Identifiers.dll

The layout of an entity identifier: prefix _ bucket random check.

public static class EntityIdFormat

Inheritance

objectEntityIdFormat

Inherited Members

object.Equals(object?), object.Equals(object?, object?), object.GetHashCode(), object.GetType(), object.MemberwiseClone(), object.ReferenceEquals(object?, object?), object.ToString()

Remarks

Generated identifier types call into this rather than carrying their own copy of the layout, so a change to the format is a change in one place. Every member is public because generated code lives in the consumer's assembly and cannot reach anything internal here.

Validation is a span scan, not a regular expression. At fixed length over a fixed alphabet a scan is both faster and simpler, and it spares an entity identifier the compiled Regex that a Pattern-constrained value object has to pay for at start-up — source generators cannot feed [GeneratedRegex], so that cost is unavoidable there and avoidable here.

Fields

ChecksumLength

The number of trailing check characters.

public const int ChecksumLength = 1

Field Value

int

MaxTotalLength

The greatest total length any profile can produce, which bounds a stack buffer.

public const int MaxTotalLength = 40

Field Value

int

RandomLength

The number of characters carrying randomness, at every granularity.

public const int RandomLength = 16

Field Value

int

Remarks

16 symbols of five bits each: 80 bits. The birthday bound is roughly 1.1 × 10¹² identifiers within one time bucket, against the 10⁴–10⁵ a bucket is sized to hold, so a collision is not a thing that happens.

Sized against the bucket, not against the table. Randomness is redrawn on every bucket, so what has to stay out of reach is the number of identifiers minted within one bucket width — orders of magnitude below the row count of the table, and the reason this is 80 bits rather than the 128 an unbucketed identifier would need.

Properties

EntropyByteCount

Gets the number of entropy bytes consumes.

public static int EntropyByteCount { get; }

Property Value

int

Remarks

One byte per random character. Taking the low five bits of a uniform byte is itself uniform, which reducing a smaller buffer modulo 32 would not be.

Epoch

Gets the instant the time bucket counts from.

public static DateTimeOffset Epoch { get; }

Property Value

DateTimeOffset

Remarks

2020, not 1970. Half a century of elapsed time spent before the first identifier is issued is half the bucket space burned for nothing, and moving the epoch forward buys that back as horizon.

Methods

BodyLength(IdGranularity)

Gets the number of characters after the prefix separator.

public static int BodyLength(IdGranularity granularity)

Parameters

granularity IdGranularity

Bucket width.

Returns

int

The body length.

CarriesPrefix(ReadOnlySpan<char>, string)

Determines whether a text opens with a prefix and its separator, ignoring case.

public static bool CarriesPrefix(ReadOnlySpan<char> text, string prefix)

Parameters

text ReadOnlySpan<char>

Text to test.

prefix string

Declared prefix, without its trailing separator.

Returns

bool

true when the prefix and its separator are present.

Create(string, IdGranularity, TimeProvider, IdEntropySource)

Builds a new identifier.

public static string Create(string prefix, IdGranularity granularity, TimeProvider timeProvider, IdEntropySource entropy)

Parameters

prefix string

Declared prefix, without its trailing separator.

granularity IdGranularity

Bucket width.

timeProvider TimeProvider

Clock supplying the bucket.

entropy IdEntropySource

Source of the random part.

Returns

string

The identifier, canonical and valid by construction.

Exceptions

ArgumentNullException

An argument is null.

ArgumentException

prefix breaks the prefix rules.

Example(string, IdGranularity)

Builds a representative identifier, for publication in an OpenAPI schema.

public static string Example(string prefix, IdGranularity granularity)

Parameters

prefix string

Declared prefix, without its trailing separator.

granularity IdGranularity

Bucket width.

Returns

string

A valid identifier of the right shape.

Remarks

Derived from a fixed instant and a fixed byte pattern rather than minted, so that regenerating the document twice produces the same bytes. An example drawn from the real entropy source would be valid and would make a committed specification churn on every build.

Normalize(ReadOnlySpan<char>, string)

Rewrites a candidate into its canonical spelling.

public static string Normalize(ReadOnlySpan<char> text, string prefix)

Parameters

text ReadOnlySpan<char>

Candidate text.

prefix string

Declared prefix, without its trailing separator.

Returns

string

The canonical identifier, or the trimmed input when it does not carry the declared prefix. Normalization never rejects: a text this could not make sense of is handed to to refuse.

Remarks

Trims surrounding whitespace, folds the body to its canonical symbols — lower case, with Crockford's aliases mapped — and restores the declared casing of the prefix. Idempotent, as the contract of a normalizer requires.

Length is preserved: nothing is dropped. Crockford allows a hyphen anywhere in an encoded value for readability, and this format deliberately does not, because these identifiers are never transcribed by hand. Repairing a hyphenated candidate would buy a spelling nobody produces at the price of a second text that maps onto the same identifier.

Exceptions

ArgumentNullException

prefix is null.

SchemaPattern(string, IdGranularity)

Builds the regular expression describing a profile, for publication in an OpenAPI schema.

public static string SchemaPattern(string prefix, IdGranularity granularity)

Parameters

prefix string

Declared prefix, without its trailing separator.

granularity IdGranularity

Bucket width.

Returns

string

An anchored pattern.

Remarks

Published as schema text and never compiled: the running validation is , a span scan. The pattern exists so that a client generated from the document rejects the same texts.

TimestampLength(IdGranularity)

Gets the number of characters the time bucket occupies at a granularity.

public static int TimestampLength(IdGranularity granularity)

Parameters

granularity IdGranularity

Bucket width.

Returns

int

The number of characters.

Exceptions

ArgumentOutOfRangeException

granularity is not a declared value.

TotalLength(string, IdGranularity)

Gets the exact length of an identifier, which is also the width of its database column.

public static int TotalLength(string prefix, IdGranularity granularity)

Parameters

prefix string

Declared prefix, without its trailing separator.

granularity IdGranularity

Bucket width.

Returns

int

The total length.

Exceptions

ArgumentNullException

prefix is null.

Validate(ReadOnlySpan<char>, string, IdGranularity)

Validates an already normalized candidate.

public static ValidationResult Validate(ReadOnlySpan<char> value, string prefix, IdGranularity granularity)

Parameters

value ReadOnlySpan<char>

Normalized candidate.

prefix string

Declared prefix, without its trailing separator.

granularity IdGranularity

Bucket width.

Returns

ValidationResult

The first rule the candidate breaks, or success.

Exceptions

ArgumentNullException

prefix is null.

Write(string, IdGranularity, DateTimeOffset, ReadOnlySpan<byte>, Span<char>)

Writes an identifier into a destination buffer.

public static int Write(string prefix, IdGranularity granularity, DateTimeOffset timestamp, ReadOnlySpan<byte> entropy, Span<char> destination)

Parameters

prefix string

Declared prefix, without its trailing separator.

granularity IdGranularity

Bucket width.

timestamp DateTimeOffset

Instant the bucket encodes.

entropy ReadOnlySpan<byte>

At least bytes of randomness.

destination Span<char>

Buffer receiving the identifier.

Returns

int

The number of characters written.

Exceptions

ArgumentNullException

prefix is null.

ArgumentException

entropy or destination is too short.