// 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 reference to a value of a specified type. /// /// The type of value to reference. public readonly ref struct NullableRef { /// /// The 1-length instance used to track the target value. /// internal readonly Span Span; /// /// Initializes a new instance of the struct. /// /// The reference to the target value. [MethodImpl(MethodImplOptions.AggressiveInlining)] public NullableRef(ref T value) { Span = MemoryMarshal.CreateSpan(ref value, 1); } /// /// Initializes a new instance of the struct. /// /// The instance to track the target reference. [MethodImpl(MethodImplOptions.AggressiveInlining)] private NullableRef(Span span) { Span = span; } /// /// Gets a instance representing a reference. /// public static NullableRef 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 { // We know that the span will always have a length of either // 1 or 0, so instead of using a cmp instruction and setting the // zero flag to produce our boolean value, we can just cast // the length to byte without overflow checks (doing a cast will // also account for the byte endianness of the current system), // and then reinterpret that value to a bool flag. // This results in a single movzx instruction on x86-64. byte length = unchecked((byte)Span.Length); return *(bool*)&length; } } /// /// Gets the reference represented by the current instance. /// /// Thrown if is . public ref T Value { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { if (!HasValue) { ThrowInvalidOperationException(); } return ref MemoryMarshal.GetReference(Span); } } /// /// Implicitly converts a instance into a one. /// /// The input instance. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator NullableRef(Ref 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(NullableRef 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