///|
/// Error categories returned by the archive readers.
pub suberror MspackError {
MspackError(String)
}
///|
pub struct Reader {
data : Bytes
pos : Int
} derive(Show)
///|
pub fn Reader::new(data : Bytes) -> Reader {
{ data, pos: 0, }
}
///|
pub fn Reader::at(self : Reader) -> Int {
self.pos
}
///|
pub fn Reader::remaining(self : Reader) -> Int {
self.data.length() - self.pos
}
///|
pub fn Reader::seek(self : Reader, offset : Int) -> Reader raise MspackError {
if offset < 0 || offset > self.data.length() {
raise MspackError("input bounds error")
}
{ ..self, pos: offset, }
}
///|
pub fn Reader::skip(self : Reader, count : Int) -> Reader raise MspackError {
self.seek(self.pos + count)
}
///|
pub fn Reader::u8(self : Reader) -> (Reader, Int) raise MspackError {
if self.pos >= self.data.length() {
raise MspackError("truncated input")
}
({ ..self, pos: self.pos + 1, }, self.data[self.pos].to_int())
}
///|
pub fn Reader::u16le(self : Reader) -> (Reader, Int) raise MspackError {
let (r, a) = self.u8()
let (r, b) = r.u8()
(r, a | (b << 8))
}
///|
pub fn Reader::u32le(self : Reader) -> (Reader, UInt) raise MspackError {
let (r, a) = self.u16le()
let (r, b) = r.u16le()
(r, a.to_uint() | (b.to_uint() << 16))
}
///|
pub fn Reader::bytes(
self : Reader,
count : Int,
) -> (Reader, Bytes) raise MspackError {
if count < 0 || self.pos + count > self.data.length() {
raise MspackError("truncated input")
}
let out : Array[Byte] = []
for i in 0.. (Reader, String) raise MspackError {
let (r, b) = self.bytes(count)
let end = b.length()
let mut n = 0
while n < end && b[n] != 0 {
n = n + 1
}
(r, b[0:n].to_string())
}
///|
pub struct Limits {
max_entries : Int
max_output : Int
max_window : Int
} derive(Show)
///|
pub let default_limits : Limits = {
max_entries: 100000,
max_output: 512 * 1024 * 1024,
max_window: 32768,
}
///|
pub struct ArchiveEntry {
pub name : String
pub size : Int
pub compressed_size : Int
pub offset : Int
pub folder : Int
pub flags : Int
} derive(Show, Eq)
///|
pub struct ArchiveListing {
pub format : String
pub entries : Array[ArchiveEntry]
pub warnings : Array[String]
} derive(Show)
///|
pub struct Extracted {
pub entry : ArchiveEntry
pub data : Bytes
} derive(Show)
///|
fn checked_range(
total : Int,
offset : Int,
size : Int,
) -> Unit raise MspackError {
if offset < 0 || size < 0 || offset > total || size > total - offset {
raise MspackError("input bounds error")
}
}
///|
fn append_bytes(dst : Array[Byte], src : Bytes) -> Unit {
for i = 0; i < src.length(); i = i + 1 {
dst.push(src[i])
}
}
///|
fn bytes_to_array(data : Bytes) -> Array[Byte] {
let a = []
for i = 0; i < data.length(); i = i + 1 {
a.push(data[i])
}
a
}
///|
fn array_to_bytes(data : Array[Byte]) -> Bytes {
Bytes::from_array(data)
}
///|
/// Create a bounded reader view for a single archive entry.
pub fn entry_view(
data : Bytes,
entry : ArchiveEntry,
limits? : Limits = default_limits,
) -> Reader raise MspackError {
if entry.offset < 0 || entry.size < 0 || entry.size > limits.max_output {
raise MspackError("invalid entry view")
}
checked_range(data.length(), entry.offset, entry.size)
Reader::new(copy_range(data, offset=entry.offset, size=entry.size, limits~))
}
///|
/// Return a stable human-readable archive summary.
pub fn summary(listing : ArchiveListing) -> String {
listing.format + ": " + listing.entries.length().to_string() + " entries"
}