// 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; using System.Runtime.Versioning; namespace CommunityToolkit.HighPerformance; /// <summary> /// A <see langword="struct"/> that can store an optional readonly reference to a value of a specified type. /// </summary> /// <typeparam name="T">The type of value to reference.</typeparam> [RequiresPreviewFeatures( "The NullableReadOnlyRef<T> type has no compiler support to ensure the lifetime of referenced values is respected, and as such using it incorrectly may lead to GC holes.", Url = "https://github.com/dotnet/runtime/issues/46104")] public readonly ref struct NullableReadOnlyRef<T> { /// <summary> /// The 1-length <see cref="ReadOnlySpan{T}"/> instance used to track the target <typeparamref name="T"/> value. /// </summary> private readonly ReadOnlySpan<T> span; /// <summary> /// Initializes a new instance of the <see cref="NullableReadOnlyRef{T}"/> struct. /// </summary> /// <param name="value">The readonly reference to the target <typeparamref name="T"/> value.</param> [MethodImpl(MethodImplOptions.AggressiveInlining)] public NullableReadOnlyRef(in T value) { ref T r0 = ref Unsafe.AsRef(value); this.span = MemoryMarshal.CreateReadOnlySpan(ref r0, 1); } /// <summary> /// Initializes a new instance of the <see cref="NullableReadOnlyRef{T}"/> struct. /// </summary> /// <param name="span">The <see cref="ReadOnlySpan{T}"/> instance to track the target <typeparamref name="T"/> reference.</param> [MethodImpl(MethodImplOptions.AggressiveInlining)] private NullableReadOnlyRef(ReadOnlySpan<T> span) { this.span = span; } /// <summary> /// Gets a <see cref="NullableReadOnlyRef{T}"/> instance representing a <see langword="null"/> reference. /// </summary> public static NullableReadOnlyRef<T> Null { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => default; } /// <summary> /// Gets a value indicating whether or not the current <see cref="NullableReadOnlyRef{T}"/> instance wraps a valid reference that can be accessed. /// </summary> public unsafe bool HasValue { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { // See comment in NullableRef<T> about this byte length = unchecked((byte)this.span.Length); return *(bool*)&length; } } /// <summary> /// Gets the <typeparamref name="T"/> reference represented by the current <see cref="NullableReadOnlyRef{T}"/> instance. /// </summary> /// <exception cref="InvalidOperationException">Thrown if <see cref="HasValue"/> is <see langword="false"/>.</exception> public ref readonly T Value { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { if (!HasValue) { ThrowInvalidOperationException(); } return ref MemoryMarshal.GetReference(this.span); } } /// <summary> /// Implicitly converts a <see cref="Ref{T}"/> instance into a <see cref="NullableReadOnlyRef{T}"/> one. /// </summary> /// <param name="reference">The input <see cref="Ref{T}"/> instance.</param> [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator NullableReadOnlyRef<T>(Ref<T> reference) { return new(reference.Span); } /// <summary> /// Implicitly converts a <see cref="ReadOnlyRef{T}"/> instance into a <see cref="NullableReadOnlyRef{T}"/> one. /// </summary> /// <param name="reference">The input <see cref="ReadOnlyRef{T}"/> instance.</param> [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator NullableReadOnlyRef<T>(ReadOnlyRef<T> reference) { return new(reference.Span); } /// <summary> /// Implicitly converts a <see cref="NullableRef{T}"/> instance into a <see cref="NullableReadOnlyRef{T}"/> one. /// </summary> /// <param name="reference">The input <see cref="Ref{T}"/> instance.</param> [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator NullableReadOnlyRef<T>(NullableRef<T> reference) { return new(reference.Span); } /// <summary> /// Explicitly gets the <typeparamref name="T"/> value from a given <see cref="NullableReadOnlyRef{T}"/> instance. /// </summary> /// <param name="reference">The input <see cref="NullableReadOnlyRef{T}"/> instance.</param> /// <exception cref="InvalidOperationException">Thrown if <see cref="HasValue"/> is <see langword="false"/>.</exception> [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator T(NullableReadOnlyRef<T> reference) { return reference.Value; } /// <summary> /// Throws a <see cref="InvalidOperationException"/> when trying to access <see cref="Value"/> for a default instance. /// </summary> private static void ThrowInvalidOperationException() { throw new InvalidOperationException("The current instance doesn't have a value that can be accessed."); } } #endif