///|
fn local_payload_size(
payload_size : Int,
usable : Int,
table_leaf : Bool,
) -> Int {
let max_local = if table_leaf {
usable - 35
} else {
(usable - 12) * 64 / 255 - 23
}
if payload_size <= max_local {
return payload_size
}
let min_local = (usable - 12) * 32 / 255 - 23
let candidate = min_local + (payload_size - min_local) % (usable - 4)
if candidate <= max_local {
candidate
} else {
min_local
}
}
///|
fn Database::cell_payload(
self : Database,
page_bytes : Bytes,
offset : Int,
payload_size : Int,
table_leaf : Bool,
) -> Bytes raise SqliteError {
if payload_size > self.limits.max_payload_bytes {
raise LimitExceeded("记录 payload 超过资源限制")
}
let usable = self.header.usable_size
let local_size = local_payload_size(payload_size, usable, table_leaf)
let extra = if local_size < payload_size { 4 } else { 0 }
if offset < 0 || offset > usable || local_size + extra > usable - offset {
raise Invalid("cell payload 越过页面可用空间")
}
if extra == 0 {
return page_bytes[offset:offset + local_size].to_owned()
}
let buffer = @buffer.Buffer(size_hint=payload_size)
buffer.write_bytes(page_bytes[offset:offset + local_size])
let mut remaining = payload_size - local_size
let mut next = page_number(read_u32(page_bytes, offset + local_size))
let visited : Map[Int, Bool] = Map([])
while remaining > 0 {
if next == 0 || visited.contains(next) {
raise Invalid("overflow 链被截断或包含环")
}
if visited.length() >= self.limits.max_pages {
raise LimitExceeded("overflow 页数超过限制")
}
visited[next] = true
let overflow = self.read_page(next)
let count = if remaining < usable - 4 { remaining } else { usable - 4 }
buffer.write_bytes(overflow[4:4 + count])
remaining = remaining - count
next = page_number(read_u32(overflow, 0))
}
if next != 0 {
raise Invalid("overflow 链长于记录声明长度")
}
buffer.to_bytes()
}