///|
/// Resource limits applied while parsing and building JRDs.
///
/// WebFinger data is untrusted input, so every bounded dimension of a JRD
/// (input size, member counts, string lengths, nesting depth) is checked
/// against a `Limits` value. Limits are enforced by *returning errors*,
/// never by silently truncating data.
///
/// `Limits::default()` is the recommended setting for general use;
/// `Limits::strict()` is suitable for constrained or hostile-input
/// environments; `Limits::permissive()` trades memory for flexibility.
///
/// Note on nesting depth: the MoonBit core JSON parser always parses with
/// its own internal depth guard (1024 levels), so `max_nesting_depth` is
/// enforced by a bounded traversal after JSON parsing, as documented in
/// `docs/limitations.md`.
///|
/// All tunable bounds of JRD processing.
pub struct Limits {
/// Maximum UTF-8 byte length of the JSON input.
max_input_bytes : Int
/// Maximum UTF-8 byte length of the `subject` value.
max_subject_bytes : Int
/// Maximum number of `aliases` entries.
max_aliases : Int
/// Maximum UTF-8 byte length of a single alias URI.
max_alias_bytes : Int
/// Maximum number of subject-level properties.
max_properties : Int
/// Maximum UTF-8 byte length of a property URI (member name).
max_property_name_bytes : Int
/// Maximum UTF-8 byte length of a string property value.
max_property_value_bytes : Int
/// Maximum number of links.
max_links : Int
/// Maximum number of titles per link.
max_titles_per_link : Int
/// Maximum number of properties per link.
max_properties_per_link : Int
/// Maximum UTF-8 byte length of any other string member (rel, href,
/// type, title text, language tag).
max_string_bytes : Int
/// Maximum number of unknown top-level members preserved as extensions.
max_extension_members : Int
/// Maximum JSON nesting depth accepted by the bounded traversal.
max_nesting_depth : Int
}
///|
/// Recommended limits for general local processing.
pub fn Limits::default() -> Limits {
{
max_input_bytes: 262144,
max_subject_bytes: 2048,
max_aliases: 32,
max_alias_bytes: 2048,
max_properties: 128,
max_property_name_bytes: 2048,
max_property_value_bytes: 65536,
max_links: 64,
max_titles_per_link: 16,
max_properties_per_link: 64,
max_string_bytes: 65536,
max_extension_members: 32,
max_nesting_depth: 32,
}
}
///|
/// Tight limits for constrained or hostile-input environments.
pub fn Limits::strict() -> Limits {
{
max_input_bytes: 16384,
max_subject_bytes: 512,
max_aliases: 4,
max_alias_bytes: 512,
max_properties: 16,
max_property_name_bytes: 512,
max_property_value_bytes: 4096,
max_links: 8,
max_titles_per_link: 4,
max_properties_per_link: 8,
max_string_bytes: 4096,
max_extension_members: 4,
max_nesting_depth: 8,
}
}
///|
/// Wide limits for large but still bounded descriptors.
pub fn Limits::permissive() -> Limits {
{
max_input_bytes: 4194304,
max_subject_bytes: 16384,
max_aliases: 1024,
max_alias_bytes: 16384,
max_properties: 1024,
max_property_name_bytes: 16384,
max_property_value_bytes: 1048576,
max_links: 1024,
max_titles_per_link: 64,
max_properties_per_link: 256,
max_string_bytes: 1048576,
max_extension_members: 128,
max_nesting_depth: 64,
}
}
///|
/// The UTF-8 byte length of a string, counted without materializing the
/// encoded copy of the whole string. Used to enforce byte limits cheaply
/// before any other processing.
pub fn utf8_byte_length(s : String) -> Int {
let mut total = 0
for c in s {
let cp = c.to_int()
if cp < 0x80 {
total = total + 1
} else if cp < 0x800 {
total = total + 2
} else if cp < 0x10000 {
total = total + 3
} else {
total = total + 4
}
}
total
}
///|
/// Whether `s` fits within `limit` UTF-8 bytes.
pub fn within_byte_limit(s : String, limit : Int) -> Bool {
utf8_byte_length(s) <= limit
}