@@ -1314,10 +1314,11 @@ cdef class _DynamicStructReader:
13141314 :type num_first_segment_words: int
13151315 :param num_first_segment_words: Size of the first segment to allocate (in words ie. 8 byte increments)
13161316
1317- :type allocate_seg_callable: Callable[[int], bytearray ]
1317+ :type allocate_seg_callable: Callable[[int], Buffer ]
13181318 :param allocate_seg_callable: A python callable object that takes the minimum number of 8-byte
1319- words to allocate (as an `int`) and returns a `bytearray`. This is used to customize the memory
1320- allocation strategy.
1319+ words to allocate (as an `int`) and returns any object supporting the writable buffer protocol
1320+ (e.g., `bytearray`, `memoryview`, `numpy.ndarray`). This enables custom memory allocation
1321+ strategies including shared memory.
13211322
13221323 :rtype: :class:`_DynamicStructBuilder`
13231324 """
@@ -1700,10 +1701,11 @@ cdef class _DynamicStructBuilder:
17001701 :type num_first_segment_words: int
17011702 :param num_first_segment_words: Size of the first segment to allocate (in words ie. 8 byte increments)
17021703
1703- :type allocate_seg_callable: Callable[[int], bytearray ]
1704+ :type allocate_seg_callable: Callable[[int], Buffer ]
17041705 :param allocate_seg_callable: A python callable object that takes the minimum number of 8-byte
1705- words to allocate (as an `int`) and returns a `bytearray`. This is used to customize the memory
1706- allocation strategy.
1706+ words to allocate (as an `int`) and returns any object supporting the writable buffer protocol
1707+ (e.g., `bytearray`, `memoryview`, `numpy.ndarray`). This enables custom memory allocation
1708+ strategies including shared memory.
17071709
17081710 :rtype: :class:`_DynamicStructBuilder`
17091711 """
@@ -3891,15 +3893,27 @@ cdef class _PyCustomMessageBuilder(_MessageBuilder):
38913893 This callable object will be invoked in the allocateSegment method of the MessageBuilder
38923894 to allocate memory. The allocated memory will be managed within the MessageBuilder.
38933895
3894- :type allocate_seg_callable: Callable[[int], bytearray ]
3896+ :type allocate_seg_callable: Callable[[int], Buffer ]
38953897 :param allocate_seg_callable: A python callable object that takes the minimum number of 8-byte
3896- words to allocate (as an `int`) and returns a `bytearray`. This is used to customize the memory
3897- allocation strategy.
3898+ words to allocate (as an `int`) and returns any object supporting the writable buffer protocol
3899+ (e.g., `bytearray`, `memoryview`, `numpy.ndarray`). This enables custom memory allocation
3900+ strategies including shared memory.
38983901
38993902 Required function signature is like this:
3900- def __call__(self, minimum_size: int) -> bytearray:
3903+ def __call__(self, minimum_size: int) -> Buffer:
3904+
3905+ Where `Buffer` is any object that:
3906+ - Supports the Python buffer protocol (PyObject_GetBuffer)
3907+ - Is writable
39013908 Note that the unit of minimum_size is words, ie. 8 byte increments.
39023909
3910+ The underlying memory must remain valid for the lifetime of the MessageBuilder.
3911+ If returning a view (e.g., `memoryview`, `numpy.ndarray`) that wraps external memory,
3912+ the allocator is responsible for properly managing the memory lifecycle。
3913+
3914+ Examples:
3915+
3916+ # Example 1: Simple bytearray allocator
39033917 class Allocator:
39043918 def __init__(self):
39053919 self.cur_size = 0
@@ -3911,9 +3925,32 @@ cdef class _PyCustomMessageBuilder(_MessageBuilder):
39113925 return bytearray(byte_count)
39123926
39133927 addressbook = capnp.load('addressbook.capnp')
3928+ allocator = Allocator()
39143929 message = capnp._PyCustomMessageBuilder(allocator)
39153930 person = message.init_root(addressbook.Person)
39163931
3932+ # Example 2: Shared memory allocator (zero-copy)
3933+ import ctypes
3934+
3935+ class ShmAllocator:
3936+ def __init__(self, shm_pool):
3937+ self.shm = shm_pool
3938+ self.buffers = []
3939+
3940+ def __call__(self, minimum_size: int) -> memoryview:
3941+ size = minimum_size * 8
3942+ ptr = self.shm.allocate(size)
3943+ buffer = (ctypes.c_uint8 * size).from_address(ptr)
3944+ self.buffers.append(buffer)
3945+ return memoryview(buffer)
3946+
3947+ def release(self):
3948+ for buffer in self.buffers:
3949+ ptr = ctypes.addressof(buffer)
3950+ size = ctypes.sizeof(buffer)
3951+ self.shm.deallocate(ptr, size)
3952+ self.buffers.clear()
3953+
39173954 :type size: int
39183955 :param size: Size of the first segment to allocate (in words ie. 8 byte increments)
39193956 """
0 commit comments