///|
priv struct CopyBudget {
mut words : Int
}
///|
fn copy_charge(budget : CopyBudget, words : Int) -> Unit raise CapnpError {
if words < 0 || words > budget.words {
raise Limit("deep-copy traversal limit exceeded")
}
budget.words -= words
}
///|
fn copy_limits(max_depth : Int, max_words : Int) -> CopyBudget raise CapnpError {
// Bound recursive call-stack use even if a source reader permits deeper input.
if max_depth < 0 || max_depth > 256 || max_words < 0 {
raise Limit("copy limits require depth 0..256 and nonnegative words")
}
{ words: max_words, }
}
///|
fn copy_transaction(
target : PointerBuilder,
action : () -> Unit raise CapnpError,
) -> Unit raise CapnpError {
let mark = target.owner.arena.length()
let mut old = 0UL
for i in 0..<8 {
old = old | (target.owner.arena[target.slot + i].to_uint64() << (8 * i))
}
errdefer {
target.owner.arena.truncate(mark)
b_write(target.owner, target.slot, old, 8)
}
action()
}
///|
fn copy_struct_contents(
target : StructBuilder,
source : StructReader,
depth : Int,
budget : CopyBudget,
) -> Unit raise CapnpError {
let start = source.state.offsets[source.segment] + source.start
for i in 0.. Unit raise CapnpError {
if depth <= 0 {
raise Limit("deep-copy nesting limit exceeded")
}
let data_words = (source.data_bytes + 7) / 8
copy_charge(budget, Int::max(1, data_words + source.pointer_count))
let result = target.init_struct(data_words, source.pointer_count)
copy_struct_contents(result, source, depth - 1, budget)
}
///|
fn copy_list(
target : PointerBuilder,
source : ListReader,
depth : Int,
budget : CopyBudget,
) -> Unit raise CapnpError {
if depth <= 0 {
raise Limit("deep-copy nesting limit exceeded")
}
if source.code == 7 {
let stride = source.data_words + source.pointer_count
let words = if stride == 0 {
source.count + 1
} else {
source.count * stride + 1
}
copy_charge(budget, words)
let result = target.init_struct_list(
source.count,
source.data_words,
source.pointer_count,
)
if stride != 0 {
for i in 0.. Unit raise CapnpError {
if source.is_null() {
target.clear()
return
}
if depth <= 0 {
raise Limit("deep-copy nesting limit exceeded")
}
match source.kind() {
Null => target.clear()
Struct => copy_struct(target, source.as_struct(), depth, budget)
List => copy_list(target, source.as_list(), depth, budget)
Capability =>
raise Unsupported(
"deep-copying capabilities requires capability-table remapping",
)
}
}
///|
/// Deep-copy an opaque pointer, preserving unknown fields and wire list widths.
/// On CapnpError the destination slot and new arena allocations are rolled back.
/// Source traversal budget is consumed even when copying fails. Aliases become
/// independent copies; cycles stop at the reader or copy traversal/depth limit.
/// Capabilities are rejected because no capability-table remapping is available.
pub fn PointerBuilder::set(
self : PointerBuilder,
source : PointerReader,
max_depth? : Int = 64,
max_words? : Int = 8388608,
) -> Unit raise CapnpError {
let budget = copy_limits(max_depth, max_words)
copy_transaction(self, () => copy_pointer(self, source, max_depth, budget))
}
///|
/// Copy an opaque struct including every on-wire data word and pointer slot.
pub fn PointerBuilder::set_struct(
self : PointerBuilder,
source : StructReader,
max_depth? : Int = 64,
max_words? : Int = 8388608,
) -> Unit raise CapnpError {
let budget = copy_limits(max_depth, max_words)
copy_transaction(self, () => copy_struct(self, source, max_depth, budget))
}
///|
/// Copy an opaque list, retaining its element width and composite struct size.
pub fn PointerBuilder::set_list(
self : PointerBuilder,
source : ListReader,
max_depth? : Int = 64,
max_words? : Int = 8388608,
) -> Unit raise CapnpError {
let budget = copy_limits(max_depth, max_words)
copy_transaction(self, () => copy_list(self, source, max_depth, budget))
}