///|
pub suberror ExrError {
Invalid(String, Int32)
Unsupported(String)
Limit(String)
} derive(Debug)
///|
pub extend ExrError with @debug.Debug::{to_repr}
///|
pub(all) struct Limits {
max_file_bytes : Int32
max_header_bytes : Int32
max_pixels : Int32
max_channels : Int32
max_attributes : Int32
max_chunks : Int32
max_decoded_bytes : Int32
} derive(Debug, Eq)
///|
pub extend Limits with @debug.Debug::{to_repr}
///|
pub extend Limits with Eq::{equal, not_equal}
///|
pub fn Limits::default() -> Limits {
{
max_file_bytes: 268435456,
max_header_bytes: 1048576,
max_pixels: 16777216,
max_channels: 64,
max_attributes: 1024,
max_chunks: 65536,
max_decoded_bytes: 268435456,
}
}
///|
fn Limits::validate(self : Limits) -> Unit raise ExrError {
if self.max_file_bytes < 8 ||
self.max_header_bytes < 1 ||
self.max_pixels < 1 ||
self.max_channels < 1 ||
self.max_attributes < 1 ||
self.max_chunks < 1 ||
self.max_decoded_bytes < 1 {
raise Limit("invalid resource limits")
}
}
///|
fn add(a : Int32, b : Int32) -> Int32 raise ExrError {
if a < 0 || b < 0 || a > 2147483647 - b {
raise Limit("integer sum")
}
a + b
}
///|
fn mul(a : Int32, b : Int32) -> Int32 raise ExrError {
if a < 0 || b < 0 || (a != 0 && b > 2147483647 / a) {
raise Limit("integer product")
}
a * b
}
///|
fn imin(a : Int32, b : Int32) -> Int32 {
if a < b {
a
} else {
b
}
}
///|
fn imax(a : Int32, b : Int32) -> Int32 {
if a > b {
a
} else {
b
}
}
///|
priv struct Reader {
data : Bytes
mut pos : Int32
end : Int32
}
///|
fn reader(data : Bytes) -> Reader {
{ data, pos: 0, end: data.length(), }
}
///|
fn Reader::take(self : Reader, n : Int32) -> Bytes raise ExrError {
if n < 0 || n > self.end - self.pos {
raise Invalid("truncated input", self.pos)
}
let bytes = self.data[self.pos:self.pos + n].to_owned()
self.pos += n
bytes
}
///|
fn Reader::u8(self : Reader) -> Int32 raise ExrError {
if self.pos >= self.end {
raise Invalid("truncated byte", self.pos)
}
let v = self.data[self.pos].to_int()
self.pos += 1
v
}
///|
fn Reader::uint(self : Reader, n : Int32) -> UInt64 raise ExrError {
let b = self.take(n)
let mut v = 0UL
for i = 0; i < n; i = i + 1 {
v = v | (b[i].to_uint64() << (8 * i))
}
v
}
///|
fn Reader::i32(self : Reader) -> Int32 raise ExrError {
self.uint(4).to_uint().reinterpret_as_int()
}
///|
fn Reader::f32(self : Reader) -> Double raise ExrError {
Float::reinterpret_from_uint(self.uint(4).to_uint()).to_double()
}
///|
fn Reader::cstring(self : Reader, maximum : Int32) -> String raise ExrError {
let begin = self.pos
while self.pos < self.end && self.data[self.pos] != 0 {
if self.pos - begin >= maximum {
raise Limit("name length")
}
self.pos += 1
}
if self.pos == self.end {
raise Invalid("unterminated string", begin)
}
let result = @utf8.decode(self.data[begin:self.pos]) catch {
_ => raise Invalid("invalid UTF-8 name", begin)
}
self.pos += 1
result
}
///|
fn index(v : UInt64) -> Int32 raise ExrError {
if v > 2147483647UL {
raise Limit("file offset exceeds host index")
}
v.to_int()
}
///|
priv struct Sink {
bytes : Array[Byte]
}
///|
fn sink() -> Sink {
{ bytes: [], }
}
///|
fn Sink::size(self : Sink) -> Int32 {
self.bytes.length()
}
///|
fn Sink::raw(self : Sink, bytes : Bytes) -> Unit {
for b in bytes {
self.bytes.push(b)
}
}
///|
fn Sink::uint(self : Sink, value : UInt64, n : Int32) -> Unit {
for i = 0; i < n; i = i + 1 {
self.bytes.push((value >> (8 * i)).to_byte())
}
}
///|
fn Sink::i32(self : Sink, v : Int32) -> Unit {
self.uint(v.reinterpret_as_uint().to_uint64(), 4)
}
///|
fn Sink::f32(self : Sink, v : Double) -> Unit {
self.uint(Float::from_double(v).reinterpret_as_uint().to_uint64(), 4)
}
///|
fn Sink::cstring(self : Sink, value : String) -> Unit {
self.raw(@utf8.encode(value))
self.bytes.push(0)
}
///|
fn Sink::finish(self : Sink) -> Bytes {
Bytes::from_array(self.bytes)
}
///|
fn finite(v : Double) -> Bool {
v == v && v.abs() <= 1.7976931348623157e308
}
///|
test "binary little endian preserves signed positions and float bits" {
let w = sink()
w.i32(-2147483648)
w.f32(-0.0)
w.cstring("层.Z")
let r = reader(w.finish())
assert_eq(r.i32(), -2147483648)
assert_eq(r.uint(4), 2147483648UL)
assert_eq(r.cstring(255), "层.Z")
assert_true(
try {
ignore(r.u8())
false
} catch {
_ => true
},
)
}
///|
test "binary checked sizes and terminated names" {
assert_true(
try {
ignore(mul(2147483647, 2))
false
} catch {
_ => true
},
)
assert_true(
try {
ignore(add(2147483647, 1))
false
} catch {
_ => true
},
)
assert_true(
try {
ignore(reader(b"abc").cstring(255))
false
} catch {
_ => true
},
)
assert_true(
try {
ignore(reader(b"abc\x00").cstring(2))
false
} catch {
_ => true
},
)
assert_eq(mul(0, 2147483647), 0)
}