// 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.Buffers; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using CommunityToolkit.HighPerformance.Buffers.Internals.Interfaces; using RuntimeHelpers = CommunityToolkit.HighPerformance.Helpers.Internals.RuntimeHelpers; namespace CommunityToolkit.HighPerformance.Buffers.Internals; /// /// A custom that casts data from a to values. /// /// The target type to cast the source characters to. internal sealed class StringMemoryManager : MemoryManager, IMemoryManager where TTo : unmanaged { /// /// The source to read data from. /// private readonly string text; /// /// The starting offset within . /// private readonly int offset; /// /// The original used length for . /// private readonly int length; /// /// Initializes a new instance of the class. /// /// The source to read data from. /// The starting offset within . /// The original used length for . public StringMemoryManager(string text, int offset, int length) { this.text = text; this.offset = offset; this.length = length; } /// public override Span GetSpan() { #if NETSTANDARD2_1_OR_GREATER ref char r0 = ref this.text.DangerousGetReferenceAt(this.offset); ref TTo r1 = ref Unsafe.As(ref r0); int length = RuntimeHelpers.ConvertLength(this.length); return MemoryMarshal.CreateSpan(ref r1, length); #else ReadOnlyMemory memory = this.text.AsMemory(this.offset, this.length); Span span = MemoryMarshal.AsMemory(memory).Span; return MemoryMarshal.Cast(span); #endif } /// public override unsafe MemoryHandle Pin(int elementIndex = 0) { if ((uint)elementIndex >= (uint)(this.length * Unsafe.SizeOf() / Unsafe.SizeOf())) { ThrowArgumentOutOfRangeExceptionForInvalidIndex(); } int bytePrefix = this.offset * Unsafe.SizeOf(); int byteSuffix = elementIndex * Unsafe.SizeOf(); int byteOffset = bytePrefix + byteSuffix; GCHandle handle = GCHandle.Alloc(this.text, GCHandleType.Pinned); ref char r0 = ref this.text.DangerousGetReference(); ref byte r1 = ref Unsafe.As(ref r0); ref byte r2 = ref Unsafe.Add(ref r1, byteOffset); void* pi = Unsafe.AsPointer(ref r2); return new MemoryHandle(pi, handle); } /// public override void Unpin() { } /// protected override void Dispose(bool disposing) { } /// public Memory GetMemory(int offset, int length) where T : unmanaged { int absoluteOffset = this.offset + RuntimeHelpers.ConvertLength(offset); int absoluteLength = RuntimeHelpers.ConvertLength(length); if (typeof(T) == typeof(char)) { ReadOnlyMemory memory = this.text.AsMemory(absoluteOffset, absoluteLength); return (Memory)(object)MemoryMarshal.AsMemory(memory); } return new StringMemoryManager(this.text, absoluteOffset, absoluteLength).Memory; } /// /// Throws an when the target index for is invalid. /// private static void ThrowArgumentOutOfRangeExceptionForInvalidIndex() { throw new ArgumentOutOfRangeException("elementIndex", "The input index is not in the valid range"); } }