///|
/// SQLite 记录中的原始值,保留整数、浮点数与二进制数据的区别。
pub(all) enum Value {
Null
Integer(Int64)
Real(Double)
Text(String)
Blob(Bytes)
} derive(Debug, Eq)
///|
pub extend Value with @debug.Debug::{to_repr}
///|
pub extend Value with Eq::{equal, not_equal}
///|
fn record_serial_length(serial : UInt64) -> Int raise SqliteError {
match serial {
0UL | 8UL | 9UL => 0
1UL => 1
2UL => 2
3UL => 3
4UL => 4
5UL => 6
6UL | 7UL => 8
10UL | 11UL => raise Invalid("记录包含保留 serial type:\{serial}")
_ => {
let length = (serial - 12UL) / 2UL
if length > 2147483647UL {
raise Invalid("记录字段长度超出可读取范围")
}
length.to_int()
}
}
}
///|
fn record_integer(payload : Bytes, offset : Int, count : Int) -> Int64 {
let mut bits = 0UL
for i = 0; i < count; i = i + 1 {
bits = (bits << 8) | payload[offset + i].to_uint64()
}
if count < 8 && payload[offset].to_int() >= 128 {
// SQLite 使用大端二进制补码,短整数必须进行符号扩展。
bits.reinterpret_as_int64() - (1L << (count * 8))
} else {
bits.reinterpret_as_int64()
}
}
///|
fn record_text(data : BytesView, encoding : Int) -> String raise SqliteError {
try {
match encoding {
1 => @utf8.decode(data, ignore_bom=false)
2 => @utf16.decode(data, ignore_bom=false, endianness=@utf16.Little)
3 => @utf16.decode(data, ignore_bom=false, endianness=@utf16.Big)
_ => raise Invalid("无效文本编码:\{encoding}")
}
} catch {
_ => raise Invalid("记录文本不符合指定的 Unicode 编码")
}
}
///|
/// 解码完整的 SQLite record payload;编码编号与数据库文件头保持一致。
pub fn decode_record(
payload : Bytes,
encoding : Int,
) -> Array[Value] raise SqliteError {
if encoding < 1 || encoding > 3 {
raise Invalid("无效文本编码:\{encoding}")
}
let (header_size, header_varint_size) = decode_varint(payload, 0)
if header_size < header_varint_size.to_uint64() ||
header_size > payload.length().to_uint64() {
raise Invalid("记录头长度超出 payload 边界")
}
let header_end = header_size.to_int()
let serials : Array[UInt64] = []
let mut header_cursor = header_varint_size
while header_cursor < header_end {
let (serial, size) = decode_varint(payload, header_cursor)
if size > header_end - header_cursor {
raise Invalid("serial type varint 跨越记录头边界")
}
serials.push(serial)
header_cursor = header_cursor + size
}
let values : Array[Value] = []
let mut body_cursor = header_end
for serial in serials {
let count = record_serial_length(serial)
require_range(payload, body_cursor, count)
let value = match serial {
0UL => Null
1UL | 2UL | 3UL | 4UL | 5UL | 6UL =>
Integer(record_integer(payload, body_cursor, count))
7UL =>
Real(record_integer(payload, body_cursor, 8).reinterpret_as_double())
8UL => Integer(0L)
9UL => Integer(1L)
_ => {
let data = payload.exact_view(
start=body_cursor,
end=body_cursor + count,
)
if serial % 2UL == 0UL {
Blob(data.to_owned())
} else {
Text(record_text(data, encoding))
}
}
}
values.push(value)
body_cursor = body_cursor + count
}
if body_cursor != payload.length() {
raise Invalid("记录体含有未被 serial type 描述的剩余字节")
}
values
}