Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
793c0b4
add memoryview and custom builder
May 14, 2025
fbcb104
support set dynamic field
May 14, 2025
38b094f
rebase master
May 14, 2025
6749be3
add curSize
May 14, 2025
64a839d
add initialSize and lastSize
May 15, 2025
dd25dc2
change StringPtr name
May 15, 2025
ed457f9
add test case
May 15, 2025
a5e2bf9
refine test case
May 15, 2025
f3da3bb
convert func to py callable object
May 16, 2025
451e875
add initial value
May 17, 2025
fa88cf5
refine example
May 19, 2025
5e80037
add copy as_reader and new_message, make structReader's data field re…
May 19, 2025
ffacb7b
lazy zero allocation
Aug 31, 2025
a69fd13
add example
Sep 1, 2025
1fc8c04
support new message
Sep 1, 2025
ee84684
add memoryview and custom builder
May 14, 2025
c10345f
support set dynamic field
May 14, 2025
1720587
add curSize
May 14, 2025
ca5fd37
add initialSize and lastSize
May 15, 2025
e496146
change StringPtr name
May 15, 2025
b88d95a
add test case
May 15, 2025
24efb3d
refine test case
May 15, 2025
c6af46b
convert func to py callable object
May 16, 2025
e9227a7
add initial value
May 17, 2025
ed8c4ad
refine example
May 19, 2025
2c9f765
add copy as_reader and new_message, make structReader's data field re…
May 19, 2025
0da8487
rebase master and bugfix
Sep 9, 2025
78fa3a1
reformat flake8
Sep 9, 2025
02cfd5a
refine test case
Sep 9, 2025
d56ac3f
refine test cases for blob
Sep 9, 2025
ceae1c6
remove unused import for flake8
Sep 9, 2025
14f7f2b
run black .
Sep 10, 2025
1634bed
Merge branch 'master' into brianxu/lazy_zero_allocation
Sep 10, 2025
3f30c30
rebase custom segment allocation
Sep 10, 2025
26b613c
rebase upstream
Sep 22, 2025
eb61b94
temp
Sep 27, 2025
a1c4acc
refine example
Sep 30, 2025
25fceb4
refine structure
Sep 30, 2025
438f255
refine get_options
Sep 30, 2025
9e9b2d2
refine example
Sep 30, 2025
320febe
refine example
Sep 30, 2025
bf566a9
refine example
Sep 30, 2025
946d175
finish example
Sep 30, 2025
f382511
new line
Sep 30, 2025
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
12 changes: 12 additions & 0 deletions capnp/includes/schema_cpp.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
# distutils: language = c++

from libc.stdint cimport *
from libcpp.set cimport set
from capnp.helpers.non_circular cimport c_reraise_kj_exception as reraise_kj_exception
from capnp.includes.capnp_cpp cimport TypeWhich

from capnp.includes.types cimport *

Expand Down Expand Up @@ -649,6 +651,13 @@ cdef extern from "capnp/message.h" namespace " ::capnp":
uint64_t traversalLimitInWords
uint nestingLimit

cdef cppclass LazyZeroSegmentAlloc "BuilderOptions::LazyZeroSegmentAlloc":
bint enableLazyZero
set[TypeWhich] skipLazyZeroTypes

cdef cppclass BuilderOptions:
LazyZeroSegmentAlloc lazyZeroSegmentAlloc

cdef cppclass MessageBuilder nogil:
CodeGeneratorRequest.Builder getRootCodeGeneratorRequest'getRoot< ::capnp::schema::CodeGeneratorRequest>'()
CodeGeneratorRequest.Builder initRootCodeGeneratorRequest'initRoot< ::capnp::schema::CodeGeneratorRequest>'()
Expand Down Expand Up @@ -683,6 +692,9 @@ cdef extern from "capnp/message.h" namespace " ::capnp":

DynamicOrphan newOrphan'getOrphanage().newOrphan'(StructSchema)

void setOptions'setOptions'(BuilderOptions options)
BuilderOptions getOptions'getOptions'() const

cdef cppclass MessageReader nogil:
CodeGeneratorRequest.Reader getRootCodeGeneratorRequest'getRoot< ::capnp::schema::CodeGeneratorRequest>'()
InterfaceNode.Reader getRootInterfaceNode'getRoot< ::capnp::schema::InterfaceNode>'()
Expand Down
9 changes: 9 additions & 0 deletions capnp/lib/capnp.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,15 @@ cdef class _MessageBuilder:
cpdef set_root(self, value)
cpdef get_segments_for_output(self)
cpdef new_orphan(self, schema)
cpdef get_options(self)
cpdef set_options(self, BuilderOptions py_opts)

cdef class LazyZeroSegmentAlloc:
cdef public bint enableLazyZero
cdef public object skipLazyZeroTypes

cdef class BuilderOptions:
cdef public LazyZeroSegmentAlloc lazyZeroSegmentAlloc

cdef to_python_reader(C_DynamicValue.Reader self, object parent)
cdef to_python_builder(C_DynamicValue.Builder self, object parent)
Expand Down
56 changes: 53 additions & 3 deletions capnp/lib/capnp.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -3196,12 +3196,14 @@ class _StructABCMeta(type):
return isinstance(obj, cls.__base__) and obj.schema == cls._schema


cdef _new_message(self, kwargs, num_first_segment_words, allocate_seg_callable):
cdef _new_message(self, kwargs, num_first_segment_words, allocate_seg_callable, builder_options):
cdef _MessageBuilder builder
if allocate_seg_callable is None:
builder = _MallocMessageBuilder(num_first_segment_words)
builder.set_options(builder_options)
else:
builder = _PyCustomMessageBuilder(allocate_seg_callable, num_first_segment_words)
builder.set_options(builder_options)
msg = builder.init_root(self.schema)
if kwargs is not None:
msg.from_dict(kwargs)
Expand Down Expand Up @@ -3442,7 +3444,7 @@ class _StructModule(object):
def __call__(self, num_first_segment_words=None, **kwargs):
return self.new_message(num_first_segment_words=num_first_segment_words, **kwargs)

def new_message(self, num_first_segment_words=None, allocate_seg_callable=None, **kwargs):
def new_message(self, num_first_segment_words=None, allocate_seg_callable=None, builder_options=BuilderOptions(), **kwargs):
"""Returns a newly allocated builder message.

:type num_first_segment_words: int
Expand All @@ -3453,6 +3455,9 @@ class _StructModule(object):
words to allocate (as an `int`) and returns a `bytearray`. This is used to customize the memory
allocation strategy.

:type builder_options: BuilderOptions
:param builder_options: This is for configuring message builder options, such as lazy zero segment alloc.

:type kwargs: dict
:param kwargs: A list of fields and their values to initialize in the struct.

Expand All @@ -3461,7 +3466,7 @@ class _StructModule(object):

:rtype: :class:`_DynamicStructBuilder`
"""
return _new_message(self, kwargs, num_first_segment_words, allocate_seg_callable)
return _new_message(self, kwargs, num_first_segment_words, allocate_seg_callable, builder_options)


class _InterfaceModule(object):
Expand Down Expand Up @@ -3795,6 +3800,51 @@ cdef class _MessageBuilder:
ptr = s._thisptr()
return _DynamicOrphan()._init(self.thisptr.newOrphan(ptr), self)

cpdef set_options(self, BuilderOptions py_opts):
if py_opts is None:
raise ValueError("options must be an BuilderOptions instance, got None")

cdef schema_cpp.BuilderOptions opts
opts.lazyZeroSegmentAlloc.enableLazyZero = <bint> py_opts.lazyZeroSegmentAlloc.enableLazyZero

if py_opts.lazyZeroSegmentAlloc is not None:
py_set = py_opts.lazyZeroSegmentAlloc.skipLazyZeroTypes
if py_set is not None:
try:
if types.Data in py_set:
opts.lazyZeroSegmentAlloc.skipLazyZeroTypes.insert(capnp.TypeWhichDATA)
except TypeError:
raise TypeError("skipLazyZeroTypes must be an iterable (e.g. set)")

self.thisptr.setOptions(opts)


cpdef get_options(self):
cdef schema_cpp.BuilderOptions opts = self.thisptr.getOptions()

cdef BuilderOptions py_opts = BuilderOptions()
py_opts.lazyZeroSegmentAlloc = LazyZeroSegmentAlloc()
py_opts.lazyZeroSegmentAlloc.enableLazyZero = opts.lazyZeroSegmentAlloc.enableLazyZero
py_opts.lazyZeroSegmentAlloc.skipLazyZeroTypes = set()

if opts.lazyZeroSegmentAlloc.skipLazyZeroTypes.count(capnp.TypeWhichDATA):
py_opts.lazyZeroSegmentAlloc.skipLazyZeroTypes.add(types.Data)

return py_opts

cdef class LazyZeroSegmentAlloc:
def __init__(self, enableLazyZero=False, skipLazyZeroTypes=None):
self.enableLazyZero = enableLazyZero
self.skipLazyZeroTypes = skipLazyZeroTypes
if skipLazyZeroTypes == None:
self.skipLazyZeroTypes = set()

cdef class BuilderOptions:
def __init__(self, lazyZeroSegmentAlloc=None):
self.lazyZeroSegmentAlloc = lazyZeroSegmentAlloc
if lazyZeroSegmentAlloc == None:
self.lazyZeroSegmentAlloc = LazyZeroSegmentAlloc()


cdef class _MallocMessageBuilder(_MessageBuilder):
"""The main class for building Cap'n Proto messages
Expand Down
41 changes: 41 additions & 0 deletions examples/lazy_zero_allocation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import capnp
import random

import addressbook_capnp


WORD_SIZE = 8


class Allocator:
def __init__(self):
return

def __call__(self, minimum_size: int) -> bytearray:
# dirty the memory on purpose, to simulate reusing shared memory
return bytearray(random.getrandbits(8) for _ in range(minimum_size * WORD_SIZE))


lazy_zero = capnp.LazyZeroSegmentAlloc(enableLazyZero=True, skipLazyZeroTypes={capnp.types.Data})
builder_options=capnp.BuilderOptions(lazyZeroSegmentAlloc=lazy_zero)

# message creation method 1
person = addressbook_capnp.Person.new_message(allocate_seg_callable=Allocator(), builder_options=builder_options)
print(person.name) # guaranteed empty string
person.name = "test name 1"
print(person.name)
person.init("extraData", 100) # random dirty bytes
print(bytes(person.extraData))
print()

# message creation method 2
builder = capnp._PyCustomMessageBuilder(allocate_seg_callable=Allocator())
builder.set_options(builder_options)
person = builder.init_root(addressbook_capnp.Person)
print(person.name) # guaranteed empty string
person.name = "test name 2"
print(person.name)
person.init("extraData", 100) # random dirty bytes
print(bytes(person.extraData))
builder.get_options()
print(len(builder.get_options().lazyZeroSegmentAlloc.skipLazyZeroTypes)) # return a set of length 1