diff --git a/.editorconfig b/.editorconfig index c8104ec..413befe 100644 --- a/.editorconfig +++ b/.editorconfig @@ -80,6 +80,9 @@ csharp_style_expression_bodied_properties = true csharp_style_pattern_matching_over_as_with_null_check = true:suggestion csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +# Namespace Declaration Style +csharp_style_namespace_declarations = block_scoped + # Null-checking preferences csharp_style_conditional_delegate_call = true:suggestion diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index afa8679..dff77a5 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -4,6 +4,7 @@ on: push: branches: - main + - 'release/**' paths-ignore: - '**.md' - '**/renovate.json' @@ -16,7 +17,7 @@ on: jobs: unit-tests: name: Unit Tests - runs-on: ubuntu-latest + runs-on: windows-latest env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: @@ -25,37 +26,31 @@ jobs: with: fetch-depth: 0 show-progress: false - - name: NuGet Cache - uses: actions/cache@v3 - with: - path: ~/.nuget/packages - key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }} - restore-keys: ${{ runner.os}}-nuget - name: Install .NET SDK uses: actions/setup-dotnet@v4 with: global-json-file: global.json - - name: Build - run: dotnet build -c release - name: Test run: | - dotnet test -c release --no-restore --no-build \ - --logger:trx \ - --results-directory ${{ runner.temp }}/TestResults \ - -p:CollectCoverage=true \ - -p:CoverletOutputFormat=opencover \ - -p:CoverletOutput=${{ runner.temp }}/coverage.opencover.xml + dotnet test -c release ` + --logger:trx ` + --results-directory ${{ runner.temp }}\TestResults ` + -p:CollectCoverage=true ` + -p:CoverletOutputFormat=opencover ` + -p:CoverletOutput=${{ github.workspace }}\CodeCoverage\coverage.opencover.xml - name: Publish Test Results uses: dorny/test-reporter@v1 if: success() || failure() with: name: Test Results - path: ${{ runner.temp }}/TestResults/*.trx + path: ${{ runner.temp }}\TestResults\*.trx reporter: dotnet-trx + path-replace-backslashes: true - name: Codacy Coverage if: always() uses: codacy/codacy-coverage-reporter-action@v1 with: project-token: ${{ secrets.CODACY_TOKEN }} - coverage-reports: ${{ runner.temp }}/coverage.opencover.xml + coverage-reports: CodeCoverage/coverage.opencover.*.xml + \ No newline at end of file diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 670eed8..00556f3 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -9,7 +9,7 @@ on: jobs: release: name: Release - runs-on: ubuntu-latest + runs-on: windows-latest env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: @@ -18,23 +18,17 @@ jobs: with: fetch-depth: 0 show-progress: false - - name: NuGet Cache - uses: actions/cache@v3 - with: - path: ~/.nuget/packages - key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }} - restore-keys: ${{ runner.os}}-nuget - name: Install .NET SDK uses: actions/setup-dotnet@v4 with: global-json-file: global.json - name: Install GitVersion - uses: gittools/actions/gitversion/setup@v0.10.2 + uses: gittools/actions/gitversion/setup@v1.1.1 with: versionSpec: '5.x' - name: Determine Version id: gitversion - uses: gittools/actions/gitversion/execute@v0.10.2 + uses: gittools/actions/gitversion/execute@v1.1.1 with: useConfigFile: true - name: Restore diff --git a/Directory.Build.props b/Directory.Build.props index 5abc857..c2cb8fb 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -2,12 +2,7 @@ 0.0.1 Copyright (c) 2023 Visus Development Team - enable - enable - true - - - - true + 12 + enable diff --git a/Directory.Packages.props b/Directory.Packages.props index e9bcea9..a0fdced 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,19 +1,22 @@ - - true - true - - - - - - - - - - - - - - + + true + true + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/renovate.json b/renovate.json index 86ce457..606436f 100644 --- a/renovate.json +++ b/renovate.json @@ -1,10 +1,15 @@ { "extends": [ - "config:base", + "mergeConfidence:all-badges", + "config:recommended", ":disableDependencyDashboard", ":gitSignOff" ], "assigneesFromCodeOwners": true, + "timezone": "America/New_York", + "schedule": [ + "before 5am" + ], "packageRules": [ { "matchPackagePatterns": [ @@ -21,8 +26,7 @@ ], "automerge": true, "automergeType": "pr", - "platformAutomerge": true, - "ignoreTests": true + "platformAutomerge": true }, { "matchPackagePatterns": [ diff --git a/src/cuid.net/Abstractions/FingerprintVersion.cs b/src/cuid.net/Abstractions/FingerprintVersion.cs index 049079d..eeb76bf 100644 --- a/src/cuid.net/Abstractions/FingerprintVersion.cs +++ b/src/cuid.net/Abstractions/FingerprintVersion.cs @@ -1,8 +1,9 @@ -namespace Visus.Cuid.Abstractions; - -internal enum FingerprintVersion : byte +namespace Visus.Cuid.Abstractions { - None = 0, - One = 1, - Two = 2 + internal enum FingerprintVersion : byte + { + None = 0, + One = 1, + Two = 2 + } } diff --git a/src/cuid.net/Cuid.cs b/src/cuid.net/Cuid.cs index c1e6ec8..f4d29ad 100644 --- a/src/cuid.net/Cuid.cs +++ b/src/cuid.net/Cuid.cs @@ -1,427 +1,461 @@ -namespace Visus.Cuid; - -using System.Buffers.Binary; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Text; -using System.Text.Json.Serialization; -using System.Xml; -using System.Xml.Schema; -using System.Xml.Serialization; -using Abstractions; -using Extensions; -using Serialization.Json.Converters; - -/// -/// Represents a collision resistant unique identifier (CUID). -/// -[StructLayout(LayoutKind.Sequential)] -[JsonConverter(typeof(CuidConverter))] -[XmlRoot("cuid")] -[Obsolete(Obsoletions.CuidMessage, DiagnosticId = Obsoletions.CuidDiagId)] -public readonly struct Cuid : IComparable, IComparable, IEquatable, IXmlSerializable +namespace Visus.Cuid { - /// - /// A read-only instance of structure whose values are all zeros. - /// - public static readonly Cuid Empty; - - private const int BlockSize = 4; - - private const string Prefix = "c"; - - private const int ValueLength = 25; - - private readonly ulong _counter; - - private readonly string _fingerprint = default!; - - private readonly ulong _random; - - private readonly long _timestamp; + using System; + using System.Buffers.Binary; + using System.Collections; + using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; + using System.Linq; + using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; + using System.Text; + using System.Text.Json.Serialization; + using System.Threading; + using System.Xml; + using System.Xml.Schema; + using System.Xml.Serialization; + using Abstractions; + using CommunityToolkit.Diagnostics; + using Extensions; + using Serialization.Json.Converters; /// - /// Initializes a new instance of the structure by using the value represented by the specified - /// string. + /// Represents a collision resistant unique identifier (CUID). /// - /// - /// A string that contains a CUID. - /// - public Cuid(string c) + [StructLayout(LayoutKind.Sequential)] + [JsonConverter(typeof(CuidConverter))] + [XmlRoot("cuid")] +#if NET6_0_OR_GREATER + [Obsolete(Obsoletions.CuidMessage, DiagnosticId = Obsoletions.CuidDiagId)] +#else + [Obsolete(Obsoletions.CuidMessage)] +#endif + public readonly struct Cuid : IComparable, IComparable, IEquatable, IXmlSerializable { - ArgumentNullException.ThrowIfNull(c); - - CuidResult result = new(); + /// + /// A read-only instance of structure whose values are all zeros. + /// + public static readonly Cuid Empty; - _ = TryParseCuid(c, true, ref result); + private const int BlockSize = 4; - this = result.ToCuid(); - } - - /// - /// Initializes a new instance of the structure. - /// - /// A new CUID object. - public static Cuid NewCuid() - { - CuidResult result = new() - { - _counter = Counter.Instance.Value, - _fingerprint = Context.IdentityFingerprint, - _random = BinaryPrimitives.ReadUInt64LittleEndian(Utils.GenerateRandom()), - _timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() - }; - - return result.ToCuid(); - } + private const string Prefix = "c"; - /// - /// Indicates whether the values of two specified objects are equal. - /// - /// The first object to compare. - /// The second object to compare. - /// true if left and right are equal; otherwise, false. - public static bool operator ==(Cuid left, Cuid right) - { - return left.Equals(right); - } + private const int ValueLength = 25; - /// - /// Compares two values to determine which is greater. - /// - /// The first object to compare. - /// The second object to compare. - /// true if left is greater than right; otherwise, false. - public static bool operator >(Cuid left, Cuid right) - { - return left.CompareTo(right) > 0; - } + private readonly ulong _counter; - /// - /// Compares two values to determine which is greater or equal. - /// - /// The first object to compare. - /// The second object to compare. - /// true if left is greater than or equal to right; otherwise, false. - public static bool operator >=(Cuid left, Cuid right) - { - return left.CompareTo(right) >= 0; - } + private readonly byte[] _fingerprint; - /// - /// Indicates whether the values of two specified objects are not equal. - /// - /// The first object to compare. - /// The second object to compare. - /// true if left and right are not equal; otherwise, false. - public static bool operator !=(Cuid left, Cuid right) - { - return !left.Equals(right); - } + private readonly ulong _random; - /// - /// Compares two values to determine which is less. - /// - /// The first object to compare. - /// The second object to compare. - /// true if left is less than right; otherwise, false. - public static bool operator <(Cuid left, Cuid right) - { - return left.CompareTo(right) < 0; - } + private readonly long _timestamp; - /// - /// Compares two values to determine which is less or equal. - /// - /// The first object to compare. - /// The second object to compare. - /// true if left is less than or equal to right; otherwise, false. - public static bool operator <=(Cuid left, Cuid right) - { - return left.CompareTo(right) <= 0; - } + /// + /// Initializes a new instance of the structure by using the value represented by the specified + /// string. + /// + /// + /// A string that contains a CUID. + /// + public Cuid(string c) + { + Guard.IsNotNullOrWhiteSpace(c); + CuidResult result = new(); - /// - /// Converts the string representation of a CUID to the equivalent structure. - /// - /// The string to convert. - /// A structure that contains the value that was parsed. - public static Cuid Parse(string input) - { - ArgumentNullException.ThrowIfNull(input); + _ = TryParseCuid(c.AsSpan(), true, ref result); - return Parse((ReadOnlySpan) input); - } + this = result.ToCuid(); + } - /// - /// Converts a read-only character span that represents a CUID to the equivalent structure. - /// - /// A read-only span containing the bytes representing a CUID. - /// A structure that contains the value that was parsed. - [SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] - public static Cuid Parse(ReadOnlySpan input) - { - CuidResult result = new(); + /// + /// Initializes a new instance of the structure. + /// + /// A new CUID object. + public static Cuid NewCuid() + { + CuidResult result = new() + { + _counter = Counter.Instance.Value, + _fingerprint = Context.IdentityFingerprint, + _random = BinaryPrimitives.ReadInt64LittleEndian(Utils.GenerateRandom()), + _timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() + }; - _ = TryParseCuid(input, true, ref result); + return result.ToCuid(); + } - return result.ToCuid(); - } + /// + /// Indicates whether the values of two specified objects are equal. + /// + /// The first object to compare. + /// The second object to compare. + /// true if left and right are equal; otherwise, false. + public static bool operator ==(Cuid left, Cuid right) + { + return left.Equals(right); + } - /// - /// Converts the specified read-only span of characters containing the representation of a CUID to the equivalent - /// structure. - /// - /// A span containing the characters representing the CUID to convert. - /// - /// When this method returns, contains the parsed value. If the method returns true, - /// result contains a valid Guid. If the method returns false, result equals . - /// - /// true if the parse operation was successful; otherwise, false. - public static bool TryParse(ReadOnlySpan input, out Cuid result) - { - CuidResult parseResult = new(); - if ( TryParseCuid(input, false, ref parseResult) ) + /// + /// Compares two values to determine which is greater. + /// + /// The first object to compare. + /// The second object to compare. + /// true if left is greater than right; otherwise, false. + public static bool operator >(Cuid left, Cuid right) { - result = parseResult.ToCuid(); - return true; + return left.CompareTo(right) > 0; } - result = default; - return false; - } + /// + /// Compares two values to determine which is greater or equal. + /// + /// The first object to compare. + /// The second object to compare. + /// true if left is greater than or equal to right; otherwise, false. + public static bool operator >=(Cuid left, Cuid right) + { + return left.CompareTo(right) >= 0; + } - /// - /// Converts the string representation of a CUID to the equivalent structure. - /// - /// A string containing the CUID to convert. - /// - /// When this method returns, contains the parsed value. If the method returns true, - /// result contains a valid Guid. If the method returns false, result equals . - /// - /// true if the parse operation was successful; otherwise, false. - public static bool TryParse([NotNullWhen(true)] string? input, out Cuid result) - { - if ( input is not null ) + /// + /// Indicates whether the values of two specified objects are not equal. + /// + /// The first object to compare. + /// The second object to compare. + /// true if left and right are not equal; otherwise, false. + public static bool operator !=(Cuid left, Cuid right) { - return TryParse((ReadOnlySpan) input, out result); + return !left.Equals(right); } - result = default; - return false; - } + /// + /// Compares two values to determine which is less. + /// + /// The first object to compare. + /// The second object to compare. + /// true if left is less than right; otherwise, false. + public static bool operator <(Cuid left, Cuid right) + { + return left.CompareTo(right) < 0; + } - /// - public int CompareTo(Cuid other) - { - int cComparison = _counter.CompareTo(other._counter); - if ( cComparison != 0 ) + /// + /// Compares two values to determine which is less or equal. + /// + /// The first object to compare. + /// The second object to compare. + /// true if left is less than or equal to right; otherwise, false. + public static bool operator <=(Cuid left, Cuid right) { - return cComparison; + return left.CompareTo(right) <= 0; } - int fComparison = string.Compare(_fingerprint, other._fingerprint, StringComparison.OrdinalIgnoreCase); - if ( fComparison != 0 ) + /// + /// Converts the string representation of a CUID to the equivalent structure. + /// + /// The string to convert. + /// A structure that contains the value that was parsed. + public static Cuid Parse(string input) { - return fComparison; + return string.IsNullOrWhiteSpace(input) ? Empty : Parse(input.AsSpan()); } - int rComparison = _random.CompareTo(other._random); - return rComparison != 0 ? rComparison : _timestamp.CompareTo(other._timestamp); - } + /// + /// Converts a read-only character span that represents a CUID to the equivalent structure. + /// + /// A read-only span containing the bytes representing a CUID. + /// A structure that contains the value that was parsed. + [SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] + public static Cuid Parse(ReadOnlySpan input) + { + CuidResult result = new(); - /// - public int CompareTo(object? obj) - { - if ( ReferenceEquals(null, obj) ) + _ = TryParseCuid(input, true, ref result); + + return result.ToCuid(); + } + + /// + /// Converts the specified read-only span of characters containing the representation of a CUID to the equivalent + /// structure. + /// + /// A span containing the characters representing the CUID to convert. + /// + /// When this method returns, contains the parsed value. If the method returns true, + /// result contains a valid Guid. If the method returns false, result equals . + /// + /// true if the parse operation was successful; otherwise, false. + public static bool TryParse(ReadOnlySpan input, out Cuid result) { - return 1; + CuidResult parseResult = new(); + + if ( TryParseCuid(input, false, ref parseResult) ) + { + result = parseResult.ToCuid(); + return true; + } + + result = default; + return false; } - return obj is Cuid other - ? CompareTo(other) - : throw new ArgumentException($"Object must be of type {nameof(Cuid)}"); - } + /// + /// Converts the string representation of a CUID to the equivalent structure. + /// + /// A string containing the CUID to convert. + /// + /// When this method returns, contains the parsed value. If the method returns true, + /// result contains a valid Guid. If the method returns false, result equals . + /// + /// true if the parse operation was successful; otherwise, false. +#if NET6_0_OR_GREATER + public static bool TryParse([NotNullWhen(true)] string? input, out Cuid result) +#else + public static bool TryParse([AllowNull] string input, out Cuid result) +#endif + { + if ( !string.IsNullOrWhiteSpace(input) ) + { + return TryParse(input.AsSpan(), out result); + } - /// - public bool Equals(Cuid other) - { - return _counter == other._counter && - string.Equals(_fingerprint, other._fingerprint, StringComparison.OrdinalIgnoreCase) && - _random == other._random && - _timestamp == other._timestamp; - } + result = default; + return false; + } - /// - public override bool Equals(object? obj) - { - return obj is Cuid other && Equals(other); - } + /// +#if NET6_0_OR_GREATER + public int CompareTo(object? obj) +#else + public int CompareTo(object obj) +#endif + { + if ( ReferenceEquals(null, obj) ) + { + return 1; + } - /// - public override int GetHashCode() - { - HashCode hashCode = new(); + return obj is Cuid other + ? CompareTo(other) + : throw new ArgumentException($"Object must be of type {nameof(Cuid)}"); + } + + /// + public bool Equals(Cuid other) + { + if ( _fingerprint == null ) + { + return _counter == other._counter && + _random == other._random && + _timestamp == other._timestamp; + } - hashCode.Add(_counter); - hashCode.Add(_fingerprint, StringComparer.OrdinalIgnoreCase); - hashCode.Add(_random); - hashCode.Add(_timestamp); + return _counter == other._counter && + _fingerprint.SequenceEqual(other._fingerprint) && + _random == other._random && + _timestamp == other._timestamp; + } - return hashCode.ToHashCode(); - } + /// +#if NET6_0_OR_GREATER + public override bool Equals(object? obj) +#else + public override bool Equals(object obj) +#endif + { + return obj is Cuid other && Equals(other); + } - /// - /// Returns a string representation of the value of this instance. - /// - /// The value of this . - public override string ToString() - { - return string.Create(25, ( _t: _timestamp, _c: _counter, _f: _fingerprint, _r: _random ), - (dest, buffer) => - { - Prefix.WriteTo(ref dest); + /// + public override int GetHashCode() + { + return HashCode.Combine(_counter, StructuralComparisons.StructuralEqualityComparer.GetHashCode(_fingerprint), _random, _timestamp); + } - Utils.Encode((ulong) buffer._t) - .WriteTo(ref dest); + /// + public void ReadXml(XmlReader reader) + { + reader.Read(); - Utils.Encode(buffer._c) - .TrimPad(BlockSize) - .WriteTo(ref dest); + CuidResult result = new(); - buffer._f.WriteTo(ref dest); + _ = TryParseCuid(reader.Value.AsSpan(), true, ref result); - Utils.Encode(buffer._r) - .TrimPad(BlockSize * 2) - .WriteTo(ref dest); - }); - } + Unsafe.AsRef(in this) = result.ToCuid(); + } - private static bool IsAlphaNum(ReadOnlySpan input) - { - foreach ( char t in input ) + /// + /// Returns a string representation of the value of this instance. + /// + /// The value of this . + public override string ToString() { - if ( !char.IsLetterOrDigit(t) ) - { - return false; - } +#if NET6_0_OR_GREATER + return string.Create(25, ( _t: _timestamp, _c: _counter, _f: _fingerprint, _r: _random ), + (dest, buffer) => + { + Prefix.WriteTo(ref dest); + + Utils.Encode((ulong) buffer._t) + .WriteTo(ref dest); + + Utils.Encode(buffer._c) + .TrimPad(BlockSize) + .WriteTo(ref dest); + + Encoding.UTF8.GetString(buffer._f).WriteTo(ref dest); + + Utils.Encode(buffer._r) + .TrimPad(BlockSize * 2) + .WriteTo(ref dest); + }); +#else + List items = + [ + Prefix, + Utils.Encode((ulong) _timestamp), + Utils.Encode(_counter).TrimPad(BlockSize), + Encoding.UTF8.GetString(_fingerprint), + Utils.Encode(_random).TrimPad(BlockSize * 2) + ]; + + return string.Join(string.Empty, items); +#endif } - return true; - } + /// + public void WriteXml(XmlWriter writer) + { + writer.WriteString(ToString()); + } - private static bool TryParseCuid(ReadOnlySpan cuidString, bool throwException, ref CuidResult result) - { - cuidString = cuidString.Trim(); - if ( cuidString.Length != ValueLength || !cuidString.StartsWith(Prefix) || !IsAlphaNum(cuidString) ) + private static bool IsAlphaNum(ReadOnlySpan input) { - if ( throwException ) + foreach ( char t in input ) { - result.SetFailure(Resources.Resources.Format_CuidUnrecognized); + if ( !char.IsLetterOrDigit(t) ) + { + return false; + } } - return false; + return true; } - ReadOnlySpan timestamp = cuidString[1..9]; - ReadOnlySpan counter = cuidString[9..^12]; - ReadOnlySpan fingerprint = cuidString[13..^8]; - ReadOnlySpan random = cuidString[^8..]; - - result._counter = Utils.Decode(counter); - result._fingerprint = fingerprint.ToString(); - result._random = Utils.Decode(random); - result._timestamp = (long) Utils.Decode(timestamp); + private static bool TryParseCuid(ReadOnlySpan cuidString, bool throwException, ref CuidResult result) + { + cuidString = cuidString.Trim(); + if ( cuidString.Length != ValueLength || !cuidString.StartsWith(Prefix.AsSpan()) || !IsAlphaNum(cuidString) ) + { + if ( throwException ) + { + result.SetFailure(Resources.Resources.Format_CuidUnrecognized); + } - return true; - } + return false; + } - [ExcludeFromCodeCoverage] - XmlSchema? IXmlSerializable.GetSchema() - { - return null; - } + ReadOnlySpan timestamp = cuidString[1..9]; + ReadOnlySpan counter = cuidString[9..^12]; + ReadOnlySpan fingerprint = cuidString[13..^8]; + ReadOnlySpan random = cuidString[^8..]; - /// - public void ReadXml(XmlReader reader) - { - reader.Read(); - - CuidResult result = new(); - - _ = TryParseCuid(reader.Value, true, ref result); + result._counter = Utils.Decode(counter); + result._fingerprint = Encoding.UTF8.GetBytes(fingerprint.ToString()); + result._random = Utils.Decode(random); + result._timestamp = Utils.Decode(timestamp); - Unsafe.AsRef(in this) = result.ToCuid(); - } + return true; + } - /// - public void WriteXml(XmlWriter writer) - { - writer.WriteString(ToString()); - } - - [SuppressMessage("ReSharper", "InconsistentNaming")] - [StructLayout(LayoutKind.Explicit)] - private struct CuidResult - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly Cuid ToCuid() + [ExcludeFromCodeCoverage] +#if NET6_0_OR_GREATER + XmlSchema? IXmlSerializable.GetSchema() +#else + XmlSchema IXmlSerializable.GetSchema() +#endif { - CuidResult result = this; - return Unsafe.As(ref Unsafe.AsRef(ref result)); + return null; } - #pragma warning disable CA1822 - // ReSharper disable once MemberCanBeMadeStatic.Local - internal readonly void SetFailure(string message) + [SuppressMessage("ReSharper", "InconsistentNaming")] + [StructLayout(LayoutKind.Explicit)] + private struct CuidResult { - throw new FormatException(message); - } - #pragma warning restore CA1822 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Cuid ToCuid() + { +#if NET8_0_OR_GREATER + CuidResult result = this; + return Unsafe.As(ref Unsafe.AsRef(ref result)); +#else + return Unsafe.As(ref Unsafe.AsRef(in this)); +#endif + } - #pragma warning disable S4487 - [FieldOffset(8)] internal ulong _counter; + #pragma warning disable CA1822 + // ReSharper disable once MemberCanBeMadeStatic.Local + internal readonly void SetFailure(string message) + { + throw new FormatException(message); + } + #pragma warning restore CA1822 - [FieldOffset(0)] internal string _fingerprint; + #pragma warning disable S4487 + [FieldOffset(8)] + internal long _counter; - [FieldOffset(16)] internal ulong _random; + [FieldOffset(0)] + internal byte[] _fingerprint; - [FieldOffset(24)] internal long _timestamp; - #pragma warning restore S4487 - } + [FieldOffset(16)] + internal long _random; - private static class Context - { - public static readonly string IdentityFingerprint = GenerateFingerprint(); + [FieldOffset(24)] + internal long _timestamp; + #pragma warning restore S4487 + } - private static string GenerateFingerprint() + private static class Context { - byte[] identity = Fingerprint.Generate(FingerprintVersion.One); - - return Encoding.UTF8.GetString(identity); + public static readonly byte[] IdentityFingerprint = Fingerprint.Generate(FingerprintVersion.One); } - } - private sealed class Counter - { - // ReSharper disable once InconsistentNaming - private static readonly Lazy _counter = new(() => new Counter()); - private static readonly ulong DiscreteValues = (ulong) Math.Pow(36, 4); + private sealed class Counter + { + // ReSharper disable once InconsistentNaming + private static readonly Lazy _counter = new(() => new Counter()); - private volatile uint _value; + private static readonly long DiscreteValues = (long) Math.Pow(36, 4); - public static Counter Instance => _counter.Value; + private volatile int _value; - public uint Value - { - get + public static Counter Instance => _counter.Value; + + public int Value { - _value = _value < DiscreteValues ? _value : 0; - Interlocked.Increment(ref _value); + get + { + _value = _value < DiscreteValues ? _value : 0; + Interlocked.Increment(ref _value); - return _value; + return _value; + } } } + + /// + public int CompareTo(Cuid other) + { + int counterComparison = _counter.CompareTo(other._counter); + if ( counterComparison != 0 ) + { + return counterComparison; + } + + int randomComparison = _random.CompareTo(other._random); + return randomComparison != 0 ? randomComparison : _timestamp.CompareTo(other._timestamp); + } } } diff --git a/src/cuid.net/Cuid2.cs b/src/cuid.net/Cuid2.cs index e9e559b..654f1aa 100644 --- a/src/cuid.net/Cuid2.cs +++ b/src/cuid.net/Cuid2.cs @@ -1,149 +1,169 @@ -namespace Visus.Cuid; - -using System.Buffers.Binary; -using System.Runtime.InteropServices; -using NSec.Cryptography; - -/// -/// Represents a collision resistant unique identifier (CUID). -/// -[StructLayout(LayoutKind.Sequential)] -public readonly struct Cuid2 : IEquatable +namespace Visus.Cuid { - private const int DefaultLength = 24; + using System; + using System.Buffers.Binary; + using System.Collections; + using System.Diagnostics.CodeAnalysis; + using System.Linq; + using System.Runtime.InteropServices; + using System.Threading; + using CommunityToolkit.Diagnostics; + using Org.BouncyCastle.Crypto.Digests; - private readonly ulong _counter = Counter.Instance.Value; + /// + /// Represents a collision resistant unique identifier (CUID). + /// + [StructLayout(LayoutKind.Sequential)] + public readonly struct Cuid2 : IEquatable + { + private const int DefaultLength = 24; - private readonly byte[] _fingerprint; + private readonly long _counter; - private readonly int _maxLength; + private readonly byte[] _fingerprint; - private readonly char _prefix; + private readonly int _maxLength; - private readonly byte[] _random; + private readonly char _prefix; - private readonly long _timestamp; + private readonly byte[] _random; - /// - /// Initializes a new instance of the structure. - /// - /// The structure will initialize with a default maximum length of 24. - /// A new CUID object. - public Cuid2() - : this(DefaultLength) - { - } + private readonly long _timestamp; - /// - /// Initializes a new instance of the structure. - /// - /// Defines the maximum string length value of . - /// The value defined for cannot be less than 4 or greater than 32. - /// - /// The value of was less than 4 or greater - /// than 32. - /// - public Cuid2(int maxLength) - { - if ( maxLength is < 4 or > 32 ) + /// + /// Initializes a new instance of the structure. + /// + /// The structure will initialize with a default maximum length of 24. + /// A new CUID object. + public Cuid2() + : this(DefaultLength) { - throw new ArgumentOutOfRangeException(nameof(maxLength), - string.Format(Resources.Resources.Arg_Cuid2IntCtor, "4", "32")); } + + /// + /// Initializes a new instance of the structure. + /// + /// Defines the maximum string length value of . + /// The value defined for cannot be less than 4 or greater than 32. + /// + /// The value of was less than 4 or greater + /// than 32. + /// + public Cuid2(int maxLength) + { + Guard.IsInRange(maxLength, 4, 33); - _maxLength = maxLength; - - _fingerprint = Context.IdentityFingerprint; - _prefix = Utils.GenerateCharacterPrefix(); - _random = Utils.GenerateRandom(maxLength); - _timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); - } - - /// - /// Indicates whether the values of two specified objects are equal. - /// - /// The first object to compare. - /// The second object to compare. - /// true if left and right are equal; otherwise, false. - public static bool operator ==(Cuid2 left, Cuid2 right) - { - return left.Equals(right); - } + _counter = Counter.Instance.Value; + _maxLength = maxLength; - /// - /// Indicates whether the values of two specified objects are not equal. - /// - /// The first object to compare. - /// The second object to compare. - /// true if left and right are not equal; otherwise, false. - public static bool operator !=(Cuid2 left, Cuid2 right) - { - return !left.Equals(right); - } + _fingerprint = Context.IdentityFingerprint; + _prefix = Utils.GenerateCharacterPrefix(); + _random = Utils.GenerateRandom(maxLength); + _timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); + } - /// - public bool Equals(Cuid2 other) - { - return _counter == other._counter && - _fingerprint.Equals(other._fingerprint) && - _prefix == other._prefix && - _random.Equals(other._random) && - _timestamp == other._timestamp; - } + /// + /// Indicates whether the values of two specified objects are equal. + /// + /// The first object to compare. + /// The second object to compare. + /// true if left and right are equal; otherwise, false. + public static bool operator ==(Cuid2 left, Cuid2 right) + { + return left.Equals(right); + } - /// - public override bool Equals(object? obj) - { - return obj is Cuid2 other && Equals(other); - } + /// + /// Indicates whether the values of two specified objects are not equal. + /// + /// The first object to compare. + /// The second object to compare. + /// true if left and right are not equal; otherwise, false. + public static bool operator !=(Cuid2 left, Cuid2 right) + { + return !left.Equals(right); + } - /// - public override int GetHashCode() - { - return HashCode.Combine(_counter, _fingerprint, _prefix, _random, _timestamp); - } + /// + [SuppressMessage("ReSharper", "SimplifyConditionalTernaryExpression")] + public bool Equals(Cuid2 other) + { + return ( _counter == other._counter && + _fingerprint == null ) || ( _fingerprint.SequenceEqual(other._fingerprint) && + _prefix == other._prefix && + _random == null ) || ( _random.SequenceEqual(other._random) && + _timestamp == other._timestamp ); + } - /// - /// Returns a string representation of the value of this instance. - /// - /// The value of this . - public override string ToString() - { - Span buffer = stackalloc byte[16]; + /// +#if NET6_0_OR_GREATER + public override bool Equals(object? obj) +#else + public override bool Equals(object obj) +#endif + { + return obj is Cuid2 other && Equals(other); + } - BinaryPrimitives.WriteInt64LittleEndian(buffer[..8], _timestamp); - BinaryPrimitives.WriteUInt64LittleEndian(buffer[^8..], _counter); + /// + public override int GetHashCode() + { + return HashCode.Combine(_counter, StructuralComparisons.StructuralEqualityComparer.GetHashCode(_fingerprint), _prefix, _random, _timestamp); + } - IncrementalHash.Initialize(HashAlgorithm.Sha512, out IncrementalHash state); + /// + /// Returns a string representation of the value of this instance. + /// + /// The value of this . + public override string ToString() + { + if ( _counter == 0 || + _fingerprint == null || + _maxLength == 0 || + _prefix == char.MinValue || + _random == null || + _timestamp == 0 ) + { + return new string('0', DefaultLength); + } - IncrementalHash.Update(ref state, buffer); - IncrementalHash.Update(ref state, _fingerprint); - IncrementalHash.Update(ref state, _random); + Span buffer = stackalloc byte[16]; - byte[] hash = IncrementalHash.Finalize(ref state); + BinaryPrimitives.WriteInt64LittleEndian(buffer[..8], _timestamp); + BinaryPrimitives.WriteInt64LittleEndian(buffer[^8..], _counter); - return _prefix + Utils.Encode(hash)[..( _maxLength - 1 )]; - } + Sha3Digest digest = new(512); - private static class Context - { - public static readonly byte[] IdentityFingerprint = Fingerprint.Generate(); - } + digest.BlockUpdate(buffer.ToArray(), 0, buffer.Length); + digest.BlockUpdate(_fingerprint, 0, _fingerprint.Length); + digest.BlockUpdate(_random, 0, _random.Length); - private sealed class Counter - { - // ReSharper disable once InconsistentNaming - private static readonly Lazy _counter = new(() => new Counter()); + byte[] hash = new byte[digest.GetByteLength()]; + digest.DoFinal(hash, 0); - private ulong _value; + return _prefix + Utils.Encode(hash)[..( _maxLength - 1 )]; + } - private Counter() + private static class Context { - _value = BinaryPrimitives.ReadUInt64LittleEndian(Utils.GenerateRandom()) * 476782367; + public static readonly byte[] IdentityFingerprint = Fingerprint.Generate(); } - public static Counter Instance => _counter.Value; + private sealed class Counter + { + // ReSharper disable once InconsistentNaming + private static readonly Lazy _counter = new(() => new Counter()); + + private long _value; - public ulong Value => Interlocked.Increment(ref _value); + private Counter() + { + _value = BinaryPrimitives.ReadInt64LittleEndian(Utils.GenerateRandom()) * 476782367; + } + + public static Counter Instance => _counter.Value; + + public long Value => Interlocked.Increment(ref _value); + } } } diff --git a/src/cuid.net/Extensions/StringExtensions.cs b/src/cuid.net/Extensions/StringExtensions.cs index b49ebe4..54a1be0 100644 --- a/src/cuid.net/Extensions/StringExtensions.cs +++ b/src/cuid.net/Extensions/StringExtensions.cs @@ -1,22 +1,25 @@ -namespace Visus.Cuid.Extensions; - -internal static class StringExtensions +namespace Visus.Cuid.Extensions { - internal static string TrimPad(this string source, int size) - { - return string.IsNullOrWhiteSpace(source) - ? string.Empty - : source.PadLeft(9, '0')[^size..]; - } + using System; - internal static void WriteTo(this string source, ref Span destination) + internal static class StringExtensions { - source.AsSpan().WriteToInternal(ref destination); - } + internal static string TrimPad(this string source, int size) + { + return string.IsNullOrWhiteSpace(source) + ? string.Empty + : source.PadLeft(9, '0')[^size..]; + } - private static void WriteToInternal(this ReadOnlySpan source, ref Span destination) - { - source.CopyTo(destination); - destination = destination[source.Length..]; + internal static void WriteTo(this string source, ref Span destination) + { + source.AsSpan().WriteToInternal(ref destination); + } + + private static void WriteToInternal(this ReadOnlySpan source, ref Span destination) + { + source.CopyTo(destination); + destination = destination[source.Length..]; + } } } diff --git a/src/cuid.net/Fingerprint.cs b/src/cuid.net/Fingerprint.cs index f2839b9..16c3766 100644 --- a/src/cuid.net/Fingerprint.cs +++ b/src/cuid.net/Fingerprint.cs @@ -1,86 +1,116 @@ -namespace Visus.Cuid; - -using System.Buffers.Binary; -using System.Globalization; -using System.Text; -using Abstractions; -using Extensions; - -internal static class Fingerprint +namespace Visus.Cuid { - public static byte[] Generate(FingerprintVersion version = FingerprintVersion.Two) - { - return version == FingerprintVersion.One - ? GenerateLegacyIdentity() - : GenerateIdentity(); - } - - private static byte[] GenerateIdentity() + using System; + using System.Buffers.Binary; + using System.Globalization; + using System.Linq; + using System.Text; + using Abstractions; + using Extensions; +#if NETSTANDARD2_0 || NET472 + using System.Diagnostics; + using System.Runtime.InteropServices; +#endif + + internal static class Fingerprint { - byte[] identity = Encoding.UTF8.GetBytes(RetrieveSystemName()); - - Span buffer = stackalloc byte[identity.Length + 40]; - - identity.CopyTo(buffer[..identity.Length]); - - BinaryPrimitives.WriteInt32LittleEndian( - buffer.Slice(identity.Length + 1, 4), - Environment.ProcessId - ); - - BinaryPrimitives.WriteInt32LittleEndian( - buffer.Slice(identity.Length + 6, 4), - Environment.CurrentManagedThreadId - ); - - Utils.GenerateRandom(32).CopyTo(buffer[^32..]); + public static byte[] Generate(FingerprintVersion version = FingerprintVersion.Two) + { + return version == FingerprintVersion.One + ? GenerateLegacyIdentity() + : GenerateIdentity(); + } - return buffer.ToArray(); - } + private static byte[] GenerateIdentity() + { + byte[] identity = Encoding.UTF8.GetBytes(RetrieveSystemName()); - private static byte[] GenerateLegacyIdentity() - { - string machineName = RetrieveSystemName(); + Span buffer = stackalloc byte[identity.Length + 40]; - int machineIdentifier = machineName.Length + 36; - machineIdentifier = machineName.Aggregate(machineIdentifier, (i, c) => i + c); + identity.CopyTo(buffer[..identity.Length]); - string result = string.Create(4, machineIdentifier, (dest, _) => - { - Environment.ProcessId - .ToString(CultureInfo.InvariantCulture) - .TrimPad(2).WriteTo(ref dest); - machineIdentifier.ToString(CultureInfo.InvariantCulture) - .TrimPad(2).WriteTo(ref dest); - }); +#if NET6_0_OR_GREATER + BinaryPrimitives.WriteInt32LittleEndian( + buffer.Slice(identity.Length + 1, 4), + Environment.ProcessId + ); +#else + BinaryPrimitives.WriteInt32LittleEndian( + buffer.Slice(identity.Length + 1, 4), + Process.GetCurrentProcess().Id + ); +#endif - return Encoding.UTF8.GetBytes(result); - } + BinaryPrimitives.WriteInt32LittleEndian( + buffer.Slice(identity.Length + 6, 4), + Environment.CurrentManagedThreadId + ); - private static string GenerateSystemName() - { - byte[] bytes = Utils.GenerateRandom(32); - string hostname = Convert.ToHexString(bytes).ToUpperInvariant(); + Utils.GenerateRandom(32).CopyTo(buffer[^32..]); - return OperatingSystem.IsWindows() - ? hostname[..15] // windows hostnames are limited to 15 characters - : hostname; - } + return buffer.ToArray(); + } - private static string RetrieveSystemName() - { - string machineName; - try + private static byte[] GenerateLegacyIdentity() { - machineName = !string.IsNullOrWhiteSpace(Environment.MachineName) - ? Environment.MachineName - : GenerateSystemName(); + string machineName = RetrieveSystemName(); + + int machineIdentifier = machineName.Length + 36; + machineIdentifier = machineName.Aggregate(machineIdentifier, (i, c) => i + c); + +#if NET6_0_OR_GREATER + string result = string.Create(4, machineIdentifier, (dest, _) => + { + Environment.ProcessId + .ToString(CultureInfo.InvariantCulture) + .TrimPad(2).WriteTo(ref dest); + machineIdentifier.ToString(CultureInfo.InvariantCulture) + .TrimPad(2).WriteTo(ref dest); + }); +#else + StringBuilder sb = new(); + + sb.Append(Process.GetCurrentProcess().Id.ToString(CultureInfo.InvariantCulture).TrimPad(2)); + sb.Append(machineIdentifier.ToString(CultureInfo.InvariantCulture).TrimPad(2)); + + string result = sb.ToString(); +#endif + + return Encoding.UTF8.GetBytes(result); } - catch ( InvalidOperationException ) + + private static string GenerateSystemName() { - machineName = GenerateSystemName(); + byte[] bytes = Utils.GenerateRandom(32); + +#if NET6_0_OR_GREATER + string hostname = Convert.ToHexString(bytes).ToUpperInvariant(); + return OperatingSystem.IsWindows() + ? hostname[..15] // windows hostnames are limited to 15 characters + : hostname; +#else + string hostname = BitConverter.ToString(bytes); + return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + ? hostname[..15] + : hostname; +#endif } - return machineName; + private static string RetrieveSystemName() + { + string machineName; + try + { + machineName = !string.IsNullOrWhiteSpace(Environment.MachineName) + ? Environment.MachineName + : GenerateSystemName(); + } + catch ( InvalidOperationException ) + { + machineName = GenerateSystemName(); + } + + return machineName; + } } } diff --git a/src/cuid.net/Obsoletions.cs b/src/cuid.net/Obsoletions.cs index 1d094d0..dec1265 100644 --- a/src/cuid.net/Obsoletions.cs +++ b/src/cuid.net/Obsoletions.cs @@ -1,7 +1,8 @@ -namespace Visus.Cuid; - -internal static class Obsoletions +namespace Visus.Cuid { - internal const string CuidDiagId = "VISLIB0001"; - internal const string CuidMessage = "Cuid is deprecated and should not be used. Use Cuid2 instead."; + internal static class Obsoletions + { + internal const string CuidDiagId = "VISLIB0001"; + internal const string CuidMessage = "Cuid is deprecated and should not be used. Use Cuid2 instead."; + } } diff --git a/src/cuid.net/Resources/Resources.resx b/src/cuid.net/Resources/Resources.resx index 7a91191..e34853a 100644 --- a/src/cuid.net/Resources/Resources.resx +++ b/src/cuid.net/Resources/Resources.resx @@ -1,7 +1,8 @@  - diff --git a/src/cuid.net/Serialization/Json/Converters/CuidConverter.cs b/src/cuid.net/Serialization/Json/Converters/CuidConverter.cs index 9a44ba1..54e9c32 100644 --- a/src/cuid.net/Serialization/Json/Converters/CuidConverter.cs +++ b/src/cuid.net/Serialization/Json/Converters/CuidConverter.cs @@ -1,21 +1,26 @@ -#pragma warning disable VISLIB0001 -namespace Visus.Cuid.Serialization.Json.Converters; - -using System.Text.Json; -using System.Text.Json.Serialization; - -/// -public class CuidConverter : JsonConverter +#if NETSTANDARD2_0 || NET472 +#pragma warning disable CS0618 // Type or member is obsolete +#endif +#pragma warning disable VISLIB0001 +namespace Visus.Cuid.Serialization.Json.Converters { - /// - public override Cuid Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - return Cuid.Parse(reader.GetString()!); - } + using System; + using System.Text.Json; + using System.Text.Json.Serialization; /// - public override void Write(Utf8JsonWriter writer, Cuid value, JsonSerializerOptions options) + public class CuidConverter : JsonConverter { - writer.WriteStringValue(value.ToString()); + /// + public override Cuid Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return Cuid.Parse(reader.GetString()!); + } + + /// + public override void Write(Utf8JsonWriter writer, Cuid value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.ToString()); + } } } diff --git a/src/cuid.net/Utils.cs b/src/cuid.net/Utils.cs index 179eed0..bcc35f8 100644 --- a/src/cuid.net/Utils.cs +++ b/src/cuid.net/Utils.cs @@ -1,76 +1,126 @@ -namespace Visus.Cuid; - -using System.Numerics; -using System.Security.Cryptography; - -internal static class Utils +namespace Visus.Cuid { - private static readonly BigInteger BigRadix = new(36); + using System; + using System.Linq; + using System.Numerics; + using System.Runtime.CompilerServices; + using System.Security.Cryptography; - private static readonly double BitsPerDigit = Math.Log(36, 2); + internal static class Utils + { +#if NET6_0_OR_GREATER + private static readonly BigInteger BigRadix = new(36); +#else + private static readonly BigInteger BigRadix = new BigInteger(36); +#endif - private const int Radix = 36; + private static readonly double BitsPerDigit = Math.Log(36, 2); - private static readonly Random Random = new(); + private const int Radix = 36; - internal static ulong Decode(ReadOnlySpan input) - { - return input.ToString() - .Select(s => s is >= '0' and <= '9' ? s - '0' : 10 + s - 'a') - .Aggregate((ulong) 0, (i, c) => ( i * Radix ) + (uint) c); - } +#if NET6_0_OR_GREATER + private static readonly Random Random = new(); +#else + private static readonly Random Random = new Random(); +#endif - internal static string Encode(ReadOnlySpan value) - { - if ( value.IsEmpty ) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static long Decode(ReadOnlySpan input) { - return string.Empty; +#if NET6_0_OR_GREATER + return input.ToString() + .Select(s => s is >= '0' and <= '9' ? s - '0' : 10 + s - 'a') + .Aggregate((long) 0, (i, c) => ( i * Radix ) + c); +#else + return input.ToString() + .Select(s => s >= '0' && s <= '9' ? s - '0' : 10 + s - 'a') + .Aggregate((long) 0, (i, c) => ( i * Radix ) + c); +#endif } - int length = (int) Math.Ceiling(value.Length * 8 / BitsPerDigit); - int i = length; - Span buffer = stackalloc char[length]; - - BigInteger d = new(value, true); - while ( !d.IsZero ) + internal static string Encode(ReadOnlySpan value) { - d = BigInteger.DivRem(d, BigRadix, out BigInteger r); - int c = (int) r; - buffer[--i] = (char) ( c is >= 0 and <= 9 ? c + 48 : c + 'a' - 10 ); + if ( value.IsEmpty ) + { + return string.Empty; + } + + int length = (int) Math.Ceiling(value.Length * 8 / BitsPerDigit); + int i = length; + Span buffer = stackalloc char[length]; + +#if NET6_0_OR_GREATER + BigInteger d = new(value, true); +#else + byte[] unsigned = value.ToArray().Concat(new byte[] { 00 }).ToArray(); + BigInteger d = new(unsigned); +#endif + while ( !d.IsZero ) + { + d = BigInteger.DivRem(d, BigRadix, out BigInteger r); + int c = (int) r; + + buffer[--i] = (char) ( c is >= 0 and <= 9 ? c + 48 : c + 'a' - 10 ); + } + +#if NET6_0_OR_GREATER + return new string(buffer.Slice(i, length - i)); +#else + return new string(buffer.Slice(i, length - i).ToArray()); +#endif } - return new string(buffer.Slice(i, length - i)); - } - - internal static string Encode(ulong value) - { - if ( value is 0 ) + internal static string Encode(ulong value) { - return string.Empty; + if ( value is 0 ) + { + return string.Empty; + } + + const int length = 32; + int i = length; + Span buffer = stackalloc char[length]; + + do + { + ulong c = value % Radix; + +#if NET6_0_OR_GREATER + buffer[--i] = (char) ( c is >= 0 and <= 9 ? c + 48 : c + 'a' - 10 ); +#else + c += (ulong) ( c <= 9 ? 48 : 'a' - 10 ); + buffer[--i] = (char) c; +#endif + + value /= Radix; + } while ( value > 0 ); + +#if NET6_0_OR_GREATER + return new string(buffer.Slice(i, length - i)); +#else + return new string(buffer.Slice(i, length - i).ToArray()); +#endif } - const int length = 32; - int i = length; - Span buffer = stackalloc char[length]; - - do + internal static char GenerateCharacterPrefix() { - ulong c = value % Radix; - buffer[--i] = (char) ( c is >= 0 and <= 9 ? c + 48 : c + 'a' - 10 ); - value /= Radix; - } while ( value > 0 ); - - return new string(buffer.Slice(i, length - i)); - } - - internal static char GenerateCharacterPrefix() - { - int c = Random.Next(26); - return c > 13 ? char.ToLowerInvariant((char) ( 'a' + c )) : (char) ( 'a' + c ); - } + int c = Random.Next(26); + return c > 13 ? char.ToLowerInvariant((char) ( 'a' + c )) : (char) ( 'a' + c ); + } - internal static byte[] GenerateRandom(int length = 8) - { - return RandomNumberGenerator.GetBytes(length); +#if NET6_0_OR_GREATER + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif + internal static byte[] GenerateRandom(int length = 8) + { +#if NET6_0_OR_GREATER + return RandomNumberGenerator.GetBytes(length); +#else + byte[] seed = new byte[length]; + RandomNumberGenerator.Create().GetBytes(seed); + + return seed; +#endif + } } } diff --git a/src/cuid.net/cuid.net.csproj b/src/cuid.net/cuid.net.csproj index 064cb68..9cdc255 100644 --- a/src/cuid.net/cuid.net.csproj +++ b/src/cuid.net/cuid.net.csproj @@ -1,7 +1,7 @@ - net8.0 + net472;netstandard2.0;net6.0;net8.0 Visus.Cuid Copyright (c) 2023 Visus Development Team @@ -13,10 +13,13 @@ .NET implementation of collision-resistant ids + + true + + $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb true - true true true MIT @@ -34,6 +37,9 @@ + + + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -42,7 +48,11 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + diff --git a/src/cuid.net/packages.lock.json b/src/cuid.net/packages.lock.json deleted file mode 100644 index 5a61db6..0000000 --- a/src/cuid.net/packages.lock.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "version": 2, - "dependencies": { - "net8.0": { - "Microsoft.CodeAnalysis.NetAnalyzers": { - "type": "Direct", - "requested": "[8.0.0, )", - "resolved": "8.0.0", - "contentHash": "DxiTgkCl3CGq1rYmBX2wjY7XGbxiBdL4J+/AJIAFLKy5z70NxhnVRnPghnicXZ8oF6JKVXlW3xwznRbI3ioEKg==" - }, - "Microsoft.NET.ILLink.Tasks": { - "type": "Direct", - "requested": "[8.0.4, )", - "resolved": "8.0.4", - "contentHash": "PZb5nfQ+U19nhnmnR9T1jw+LTmozhuG2eeuzuW5A7DqxD/UXW2ucjmNJqnqOuh8rdPzM3MQXoF8AfFCedJdCUw==" - }, - "Microsoft.SourceLink.GitHub": { - "type": "Direct", - "requested": "[8.0.0, )", - "resolved": "8.0.0", - "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", - "dependencies": { - "Microsoft.Build.Tasks.Git": "8.0.0", - "Microsoft.SourceLink.Common": "8.0.0" - } - }, - "NSec.Cryptography": { - "type": "Direct", - "requested": "[24.4.0, )", - "resolved": "24.4.0", - "contentHash": "R89OF0T5OY9QnPRoTsMCaPKMqtXcWKZSWAO4zyUaFHI+ZRMgXU7WaIwg/rPP3vOfdaLl8HdUHWkDB+2B9FOO7g==", - "dependencies": { - "libsodium": "[1.0.19, 1.0.20)" - } - }, - "libsodium": { - "type": "Transitive", - "resolved": "1.0.19", - "contentHash": "tupm/HViwBN6Knd/gckR+cLaJGR39GLmiU4iDMM5hp/1BoczMr8fwJhSU+3/C2V4hi9nBK/4FICRKtTLU30TCA==" - }, - "Microsoft.Build.Tasks.Git": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" - }, - "Microsoft.SourceLink.Common": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" - } - } - } -} \ No newline at end of file diff --git a/tests/cuid.net.tests/ApiFacts.cs b/tests/cuid.net.tests/ApiFacts.cs index 7044a12..6787c93 100644 --- a/tests/cuid.net.tests/ApiFacts.cs +++ b/tests/cuid.net.tests/ApiFacts.cs @@ -1,24 +1,29 @@ -namespace Visus.Cuid.Tests; - -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; -using PublicApiGenerator; - -[ExcludeFromCodeCoverage] -public class ApiFacts +namespace Visus.Cuid.Tests { - [Fact] - [MethodImpl(MethodImplOptions.NoInlining)] - public async Task Cuid_NoBreakingChanges_Async() + using System.Diagnostics.CodeAnalysis; + using System.Runtime.CompilerServices; + using System.Threading.Tasks; + using PublicApiGenerator; + using Xunit; +#if NET6_0_OR_GREATER + using VerifyXunit; +#endif + + [ExcludeFromCodeCoverage] + public class ApiFacts { - var api = typeof(Cuid2).Assembly.GeneratePublicApi(new ApiGeneratorOptions +#if NET6_0_OR_GREATER + [Fact] + [MethodImpl(MethodImplOptions.NoInlining)] + public async Task Cuid_NoBreakingChanges_Async() { - ExcludeAttributes = new[] + var api = typeof(Cuid2).Assembly.GeneratePublicApi(new ApiGeneratorOptions { - "System.Runtime.Versioning.TargetFrameworkAttribute", "System.Reflection.AssemblyMetadataAttribute" - } - }); + ExcludeAttributes = ["System.Runtime.Versioning.TargetFrameworkAttribute", "System.Reflection.AssemblyMetadataAttribute"] + }); - await Verify(api); + await Verifier.Verify(api); + } +#endif } } diff --git a/tests/cuid.net.tests/Cuid2Facts.cs b/tests/cuid.net.tests/Cuid2Facts.cs index a87caf3..b98929d 100644 --- a/tests/cuid.net.tests/Cuid2Facts.cs +++ b/tests/cuid.net.tests/Cuid2Facts.cs @@ -1,54 +1,61 @@ -namespace Visus.Cuid.Tests; - -using System.Diagnostics.CodeAnalysis; - -[ExcludeFromCodeCoverage] -public class Cuid2Facts +namespace Visus.Cuid.Tests { - [Fact] - public void Cuid2_Constructor() + using System; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Linq; + using Xunit; + + [ExcludeFromCodeCoverage] + public class Cuid2Facts { - var cuid = new Cuid2(); + [Fact] + public void Cuid2_Constructor() + { + var cuid = new Cuid2(); - var cuidString = cuid.ToString(); + var cuidString = cuid.ToString(); - var result = cuidString.Length == 24 - && cuidString.All(char.IsLetterOrDigit); + var result = cuidString.Length == 24 + && cuidString.All(char.IsLetterOrDigit); - Assert.True(result); - } + Debug.WriteLine(result); - [Fact] - public void Cuid2_Constructor_DefinedLength() - { - var cuid = new Cuid2(10); + Assert.True(result); + } - var cuidString = cuid.ToString(); + [Fact] + public void Cuid2_Constructor_DefinedLength() + { + var cuid = new Cuid2(10); - var result = cuidString.Length == 10 - && cuidString.All(char.IsLetterOrDigit); + var cuidString = cuid.ToString(); - Assert.True(result); - } + var result = cuidString.Length == 10 + && cuidString.All(char.IsLetterOrDigit); - [Fact] - public void Cuid2_Constructor_ThrowsArgumentOutOfRangeException() - { - Assert.Throws(() => new Cuid2(64)); - } + Assert.True(result); + } - [Fact] - public void Cuid2_Equality() - { - var c1 = new Cuid2(); - var c2 = new Cuid2(); + [Fact] + public void Cuid2_Constructor_ThrowsArgumentOutOfRangeException() + { + Assert.Throws(() => new Cuid2(64)); + } + + [Fact] + public void Cuid2_Equality() + { + var c1 = new Cuid2(10); + var c2 = new Cuid2(10); - Assert.False(c1.Equals(c2)); - Assert.False(c1.Equals((object) c2)); + Assert.False(c1.Equals(c2)); + Assert.False(c1.Equals((object) c2)); - Assert.False(c1 == c2); - Assert.True(c1 != c2); + Assert.False(c1 == c2); + Assert.True(c1 != c2); - Assert.False(c1.GetHashCode() == c2.GetHashCode()); + Assert.False(c1.GetHashCode() == c2.GetHashCode()); + } } } diff --git a/tests/cuid.net.tests/CuidFacts.cs b/tests/cuid.net.tests/CuidFacts.cs index 509f66d..b0cb399 100644 --- a/tests/cuid.net.tests/CuidFacts.cs +++ b/tests/cuid.net.tests/CuidFacts.cs @@ -1,229 +1,273 @@ -#pragma warning disable VISLIB0001 -namespace Visus.Cuid.Tests; - -using System.Diagnostics.CodeAnalysis; -using System.Text; -using System.Text.Json; -using System.Xml; -using System.Xml.Serialization; - -[ExcludeFromCodeCoverage] -public class CuidFacts +#if NETSTANDARD2_0 || NET472 +#pragma warning disable CS0618 // Type or member is obsolete +#endif +#pragma warning disable VISLIB0001 +namespace Visus.Cuid.Tests { - // _t _c _f _r - // c lbqylg5v 0001 08mn 7kmn0t1e - private const string CuidString = "clbqylg5v000108mn7kmn0t1e"; + using System; + using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; + using System.IO; + using System.Linq; + using System.Text; + using System.Text.Json; + using System.Xml; + using System.Xml.Serialization; + using Xunit; + + [ExcludeFromCodeCoverage] + public class CuidFacts + { + // _t _c _f _r + // c lbqylg5v 0001 08mn 7kmn0t1e + private const string CuidString = "clbqylg5v000108mn7kmn0t1e"; - private const string InvalidCuidString = "xSQcDXq7N6YTJZ7i1zNXCA=="; + private const string InvalidCuidString = "xSQcDXq7N6YTJZ7i1zNXCA=="; - [Fact] - public void Cuid_ConstructFromString() - { - var cuid = new Cuid(CuidString); + [Fact] + public void Cuid_ConstructFromString() + { + var cuid = new Cuid(CuidString); - Assert.Equal(CuidString, cuid.ToString()); - } + Assert.Equal(CuidString, cuid.ToString()); + } - [Fact] - public void Cuid_ConstructFromString_ThrowsFormatException() - { - Assert.Throws(() => new Cuid(InvalidCuidString)); - } + [Fact] + public void Cuid_ConstructFromString_ThrowsFormatException() + { + Assert.Throws(() => new Cuid(InvalidCuidString)); + } - [Fact] - public void Cuid_Equality() - { - var c1 = new Cuid(CuidString); - var c2 = new Cuid(CuidString); + [Fact] + public void Cuid_Constructor_IsCuidEmpty() + { + var cuid = new Cuid(); + Assert.Equal(cuid, Cuid.Empty); + } - Assert.True(c1 == c2); - } + [Fact] + public void Cuid_Equality() + { + var c1 = new Cuid(CuidString); + var c2 = new Cuid(CuidString); - [Fact] - public void Cuid_Equals() - { - var c1 = new Cuid(CuidString); - var c2 = new Cuid(CuidString); + Assert.True(c1 == c2); + } - Assert.True(c1.Equals(c2)); - Assert.True(c1.Equals((object) c2)); + [Fact] + public void Cuid_Equals() + { + var c1 = new Cuid(CuidString); + var c2 = new Cuid(CuidString); - Assert.True(c1.CompareTo(null) > 0); - Assert.True(c1.CompareTo(c2) == 0); - Assert.True(c1.CompareTo((object) c2) == 0); + Assert.True(c1.Equals(c2)); + Assert.True(c1.Equals((object) c2)); - Assert.True(c1 >= c2); - Assert.True(c1 <= c2); + Assert.True(c1.CompareTo(null) > 0); + Assert.Equal(0, c1.CompareTo(c2)); + Assert.Equal(0, c1.CompareTo((object) c2)); - Assert.True(c1.GetHashCode() == c2.GetHashCode()); - } + Assert.True(c1 >= c2); + Assert.True(c1 <= c2); - [Fact] - public void Cuid_GreaterThan() - { - var c1 = new Cuid("clbqylg5v000108mn7kmn0t1e"); - var c2 = new Cuid("clbqylg5v000208mn7kmn0t1e"); + Assert.True(c1.GetHashCode() == c2.GetHashCode()); + } - Assert.True(c2 > c1); - } + [Fact] + public void Cuid_GreaterThan() + { + var c1 = new Cuid("clbqylg5v000108mn7kmn0t1e"); + var c2 = new Cuid("clbqylg5v000208mn7kmn0t1e"); - [Fact] - public void Cuid_Inequality() - { - var c1 = new Cuid(); - var c2 = new Cuid(CuidString); + Assert.True(c2 > c1); + } - Assert.False(c1 == c2); - } + [Fact] + public void Cuid_Inequality() + { + var c1 = new Cuid(); + var c2 = new Cuid(CuidString); - [Fact] - public void Cuid_Json_Deserialize() - { - var result = JsonSerializer.Deserialize($"\"{CuidString}\""); + Assert.False(c1 == c2); + } - Assert.Equal(CuidString, result.ToString()); - } + [Fact] + public void Cuid_Json_Deserialize() + { + var result = JsonSerializer.Deserialize($"\"{CuidString}\""); - [Fact] - public void Cuid_Json_Serialize() - { - var cuid = new Cuid(CuidString); + Assert.Equal(CuidString, result.ToString()); + } - var result = JsonSerializer.Serialize(cuid); + [Fact] + public void Cuid_Json_Serialize() + { + var cuid = new Cuid(CuidString); - Assert.Equal($"\"{CuidString}\"", result); - } + var result = JsonSerializer.Serialize(cuid); - [Fact] - public void Cuid_LessThan() - { - var c1 = new Cuid("clbqylg5v000108mn7kmn0t1e"); - var c2 = new Cuid("clbqylg5v000208mn7kmn0t1e"); + Assert.Equal($"\"{CuidString}\"", result); + } - Assert.True(c1 < c2); - } + [Fact] + public void Cuid_LessThan() + { + var c1 = new Cuid("clbqylg5v000108mn7kmn0t1e"); + var c2 = new Cuid("clbqylg5v000208mn7kmn0t1e"); - [Fact] - public void Cuid_NewCuid() - { - var cuid = Cuid.NewCuid(); + Assert.True(c1 < c2); + } - var cuidString = cuid.ToString(); + [Fact] + public void Cuid_NewCuid() + { + var cuid = Cuid.NewCuid(); - var result = cuidString.Length == 25 - && cuidString.All(char.IsLetterOrDigit) - && cuid != Cuid.Empty; + var cuidString = cuid.ToString(); - Assert.True(result); - } + var result = cuidString.Length == 25 + && cuidString.All(char.IsLetterOrDigit) + && cuid != Cuid.Empty; - [Fact] - public void Cuid_NotEquals() - { - var c1 = new Cuid(); - var c2 = new Cuid(CuidString); + Assert.True(result); + } - Assert.False(c1.Equals(c2)); - Assert.False(c1.Equals((object) c2)); + [Fact] + public void Cuid_NotEquals() + { + var c1 = new Cuid(); + var c2 = new Cuid(CuidString); - Assert.False(c1.CompareTo(c2) == 0); - Assert.False(c1.CompareTo((object) c2) == 0); + Assert.False(c1.Equals(c2)); + Assert.False(c1.Equals((object) c2)); - Assert.False(c1.GetHashCode() == c2.GetHashCode()); - } + Assert.False(c1.CompareTo(c2) == 0); + Assert.False(c1.CompareTo((object) c2) == 0); - [Fact] - public void Cuid_Parse() - { - var cuid = Cuid.Parse(CuidString); + Assert.False(c1.GetHashCode() == c2.GetHashCode()); + } - Assert.Equal(CuidString, cuid.ToString()); - } + [Fact] + public void Cuid_Parse() + { + var cuid = Cuid.Parse(CuidString); - [Fact] - public void Cuid_Parse_ThrowsFormatException() - { - Assert.Throws(() => Cuid.Parse(InvalidCuidString)); - } + Assert.Equal(CuidString, cuid.ToString()); + } - [Fact] - public void Cuid_TryParse_Null_ReturnsFalse() - { - var result = Cuid.TryParse(null, out _); + [Fact] + public void Cuid_Parse_ThrowsFormatException() + { + Assert.Throws(() => Cuid.Parse(InvalidCuidString)); + } - Assert.False(result); - } + [Fact] + public void Cuid_TryParse_Null_ReturnsFalse() + { +#if NET6_0_OR_GREATER + var result = Cuid.TryParse(null, out _); +#else + var result = Cuid.TryParse((string) null, out _); +#endif - [Fact] - public void Cuid_TryParse_ReturnsFalse() - { - var result = Cuid.TryParse(InvalidCuidString, out var cuid); + Assert.False(result); + } - Assert.False(result); - Assert.Equal(cuid, Cuid.Empty); - } + [Fact] + public void Cuid_TryParse_ReturnsFalse() + { + var result = Cuid.TryParse(InvalidCuidString, out var cuid); - [Fact] - public void Cuid_TryParse_ReturnsTrue() - { - var result = Cuid.TryParse(CuidString, out var cuid); + Assert.False(result); + Assert.Equal(cuid, Cuid.Empty); + } - Assert.True(result); - Assert.Equal(CuidString, cuid.ToString()); - } + [Fact] + public void Cuid_TryParse_ReturnsTrue() + { + var result = Cuid.TryParse(CuidString, out var cuid); - [Fact] - public void Cuid_Uniqueness() - { - var cuids = new HashSet(); + Assert.True(result); + Assert.Equal(CuidString, cuid.ToString()); + } - for ( var i = 0; i < 1000000; i++ ) + [Fact] + public void Cuid_Uniqueness() { - var cuid = Cuid.NewCuid(); - if ( cuids.Contains(cuid) ) + var cuids = new HashSet(); + + for ( var i = 0; i < 1000000; i++ ) { - Assert.Fail($"Collision detected at iteration {i}"); - } + var cuid = Cuid.NewCuid(); + if ( cuids.Contains(cuid) ) + { + Assert.Fail($"Collision detected at iteration {i}"); + } - cuids.Add(cuid); + cuids.Add(cuid); + } } - } - [Fact] - public void Cuid_Xml_Deserialize() - { - const string xml = "clbqylg5v000108mn7kmn0t1e"; + [Fact] + public void Cuid_Xml_Deserialize() + { + const string xml = "clbqylg5v000108mn7kmn0t1e"; - var expected = new Cuid(CuidString); + var expected = new Cuid(CuidString); - var serializer = new XmlSerializer(typeof(Cuid)); + var serializer = new XmlSerializer(typeof(Cuid)); - using var stringReader = new StringReader(xml); - using var xmlReader = XmlReader.Create(stringReader); +#if NET6_0_OR_GREATER + using var stringReader = new StringReader(xml); + using var xmlReader = XmlReader.Create(stringReader); - var cuid = serializer.Deserialize(xmlReader); + var cuid = serializer.Deserialize(xmlReader); - Assert.Equal(expected, cuid); - } + Assert.Equal(expected, cuid); +#else + using ( var stringReader = new StringReader(xml) ) + { + using ( var xmlReader = XmlReader.Create(stringReader) ) + { + var cuid = serializer.Deserialize(xmlReader); - [Fact] - public void Cuid_Xml_Serialize() - { - const string expected = "clbqylg5v000108mn7kmn0t1e"; - var cuid = new Cuid(CuidString); + Assert.Equal(expected, cuid); + } + } +#endif + } - var serializer = new XmlSerializer(typeof(Cuid)); - var settings = new XmlWriterSettings + [Fact] + public void Cuid_Xml_Serialize() { - Indent = false, - Encoding = new UnicodeEncoding(false, false) - }; + const string expected = "clbqylg5v000108mn7kmn0t1e"; + var cuid = new Cuid(CuidString); - using var stringWriter = new StringWriter(); - using var xmlWriter = XmlWriter.Create(stringWriter, settings); + var serializer = new XmlSerializer(typeof(Cuid)); + var settings = new XmlWriterSettings + { + Indent = false, + Encoding = new UnicodeEncoding(false, false) + }; - serializer.Serialize(xmlWriter, cuid); +#if NET6_0_OR_GREATER + using var stringWriter = new StringWriter(); + using var xmlWriter = XmlWriter.Create(stringWriter, settings); - Assert.Equal(expected, stringWriter.ToString()); + serializer.Serialize(xmlWriter, cuid); + + Assert.Equal(expected, stringWriter.ToString()); +#else + using ( var stringWriter = new StringWriter() ) + { + using ( var xmlWriter = XmlWriter.Create(stringWriter, settings) ) + { + serializer.Serialize(xmlWriter, cuid); + + Assert.Equal(expected, stringWriter.ToString()); + } + } +#endif + } } } diff --git a/tests/cuid.net.tests/Usings.cs b/tests/cuid.net.tests/Usings.cs deleted file mode 100644 index c802f44..0000000 --- a/tests/cuid.net.tests/Usings.cs +++ /dev/null @@ -1 +0,0 @@ -global using Xunit; diff --git a/tests/cuid.net.tests/cuid.net.tests.csproj b/tests/cuid.net.tests/cuid.net.tests.csproj index 8009bf4..af8a90b 100644 --- a/tests/cuid.net.tests/cuid.net.tests.csproj +++ b/tests/cuid.net.tests/cuid.net.tests.csproj @@ -1,8 +1,7 @@ - net8.0 - + net472;net6.0;net8.0 false Visus.Cuid.Tests Library @@ -12,16 +11,17 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + + - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/tests/cuid.net.tests/packages.lock.json b/tests/cuid.net.tests/packages.lock.json deleted file mode 100644 index 1e56ea8..0000000 --- a/tests/cuid.net.tests/packages.lock.json +++ /dev/null @@ -1,223 +0,0 @@ -{ - "version": 2, - "dependencies": { - "net8.0": { - "coverlet.collector": { - "type": "Direct", - "requested": "[6.0.2, )", - "resolved": "6.0.2", - "contentHash": "bJShQ6uWRTQ100ZeyiMqcFlhP7WJ+bCuabUs885dJiBEzMsJMSFr7BOyeCw4rgvQokteGi5rKQTlkhfQPUXg2A==" - }, - "coverlet.msbuild": { - "type": "Direct", - "requested": "[6.0.2, )", - "resolved": "6.0.2", - "contentHash": "8b4jBNH7mcQy1otyQErjjIUuGD74XxKZ1wvDufbY7jhWwckl7wIa+icjwdPYeI0aYMS4Tp63LIZvyMFjWwOMDw==" - }, - "Microsoft.CodeAnalysis.NetAnalyzers": { - "type": "Direct", - "requested": "[8.0.0, )", - "resolved": "8.0.0", - "contentHash": "DxiTgkCl3CGq1rYmBX2wjY7XGbxiBdL4J+/AJIAFLKy5z70NxhnVRnPghnicXZ8oF6JKVXlW3xwznRbI3ioEKg==" - }, - "Microsoft.NET.Test.Sdk": { - "type": "Direct", - "requested": "[17.9.0, )", - "resolved": "17.9.0", - "contentHash": "7GUNAUbJYn644jzwLm5BD3a2p9C1dmP8Hr6fDPDxgItQk9hBs1Svdxzz07KQ/UphMSmgza9AbijBJGmw5D658A==", - "dependencies": { - "Microsoft.CodeCoverage": "17.9.0", - "Microsoft.TestPlatform.TestHost": "17.9.0" - } - }, - "PublicApiGenerator": { - "type": "Direct", - "requested": "[11.1.0, )", - "resolved": "11.1.0", - "contentHash": "iHU9N149uYirtVxGZh8dKIGv1CpecHD+5E2NVx1c490LqGyWCORZ52DpVqgjNs1rGluT1LNasRNBBjARssGcIg==", - "dependencies": { - "Mono.Cecil": "0.11.5", - "System.CodeDom": "8.0.0" - } - }, - "Verify.Xunit": { - "type": "Direct", - "requested": "[24.2.0, )", - "resolved": "24.2.0", - "contentHash": "qyN2PWmhog5185n0xFzRIM4Is9Dgukq24Vdph3t+Df/g1rVeB0hMzb0HFWk3HvDRh3fuvTJN6orNnJYBXnFN1w==", - "dependencies": { - "Verify": "24.2.0", - "xunit.abstractions": "2.0.3", - "xunit.extensibility.execution": "2.8.0" - } - }, - "xunit": { - "type": "Direct", - "requested": "[2.8.0, )", - "resolved": "2.8.0", - "contentHash": "US3a3twJziAif1kFPGdk9fALwILHxV0n1roX5j67bN/d3o4DGNLHnV3tr5ZX+uinVrzfkf0avH3zGX8JPBC0qA==", - "dependencies": { - "xunit.analyzers": "1.13.0", - "xunit.assert": "2.8.0", - "xunit.core": "[2.8.0]" - } - }, - "xunit.runner.visualstudio": { - "type": "Direct", - "requested": "[2.8.0, )", - "resolved": "2.8.0", - "contentHash": "mqQbS2zr8dfgSWxkNOC6UTzO8JoqpTmM5+FFn2XR/2nVmx2JvEY0YbM5pt2FmXVg9YVe+jKUPHd6KrroyCl67w==" - }, - "Argon": { - "type": "Transitive", - "resolved": "0.17.0", - "contentHash": "TC/5P8sb2AKeC6dLAEx7ex1sr2IiQOI0tZ8yV6J3QT8qp7bUfP/blmwrgZ/zIab84XQ+XScT1zUeok96iZ7ReQ==" - }, - "DiffEngine": { - "type": "Transitive", - "resolved": "15.4.0", - "contentHash": "2UTLer/h9u6zKTEXuHmgG7lQPjUiOP7YuIWKY5XP25v7uCDQmJXQnyaNbW6g4JzG5gyMhgbulVYPyqaglwP1zw==", - "dependencies": { - "EmptyFiles": "8.2.0", - "System.Management": "8.0.0" - } - }, - "EmptyFiles": { - "type": "Transitive", - "resolved": "8.2.0", - "contentHash": "LnZLh5IPCc0zDjVWnta9pwLR9UxH0Ki08iru+2BRZ2tMigZDSlQQ9u3GnEjaDemaSCJwKLQyf3lsnVnbVldQ/g==" - }, - "libsodium": { - "type": "Transitive", - "resolved": "1.0.19", - "contentHash": "tupm/HViwBN6Knd/gckR+cLaJGR39GLmiU4iDMM5hp/1BoczMr8fwJhSU+3/C2V4hi9nBK/4FICRKtTLU30TCA==" - }, - "Microsoft.CodeCoverage": { - "type": "Transitive", - "resolved": "17.9.0", - "contentHash": "RGD37ZSrratfScYXm7M0HjvxMxZyWZL4jm+XgMZbkIY1UPgjUpbNA/t+WTGj/rC/0Hm9A3IrH3ywbKZkOCnoZA==" - }, - "Microsoft.TestPlatform.ObjectModel": { - "type": "Transitive", - "resolved": "17.9.0", - "contentHash": "1ilw/8vgmjLyKU+2SKXKXaOqpYFJCQfGqGz+x0cosl981VzjrY74Sv6qAJv+neZMZ9ZMxF3ArN6kotaQ4uvEBw==", - "dependencies": { - "System.Reflection.Metadata": "1.6.0" - } - }, - "Microsoft.TestPlatform.TestHost": { - "type": "Transitive", - "resolved": "17.9.0", - "contentHash": "Spmg7Wx49Ya3SxBjyeAR+nQpjMTKZwTwpZ7KyeOTIqI/WHNPnBU4HUvl5kuHPQAwGWqMy4FGZja1HvEwvoaDiA==", - "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.9.0", - "Newtonsoft.Json": "13.0.1" - } - }, - "Mono.Cecil": { - "type": "Transitive", - "resolved": "0.11.5", - "contentHash": "fxfX+0JGTZ8YQeu1MYjbBiK2CYTSzDyEeIixt+yqKKTn7FW8rv7JMY70qevup4ZJfD7Kk/VG/jDzQQTpfch87g==" - }, - "Newtonsoft.Json": { - "type": "Transitive", - "resolved": "13.0.1", - "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" - }, - "SimpleInfoName": { - "type": "Transitive", - "resolved": "2.2.0", - "contentHash": "jZwJajqdF56n0HcwvM8wqrwhr8tBqp7E3dWVoa5XWMC4tqSXP4+rIfYipeVIavRUI5MSj5vYPCdwtF3h4RJvxA==" - }, - "System.CodeDom": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "WTlRjL6KWIMr/pAaq3rYqh0TJlzpouaQ/W1eelssHgtlwHAH25jXTkUphTYx9HaIIf7XA6qs/0+YhtLEQRkJ+Q==" - }, - "System.IO.Hashing": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "ne1843evDugl0md7Fjzy6QjJrzsjh46ZKbhf8GwBXb5f/gw97J4bxMs0NQKifDuThh/f0bZ0e62NPl1jzTuRqA==" - }, - "System.Management": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "jrK22i5LRzxZCfGb+tGmke2VH7oE0DvcDlJ1HAKYU8cPmD8XnpUT0bYn2Gy98GEhGjtfbR/sxKTVb+dE770pfA==", - "dependencies": { - "System.CodeDom": "8.0.0" - } - }, - "System.Reflection.Metadata": { - "type": "Transitive", - "resolved": "1.6.0", - "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==" - }, - "Verify": { - "type": "Transitive", - "resolved": "24.2.0", - "contentHash": "PlWBOlByTPNp9cEc22TZ6I04yfbqICKDMJOw1k+6eWomVl1qSOhbc8QITBkoNspOsSb4oyrkYusjd3DspFerPg==", - "dependencies": { - "Argon": "0.17.0", - "DiffEngine": "15.4.0", - "SimpleInfoName": "2.2.0", - "System.IO.Hashing": "8.0.0" - } - }, - "xunit.abstractions": { - "type": "Transitive", - "resolved": "2.0.3", - "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg==" - }, - "xunit.analyzers": { - "type": "Transitive", - "resolved": "1.13.0", - "contentHash": "Pai9YnDV71/Ox14nBHB6/f62iyPyLbmUG/YYMiA4dfdFZvr0gIYE9yGxSr0i5Tr3INK75wgL2MCUNEKpeiZ2Fw==" - }, - "xunit.assert": { - "type": "Transitive", - "resolved": "2.8.0", - "contentHash": "lwf7Dy5/5HbDkaPx1YrGXCByytCEEcIn+KPI74jh2BD/RU/7RhO8c+S3k0Ph+Mr7+cLf338fl+o6UcgPCLa6PA==" - }, - "xunit.core": { - "type": "Transitive", - "resolved": "2.8.0", - "contentHash": "McSTFGTETCxLpmJKE9TWi9FtFthrRbpRrjz2V2g8sK2wRt1+JHs15vwi+B+nfftFkV9aFWIXZfzZM95TIGZNIA==", - "dependencies": { - "xunit.extensibility.core": "[2.8.0]", - "xunit.extensibility.execution": "[2.8.0]" - } - }, - "xunit.extensibility.core": { - "type": "Transitive", - "resolved": "2.8.0", - "contentHash": "eBJv9xQeY0p5z+C/L1tFjUFYqtl5pQqIEYCGTMl+MbRzA7sOlgYKwJE//vEePBp+mgBh7NjD0Qhz0liZBYM27w==", - "dependencies": { - "xunit.abstractions": "2.0.3" - } - }, - "xunit.extensibility.execution": { - "type": "Transitive", - "resolved": "2.8.0", - "contentHash": "TyyrZesHB9ODZMS9c73OqiBz4x0vL944JCkSPBWW5w6PF2LlUfdfXRjjOhoIOuY6lTmEgl07rS4/Jot9mCYnpg==", - "dependencies": { - "xunit.extensibility.core": "[2.8.0]" - } - }, - "cuid.net": { - "type": "Project", - "dependencies": { - "NSec.Cryptography": "[24.4.0, )" - } - }, - "NSec.Cryptography": { - "type": "CentralTransitive", - "requested": "[24.4.0, )", - "resolved": "24.4.0", - "contentHash": "R89OF0T5OY9QnPRoTsMCaPKMqtXcWKZSWAO4zyUaFHI+ZRMgXU7WaIwg/rPP3vOfdaLl8HdUHWkDB+2B9FOO7g==", - "dependencies": { - "libsodium": "[1.0.19, 1.0.20)" - } - } - } - } -} \ No newline at end of file