///|
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
}
}
///|
priv struct PayloadReader {
db : Database
buffer : @buffer.Buffer
occupied : Map[Int, PageUse]
observer : ScanObserver
mut remaining : Int
mut next : Int
mut parent : Int
mut first : Bool
}
///|
fn Database::payload_reader(
self : Database,
bytes : Bytes,
offset : Int,
size : Int,
table : Bool,
occupied : Map[Int, PageUse],
owner_page : Int,
observer : ScanObserver,
) -> PayloadReader raise SqliteError {
if size > self.limits.max_payload_bytes {
raise LimitExceeded("记录 payload 超过资源限制")
}
let usable = self.header.usable_size
let local_size = local_payload_size(size, usable, table)
let extra = if local_size < size { 4 } else { 0 }
if offset < 0 || offset > usable || local_size + extra > usable - offset {
raise Invalid("cell payload 越过页面可用空间")
}
let buffer = @buffer.Buffer(size_hint=size)
buffer.write_bytes(bytes[offset:offset + local_size])
if extra != 0 {
observer.trace.at(
OverflowChain,
Some(owner_page),
byte_offset=Some(offset + local_size),
)
}
{
db: self,
buffer,
occupied,
observer,
remaining: size - local_size,
next: if extra == 0 {
0
} else {
page_number(read_u32(bytes, offset + local_size))
},
parent: owner_page,
first: true,
}
}
///|
fn PayloadReader::next_page(self : PayloadReader) -> Int? raise SqliteError {
if self.remaining == 0 {
if self.next != 0 {
raise Invalid("overflow 链长于记录声明长度")
}
return None
}
if self.next == 0 {
raise Invalid("overflow 链被截断")
}
if self.occupied.get(self.next) is Some(page_use) {
raise Invalid(
"overflow 页 \{self.next} 已用于 \{page_use_name(page_use)},存在环或重复占用",
)
}
if self.occupied.length() >= self.db.limits.max_pages {
raise LimitExceeded("扫描总页数超过限制")
}
self.observer.trace.at(ReadPage, Some(self.next))
Some(self.next)
}
///|
fn PayloadReader::provide(
self : PayloadReader,
bytes : Bytes,
) -> Unit raise SqliteError {
let number = self
.next_page()
.unwrap_or_else(() => raise Invalid("payload 已读取完成"))
if bytes.length() != self.db.header.page_size {
raise Invalid("overflow 页面长度不正确")
}
self.observer.trace.at(PageOwnershipCheck, Some(number))
(self.observer.claim)(
number,
if self.first {
OverflowFirst
} else {
OverflowContinuation
},
Some(self.parent),
)
self.occupied[number] = Overflow
let count = Int::min(self.remaining, self.db.header.usable_size - 4)
self.buffer.write_bytes(bytes[4:4 + count])
self.remaining = self.remaining - count
self.parent = number
self.first = false
self.observer.trace.at(OverflowChain, Some(number), byte_offset=Some(0))
self.next = page_number(read_u32(bytes, 0))
}
///|
fn PayloadReader::bytes(self : PayloadReader) -> Bytes raise SqliteError {
if self.next_page() is Some(_) {
raise Invalid("payload 尚未读取完成")
}
self.buffer.to_bytes()
}