///|
/// Host transport fills a caller-owned buffer and returns the number of bytes read.
/// Zero is EOF; negative results indicate I/O failure. The source stays stable for the call.
pub(all) struct MatSource {
size : Int
read_into : (Int, FixedArray[Byte], Int, Int) -> Int
}
///|
pub(all) struct SourceLimits {
max_source_bytes : Int
max_expanded_bytes : Int
max_result_bytes : Int
max_buffered_root_bytes : Int
max_metadata_bytes : Int
chunk_bytes : Int
max_elements : Int
max_variables : Int
max_dimensions : Int
max_name_bytes : Int
} derive(Eq, @debug.Debug)
///|
pub fn default_source_limits() -> SourceLimits {
{
max_source_bytes: 1073741824,
max_expanded_bytes: 1073741824,
max_result_bytes: 67108864,
max_buffered_root_bytes: 67108864,
max_metadata_bytes: 8388608,
chunk_bytes: 1048576,
max_elements: 134217728,
max_variables: 4096,
max_dimensions: 32,
max_name_bytes: 1024,
}
}
///|
fn source_limits(limits : SourceLimits) -> Limits raise MatError {
for
n in [
limits.max_source_bytes,
limits.max_expanded_bytes,
limits.max_result_bytes,
limits.max_buffered_root_bytes,
limits.max_metadata_bytes,
] {
if n < 128 || n > 1073741824 {
fail("invalid-limits", 0, "Invalid source byte quota")
}
}
if limits.chunk_bytes < 1 || limits.chunk_bytes > 8388608 {
fail("invalid-limits", 0, "Chunk size must be 1..8388608")
}
let basic : Limits = {
max_file_bytes: limits.max_source_bytes,
max_expanded_bytes: limits.max_expanded_bytes,
max_elements: limits.max_elements,
max_variables: limits.max_variables,
max_dimensions: limits.max_dimensions,
max_name_bytes: limits.max_name_bytes,
}
validate_limits(basic)
basic
}
///|
fn source_fill(
source : MatSource,
offset : Int,
buffer : FixedArray[Byte],
start : Int,
length : Int,
chunk : Int,
) -> Unit raise MatError {
if offset < 0 ||
length < 0 ||
offset > source.size - length ||
start < 0 ||
start > buffer.length() - length {
fail("io-error", offset, "Source read range is invalid")
}
let mut done = 0
while done < length {
let count = if length - done > chunk { chunk } else { length - done }
let n = (source.read_into)(offset + done, buffer, start + done, count)
if n <= 0 || n > count {
fail(
"io-error",
offset + done,
"Source returned an invalid count or premature EOF",
)
}
done = done + n
}
}
///|
fn source_charge_metadata(
bytes : Bytes,
outer : Element,
little : Bool,
budget : TreeBudget,
) -> Unit raise MatError {
let end = outer.start + outer.size
let flags = tag(bytes, outer.start, end, little)
let dims = tag(bytes, flags.next, end, little)
let name = tag(bytes, dims.next, end, little)
let mut count = dims.size + name.size
let class = (bits_at(bytes, flags.start, 4, little) & 255UL).to_int()
let mut position = name.next
if class == 3 {
let item = tag(bytes, position, end, little)
count = count + item.size
position = item.next
}
if class == 2 || class == 3 {
let width = tag(bytes, position, end, little)
let fields = tag(bytes, width.next, end, little)
count = count + fields.size
}
if count > budget.metadata_limit - budget.metadata {
fail(
"metadata-limit",
outer.start,
"Cumulative metadata exceeds source quota",
)
}
budget.metadata = budget.metadata + count
}
///|
pub fn read_tree_from(
source : MatSource,
limits? : SourceLimits = default_source_limits(),
tree_limits? : TreeLimits = default_tree_limits(),
) -> MatTree raise MatError {
scan_source_stream(source, None, false, limits, tree_limits).0
}
///|
pub fn read_selected_from(
source : MatSource,
names : Array[String],
limits? : SourceLimits = default_source_limits(),
tree_limits? : TreeLimits = default_tree_limits(),
) -> MatTree raise MatError {
scan_source_stream(source, Some(names), false, limits, tree_limits).0
}
///|
pub fn inspect_roots_from(
source : MatSource,
limits? : SourceLimits = default_source_limits(),
tree_limits? : TreeLimits = default_tree_limits(),
) -> Array[MatRootInfo] raise MatError {
scan_source_stream(source, Some([]), true, limits, tree_limits).1
}
///|
pub extend SourceLimits with Eq::{equal, not_equal}
///|
pub extend SourceLimits with @debug.Debug::{to_repr}