///|
// Resolve at most the two landing-pad words allowed by the wire format.
// Result: segment, body offset in words, resolved tag.
fn PointerReader::read_resolve(
self : PointerReader,
) -> (Int, Int, UInt64) raise CapnpError {
if self.depth <= 0 {
raise Limit("message nesting limit exceeded")
}
let tag = read_word(self.state, self.segment, self.word)
let low = tag.to_uint()
if (low & 3) != 2 {
let offset = low.reinterpret_as_int() >> 2
// Pointer position is bounded by the actual Bytes length; signed addition
// is checked in Int64 before narrowing to host indices.
let target = self.word.to_int64() + 1 + offset.to_int64()
if target < 0 || target > self.state.sizes[self.segment].to_int64() {
raise Bounds("pointer offset outside segment")
}
return (self.segment, target.to_int(), tag)
}
let segment64 = tag >> 32
if segment64 >= self.state.sizes.length().to_uint64() {
raise Bounds("far pointer names unknown segment")
}
let segment = segment64.to_int()
let pad = (low >> 3).reinterpret_as_int()
if (low & 4) == 0 {
let landing = read_word(self.state, segment, pad)
read_charge(self.state, 1)
let landing_low = landing.to_uint()
if (landing_low & 3) > 1 {
raise InvalidMessage(
"single-far landing pad must be a struct or list pointer",
)
}
let target = pad.to_int64() +
1 +
(landing_low.reinterpret_as_int() >> 2).to_int64()
if target < 0 || target > self.state.sizes[segment].to_int64() {
raise Bounds("far pointer target outside segment")
}
return (segment, target.to_int(), landing)
}
read_extent(self.state, segment, pad, 2)
read_charge(self.state, 2)
let landing = read_word(self.state, segment, pad)
let object_tag = read_word(self.state, segment, pad + 1)
let landing_low = landing.to_uint()
if (landing_low & 7) != 2 {
raise InvalidMessage(
"double-far landing pad must begin with a single far pointer",
)
}
if (object_tag.to_uint() & 3) > 1 || object_tag.to_uint() >> 2 != 0 {
raise InvalidMessage("double-far tag has invalid kind or non-zero offset")
}
let object_segment64 = landing >> 32
if object_segment64 >= self.state.sizes.length().to_uint64() {
raise Bounds("double-far pointer names unknown segment")
}
let object_segment = object_segment64.to_int()
let target = (landing_low >> 3).reinterpret_as_int()
read_extent(self.state, object_segment, target, 0)
(object_segment, target, object_tag)
}
///|
pub fn PointerReader::as_struct(
self : PointerReader,
) -> StructReader raise CapnpError {
if self.is_null() {
return read_empty_struct(self.state, self.depth)
}
let (segment, target, tag) = PointerReader::read_resolve(self)
match tag.to_uint() & 3 {
0 => ()
3 =>
raise Unsupported("capability pointers require an RPC capability table")
_ => raise TypeMismatch("expected struct pointer")
}
let data_words = ((tag >> 32) & 0xffff).to_int()
let pointers = (tag >> 48).to_int()
read_extent(self.state, segment, target, data_words + pointers)
read_charge(self.state, data_words + pointers)
{
state: self.state,
segment,
start: target * 8,
data_bytes: data_words * 8,
pointer_start: target + data_words,
pointer_count: pointers,
depth: self.depth - 1,
}
}
///|
pub fn PointerReader::as_list(
self : PointerReader,
) -> ListReader raise CapnpError {
if self.is_null() {
return read_empty_list(self.state, self.depth)
}
let (segment, target, tag) = PointerReader::read_resolve(self)
match tag.to_uint() & 3 {
1 => ()
3 =>
raise Unsupported("capability pointers require an RPC capability table")
_ => raise TypeMismatch("expected list pointer")
}
let upper = (tag >> 32).to_uint()
let code = (upper & 7).reinterpret_as_int()
let count = (upper >> 3).reinterpret_as_int()
if code == 7 {
read_extent(self.state, segment, target, count + 1)
let element_tag = read_word(self.state, segment, target)
if (element_tag.to_uint() & 3) != 0 {
raise InvalidMessage("inline composite tag must describe a struct")
}
let elements = (element_tag.to_uint() >> 2).reinterpret_as_int()
let data_words = ((element_tag >> 32) & 0xffff).to_int()
let pointers = (element_tag >> 48).to_int()
let stride = data_words + pointers
if stride != 0 && elements > count / stride {
raise Bounds("inline composite elements exceed declared word count")
}
// Zero-width lists can amplify into arbitrarily many logical objects.
read_charge(self.state, if stride == 0 { elements + 1 } else { count + 1 })
return {
state: self.state,
segment,
start: (target + 1) * 8,
count: elements,
code,
stride: stride * 8,
data_words,
pointer_count: pointers,
depth: self.depth - 1,
}
}
let stride = match code {
0 => 0
1 => 0
2 => 1
3 => 2
4 => 4
_ => 8
}
let words64 = if code == 1 {
(count.to_int64() + 63) / 64
} else {
(count.to_int64() * stride.to_int64() + 7) / 8
}
if words64 > self.state.sizes[segment].to_int64() {
raise Bounds("list word count exceeds segment")
}
read_extent(self.state, segment, target, words64.to_int())
read_charge(self.state, if code == 0 { count } else { words64.to_int() })
{
state: self.state,
segment,
start: target * 8,
count,
code,
stride,
data_words: 0,
pointer_count: 0,
depth: self.depth - 1,
}
}
///|
pub fn PointerReader::as_data(
self : PointerReader,
default? : Bytes = b"",
) -> Bytes raise CapnpError {
if self.is_null() {
return default
}
let list = self.as_list()
if list.code != 2 {
raise TypeMismatch("expected byte list")
}
let start = self.state.offsets[list.segment] + list.start
self.state.bytes[start:start + list.count].to_owned()
}
///|
pub fn PointerReader::as_text(
self : PointerReader,
default? : String = "",
) -> String raise CapnpError {
if self.is_null() {
return default
}
let list = self.as_list()
if list.code != 2 {
raise TypeMismatch("expected text byte list")
}
if list.count == 0 {
raise InvalidMessage("text is missing its NUL terminator")
}
let start = self.state.offsets[list.segment] + list.start
if self.state.bytes[start + list.count - 1] != 0 {
raise InvalidMessage("text is missing its NUL terminator")
}
@utf8.decode(self.state.bytes[start:start + list.count - 1]) catch {
_ => raise InvalidMessage("text contains invalid UTF-8")
}
}
///|
pub fn StructReader::get_pointer(
self : StructReader,
index : Int,
) -> PointerReader raise CapnpError {
if index < 0 {
raise Bounds("negative struct pointer index")
}
if index >= self.pointer_count {
return read_null(self.state, self.depth)
}
{
state: self.state,
segment: self.segment,
word: self.pointer_start + index,
depth: self.depth,
}
}
///|
pub fn StructReader::get_struct(
self : StructReader,
index : Int,
) -> StructReader raise CapnpError {
self.get_pointer(index).as_struct()
}
///|
pub fn StructReader::get_list(
self : StructReader,
index : Int,
) -> ListReader raise CapnpError {
self.get_pointer(index).as_list()
}
///|
pub fn StructReader::get_text(
self : StructReader,
index : Int,
default? : String = "",
) -> String raise CapnpError {
self.get_pointer(index).as_text(default~)
}
///|
pub fn StructReader::get_data(
self : StructReader,
index : Int,
default? : Bytes = b"",
) -> Bytes raise CapnpError {
self.get_pointer(index).as_data(default~)
}
///|
pub fn StructReader::data_size_bytes(self : StructReader) -> Int {
self.data_bytes
}
///|
pub fn StructReader::pointer_count(self : StructReader) -> Int {
self.pointer_count
}