// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. #if NETSTANDARD2_1_OR_GREATER using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace CommunityToolkit.HighPerformance; /// /// A that can store an optional readonly reference to a value of a specified type. /// /// The type of value to reference. public readonly ref struct NullableReadOnlyRef { /// /// The 1-length instance used to track the target value. /// private readonly ReadOnlySpan span; /// /// Initializes a new instance of the struct. /// /// The readonly reference to the target value. [MethodImpl(MethodImplOptions.AggressiveInlining)] public NullableReadOnlyRef(in T value) { ref T r0 = ref Unsafe.AsRef(value); this.span = MemoryMarshal.CreateReadOnlySpan(ref r0, 1); } /// /// Initializes a new instance of the struct. /// /// The instance to track the target reference. [MethodImpl(MethodImplOptions.AggressiveInlining)] private NullableReadOnlyRef(ReadOnlySpan span) { this.span = span; } /// /// Gets a instance representing a reference. /// public static NullableReadOnlyRef Null { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => default; } /// /// Gets a value indicating whether or not the current instance wraps a valid reference that can be accessed. /// public unsafe bool HasValue { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { // See comment in NullableRef about this byte length = unchecked((byte)this.span.Length); return *(bool*)&length; } } /// /// Gets the reference represented by the current instance. /// /// Thrown if is . public ref readonly T Value { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { if (!HasValue) { ThrowInvalidOperationException(); } return ref MemoryMarshal.GetReference(this.span); } } /// /// Implicitly converts a instance into a one. /// /// The input instance. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator NullableReadOnlyRef(Ref reference) { return new(reference.Span); } /// /// Implicitly converts a instance into a one. /// /// The input instance. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator NullableReadOnlyRef(ReadOnlyRef reference) { return new(reference.Span); } /// /// Implicitly converts a instance into a one. /// /// The input instance. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator NullableReadOnlyRef(NullableRef reference) { return new(reference.Span); } /// /// Explicitly gets the value from a given instance. /// /// The input instance. /// Thrown if is . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator T(NullableReadOnlyRef reference) { return reference.Value; } /// /// Throws a when trying to access for a default instance. /// private static void ThrowInvalidOperationException() { throw new InvalidOperationException("The current instance doesn't have a value that can be accessed."); } } #endif