diff --git a/src/core/IronPython/Runtime/Binding/ConversionBinder.cs b/src/core/IronPython/Runtime/Binding/ConversionBinder.cs index 61349b7aa..77b3d0b5b 100644 --- a/src/core/IronPython/Runtime/Binding/ConversionBinder.cs +++ b/src/core/IronPython/Runtime/Binding/ConversionBinder.cs @@ -734,7 +734,7 @@ private DynamicMetaObject ConvertFromMemoryToBufferProtocol(DynamicMetaObject se return new DynamicMetaObject( AstUtils.Convert( Ast.New( - typeof(MemoryBufferProtocolWrapper).GetConstructor(new Type[] { fromType }), + typeof(MemoryBufferProtocolWrapper).GetConstructor([fromType]), AstUtils.Convert(self.Expression, fromType) ), typeof(IBufferProtocol) diff --git a/src/core/IronPython/Runtime/ConversionWrappers.cs b/src/core/IronPython/Runtime/ConversionWrappers.cs index 819e3f66e..f6fa6aaf4 100644 --- a/src/core/IronPython/Runtime/ConversionWrappers.cs +++ b/src/core/IronPython/Runtime/ConversionWrappers.cs @@ -10,6 +10,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Numerics; +using System.Runtime.InteropServices; namespace IronPython.Runtime { @@ -376,79 +377,90 @@ IEnumerator IEnumerable.GetEnumerator() { #endregion } - public sealed class MemoryBufferWrapper : IPythonBuffer { - private readonly ReadOnlyMemory _rom; - private readonly Memory? _memory; - private readonly BufferFlags _flags; + public sealed class MemoryBufferProtocolWrapper : IBufferProtocol where T : unmanaged { + private readonly ReadOnlyMemory _rom; + private readonly Memory? _memory; + private readonly char _format; - public MemoryBufferWrapper(ReadOnlyMemory memory, BufferFlags flags) { + public MemoryBufferProtocolWrapper(ReadOnlyMemory memory) { _rom = memory; _memory = null; - _flags = flags; + _format = GetFormatChar(); } - public MemoryBufferWrapper(Memory memory, BufferFlags flags) { + public MemoryBufferProtocolWrapper(Memory memory) { _rom = memory; _memory = memory; - _flags = flags; + _format = GetFormatChar(); } - public void Dispose() { } - - public object Object => _memory ?? _rom; - - public bool IsReadOnly => !_memory.HasValue; + public IPythonBuffer? GetBuffer(BufferFlags flags, bool throwOnError) { + if (flags.HasFlag(BufferFlags.Writable) && !_memory.HasValue) { + if (throwOnError) { + throw Operations.PythonOps.BufferError("ReadOnlyMemory is not writable."); + } + return null; + } - public ReadOnlySpan AsReadOnlySpan() => _rom.Span; + return new MemoryBufferWrapper(this, flags); + } + + private static char GetFormatChar() + => Type.GetTypeCode(typeof(T)) switch { + TypeCode.SByte => 'b', + TypeCode.Byte => 'B', + TypeCode.Char => 'u', + TypeCode.Int16 => 'h', + TypeCode.UInt16 => 'H', + TypeCode.Int32 => 'i', + TypeCode.UInt32 => 'I', + TypeCode.Int64 => 'q', + TypeCode.UInt64 => 'Q', + TypeCode.Single => 'f', + TypeCode.Double => 'd', + _ => throw new ArgumentException("Unsupported type"), + }; + + + private sealed unsafe class MemoryBufferWrapper : IPythonBuffer { + private readonly MemoryBufferProtocolWrapper _wrapper; + private readonly BufferFlags _flags; + + public MemoryBufferWrapper(MemoryBufferProtocolWrapper wrapper, BufferFlags flags) { + _wrapper = wrapper; + _flags = flags; + } - public Span AsSpan() => _memory.HasValue ? _memory.Value.Span : throw new InvalidOperationException("ReadOnlyMemory is not writable"); + public void Dispose() { } - public MemoryHandle Pin() => _rom.Pin(); + public object Object => _wrapper._memory ?? _wrapper._rom; - public int Offset => 0; + public bool IsReadOnly => !_wrapper._memory.HasValue; - public string? Format => _flags.HasFlag(BufferFlags.Format) ? "B" : null; + public ReadOnlySpan AsReadOnlySpan() => MemoryMarshal.Cast(_wrapper._rom.Span); - public int ItemCount => _rom.Length; + public Span AsSpan() + => _wrapper._memory.HasValue + ? MemoryMarshal.Cast(_wrapper._memory.Value.Span) + : throw new InvalidOperationException("ReadOnlyMemory is not writable"); - public int ItemSize => 1; + public MemoryHandle Pin() => _wrapper._rom.Pin(); - public int NumOfDims => 1; + public int Offset => 0; - public IReadOnlyList? Shape => null; + public string? Format => _flags.HasFlag(BufferFlags.Format) ? _wrapper._format.ToString() : null; - public IReadOnlyList? Strides => null; + public int ItemCount => _wrapper._rom.Length; - public IReadOnlyList? SubOffsets => null; - } + public int ItemSize => sizeof(T); - public class MemoryBufferProtocolWrapper : IBufferProtocol { - private readonly ReadOnlyMemory _rom; - private readonly Memory? _memory; + public int NumOfDims => 1; - public MemoryBufferProtocolWrapper(ReadOnlyMemory memory) { - _rom = memory; - _memory = null; - } + public IReadOnlyList? Shape => null; - public MemoryBufferProtocolWrapper(Memory memory) { - _rom = memory; - _memory = memory; - } - - public IPythonBuffer? GetBuffer(BufferFlags flags, bool throwOnError) { - if (_memory.HasValue) { - return new MemoryBufferWrapper(_memory.Value, flags); - } - - if (flags.HasFlag(BufferFlags.Writable)) { - if (throwOnError) { - throw Operations.PythonOps.BufferError("ReadOnlyMemory is not writable."); - } - return null; - } + public IReadOnlyList? Strides => null; - return new MemoryBufferWrapper(_rom, flags); + public IReadOnlyList? SubOffsets => null; } } }