// 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.
using System;
using System.Runtime.CompilerServices;
#if NETSTANDARD2_1_OR_GREATER
using System.Runtime.InteropServices;
#else
using CommunityToolkit.HighPerformance.Helpers;
#endif
namespace CommunityToolkit.HighPerformance;
///
/// A that can store a readonly reference to a value of a specified type.
///
/// The type of value to reference.
public readonly ref struct ReadOnlyRef
{
#if NETSTANDARD2_1_OR_GREATER
///
/// The 1-length instance used to track the target value.
///
internal readonly ReadOnlySpan Span;
///
/// Initializes a new instance of the struct.
///
/// The readonly reference to the target value.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlyRef(in T value)
{
ref T r0 = ref Unsafe.AsRef(value);
Span = MemoryMarshal.CreateReadOnlySpan(ref r0, 1);
}
///
/// Initializes a new instance of the struct.
///
/// The pointer to the target value.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe ReadOnlyRef(void* pointer)
: this(in Unsafe.AsRef(pointer))
{
}
///
/// Gets the readonly reference represented by the current instance.
///
public ref readonly T Value
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref MemoryMarshal.GetReference(Span);
}
///
/// Implicitly converts a instance into a one.
///
/// The input instance.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator ReadOnlyRef(Ref reference)
{
return new(in reference.Value);
}
#else
///
/// The owner the current instance belongs to
///
private readonly object owner;
///
/// The target offset within the current instance is pointing to
///
private readonly IntPtr offset;
///
/// Initializes a new instance of the struct.
///
/// The owner to create a portable reference for.
/// The target offset within for the target reference.
/// The parameter is not validated, and it's responsibility of the caller to ensure it's valid.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private ReadOnlyRef(object owner, IntPtr offset)
{
this.owner = owner;
this.offset = offset;
}
///
/// Initializes a new instance of the struct.
///
/// The owner to create a portable reference for.
/// The target reference to point to (it must be within ).
/// The parameter is not validated, and it's responsibility of the caller to ensure it's valid.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlyRef(object owner, in T value)
{
this.owner = owner;
this.offset = ObjectMarshal.DangerousGetObjectDataByteOffset(owner, ref Unsafe.AsRef(value));
}
///
/// Gets the readonly reference represented by the current instance.
///
public ref readonly T Value
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref ObjectMarshal.DangerousGetObjectDataReferenceAt(this.owner, this.offset);
}
///
/// Implicitly converts a instance into a one.
///
/// The input instance.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator ReadOnlyRef(Ref reference)
{
return new(reference.Owner, reference.Offset);
}
#endif
///
/// Implicitly gets the value from a given instance.
///
/// The input instance.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator T(ReadOnlyRef reference)
{
return reference.Value;
}
}