Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/core/IronPython.Modules/_ctypes/CData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ public void Dispose() {

private int _numExports;

IPythonBuffer IBufferProtocol.GetBuffer(BufferFlags flags) {
IPythonBuffer IBufferProtocol.GetBuffer(BufferFlags flags, bool throwOnError) {
if (_disposed) throw new ObjectDisposedException(GetType().Name);
_ = MemHolder; // check if fully initialized
Interlocked.Increment(ref _numExports);
Expand Down
2 changes: 1 addition & 1 deletion src/core/IronPython.Modules/array.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1037,7 +1037,7 @@ bool ICollection<object>.Remove(object item) {

#region IBufferProtocol Members

IPythonBuffer IBufferProtocol.GetBuffer(BufferFlags flags) {
IPythonBuffer IBufferProtocol.GetBuffer(BufferFlags flags, bool throwOnError) {
return _data.GetBuffer(this, _typeCode.ToString(), flags);
}

Expand Down
11 changes: 7 additions & 4 deletions src/core/IronPython.Modules/mmap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1314,10 +1314,13 @@ void IWeakReferenceable.SetFinalizer(WeakRefTracker value) {

#endregion

public IPythonBuffer GetBuffer(BufferFlags flags = BufferFlags.Simple) {
if (flags.HasFlag(BufferFlags.Writable) && IsReadOnly)
throw PythonOps.BufferError("Object is not writable.");

public IPythonBuffer? GetBuffer(BufferFlags flags, bool throwOnError) {
if (flags.HasFlag(BufferFlags.Writable) && IsReadOnly) {
if (throwOnError) {
throw PythonOps.BufferError("Object is not writable.");
}
return null;
}
return new MmapBuffer(this, flags);
}

Expand Down
35 changes: 32 additions & 3 deletions src/core/IronPython/Runtime/BufferProtocol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;

using IronPython.Runtime.Exceptions;
Expand All @@ -17,7 +18,20 @@ namespace IronPython.Runtime {
/// Equivalent functionality of CPython's <a href="https://docs.python.org/3/c-api/buffer.html">Buffer Protocol</a>.
/// </summary>
public interface IBufferProtocol {
IPythonBuffer GetBuffer(BufferFlags flags = BufferFlags.Simple);
/// <summary>
/// Exports the object's data as a buffer.
/// </summary>
/// <param name="flags">
/// Flags specifying the type of buffer the consumer is prepared to deal with.
/// </param>
/// <param name="throwOnError">
/// This parameter is advisory: if true, the method may throw <c>BufferError</c> instead of returning null if the buffer type is not supported,
/// with the exception's message providing more information.
/// </param>
/// <returns>
/// An instance of <see cref="IPythonBuffer"/> if the requested buffer type is supported, or null if not.
/// </returns>
IPythonBuffer? GetBuffer(BufferFlags flags, bool throwOnError);
}

/// <summary>
Expand Down Expand Up @@ -119,16 +133,31 @@ public enum BufferFlags {
#endregion
}

internal static class BufferProtocolExtensions {

public static class BufferProtocolExtensions {
public static IPythonBuffer GetBuffer(this IBufferProtocol bufferProtocol, BufferFlags flags = BufferFlags.Simple)
=> bufferProtocol.GetBuffer(flags, throwOnError: true) ?? throw new BufferException("Buffer type not supported");

internal static IPythonBuffer? GetBufferNoThrow(this IBufferProtocol bufferProtocol, BufferFlags flags = BufferFlags.Simple) {
try {
return bufferProtocol.GetBuffer(flags);
return bufferProtocol.GetBuffer(flags, throwOnError: false);
} catch (BufferException) {
return null;
}
}

internal static bool TryGetBuffer(this IBufferProtocol bufferProtocol, BufferFlags flags, [NotNullWhen(true)] out IPythonBuffer? buffer) {
try {
buffer = bufferProtocol.GetBuffer(flags, throwOnError: false);
return buffer is not null;
} catch (BufferException) {
buffer = null;
return false;
}
}
}


/// <summary>
/// Provides low-level read-write access to byte data of the underlying object.
/// </summary>
Expand Down
2 changes: 1 addition & 1 deletion src/core/IronPython/Runtime/ByteArray.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1584,7 +1584,7 @@ private bool Equals(ReadOnlySpan<byte> other) {

#region IBufferProtocol Members

IPythonBuffer IBufferProtocol.GetBuffer(BufferFlags flags) {
IPythonBuffer IBufferProtocol.GetBuffer(BufferFlags flags, bool throwOnError) {
return _bytes.GetBuffer(this, "B", flags);
}

Expand Down
11 changes: 7 additions & 4 deletions src/core/IronPython/Runtime/Bytes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1241,10 +1241,13 @@ Expression IExpressionSerializable.CreateExpression() {

#region IBufferProtocol Support

IPythonBuffer IBufferProtocol.GetBuffer(BufferFlags flags) {
if (flags.HasFlag(BufferFlags.Writable))
throw PythonOps.BufferError("Object is not writable.");

IPythonBuffer? IBufferProtocol.GetBuffer(BufferFlags flags, bool throwOnError) {
if (flags.HasFlag(BufferFlags.Writable)) {
if (throwOnError) {
throw PythonOps.BufferError("bytes object is not writable");
}
return null;
}
return new BytesView(this, flags);
}

Expand Down
7 changes: 5 additions & 2 deletions src/core/IronPython/Runtime/ConversionWrappers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -436,13 +436,16 @@ public MemoryBufferProtocolWrapper(Memory<byte> memory) {
_memory = memory;
}

public IPythonBuffer GetBuffer(BufferFlags flags) {
public IPythonBuffer? GetBuffer(BufferFlags flags, bool throwOnError) {
if (_memory.HasValue) {
return new MemoryBufferWrapper(_memory.Value, flags);
}

if (flags.HasFlag(BufferFlags.Writable)) {
throw Operations.PythonOps.BufferError("ReadOnlyMemory is not writable.");
if (throwOnError) {
throw Operations.PythonOps.BufferError("ReadOnlyMemory is not writable.");
}
return null;
}

return new MemoryBufferWrapper(_rom, flags);
Expand Down
23 changes: 15 additions & 8 deletions src/core/IronPython/Runtime/MemoryView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -963,32 +963,39 @@ void IWeakReferenceable.SetFinalizer(WeakRefTracker value) {

#region IBufferProtocol Members

IPythonBuffer IBufferProtocol.GetBuffer(BufferFlags flags) {
IPythonBuffer? IBufferProtocol.GetBuffer(BufferFlags flags, bool throwOnError) {
CheckBuffer();

if (flags.HasFlag(BufferFlags.Writable) && _isReadOnly)
throw PythonOps.BufferError("memoryview: underlying buffer is not writable");
return ReportError("memoryview: underlying buffer is not writable");

if (flags.HasFlag(BufferFlags.CContiguous) && !_isCContig)
throw PythonOps.BufferError("memoryview: underlying buffer is not C-contiguous");
return ReportError("memoryview: underlying buffer is not C-contiguous");

if (flags.HasFlag(BufferFlags.FContiguous) && !_isFContig)
throw PythonOps.BufferError("memoryview: underlying buffer is not Fortran contiguous");
return ReportError("memoryview: underlying buffer is not Fortran contiguous");

if (flags.HasFlag(BufferFlags.AnyContiguous) && !_isCContig && !_isFContig)
throw PythonOps.BufferError("memoryview: underlying buffer is not contiguous");
return ReportError("memoryview: underlying buffer is not contiguous");

// TODO: Support for suboffsets
//if (!flags.HasFlag(!BufferFlags.Indirect) && _suboffsets != null)
// throw PythonOps.BufferError("memoryview: underlying buffer requires suboffsets");
// return ReportError("memoryview: underlying buffer requires suboffsets");

if (!flags.HasFlag(BufferFlags.Strides) && !_isCContig)
throw PythonOps.BufferError("memoryview: underlying buffer is not C-contiguous");
return ReportError("memoryview: underlying buffer is not C-contiguous");

if (!flags.HasFlag(BufferFlags.ND) && flags.HasFlag(BufferFlags.Format))
throw PythonOps.BufferError("memoryview: cannot cast to unsigned bytes if the format flag is present");
return ReportError("memoryview: cannot cast to unsigned bytes if the format flag is present");

return new MemoryView(this, flags);

IPythonBuffer? ReportError(string msg) {
if (throwOnError) {
throw PythonOps.BufferError(msg);
}
return null;
}
}

#endregion
Expand Down