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
36 changes: 9 additions & 27 deletions Src/IronPython.Modules/_ctypes/ArrayType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
using IronPython.Runtime.Operations;
using IronPython.Runtime.Types;

using Microsoft.Scripting.Runtime;
using Microsoft.Scripting.Utils;

namespace IronPython.Modules {
Expand Down Expand Up @@ -109,35 +110,16 @@ public _Array from_buffer(CodeContext/*!*/ context, ArrayModule.array array, [De
return res;
}

public _Array from_buffer_copy(CodeContext/*!*/ context, ArrayModule.array array, [DefaultParameterValue(0)] int offset) {
ValidateArraySizes(array, offset, ((INativeType)this).Size);

_Array res = (_Array)CreateInstance(context);
res.MemHolder = new MemoryHolder(((INativeType)this).Size);
res.MemHolder.CopyFrom(array.GetArrayAddress().Add(offset), new IntPtr(((INativeType)this).Size));
GC.KeepAlive(array);
return res;
}

public _Array from_buffer_copy(CodeContext/*!*/ context, Bytes array, [DefaultParameterValue(0)] int offset) {
ValidateArraySizes(array, offset, ((INativeType)this).Size);
public _Array from_buffer_copy(CodeContext/*!*/ context, [NotNull] IBufferProtocol data, int offset = 0) {
using var buffer = data.GetBuffer();
var span = buffer.AsReadOnlySpan();
var size = ((INativeType)this).Size;
ValidateArraySizes(span.Length, offset, size);
span = span.Slice(offset, size);

_Array res = (_Array)CreateInstance(context);
res.MemHolder = new MemoryHolder(((INativeType)this).Size);
for (int i = 0; i < ((INativeType)this).Size; i++) {
res.MemHolder.WriteByte(i, ((IList<byte>)array)[i]);
}
return res;
}

public _Array from_buffer_copy(CodeContext/*!*/ context, string data, int offset = 0) {
ValidateArraySizes(data, offset, ((INativeType)this).Size);

_Array res = (_Array)CreateInstance(context);
res.MemHolder = new MemoryHolder(((INativeType)this).Size);
for (int i = 0; i < ((INativeType)this).Size; i++) {
res.MemHolder.WriteByte(i, (byte)data[i]);
}
res.MemHolder = new MemoryHolder(size);
res.MemHolder.WriteSpan(0, span);
return res;
}

Expand Down
8 changes: 5 additions & 3 deletions Src/IronPython.Modules/_ctypes/MemoryHolder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
using System.Runtime.CompilerServices;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;

using IronPython.Runtime;
Expand Down Expand Up @@ -282,10 +281,13 @@ internal void WriteUnicodeString(int offset, string value) {
WriteInt16(checked(offset + i * 2), (short)value[i]);
}
}

internal void WriteSpan(int offset, ReadOnlySpan<byte> value) {
for (int i = 0; i < value.Length; i++) {
WriteByte(checked(offset + i), value[i]);
unsafe {
var dest = new Span<byte>((void*)_data, _size).Slice(offset);
value.CopyTo(dest);
}
GC.KeepAlive(this);
}

public MemoryHolder GetSubBlock(int offset) {
Expand Down
15 changes: 9 additions & 6 deletions Src/IronPython.Modules/_ctypes/SimpleType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,13 +141,16 @@ public SimpleCData from_buffer(ArrayModule.array array, int offset = 0) {
return res;
}

public SimpleCData from_buffer_copy(ArrayModule.array array, int offset = 0) {
ValidateArraySizes(array, offset, ((INativeType)this).Size);
public SimpleCData from_buffer_copy(CodeContext/*!*/ context, [NotNull] IBufferProtocol data, int offset = 0) {
using var buffer = data.GetBuffer();
var span = buffer.AsReadOnlySpan();
var size = ((INativeType)this).Size;
ValidateArraySizes(span.Length, offset, size);
span = span.Slice(offset, size);

SimpleCData res = (SimpleCData)CreateInstance(Context.SharedContext);
res.MemHolder = new MemoryHolder(((INativeType)this).Size);
res.MemHolder.CopyFrom(array.GetArrayAddress().Add(offset), new IntPtr(((INativeType)this).Size));
GC.KeepAlive(array);
SimpleCData res = (SimpleCData)CreateInstance(context);
res.MemHolder = new MemoryHolder(size);
res.MemHolder.WriteSpan(0, span);
return res;
}

Expand Down
24 changes: 16 additions & 8 deletions Src/IronPython.Modules/_ctypes/StructType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,15 @@ public StructType(CodeContext/*!*/ context, string name, PythonTuple bases, Pyth
}

if (members.TryGetValue("_pack_", out object pack)) {
if (!(pack is int) || ((int)pack < 0)) {
object index = PythonOps.Index(pack); // since 3.8
_pack = index switch {
int i => i,
BigInteger bi => (int)bi, // CPython throws the ValueError below on overflow
_ => throw new InvalidOperationException(),
};
if (_pack < 0) {
throw PythonOps.ValueError("pack must be a non-negative integer");
}
_pack = (int)pack;
}

if (members.TryGetValue("_fields_", out object fields)) {
Expand Down Expand Up @@ -99,13 +104,16 @@ public _Structure from_buffer(CodeContext/*!*/ context, ArrayModule.array array,
return res;
}

public _Structure from_buffer_copy(CodeContext/*!*/ context, ArrayModule.array array, int offset = 0) {
ValidateArraySizes(array, offset, ((INativeType)this).Size);
public _Structure from_buffer_copy(CodeContext/*!*/ context, [NotNull] IBufferProtocol data, int offset = 0) {
using var buffer = data.GetBuffer();
var span = buffer.AsReadOnlySpan();
var size = ((INativeType)this).Size;
ValidateArraySizes(span.Length, offset, size);
span = span.Slice(offset, size);

_Structure res = (_Structure)CreateInstance(Context.SharedContext);
res.MemHolder = new MemoryHolder(((INativeType)this).Size);
res.MemHolder.CopyFrom(array.GetArrayAddress().Add(offset), new IntPtr(((INativeType)this).Size));
GC.KeepAlive(array);
_Structure res = (_Structure)CreateInstance(context);
res.MemHolder = new MemoryHolder(size);
res.MemHolder.WriteSpan(0, span);
return res;
}

Expand Down
18 changes: 1 addition & 17 deletions Src/IronPython.Modules/_ctypes/_ctypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -683,27 +683,11 @@ private static void ValidateArraySizes(ArrayModule.array array, int offset, int
ValidateArraySizes(array.__len__() * array.itemsize, offset, size);
}

private static void ValidateArraySizes(Bytes bytes, int offset, int size) {
ValidateArraySizes(bytes.Count, offset, size);
}

private static void ValidateArraySizes(string data, int offset, int size) {
ValidateArraySizes(data.Length, offset, size);
}

private static void ValidateArraySizes(int arraySize, int offset, int size) {
if (offset < 0) {
throw PythonOps.ValueError("offset cannot be negative");
} else if (arraySize < size + offset) {
throw PythonOps.ValueError($"Buffer size too small ({arraySize} instead of at least {size} bytes)");
}
}

private static void ValidateArraySizes(BigInteger arraySize, int offset, int size) {
if (offset < 0) {
throw PythonOps.ValueError("offset cannot be negative");
} else if (arraySize < size + offset) {
throw PythonOps.ValueError($"Buffer size too small ({arraySize} instead of at least {size} bytes)");
throw PythonOps.ValueError($"Buffer size too small ({arraySize} instead of at least {offset + size} bytes)");
}
}

Expand Down
2 changes: 1 addition & 1 deletion Src/IronPythonTest/Cases/CPythonCasesManifest.ini
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Ignore=true # blocked by https://github.com/IronLanguages/ironpython3/issues/129
Ignore=true
Reason=Current implementation of get_last_error needs to be debugged

[CPython.ctypes.test_frombuffer]
[CPython.ctypes.test_frombuffer] # IronPython.test_ctypes_frombuffer_stdlib
Ignore=true # blocked by https://github.com/IronLanguages/ironpython3/issues/1297

[CPython.ctypes.test_functions]
Expand Down
30 changes: 30 additions & 0 deletions Tests/test_ctypes_frombuffer_stdlib.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Licensed to the .NET Foundation under one or more agreements.
# The .NET Foundation licenses this file to you under the Apache 2.0 License.
# See the LICENSE file in the project root for more information.

##
## Run selected tests from ctypes.test.test_frombuffer from StdLib
##

import unittest
import sys

from iptest import run_test

import ctypes.test.test_frombuffer

def load_tests(loader, standard_tests, pattern):
if sys.implementation.name == 'ironpython':
suite = unittest.TestSuite()
suite.addTest(ctypes.test.test_frombuffer.Test('test_fortran_contiguous'))
suite.addTest(unittest.expectedFailure(ctypes.test.test_frombuffer.Test('test_from_buffer')))
suite.addTest(ctypes.test.test_frombuffer.Test('test_from_buffer_copy'))
suite.addTest(ctypes.test.test_frombuffer.Test('test_from_buffer_copy_with_offset'))
suite.addTest(unittest.expectedFailure(ctypes.test.test_frombuffer.Test('test_from_buffer_memoryview')))
suite.addTest(ctypes.test.test_frombuffer.Test('test_from_buffer_with_offset'))
return suite

else:
return loader.loadTestsFromModule(ctypes.test.test_frombuffer, pattern)

run_test(__name__)