///|
fn gzip_u32(data : Bytes, at : Int32) -> UInt32 {
data[at].to_uint() |
(data[at + 1].to_uint() << 8) |
(data[at + 2].to_uint() << 16) |
(data[at + 3].to_uint() << 24)
}
///|
fn gunzip_bounded(
data : Bytes,
origin : Location,
limit : Int32,
) -> Bytes raise ResourceError {
let output : Array[Byte] = []
let buffer : FixedArray[Byte] = FixedArray::make(8192, 0)
let mut offset = 0
if data.is_empty() {
raise archive_error(origin, "Missing gzip member")
}
while offset < data.length() {
let start = offset
if data.length() - offset < 10 ||
data[offset] != 0x1f ||
data[offset + 1] != 0x8b ||
data[offset + 2] != 8 {
raise archive_error(
origin, "Truncated or invalid gzip header or trailing data",
)
}
let flags = data[offset + 3].to_int()
if (flags & 0xe0) != 0 {
raise archive_error(origin, "Reserved gzip flags are unsupported")
}
offset += 10
if (flags & 4) != 0 {
if data.length() - offset < 2 {
raise archive_error(origin, "Truncated gzip extra length")
}
let length = data[offset].to_int() | (data[offset + 1].to_int() << 8)
offset += 2
if length > data.length() - offset {
raise archive_error(origin, "Truncated gzip extra field")
}
offset += length
}
for flag in [8, 16] {
if (flags & flag) != 0 {
while offset < data.length() && data[offset] != 0 {
offset += 1
}
if offset == data.length() {
raise archive_error(origin, "Truncated gzip name or comment")
}
offset += 1
}
}
if (flags & 2) != 0 {
if data.length() - offset < 2 {
raise archive_error(origin, "Truncated gzip header checksum")
}
let expected = data[offset].to_uint() | (data[offset + 1].to_uint() << 8)
if (@checksum.crc32(data[start:offset].to_owned()) & 0xffff) != expected {
raise ResourceError(
IntegrityFailure,
Provenance::new(origin),
"Gzip header CRC-16 mismatch",
)
}
offset += 2
}
let inflater = @flate.Inflater::new()
let crc = @checksum.Crc32::new()
let member_start = output.length()
while true {
let (status, consumed, written) = inflater.step(
data[offset:],
buffer.mut_view(),
) catch {
_ => raise archive_error(origin, "Invalid gzip DEFLATE stream")
}
if written > limit - output.length() {
raise ResourceError(
ResourceLimit,
Provenance::new(origin),
"Expanded gzip exceeds byte limit",
)
}
for i in 0..