// 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