///|
/// Merges two Bytes objects into a single new Bytes object.
pub fn merge_bytes(b1 : Bytes, b2 : Bytes) -> Bytes {
let len1 = b1.length()
let len2 = b2.length()
let arr = Array::make(len1 + len2, (0).to_byte())
for i = 0; i < len1; i = i + 1 {
arr[i] = b1[i]
}
for i = 0; i < len2; i = i + 1 {
arr[len1 + i] = b2[i]
}
Bytes::from_array(arr)
}
///|
/// Container for a record retrieved from a WAL data file.
pub struct LogRecord {
record : Record
offset : Int
size : Int
}
///|
/// Sequentially reads and parses all records from a WAL data file.
pub fn read_records_from_file(
path : String,
) -> Array[LogRecord] raise MoonKVError {
try {
if @fs.path_exists(path) == false {
return []
}
let file_bytes = @fs.read_file_to_bytes(path)
let total_len = file_bytes.length()
let mut offset = 0
let records = Array::new()
while offset < total_len {
if total_len - offset < 28 {
raise MoonKVError::CorruptedDatabase(
"Incomplete record header at offset \{offset} in \{path}",
)
}
// Decode header
let header_arr = Array::make(28, (0).to_byte())
for i = 0; i < 28; i = i + 1 {
header_arr[i] = file_bytes[offset + i]
}
let header = Bytes::from_array(header_arr)
let (_, _, _, key_sz, val_sz) = decode_header(header)
if key_sz < 0 || val_sz < -1 {
raise MoonKVError::CorruptedDatabase(
"Invalid record sizes at offset \{offset} in \{path}",
)
}
let val_len = if val_sz < 0 { 0 } else { val_sz }
let remaining = total_len - offset - 28
if key_sz > remaining || (!(val_sz < 0) && val_len > remaining - key_sz) {
raise MoonKVError::CorruptedDatabase(
"Record size exceeds file bounds at offset \{offset} in \{path}",
)
}
let record_sz = 28 + key_sz + val_len
if offset + record_sz > total_len {
raise MoonKVError::CorruptedDatabase(
"Record size exceeds file bounds at offset \{offset} in \{path}",
)
}
// Extract record bytes
let record_arr = Array::make(record_sz, (0).to_byte())
for i = 0; i < record_sz; i = i + 1 {
record_arr[i] = file_bytes[offset + i]
}
let record_bytes = Bytes::from_array(record_arr)
let record = deserialize_record(record_bytes)
records.push({ record, offset, size: record_sz })
offset = offset + record_sz
}
records
} catch {
_ => raise MoonKVError::IOError("Failed to read records from file \{path}")
}
}