Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 8e029ef

Browse files
koubaathesn10
andauthored
Pybuffer (#1195): add Python buffer api support
Co-authored-by: SnGmng <[email protected]>
1 parent f72a16d commit 8e029ef

9 files changed

+497
-0
lines changed

AUTHORS.md

+1
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
- ([@OneBlue](https://github.com/OneBlue))
7272
- ([@rico-chet](https://github.com/rico-chet))
7373
- ([@rmadsen-ks](https://github.com/rmadsen-ks))
74+
- ([@SnGmng](https://github.com/SnGmng))
7475
- ([@stonebig](https://github.com/stonebig))
7576
- ([@testrunner123](https://github.com/testrunner123))
7677
- ([@DanBarzilian](https://github.com/DanBarzilian))

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ This version improves performance on benchmarks significantly compared to 2.3.
3535
- Support for Python 3.8
3636
- Codecs as the designated way to handle automatic conversions between
3737
.NET and Python types
38+
- Added Python 3 buffer api support and PyBuffer interface for fast byte and numpy array read/write ([#980][p980])
3839

3940
### Changed
4041

src/embed_tests/Python.EmbeddingTest.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@
9797
<Compile Include="TestFinalizer.cs" />
9898
<Compile Include="TestInstanceWrapping.cs" />
9999
<Compile Include="TestPyAnsiString.cs" />
100+
<Compile Include="TestPyBuffer.cs" />
100101
<Compile Include="TestPyFloat.cs" />
101102
<Compile Include="TestPyInt.cs" />
102103
<Compile Include="TestPyList.cs" />

src/embed_tests/TestPyBuffer.cs

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
using System.Text;
2+
using NUnit.Framework;
3+
using Python.Runtime;
4+
5+
namespace Python.EmbeddingTest {
6+
class TestPyBuffer
7+
{
8+
[OneTimeSetUp]
9+
public void SetUp()
10+
{
11+
PythonEngine.Initialize();
12+
}
13+
14+
[OneTimeTearDown]
15+
public void Dispose()
16+
{
17+
PythonEngine.Shutdown();
18+
}
19+
20+
[Test]
21+
public void TestBufferWrite()
22+
{
23+
string bufferTestString = "hello world! !$%&/()=?";
24+
25+
using (Py.GIL())
26+
{
27+
using (var scope = Py.CreateScope())
28+
{
29+
scope.Exec($"arr = bytearray({bufferTestString.Length})");
30+
PyObject pythonArray = scope.Get("arr");
31+
byte[] managedArray = new UTF8Encoding().GetBytes(bufferTestString);
32+
33+
using (PyBuffer buf = pythonArray.GetBuffer())
34+
{
35+
buf.Write(managedArray, 0, managedArray.Length);
36+
}
37+
38+
string result = scope.Eval("arr.decode('utf-8')").ToString();
39+
Assert.IsTrue(result == bufferTestString);
40+
}
41+
}
42+
}
43+
44+
[Test]
45+
public void TestBufferRead()
46+
{
47+
string bufferTestString = "hello world! !$%&/()=?";
48+
49+
using (Py.GIL())
50+
{
51+
using (var scope = Py.CreateScope())
52+
{
53+
scope.Exec($"arr = b'{bufferTestString}'");
54+
PyObject pythonArray = scope.Get("arr");
55+
byte[] managedArray = new byte[bufferTestString.Length];
56+
57+
using (PyBuffer buf = pythonArray.GetBuffer())
58+
{
59+
buf.Read(managedArray, 0, managedArray.Length);
60+
}
61+
62+
string result = new UTF8Encoding().GetString(managedArray);
63+
Assert.IsTrue(result == bufferTestString);
64+
}
65+
}
66+
}
67+
}
68+
}

src/runtime/Python.Runtime.csproj

+2
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
<Compile Include="arrayobject.cs" />
9090
<Compile Include="assemblymanager.cs" />
9191
<Compile Include="BorrowedReference.cs" />
92+
<Compile Include="bufferinterface.cs" />
9293
<Compile Include="classderived.cs" />
9394
<Compile Include="classbase.cs" />
9495
<Compile Include="classmanager.cs" />
@@ -130,6 +131,7 @@
130131
<Compile Include="overload.cs" />
131132
<Compile Include="propertyobject.cs" />
132133
<Compile Include="pyansistring.cs" />
134+
<Compile Include="pybuffer.cs" />
133135
<Compile Include="pydict.cs" />
134136
<Compile Include="PyExportAttribute.cs" />
135137
<Compile Include="pyfloat.cs" />

src/runtime/bufferinterface.cs

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
using System;
2+
using System.Runtime.InteropServices;
3+
4+
namespace Python.Runtime
5+
{
6+
/* buffer interface */
7+
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
8+
internal struct Py_buffer {
9+
public IntPtr buf;
10+
public IntPtr obj; /* owned reference */
11+
[MarshalAs(UnmanagedType.SysInt)]
12+
public IntPtr len;
13+
[MarshalAs(UnmanagedType.SysInt)]
14+
public IntPtr itemsize; /* This is Py_ssize_t so it can be
15+
pointed to by strides in simple case.*/
16+
[MarshalAs(UnmanagedType.Bool)]
17+
public bool _readonly;
18+
public int ndim;
19+
[MarshalAs(UnmanagedType.LPStr)]
20+
public string format;
21+
public IntPtr shape;
22+
public IntPtr strides;
23+
public IntPtr suboffsets;
24+
public IntPtr _internal;
25+
}
26+
27+
public enum BufferOrderStyle
28+
{
29+
C,
30+
Fortran,
31+
EitherOne,
32+
}
33+
34+
/* Flags for getting buffers */
35+
public enum PyBUF
36+
{
37+
/// <summary>
38+
/// Simple buffer without shape strides and suboffsets
39+
/// </summary>
40+
SIMPLE = 0,
41+
/// <summary>
42+
/// Controls the <see cref="PyBuffer.ReadOnly"/> field. If set, the exporter MUST provide a writable buffer or else report failure. Otherwise, the exporter MAY provide either a read-only or writable buffer, but the choice MUST be consistent for all consumers.
43+
/// </summary>
44+
WRITABLE = 0x0001,
45+
/// <summary>
46+
/// Controls the <see cref="PyBuffer.Format"/> field. If set, this field MUST be filled in correctly. Otherwise, this field MUST be NULL.
47+
/// </summary>
48+
FORMATS = 0x0004,
49+
/// <summary>
50+
/// N-Dimensional buffer with shape
51+
/// </summary>
52+
ND = 0x0008,
53+
/// <summary>
54+
/// Buffer with strides and shape
55+
/// </summary>
56+
STRIDES = (0x0010 | ND),
57+
/// <summary>
58+
/// C-Contigous buffer with strides and shape
59+
/// </summary>
60+
C_CONTIGUOUS = (0x0020 | STRIDES),
61+
/// <summary>
62+
/// F-Contigous buffer with strides and shape
63+
/// </summary>
64+
F_CONTIGUOUS = (0x0040 | STRIDES),
65+
/// <summary>
66+
/// C or Fortran contigous buffer with strides and shape
67+
/// </summary>
68+
ANY_CONTIGUOUS = (0x0080 | STRIDES),
69+
/// <summary>
70+
/// Buffer with suboffsets (if needed)
71+
/// </summary>
72+
INDIRECT = (0x0100 | STRIDES),
73+
/// <summary>
74+
/// Writable C-Contigous buffer with shape
75+
/// </summary>
76+
CONTIG = (ND | WRITABLE),
77+
/// <summary>
78+
/// Readonly C-Contigous buffer with shape
79+
/// </summary>
80+
CONTIG_RO = (ND),
81+
/// <summary>
82+
/// Writable buffer with shape and strides
83+
/// </summary>
84+
STRIDED = (STRIDES | WRITABLE),
85+
/// <summary>
86+
/// Readonly buffer with shape and strides
87+
/// </summary>
88+
STRIDED_RO = (STRIDES),
89+
/// <summary>
90+
/// Writable buffer with shape, strides and format
91+
/// </summary>
92+
RECORDS = (STRIDES | WRITABLE | FORMATS),
93+
/// <summary>
94+
/// Readonly buffer with shape, strides and format
95+
/// </summary>
96+
RECORDS_RO = (STRIDES | FORMATS),
97+
/// <summary>
98+
/// Writable indirect buffer with shape, strides, format and suboffsets (if needed)
99+
/// </summary>
100+
FULL = (INDIRECT | WRITABLE | FORMATS),
101+
/// <summary>
102+
/// Readonly indirect buffer with shape, strides, format and suboffsets (if needed)
103+
/// </summary>
104+
FULL_RO = (INDIRECT | FORMATS),
105+
}
106+
}

0 commit comments

Comments
 (0)