File: Passkeys\BufferSource.cs
Web Access
Project: src\src\Identity\Core\src\Microsoft.AspNetCore.Identity.csproj (Microsoft.AspNetCore.Identity)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
 
namespace Microsoft.AspNetCore.Identity;
 
/// <summary>
/// Represents a base64url-encoded byte buffer for use in passkey operations.
/// </summary>
/// <remarks>
/// This type is named after the JavaScript <c>BufferSource</c> type.
/// When included in a JSON payload, it is serialized as a base64url-encoded string.
/// When a member of type <c>BufferSource</c> is mentioned in the WebAuthn specification,
/// this type can be used to represent it in .NET.
/// </remarks>
[JsonConverter(typeof(BufferSourceJsonConverter))]
internal sealed class BufferSource : IEquatable<BufferSource>
{
    private readonly ReadOnlyMemory<byte> _bytes;
 
    /// <summary>
    /// Gets the length of the byte buffer.
    /// </summary>
    public int Length => _bytes.Length;
 
    /// <summary>
    /// Creates a new instance of <see cref="BufferSource"/> from a byte array.
    /// </summary>
    public static BufferSource FromBytes(ReadOnlyMemory<byte> bytes)
        => new(bytes);
 
    /// <summary>
    /// Creates a new instance of <see cref="BufferSource"/> from a string.
    /// </summary>
    public static BufferSource FromString(string value)
    {
        var buffer = Encoding.UTF8.GetBytes(value);
        return new(buffer);
    }
 
    private BufferSource(ReadOnlyMemory<byte> buffer)
    {
        _bytes = buffer;
    }
 
    /// <summary>
    /// Gets the byte buffer as a <see cref="ReadOnlyMemory{T}"/>.
    /// </summary>
    public ReadOnlyMemory<byte> AsMemory()
        => _bytes;
 
    /// <summary>
    /// Gets the byte buffer as a <see cref="ReadOnlySpan{T}"/>.
    /// </summary>
    public ReadOnlySpan<byte> AsSpan()
        => _bytes.Span;
 
    /// <summary>
    /// Gets the byte buffer as a byte array.
    /// </summary>
    public byte[] ToArray()
        => _bytes.ToArray();
 
    /// <summary>
    /// Performs a value-based equality comparison with another <see cref="BufferSource"/> instance.
    /// </summary>
    public bool Equals(BufferSource? other)
    {
        if (ReferenceEquals(this, other))
        {
            return true;
        }
 
        return other is not null && _bytes.Span.SequenceEqual(other._bytes.Span);
    }
 
    /// <inheritdoc/>
    public override bool Equals(object? obj)
        => obj is BufferSource other && Equals(other);
 
    /// <inheritdoc/>
    public override int GetHashCode()
        => _bytes.GetHashCode();
 
    /// <summary>
    /// Performs a value-based equality comparison between two <see cref="BufferSource"/> instances.
    /// </summary>
    public static bool operator ==(BufferSource? left, BufferSource? right)
    {
        if (ReferenceEquals(left, right))
        {
            return true;
        }
 
        if (left is null || right is null)
        {
            return false;
        }
 
        return left.Equals(right);
    }
 
    /// <summary>
    /// Performs a value-based inequality comparison between two <see cref="BufferSource"/> instances.
    /// </summary>
    public static bool operator !=(BufferSource? left, BufferSource? right)
        => !(left == right);
 
    /// <summary>
    /// Gets the UTF-8 string representation of the byte buffer.
    /// </summary>
    public override string ToString()
    {
        var span = _bytes.Span;
 
        if (span.IsEmpty)
        {
            return string.Empty;
        }
 
        return Encoding.UTF8.GetString(span);
    }
}