///|
priv struct Tree {
pages : Map[Int, Bytes]
mut root : Int
mut freelist : Int
mut next_page : Int
/// 自 `reset_changes` 以来的改动页原始字节,`None` 表示原本不存在。
undo : Map[Int, Bytes?]
/// 当前这次 `put`/`delete` 改过的页原始字节。
op_undo : Map[Int, Bytes?]
/// 当前这次操作碰过的页号。
op_touched : Map[Int, Bool]
/// 当前这次操作首次写进 `undo` 的页号。
op_added : Map[Int, Bool]
/// 当前这次操作开始时的元数据,失败时整份还原。
mut op_root : Int
mut op_freelist : Int
mut op_next : Int
}
///|
fn Tree::new() -> Tree {
{
pages: Map([]),
root: 0,
freelist: 0,
next_page: 1,
undo: Map([]),
op_undo: Map([]),
op_touched: Map([]),
op_added: Map([]),
op_root: 0,
op_freelist: 0,
op_next: 1,
}
}
///|
fn Tree::allocate(self : Tree) -> Int raise DbError {
if self.freelist == 0 {
let id = self.next_page
self.next_page = id + 1
id
} else {
let id = self.freelist
let page = match self.pages.get(id) {
Some(page) => page
None => raise DbError::Corrupt
}
self.freelist = decode_free(page)
id
}
}
///|
fn Tree::release(self : Tree, id : Int) -> Unit {
self.set_page(id, page_bytes(encode_free(self.freelist)))
self.freelist = id
}
///|
fn Tree::load(self : Tree, id : Int) -> Node raise DbError {
match self.pages.get(id) {
Some(page) => decode_node(page)
None => raise DbError::Corrupt
}
}
///|
fn Tree::store_leaf(self : Tree, id : Int, leaf : Leaf) -> Unit raise DbError {
self.set_page(id, page_bytes(encode_leaf(leaf)))
}
///|
fn Tree::store_branch(
self : Tree,
id : Int,
branch : Branch,
) -> Unit raise DbError {
self.set_page(id, page_bytes(encode_branch(branch)))
}
///|
fn find_entry(entries : Array[Entry], key : Bytes) -> Int {
let mut low = 0
let mut high = entries.length()
while low < high {
let mid = low + (high - low) / 2
if compare_bytes(entries[mid].key, key) < 0 {
low = mid + 1
} else {
high = mid
}
}
low
}
///|
fn leaf_byte_size(leaf : Leaf) -> Int {
let mut size = 7
for entry in leaf.entries {
size = size + 4 + entry.key.length() + entry.value.length()
}
size
}
///|
fn branch_byte_size(branch : Branch) -> Int {
let mut size = 3 + branch.children.length() * 4
for key in branch.keys {
size = size + 2 + key.length()
}
size
}
///|
/// 叶子 entry 的编码字节数:4 字节长度头 + key + value。
fn leaf_entry_bytes(entry : Entry) -> Int {
4 + entry.key.length() + entry.value.length()
}
///|
/// 按编码后的字节容量切分叶子。先算总负载,再从左向右推进左半边预算,
/// 每步只做常数加法,整体线性;同时保证左右都不超过一页。
fn split_leaf(leaf : Leaf) -> (Leaf, Bytes, Leaf) raise DbError {
let count = leaf.entries.length()
guard count >= 2 else { raise DbError::ValueTooLarge }
let mut total = 0
for entry in leaf.entries {
total = total + leaf_entry_bytes(entry)
}
let mut left_payload = 0
let mut mid = 0
let mut best_balance = 0x7FFFFFFF
for i in 0..<(count - 1) {
left_payload = left_payload + leaf_entry_bytes(leaf.entries[i])
let right_payload = total - left_payload
let balance = (left_payload - right_payload).abs()
if 7 + left_payload <= page_size &&
7 + right_payload <= page_size &&
balance < best_balance {
mid = i + 1
best_balance = balance
}
}
guard mid > 0 else { raise DbError::ValueTooLarge }
let left = { entries: leaf.entries[:mid].to_owned(), right: 0, }
let separator = leaf.entries[mid].key
let right = { entries: leaf.entries[mid:].to_owned(), right: leaf.right, }
(left, separator, right)
}
///|
/// 按下标的字节预算切分分支:`mid` 是分给父节点的分隔键下标,
/// 左边拿到 `keys[:mid]` 与 `children[:mid+1]`,右边拿到 `keys[mid+1:]` 与 `children[mid+1:]`。
/// 允许 `mid == 0` 或 `mid == count - 1`,超长键会切出零键节点。
/// 与叶子一样,先算总量再线性推进左侧预算。
fn split_branch(branch : Branch) -> (Branch, Bytes, Branch) raise DbError {
let count = branch.keys.length()
guard count >= 1 else { raise DbError::ValueTooLarge }
let mut key_payload = 0
for key in branch.keys {
key_payload = key_payload + 2 + key.length()
}
let children = branch.children.length()
// mid 把 keys[mid] 交给父节点:左侧 mid 个键 + mid+1 个子节点,右侧 count-mid-1 个键 + count-mid 个子节点。
let mut left_key_payload = 0
let mut mid = -1
let mut best_balance = 0x7FFFFFFF
for i in 0..= 0 else { raise DbError::ValueTooLarge }
let separator = branch.keys[mid]
let left = {
keys: branch.keys[:mid].to_owned(),
children: branch.children[:mid + 1].to_owned(),
}
let right = {
keys: branch.keys[mid + 1:].to_owned(),
children: branch.children[mid + 1:].to_owned(),
}
(left, separator, right)
}
///|
priv struct Split {
separator : Bytes
right : Int
}
///|
/// 单条 entry 至少要占 4 + key + value 字节,页头占 7 字节。
fn entry_fits_page(key : Bytes, value : Bytes) -> Bool {
7 + 4 + key.length() + value.length() <= page_size
}
///|
/// 写入或覆盖。单条 entry 连页头都放不下时立刻抛 `ValueTooLarge`,不改动任何页。
/// 成功修改一半才失败时,`errdefer` 把这次操作碰过的页恢复原样。
fn Tree::put(self : Tree, key : Bytes, value : Bytes) -> Unit raise DbError {
guard value.length() <= max_value_len else { raise DbError::ValueTooLarge }
guard entry_fits_page(key, value) else { raise DbError::ValueTooLarge }
self.begin_operation()
errdefer self.rollback_operation()
if self.root == 0 {
let id = self.allocate()
self.store_leaf(id, { entries: [{ key, value, }], right: 0, })
self.root = id
} else {
match self.insert(self.root, key, value) {
None => ()
Some(split) => {
let id = self.allocate()
self.store_branch(id, {
keys: [split.separator],
children: [self.root, split.right],
})
self.root = id
}
}
}
self.commit_operation()
}
///|
fn Tree::insert(
self : Tree,
id : Int,
key : Bytes,
value : Bytes,
) -> Split? raise DbError {
match self.load(id) {
Leaf(leaf) => self.insert_leaf(id, leaf, key, value)
Branch(branch) => self.insert_branch(id, branch, key, value)
Free => raise DbError::Corrupt
}
}
///|
fn Tree::insert_leaf(
self : Tree,
id : Int,
leaf : Leaf,
key : Bytes,
value : Bytes,
) -> Split? raise DbError {
let index = find_entry(leaf.entries, key)
if index < leaf.entries.length() &&
compare_bytes(leaf.entries[index].key, key) == 0 {
leaf.entries[index] = { key, value, }
} else {
leaf.entries.insert(index, { key, value, })
}
if leaf_byte_size(leaf) <= page_size {
self.store_leaf(id, leaf)
None
} else {
let (left, separator, right_leaf) = split_leaf(leaf)
let right_id = self.allocate()
self.store_leaf(id, { entries: left.entries, right: right_id, })
self.store_leaf(right_id, {
entries: right_leaf.entries,
right: leaf.right,
})
Some({ separator, right: right_id, })
}
}
///|
fn Tree::insert_branch(
self : Tree,
id : Int,
branch : Branch,
key : Bytes,
value : Bytes,
) -> Split? raise DbError {
let child_index = find_child_index(branch, key)
match self.insert(branch.children[child_index], key, value) {
None => None
Some(split) => {
branch.keys.insert(child_index, split.separator)
branch.children.insert(child_index + 1, split.right)
if branch_byte_size(branch) <= page_size {
self.store_branch(id, branch)
None
} else {
let (left, separator, right) = split_branch(branch)
let right_id = self.allocate()
self.store_branch(id, left)
self.store_branch(right_id, right)
Some({ separator, right: right_id, })
}
}
}
}
///|
fn find_child_index(branch : Branch, key : Bytes) -> Int {
let mut low = 0
let mut high = branch.keys.length()
while low < high {
let mid = low + (high - low) / 2
if compare_bytes(key, branch.keys[mid]) >= 0 {
low = mid + 1
} else {
high = mid
}
}
low
}
///|
fn Tree::get(self : Tree, key : Bytes) -> Bytes? raise DbError {
let mut id = self.root
let mut remaining = self.next_page
while id != 0 {
guard id > 0 && id < self.next_page && remaining > 0 else {
raise DbError::Corrupt
}
remaining = remaining - 1
let page = match self.pages.get(id) {
Some(page) => page
None => raise DbError::Corrupt
}
guard page.length() == page_size else { raise DbError::Corrupt }
if page[0] == kind_leaf {
return lookup_leaf(page, key)
}
if page[0] == kind_branch {
id = lookup_child(page, key)
} else {
raise DbError::Corrupt
}
}
None
}
///|
fn Tree::delete(self : Tree, key : Bytes) -> Bool raise DbError {
if self.root == 0 {
false
} else {
self.begin_operation()
errdefer self.rollback_operation()
let removed = self.delete_at(self.root, key)
while self.root != 0 {
match self.load(self.root) {
Branch(branch) if branch.keys.is_empty() => {
let old_root = self.root
self.root = branch.children[0]
self.release(old_root)
}
Leaf(leaf) if leaf.entries.is_empty() => {
self.release(self.root)
self.root = 0
}
_ => break
}
}
self.commit_operation()
removed
}
}
///|
fn Tree::delete_at(self : Tree, id : Int, key : Bytes) -> Bool raise DbError {
match self.load(id) {
Leaf(leaf) => {
let index = find_entry(leaf.entries, key)
if index < leaf.entries.length() &&
compare_bytes(leaf.entries[index].key, key) == 0 {
ignore(leaf.entries.remove(index))
self.store_leaf(id, leaf)
true
} else {
false
}
}
Branch(branch) => {
let index = find_child_index(branch, key)
let removed = self.delete_at(branch.children[index], key)
if removed {
self.rebalance(id, branch, index)
}
removed
}
Free => raise DbError::Corrupt
}
}
///|
fn Tree::rebalance(
self : Tree,
id : Int,
branch : Branch,
index : Int,
) -> Unit raise DbError {
if branch.children.length() < 2 {
return
}
let left_index = if index == 0 { 0 } else { index - 1 }
if self.merge_child(branch, left_index) {
self.store_branch(id, branch)
}
}
///|
fn Tree::merge_child(
self : Tree,
branch : Branch,
left_index : Int,
) -> Bool raise DbError {
let right_index = left_index + 1
let left_id = branch.children[left_index]
let right_id = branch.children[right_index]
match (self.load(left_id), self.load(right_id)) {
(Leaf(left), Leaf(right)) => {
let merged = {
entries: left.entries + right.entries,
right: right.right,
}
if leaf_byte_size(merged) > page_size {
return false
}
self.store_leaf(left_id, merged)
}
(Branch(left), Branch(right)) => {
left.keys.push(branch.keys[left_index])
left.keys.append(right.keys)
left.children.append(right.children)
if branch_byte_size(left) > page_size {
return false
}
self.store_branch(left_id, left)
}
_ => raise DbError::Corrupt
}
self.release(right_id)
ignore(branch.keys.remove(left_index))
ignore(branch.children.remove(right_index))
true
}
///|
#warnings("-unused_value")
fn Tree::keys(self : Tree) -> Array[Bytes] raise DbError {
if self.root == 0 {
[]
} else {
let mut id = self.leftmost(self.root)
let keys = []
let mut remaining = self.next_page
while id != 0 {
guard id > 0 && id < self.next_page && remaining > 0 else {
raise DbError::Corrupt
}
remaining = remaining - 1
let page = match self.pages.get(id) {
Some(page) => page
None => raise DbError::Corrupt
}
append_leaf_keys(page, keys)
id = read_u32(page, 3).reinterpret_as_int()
}
keys
}
}
///|
fn Tree::leftmost(self : Tree, id : Int) -> Int raise DbError {
let mut current = id
let mut remaining = self.next_page
while remaining > 0 {
guard current > 0 && current < self.next_page else {
raise DbError::Corrupt
}
remaining = remaining - 1
let page = match self.pages.get(current) {
Some(page) => page
None => raise DbError::Corrupt
}
guard page.length() == page_size else { raise DbError::Corrupt }
if page[0] == kind_leaf {
return current
}
guard page[0] == kind_branch else { raise DbError::Corrupt }
current = read_u32(page, 3).reinterpret_as_int()
}
raise DbError::Corrupt
}