BufferProtocolBuilder[T]¶
struct BufferProtocolBuilder[self_type: ImplicitlyDestructible]
Wraps a PythonTypeBuilder reference and installs CPython buffer protocol slots (bf_getbuffer / bf_releasebuffer), enabling zero-copy access from memoryview, numpy.frombuffer, bytes(), and any other buffer consumer.
Only 1-D C-contiguous buffers are supported.
Constructor¶
def __init__(out self, mut inner: PythonTypeBuilder)
Takes a mutable reference to the PythonTypeBuilder returned by b.add_type[T](...). Both the module builder and the protocol builder must remain alive for the duration of the PyInit_* function — which is naturally satisfied when used within a single function body.
Methods¶
def_getbuffer[method]()¶
def def_getbuffer[
method: fn(UnsafePointer[T, MutAnyOrigin], Int32) raises -> BufferInfo
](mut self) -> ref[self] Self
Installs bf_getbuffer. Called by Python whenever a consumer requests a buffer view (e.g. memoryview(obj), numpy.frombuffer(obj), bytes(obj)).
| Parameter | Description |
|---|---|
method |
Static method fn(self_ptr, flags) raises -> BufferInfo |
The flags argument is the bitmask passed by the consumer:
| Flag constant | Value | Meaning |
|---|---|---|
PyBUF_WRITABLE |
0x0001 |
Consumer needs write access |
PyBUF_FORMAT |
0x0004 |
Consumer needs the format string |
PyBUF_ND |
0x0008 |
Consumer needs shape |
PyBUF_STRIDES |
0x0018 |
Consumer needs strides |
Raise any Error from method to propagate a Python BufferError to the consumer.
See: https://docs.python.org/3/c-api/typeobj.html#c.PyBufferProcs.bf_getbuffer
def_releasebuffer()¶
def def_releasebuffer(mut self) -> ref[self] Self
Installs the default bf_releasebuffer implementation, which frees the shape/strides/format block that def_getbuffer allocates per view.
Always call def_releasebuffer() after def_getbuffer().
See: https://docs.python.org/3/c-api/typeobj.html#c.PyBufferProcs.bf_releasebuffer
BufferInfo¶
struct BufferInfo
User-friendly buffer descriptor returned by the bf_getbuffer handler.
| Field | Type | Description |
|---|---|---|
buf |
UnsafePointer[UInt8, MutAnyOrigin] |
Pointer to the first byte of data |
nitems |
Int |
Number of elements |
itemsize |
Int |
Bytes per element |
format |
String |
Python struct format code ("d", "f", "i", "B", …) |
readonly |
Bool |
True if the buffer is read-only (default True) |
The buf pointer must remain valid until the matching bf_releasebuffer is called. Do not resize the backing allocation while a buffer view is active.
Full example¶
from pontoneer import BufferProtocolBuilder, BufferInfo
struct FloatBuffer(Defaultable, Movable, Writable):
var data: List[Float64]
fn __init__(out self):
self.data = []
@staticmethod
fn get_buffer(
self_ptr: UnsafePointer[Self, MutAnyOrigin], flags: Int32
) raises -> BufferInfo:
return BufferInfo(
buf=rebind[UnsafePointer[UInt8, MutAnyOrigin]](
self_ptr[].data.unsafe_ptr()
),
nitems=len(self_ptr[].data),
itemsize=8, # sizeof(Float64)
format="d", # C double
readonly=True,
)
fn write_to(self, mut writer: Some[Writer]):
writer.write("FloatBuffer(len=", len(self.data), ")")
# In PyInit_*:
ref tb = b.add_type[FloatBuffer]("FloatBuffer")
.def_init_defaultable[FloatBuffer]()
BufferProtocolBuilder[FloatBuffer](tb)
.def_getbuffer[FloatBuffer.get_buffer]()
.def_releasebuffer()
Python side:
import numpy as np
buf = FloatBuffer()
# ... populate buf ...
arr = np.frombuffer(buf, dtype=np.float64)
mv = memoryview(buf)
Handler signature table¶
| Slot | Handler signature |
|---|---|
bf_getbuffer |
fn(self_ptr: UnsafePointer[T, MutAnyOrigin], flags: Int32) raises -> BufferInfo |
Common format codes¶
| Format | C type | itemsize |
|---|---|---|
"b" |
signed char |
1 |
"B" |
unsigned char |
1 |
"h" |
short |
2 |
"H" |
unsigned short |
2 |
"i" |
int |
4 |
"I" |
unsigned int |
4 |
"l" |
long |
4 or 8 |
"q" |
long long |
8 |
"f" |
float |
4 |
"d" |
double |
8 |
See the full list in the Python struct documentation.