mirror of
https://github.com/CommunityToolkit/dotnet.git
synced 2026-10-25 09:29:59 +08:00
* Added support for .NET Core 2.1 and 3.1 targets * Fixed T[] dangerous accessors on .NET Standard * Minor speed improvements to Array2DColumnEnumerable<T> * Updated 2D array extensions * Minor code tweaks * Added DangerousGetLookupReferenceAt<T> extension * Added BitHelper.HasLookupFlag APIs * Added BitHelper.ExtractRange and SetRange APIs * Added bool bitwise mask extensions * Added StreamExtensions class on .NET Standard 2.0 * Added IBufferWriterExtensions type * Added more Stream extensions * Added object field offset extensions * Minor code refactoring * Improved description * Switched managed count to IntPtr * Refactored GetDjb2HashCode code, minor tweaks * Unit tests fixes * Refactored Count extensions, minor tweaks * Fixed incorrect operators precedence * Added SpanHelper.GetDjb2LikeByteHash method * Minor code refactoring * Code refactoring to HashCode<T> * Minor style tweaks * Updated comments styles to follow style convention * Minor code tweaks to the Count method * More code tweaks to the Count method * Added tests for DangerousGetObjectDataByteOffset * Fixed visibility of new object extensions * Added tests for 64-bit extract/set range APIs * Added tests for bool to bitwise mask APIs * Fixed MemoryStream EOF exception type * Enabled Stream extension tests on UWP * Added StreamExtensions read/write value tests * Minor code refactoring to IBufferWriter<T> extensions * Added IBufferWriterExtensions tests * Added MemoryBufferWriter<T> type * Added IBuffer<T> interface * Added MemoryBufferWriterDebugView<T> type * Added MemoryBufferWriter<T> tests * Fixed a unit test * Fixed incorrect inlining attribute * Removed leftover partial modifier * Added missing readonly modifier for lock structs * Improved summary for the Box<T> type * Added comments for #pragma warning disable items * Added info on (x << 5) + x over x * 33 optimization * Improved XML docs for BitHelper APIs * Fixed potentially misleading test method names * Added comment for MemoryOwner<T>.Dispose test * Fixed multi-target support for string extensions * Improved IMemoryOwner.AsStream tests * Added XML remarks to the HashCode<T> type * Improved docs for ReadOnlySpanExtensions.GetDjb2HashCode<T> * Tweaked an XML link * Tweaked attributes for SpanHelper methods * Minor optimizations to SpanHelper.Count * Fixed an overflow bug in SpanHelper.Count * Speed improvements to SpanHelper.Count ~35% speedup with large collections of byte-size values, ~10% with other types * Updated .csproj description * Added float/double support to SpanHelper.Count * Added exception to the Box<T> constructor * Removed unnecessary [SuppressMessage] attributes * Removed [SuppressMessage] attributes * Changed NullableRef<T>-s exceptions to follow Nullable<T> * Added more operators to NullableRef<T> types * Added missing in modifiers in ParallelHelper.For overloads * Removed SIMD support for float/double count It lacked proper handling of NaN values and both (signed) zeros * Fixed compiler directives in SpanEnumerable<T> * Updated ReadOnlySpanEnumerable<T> type * Updated NuGet package tags * Added HighPerformance package to readme.md list * Added .NET Standard 1.4 target * Added tests for implicit NullableRef<T> operators * Removed unnecessary using directive * Added missing sealed modifiers to MemoryStream methods * Added Span<T>.IndexOf(ref T) extension * Added ReadOnlySpan<T>.IndexOf<T>(in T) extension * Added missing changes (incorrect commit) * Removed unnecessary workaround (method is inlined) * Added improved remarks for bool.ToBitwiseMask32/64 * Added internal NotNullWhen attribute when < .NET Standard 2.1 * Enabled .csproj-wide nullability annotations * Minor code style tweaks * Added [ReadOnly]NullableRef<T>.Null static properties
149 lines
7.3 KiB
C#
149 lines
7.3 KiB
C#
// 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.Diagnostics.Contracts;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Runtime.InteropServices;
|
|
|
|
namespace Microsoft.Toolkit.HighPerformance.Extensions
|
|
{
|
|
/// <summary>
|
|
/// Helpers for working with <see cref="object"/> instances.
|
|
/// </summary>
|
|
public static class ObjectExtensions
|
|
{
|
|
/// <summary>
|
|
/// Calculates the byte offset to a specific field within a given <see cref="object"/>.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of field being referenced.</typeparam>
|
|
/// <param name="obj">The input <see cref="object"/> hosting the target field.</param>
|
|
/// <param name="data">A reference to a target field of type <typeparamref name="T"/> within <paramref name="obj"/>.</param>
|
|
/// <returns>
|
|
/// The <see cref="IntPtr"/> value representing the offset to the target field from the start of the object data
|
|
/// for the parameter <paramref name="obj"/>. The offset is in relation to the first usable byte after the method table.
|
|
/// </returns>
|
|
/// <remarks>The input parameters are not validated, and it's responsability of the caller to ensure that
|
|
/// the <paramref name="data"/> reference is actually pointing to a memory location within <paramref name="obj"/>.
|
|
/// </remarks>
|
|
[Pure]
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static IntPtr DangerousGetObjectDataByteOffset<T>(this object obj, ref T data)
|
|
{
|
|
var rawObj = Unsafe.As<RawObjectData>(obj);
|
|
ref byte r0 = ref rawObj.Data;
|
|
ref byte r1 = ref Unsafe.As<T, byte>(ref data);
|
|
|
|
return Unsafe.ByteOffset(ref r0, ref r1);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a <typeparamref name="T"/> reference to data within a given <see cref="object"/> at a specified offset.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of reference to retrieve.</typeparam>
|
|
/// <param name="obj">The input <see cref="object"/> hosting the target field.</param>
|
|
/// <param name="offset">The input byte offset for the <typeparamref name="T"/> reference to retrieve.</param>
|
|
/// <returns>A <typeparamref name="T"/> reference at a specified offset within <paramref name="obj"/>.</returns>
|
|
/// <remarks>
|
|
/// None of the input arguments is validated, and it is responsability of the caller to ensure they are valid.
|
|
/// In particular, using an invalid offset might cause the retrieved reference to be misaligned with the
|
|
/// desired data, which would break the type system. Or, if the offset causes the retrieved reference to point
|
|
/// to a memory location outside of the input <see cref="object"/> instance, that might lead to runtime crashes.
|
|
/// </remarks>
|
|
[Pure]
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static ref T DangerousGetObjectDataReferenceAt<T>(this object obj, IntPtr offset)
|
|
{
|
|
var rawObj = Unsafe.As<RawObjectData>(obj);
|
|
ref byte r0 = ref rawObj.Data;
|
|
ref byte r1 = ref Unsafe.AddByteOffset(ref r0, offset);
|
|
ref T r2 = ref Unsafe.As<byte, T>(ref r1);
|
|
|
|
return ref r2;
|
|
}
|
|
|
|
// Description adapted from CoreCLR, see:
|
|
// https://source.dot.net/#System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs,301.
|
|
// CLR objects are laid out in memory as follows:
|
|
// [ sync block || pMethodTable || raw data .. ]
|
|
// ^ ^
|
|
// | \-- ref Unsafe.As<RawObjectData>(owner).Data
|
|
// \-- object
|
|
// The reference to RawObjectData.Data points to the first data byte in the
|
|
// target object, skipping over the sync block, method table and string length.
|
|
// Even though the description above links to the CoreCLR source, this approach
|
|
// can actually work on any .NET runtime, as it doesn't rely on a specific memory
|
|
// layout. Even if some 3rd party .NET runtime had some additional fields in the
|
|
// object header, before the field being referenced, the returned offset would still
|
|
// be valid when used on instances of that particular type, as it's only being
|
|
// used as a relative offset from the location pointed by the object reference.
|
|
[StructLayout(LayoutKind.Explicit)]
|
|
private sealed class RawObjectData
|
|
{
|
|
[FieldOffset(0)]
|
|
#pragma warning disable SA1401 // Fields should be private
|
|
public byte Data;
|
|
#pragma warning restore SA1401
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tries to get a boxed <typeparamref name="T"/> value from an input <see cref="object"/> instance.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of value to try to unbox.</typeparam>
|
|
/// <param name="obj">The input <see cref="object"/> instance to check.</param>
|
|
/// <param name="value">The resulting <typeparamref name="T"/> value, if <paramref name="obj"/> was in fact a boxed <typeparamref name="T"/> value.</param>
|
|
/// <returns><see langword="true"/> if a <typeparamref name="T"/> value was retrieved correctly, <see langword="false"/> otherwise.</returns>
|
|
/// <remarks>
|
|
/// This extension behaves just like the following method:
|
|
/// <code>
|
|
/// public static bool TryUnbox<T>(this object obj, out T value)
|
|
/// {
|
|
/// if (obj is T)
|
|
/// {
|
|
/// value = (T)obj;
|
|
///
|
|
/// return true;
|
|
/// }
|
|
///
|
|
/// value = default;
|
|
///
|
|
/// return false;
|
|
/// }
|
|
/// </code>
|
|
/// But in a more efficient way, and with the ability to also assign the unboxed value
|
|
/// directly on an existing T variable, which is not possible with the code above.
|
|
/// </remarks>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static bool TryUnbox<T>(this object obj, out T value)
|
|
where T : struct
|
|
{
|
|
if (obj.GetType() == typeof(T))
|
|
{
|
|
value = Unsafe.Unbox<T>(obj);
|
|
|
|
return true;
|
|
}
|
|
|
|
value = default;
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Unboxes a <typeparamref name="T"/> value from an input <see cref="object"/> instance.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of value to unbox.</typeparam>
|
|
/// <param name="obj">The input <see cref="object"/> instance, representing a boxed <typeparamref name="T"/> value.</param>
|
|
/// <returns>The <typeparamref name="T"/> value boxed in <paramref name="obj"/>.</returns>
|
|
/// <exception cref="InvalidCastException">Thrown when <paramref name="obj"/> is not of type <typeparamref name="T"/>.</exception>
|
|
[Pure]
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static ref T DangerousUnbox<T>(this object obj)
|
|
where T : struct
|
|
{
|
|
return ref Unsafe.Unbox<T>(obj);
|
|
}
|
|
}
|
|
}
|