Skip to content

Commit 9503e1d

Browse files
authored
Update from_buffer_copy to accept an IBufferProtocol (#1405)
* Update from_buffer_copy to accept an IBufferProtocol * Update after review * Fix _pack_
1 parent ddf635e commit 9503e1d

7 files changed

Lines changed: 71 additions & 62 deletions

File tree

Src/IronPython.Modules/_ctypes/ArrayType.cs

Lines changed: 9 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
using IronPython.Runtime.Operations;
1616
using IronPython.Runtime.Types;
1717

18+
using Microsoft.Scripting.Runtime;
1819
using Microsoft.Scripting.Utils;
1920

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

112-
public _Array from_buffer_copy(CodeContext/*!*/ context, ArrayModule.array array, [DefaultParameterValue(0)] int offset) {
113-
ValidateArraySizes(array, offset, ((INativeType)this).Size);
114-
115-
_Array res = (_Array)CreateInstance(context);
116-
res.MemHolder = new MemoryHolder(((INativeType)this).Size);
117-
res.MemHolder.CopyFrom(array.GetArrayAddress().Add(offset), new IntPtr(((INativeType)this).Size));
118-
GC.KeepAlive(array);
119-
return res;
120-
}
121-
122-
public _Array from_buffer_copy(CodeContext/*!*/ context, Bytes array, [DefaultParameterValue(0)] int offset) {
123-
ValidateArraySizes(array, offset, ((INativeType)this).Size);
113+
public _Array from_buffer_copy(CodeContext/*!*/ context, [NotNull] IBufferProtocol data, int offset = 0) {
114+
using var buffer = data.GetBuffer();
115+
var span = buffer.AsReadOnlySpan();
116+
var size = ((INativeType)this).Size;
117+
ValidateArraySizes(span.Length, offset, size);
118+
span = span.Slice(offset, size);
124119

125120
_Array res = (_Array)CreateInstance(context);
126-
res.MemHolder = new MemoryHolder(((INativeType)this).Size);
127-
for (int i = 0; i < ((INativeType)this).Size; i++) {
128-
res.MemHolder.WriteByte(i, ((IList<byte>)array)[i]);
129-
}
130-
return res;
131-
}
132-
133-
public _Array from_buffer_copy(CodeContext/*!*/ context, string data, int offset = 0) {
134-
ValidateArraySizes(data, offset, ((INativeType)this).Size);
135-
136-
_Array res = (_Array)CreateInstance(context);
137-
res.MemHolder = new MemoryHolder(((INativeType)this).Size);
138-
for (int i = 0; i < ((INativeType)this).Size; i++) {
139-
res.MemHolder.WriteByte(i, (byte)data[i]);
140-
}
121+
res.MemHolder = new MemoryHolder(size);
122+
res.MemHolder.WriteSpan(0, span);
141123
return res;
142124
}
143125

Src/IronPython.Modules/_ctypes/MemoryHolder.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
using System.Runtime.CompilerServices;
1111
using System.Runtime.ConstrainedExecution;
1212
using System.Runtime.InteropServices;
13-
using System.Text;
1413
using System.Threading;
1514

1615
using IronPython.Runtime;
@@ -282,10 +281,13 @@ internal void WriteUnicodeString(int offset, string value) {
282281
WriteInt16(checked(offset + i * 2), (short)value[i]);
283282
}
284283
}
284+
285285
internal void WriteSpan(int offset, ReadOnlySpan<byte> value) {
286-
for (int i = 0; i < value.Length; i++) {
287-
WriteByte(checked(offset + i), value[i]);
286+
unsafe {
287+
var dest = new Span<byte>((void*)_data, _size).Slice(offset);
288+
value.CopyTo(dest);
288289
}
290+
GC.KeepAlive(this);
289291
}
290292

291293
public MemoryHolder GetSubBlock(int offset) {

Src/IronPython.Modules/_ctypes/SimpleType.cs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -141,13 +141,16 @@ public SimpleCData from_buffer(ArrayModule.array array, int offset = 0) {
141141
return res;
142142
}
143143

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

147-
SimpleCData res = (SimpleCData)CreateInstance(Context.SharedContext);
148-
res.MemHolder = new MemoryHolder(((INativeType)this).Size);
149-
res.MemHolder.CopyFrom(array.GetArrayAddress().Add(offset), new IntPtr(((INativeType)this).Size));
150-
GC.KeepAlive(array);
151+
SimpleCData res = (SimpleCData)CreateInstance(context);
152+
res.MemHolder = new MemoryHolder(size);
153+
res.MemHolder.WriteSpan(0, span);
151154
return res;
152155
}
153156

Src/IronPython.Modules/_ctypes/StructType.cs

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,15 @@ public StructType(CodeContext/*!*/ context, string name, PythonTuple bases, Pyth
5050
}
5151

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

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

102-
public _Structure from_buffer_copy(CodeContext/*!*/ context, ArrayModule.array array, int offset = 0) {
103-
ValidateArraySizes(array, offset, ((INativeType)this).Size);
107+
public _Structure from_buffer_copy(CodeContext/*!*/ context, [NotNull] IBufferProtocol data, int offset = 0) {
108+
using var buffer = data.GetBuffer();
109+
var span = buffer.AsReadOnlySpan();
110+
var size = ((INativeType)this).Size;
111+
ValidateArraySizes(span.Length, offset, size);
112+
span = span.Slice(offset, size);
104113

105-
_Structure res = (_Structure)CreateInstance(Context.SharedContext);
106-
res.MemHolder = new MemoryHolder(((INativeType)this).Size);
107-
res.MemHolder.CopyFrom(array.GetArrayAddress().Add(offset), new IntPtr(((INativeType)this).Size));
108-
GC.KeepAlive(array);
114+
_Structure res = (_Structure)CreateInstance(context);
115+
res.MemHolder = new MemoryHolder(size);
116+
res.MemHolder.WriteSpan(0, span);
109117
return res;
110118
}
111119

Src/IronPython.Modules/_ctypes/_ctypes.cs

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -683,27 +683,11 @@ private static void ValidateArraySizes(ArrayModule.array array, int offset, int
683683
ValidateArraySizes(array.__len__() * array.itemsize, offset, size);
684684
}
685685

686-
private static void ValidateArraySizes(Bytes bytes, int offset, int size) {
687-
ValidateArraySizes(bytes.Count, offset, size);
688-
}
689-
690-
private static void ValidateArraySizes(string data, int offset, int size) {
691-
ValidateArraySizes(data.Length, offset, size);
692-
}
693-
694686
private static void ValidateArraySizes(int arraySize, int offset, int size) {
695687
if (offset < 0) {
696688
throw PythonOps.ValueError("offset cannot be negative");
697689
} else if (arraySize < size + offset) {
698-
throw PythonOps.ValueError($"Buffer size too small ({arraySize} instead of at least {size} bytes)");
699-
}
700-
}
701-
702-
private static void ValidateArraySizes(BigInteger arraySize, int offset, int size) {
703-
if (offset < 0) {
704-
throw PythonOps.ValueError("offset cannot be negative");
705-
} else if (arraySize < size + offset) {
706-
throw PythonOps.ValueError($"Buffer size too small ({arraySize} instead of at least {size} bytes)");
690+
throw PythonOps.ValueError($"Buffer size too small ({arraySize} instead of at least {offset + size} bytes)");
707691
}
708692
}
709693

Src/IronPythonTest/Cases/CPythonCasesManifest.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Ignore=true # blocked by https://github.com/IronLanguages/ironpython3/issues/129
1717
Ignore=true
1818
Reason=Current implementation of get_last_error needs to be debugged
1919

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

2323
[CPython.ctypes.test_functions]
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Licensed to the .NET Foundation under one or more agreements.
2+
# The .NET Foundation licenses this file to you under the Apache 2.0 License.
3+
# See the LICENSE file in the project root for more information.
4+
5+
##
6+
## Run selected tests from ctypes.test.test_frombuffer from StdLib
7+
##
8+
9+
import unittest
10+
import sys
11+
12+
from iptest import run_test
13+
14+
import ctypes.test.test_frombuffer
15+
16+
def load_tests(loader, standard_tests, pattern):
17+
if sys.implementation.name == 'ironpython':
18+
suite = unittest.TestSuite()
19+
suite.addTest(ctypes.test.test_frombuffer.Test('test_fortran_contiguous'))
20+
suite.addTest(unittest.expectedFailure(ctypes.test.test_frombuffer.Test('test_from_buffer')))
21+
suite.addTest(ctypes.test.test_frombuffer.Test('test_from_buffer_copy'))
22+
suite.addTest(ctypes.test.test_frombuffer.Test('test_from_buffer_copy_with_offset'))
23+
suite.addTest(unittest.expectedFailure(ctypes.test.test_frombuffer.Test('test_from_buffer_memoryview')))
24+
suite.addTest(ctypes.test.test_frombuffer.Test('test_from_buffer_with_offset'))
25+
return suite
26+
27+
else:
28+
return loader.loadTestsFromModule(ctypes.test.test_frombuffer, pattern)
29+
30+
run_test(__name__)

0 commit comments

Comments
 (0)