///|
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_tracked(
self : Database,
page_bytes : Bytes,
offset : Int,
payload_size : Int,
table_leaf : Bool,
occupied : Map[Int, PageUse],
) -> 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))
while remaining > 0 {
if next == 0 {
raise Invalid("overflow 链被截断")
}
if occupied.get(next) is Some(page_use) {
raise Invalid(
"overflow 页 \{next} 已用于 \{page_use_name(page_use)},存在环或重复占用",
)
}
if occupied.length() >= self.limits.max_pages {
raise LimitExceeded("扫描总页数超过限制")
}
occupied[next] = Overflow
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()
}