// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; using System.Buffers; using System.Collections.Generic; using System.Numerics; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Tests.Memory { internal class TestMemoryAllocator : MemoryAllocator { private readonly List allocationLog = new List(); private readonly List returnLog = new List(); public TestMemoryAllocator(byte dirtyValue = 42) { this.DirtyValue = dirtyValue; } /// /// Gets the value to initialize the result buffer with, with non-clean options () /// public byte DirtyValue { get; } public int BufferCapacityInBytes { get; set; } = int.MaxValue; public IReadOnlyList AllocationLog => this.allocationLog; public IReadOnlyList ReturnLog => this.returnLog; protected internal override int GetBufferCapacityInBytes() => this.BufferCapacityInBytes; public override IMemoryOwner Allocate(int length, AllocationOptions options = AllocationOptions.None) { T[] array = this.AllocateArray(length, options); return new BasicArrayBuffer(array, length, this); } public override IManagedByteBuffer AllocateManagedByteBuffer(int length, AllocationOptions options = AllocationOptions.None) { byte[] array = this.AllocateArray(length, options); return new ManagedByteBuffer(array, this); } private T[] AllocateArray(int length, AllocationOptions options) where T : struct { var array = new T[length + 42]; this.allocationLog.Add(AllocationRequest.Create(options, length, array)); if (options == AllocationOptions.None) { Span data = MemoryMarshal.Cast(array.AsSpan()); data.Fill(this.DirtyValue); } return array; } private void Return(BasicArrayBuffer buffer) where T : struct { this.returnLog.Add(new ReturnRequest(buffer.Array.GetHashCode())); } public struct AllocationRequest { private AllocationRequest(Type elementType, AllocationOptions allocationOptions, int length, int lengthInBytes, int hashCodeOfBuffer) { this.ElementType = elementType; this.AllocationOptions = allocationOptions; this.Length = length; this.LengthInBytes = lengthInBytes; this.HashCodeOfBuffer = hashCodeOfBuffer; if (elementType == typeof(Vector4)) { } } public static AllocationRequest Create(AllocationOptions allocationOptions, int length, T[] buffer) { Type type = typeof(T); int elementSize = Marshal.SizeOf(type); return new AllocationRequest(type, allocationOptions, length, length * elementSize, buffer.GetHashCode()); } public Type ElementType { get; } public AllocationOptions AllocationOptions { get; } public int Length { get; } public int LengthInBytes { get; } public int HashCodeOfBuffer { get; } } public struct ReturnRequest { public ReturnRequest(int hashCodeOfBuffer) { this.HashCodeOfBuffer = hashCodeOfBuffer; } public int HashCodeOfBuffer { get; } } /// /// Wraps an array as an instance. /// private class BasicArrayBuffer : MemoryManager where T : struct { private readonly TestMemoryAllocator allocator; private GCHandle pinHandle; public BasicArrayBuffer(T[] array, int length, TestMemoryAllocator allocator) { this.allocator = allocator; DebugGuard.MustBeLessThanOrEqualTo(length, array.Length, nameof(length)); this.Array = array; this.Length = length; } public BasicArrayBuffer(T[] array, TestMemoryAllocator allocator) : this(array, array.Length, allocator) { } /// /// Gets the array. /// public T[] Array { get; } /// /// Gets the length. /// public int Length { get; } /// public override Span GetSpan() => this.Array.AsSpan(0, this.Length); public override unsafe MemoryHandle Pin(int elementIndex = 0) { if (!this.pinHandle.IsAllocated) { this.pinHandle = GCHandle.Alloc(this.Array, GCHandleType.Pinned); } void* ptr = (void*)this.pinHandle.AddrOfPinnedObject(); return new MemoryHandle(ptr, this.pinHandle); } public override void Unpin() { throw new NotImplementedException(); } /// protected override void Dispose(bool disposing) { if (disposing) { this.allocator.Return(this); } } } private class ManagedByteBuffer : BasicArrayBuffer, IManagedByteBuffer { public ManagedByteBuffer(byte[] array, TestMemoryAllocator allocator) : base(array, allocator) { } } } }