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
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
MaxTotalLength
The greatest total length any profile can produce, which bounds a stack buffer.
public const int MaxTotalLength = 40
Field Value
RandomLength
The number of characters carrying randomness, at every granularity.
public const int RandomLength = 16
Field Value
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
public static int EntropyByteCount { get; }
Property Value
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
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
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
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
The identifier, canonical and valid by construction.
Exceptions
An argument is null.
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
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
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
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
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
An anchored pattern.
Remarks
Published as schema text and never compiled: the running validation is
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
The number of characters.
Exceptions
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
The total length.
Exceptions
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
The first rule the candidate breaks, or success.
Exceptions
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
Buffer receiving the identifier.
Returns
The number of characters written.
Exceptions
prefix is null.
entropy or destination is too short.