///|
pub(all) suberror QcowError {
Invalid(String)
Unsupported(String)
OutOfBounds
MissingBacking
LimitExceeded
Io(String)
} derive(Debug)
///|
#deprecated
pub extend QcowError with @debug.Debug::{to_repr}
///|
/// A stable, exact-length random access source. The caller owns its lifetime.
pub struct Source {
size : UInt64
read : (UInt64, Int32) -> Bytes raise QcowError
}
///|
/// A source callback must return exactly the requested number of bytes.
pub fn Source::new(
size : UInt64,
read : (UInt64, Int32) -> Bytes raise QcowError,
) -> Source {
{ size, read, }
}
///|
pub fn Source::from_bytes(data : Bytes) -> Source {
Source::new(data.length().to_uint64(), fn(offset, count) {
let result : Array[Byte] = []
for i = 0; i < count; i = i + 1 {
result.push(data[offset.to_int() + i])
}
Bytes::from_array(result)
})
}
///|
pub fn Source::size(self : Source) -> UInt64 {
self.size
}
///|
pub fn Source::check_range(
self : Source,
offset : UInt64,
length : UInt64,
) -> Unit raise QcowError {
if offset > self.size || length > self.size - offset {
raise Invalid("physical range exceeds source")
}
}
///|
pub fn Source::read_at(
self : Source,
offset : UInt64,
count : Int32,
) -> Bytes raise QcowError {
if count < 0 || count > 16 * 1024 * 1024 {
raise LimitExceeded
}
self.check_range(offset, count.to_uint64())
if count == 0 {
return b""
}
let bytes = (self.read)(offset, count)
if bytes.length() != count {
raise Io("short source read")
}
bytes
}
///|
fn Source::integer(
self : Source,
offset : UInt64,
width : Int32,
) -> UInt64 raise QcowError {
let bytes = self.read_at(offset, width)
integer(bytes, 0, width)
}
///|
fn integer(data : Bytes, offset : Int32, width : Int32) -> UInt64 {
let mut value = 0UL
for i = 0; i < width; i = i + 1 {
value = (value << 8) | data[offset + i].to_uint64()
}
value
}
///|
fn add(a : UInt64, b : UInt64) -> UInt64 raise QcowError {
if b > 0xffffffffffffffffUL - a {
raise Invalid("unsigned offset overflow")
}
a + b
}
///|
fn multiply(a : UInt64, b : UInt64) -> UInt64 raise QcowError {
if b != 0UL && a > 0xffffffffffffffffUL / b {
raise Invalid("unsigned size overflow")
}
a * b
}
///|
fn ceiling(value : UInt64, divisor : UInt64) -> UInt64 {
value / divisor + (if value % divisor == 0UL { 0UL } else { 1UL })
}
///|
fn min64(a : UInt64, b : UInt64) -> UInt64 {
if a < b {
a
} else {
b
}
}
///|
fn decode_text(data : Bytes) -> String raise QcowError {
@utf8.decode(data) catch {
_ => raise Invalid("invalid UTF-8 metadata string")
}
}
///|
pub(all) struct Limits {
max_read : Int32
max_chain : Int32
max_l1_entries : Int32
max_extensions : Int32
max_snapshots : Int32
max_map_entries : Int32
max_scan_clusters : Int32
cache_bytes : Int32
}
///|
pub fn Limits::default() -> Limits {
{
max_read: 16 * 1024 * 1024,
max_chain: 32,
max_l1_entries: 4 * 1024 * 1024,
max_extensions: 256,
max_snapshots: 1024,
max_map_entries: 1000000,
max_scan_clusters: 4000000,
cache_bytes: 8 * 1024 * 1024,
}
}
///|
fn Limits::validate(self : Limits) -> Unit raise QcowError {
if self.max_read <= 0 || self.max_read > 16 * 1024 * 1024 {
raise Invalid("max_read must be within 1..16777216")
}
if self.max_chain <= 0 || self.max_chain > 128 {
raise Invalid("max_chain must be within 1..128")
}
if self.max_l1_entries <= 0 || self.max_l1_entries > 4194304 {
raise Invalid("invalid L1 entry limit")
}
if self.max_extensions <= 0 || self.max_extensions > 65536 {
raise Invalid("invalid header extension limit")
}
if self.max_snapshots < 0 || self.max_snapshots > 65536 {
raise Invalid("invalid snapshot limit")
}
if self.max_map_entries <= 0 || self.max_scan_clusters <= 0 {
raise Invalid("scan limits must be positive")
}
if self.cache_bytes < 0 || self.cache_bytes > 64 * 1024 * 1024 {
raise Invalid("cache limit must be within 0..67108864")
}
}