MoonBit · the interface

Every entry
point.

The whole public surface, one block per format. Generated from each package by moon info and the /// comments in its source, so what you read is what the compiler checks. New here? Read the guide first.

moonzip — the root — checksums and the refuse signal

structAdler

Adler-32 taken a piece at a time, for a body that is never held whole. The running state and the folding come from mooncrypt/sum/adler32.

fields
inner@adler32.Adler32
methods

finish

pub fn Adler::finish(Self) -> UInt32

returns UInt32

new

pub fn Adler::new() -> Self

returns Self

push

pub fn Adler::push(Self, BytesView) -> Unit
parameters
BytesView—positional

returns Unit

structCastagnoli

CRC-32C taken a piece at a time, for a body that is never held whole.

fields
stateUInt32
methods

finish

pub fn Castagnoli::finish(Self) -> UInt32

returns UInt32

new

pub fn Castagnoli::new() -> Self

returns Self

push

pub fn Castagnoli::push(Self, BytesView) -> Unit
parameters
BytesView—positional

returns Unit

structCrc

CRC-32 taken a piece at a time, for a body that is never held whole.

A container that compresses a body it never holds cannot checksum it in one call either: the streaming writers in gzip and zip keep one of these beside their compressor and hand it each chunk as it goes past. The running state and the folding come from mooncrypt/sum/crc32.

fields
inner@crc32.Crc32
methods

finish

pub fn Crc::finish(Self) -> UInt32

returns UInt32

new

pub fn Crc::new() -> Self

returns Self

push

pub fn Crc::push(Self, BytesView) -> Unit
parameters
BytesView—positional

returns Unit

errorRefused

Why a compressed stream could not be read.

The offset is where the reader was when it gave up, in bytes from the start of the stream — enough to find the damage in a file, which "corrupt data" is not.

constructors
Truncatedat~ : Int32

The stream ends in the middle of something.

Headerat~ : Int32, what~ : String

A header field that is not what the format allows: a block type DEFLATE does not define, a compression method gzip does not name, a reserved flag.

Codeat~ : Int32

A Huffman code no symbol is assigned to, or a code table that does not add up.

Distanceat~ : Int32

A back-reference that reaches before the start of the output.

Checksumwant~ : UInt32, got~ : UInt32

The trailing checksum does not match what was decompressed.

Lengthwant~ : UInt32, got~ : Int32

The trailing length does not match what was decompressed.

TooBiglimit~ : Int32

The output passed the bound the caller set. A compressed stream says nothing about how far it expands, so a reader handling input from elsewhere says how much it is prepared to hold.

methods

equal

pub fn Refused::equal(Self, Self) -> Bool

Derived trait glue.

returns Bool

not_equal

pub fn Refused::not_equal(Self, Self) -> Bool

Derived trait glue.

returns Bool

output

pub fn Refused::output(Self, &Logger) -> Unit

Derived trait glue.

parameters
&Logger—positional

returns Unit

to_repr

pub fn Refused::to_repr(Self) -> @debug.Repr

Derived trait glue.

returns @debug.Repr

to_string

pub fn Refused::to_string(Self) -> String

Derived trait glue.

returns String

implements Show

fnadler32

pub fn adler32(BytesView) -> UInt32

Adler-32 (RFC 1950): two running sums modulo the largest prime below 65536. Cheaper than a CRC and weaker, which is the trade zlib made. The folding is mooncrypt/sum/adler32, reached from here for the same reason crc32 is.

parameters
BytesView—positional

returns UInt32

fncrc32

pub fn crc32(BytesView) -> UInt32

CRC-32 as gzip, ZIP and PNG use it (RFC 1952): the reflected form of the IEEE 802.3 polynomial, starting and ending complemented.

The folding is mooncrypt/sum/crc32; it is reached from here so the formats that specify it — gzip in its trailer, zip in every entry and again in the central directory — find it in one place instead of each importing the other.

parameters
BytesView—positional

returns UInt32

fncrc32c

pub fn crc32c(BytesView) -> UInt32

CRC-32C, the Castagnoli polynomial: the checksum that has hardware support on every x86 since Nehalem and every ARM since v8, which is why Snappy and iSCSI chose it over the older CRC-32 that gzip and ZIP use.

It lives here beside the other two for the same reason they are here: it is a checksum the formats specify, not something a format invents, and a caller comparing two of them should find them in one place.

parameters
BytesView—positional

returns UInt32

fnmask_crc32c

pub fn mask_crc32c(UInt32) -> UInt32

The mask Snappy applies to a CRC-32C before putting it in a frame, so that a checksum never looks like the length in front of it — and so that a reader that echoes the bytes back cannot be fooled into accepting them.

parameters
UInt32—positional

returns UInt32

deflate — RFC 1951

enumBlocks

Which block types the writer may emit.

A DEFLATE stream is a run of blocks, and a block may be stored verbatim, coded with the fixed Huffman table, or coded with a table of its own. All three are ordinary DEFLATE; they differ only in size.

variants
Auto

Let the writer choose per block: it writes each candidate, measures, and keeps the smallest. This is what zlib does, and on the short bodies a response compressor sees the measurement usually lands on fixed — a dynamic block must carry its own code tables, which costs more than it saves on a few hundred bytes.

Fixed

Fixed Huffman codes only. Exactly what this library wrote before 0.4.0, for a caller whose output bytes are pinned.

Dynamic

Dynamic Huffman codes only: the writer must carry its own code tables. The largest output on text, and the reason to have it on at all.

Stored

No compression at all: the input verbatim in stored blocks. This is what level = 0 means, and asking for it by name is the way to say it without going through a level.

methods

equal

pub fn Blocks::equal(Self, Self) -> Bool

Derived trait glue.

returns Bool

not_equal

pub fn Blocks::not_equal(Self, Self) -> Bool

Derived trait glue.

returns Bool

to_repr

pub fn Blocks::to_repr(Self) -> @debug.Repr

Derived trait glue.

returns @debug.Repr

implements Debug, Eq

typeReader

Decompress a raw DEFLATE stream a piece at a time.

push takes whatever has arrived and hands back whatever it decoded; finish says no more is coming, and it is an error for the stream to be unfinished when it is called. Between calls the reader holds the input it has not got to yet and nothing else — a response body can be decompressed as it downloads, without the whole of it, or of the output, being held.

The one-shot inflate is this reader fed once and finished.

methods

done

pub fn Reader::done(Self) -> Bool

Whether the last block has been read, which is what a container checks before it looks for a trailer.

returns Bool

finish

pub fn Reader::finish(Self) -> Bytes raise @moonzip.Refused

Say the stream is complete. An unfinished block is a truncated stream.

returns Bytes · raises @moonzip.Refused

new

pub fn Reader::new(limit? : Int32) -> Self
parameters
limitInt32labelled, optional

returns Self

push

pub fn Reader::push(Self, BytesView) -> Bytes raise @moonzip.Refused

Feed a chunk, take the bytes it decoded to.

parameters
BytesView—positional

returns Bytes · raises @moonzip.Refused

rest

pub fn Reader::rest(Self) -> Bytes

Input handed to the reader that the DEFLATE stream did not use, and the reader's buffer emptied.

A container that wraps DEFLATE needs this. The reader stops the moment the last block ends, so a gzip trailer or a second member is still sitting in the buffer — and the container is the only thing that knows what to do with it. gunzip takes its eight-byte trailer from here.

returns Bytes

typeWriter

Compress into a DEFLATE stream a piece at a time.

The point of the streaming writer is that a body need not be held to be compressed: push takes whatever has arrived and hands back whatever is ready, and the only thing kept between calls is the last 32 KiB — the window, which is as far back as a back-reference may reach. A block is emitted every 32 KiB of input, and matches may reach back across block boundaries into the retained window, exactly as they would in a stream written in one piece.

The stream is not finished until finish is called: that is what writes the last block, and a caller who stops pushing without it has an unterminated stream.

methods

finish

pub fn Writer::finish(Self) -> Bytes

Finish the stream: the last block, and the last bytes of it. Nothing may be pushed afterwards.

returns Bytes

new

pub fn Writer::new(level? : Int32, window? : Int32, chain? : Int32, blocks? : Blocks) -> Self
parameters
levelInt32labelled, optional
windowInt32labelled, optional
chainInt32labelled, optional
blocksBlockslabelled, optional

returns Self

push

pub fn Writer::push(Self, BytesView) -> Bytes

Feed a chunk, take the compressed bytes it produced.

parameters
BytesView—positional

returns Bytes

fndeflate

pub fn deflate(BytesView, level? : Int32, window? : Int32, chain? : Int32, blocks? : Blocks) -> Bytes

Compress into a DEFLATE stream, one block over the whole input.

LZ77 finds back-references with a hash chain over the window, capped so the search stays linear in the input. blocks decides how the result is coded.

level runs 0 to 9 and means what it means in the format's reference: 0 stores the input without compressing it, 9 searches hardest, and 6 is the default every mainstream encoder uses. It sets how far back the match finder looks, with the same numbers the reference tables give it.

window is how far a back-reference may reach — 32 KiB is the most DEFLATE allows and the only value a reader can be assumed to handle. chain sets the search depth directly for a caller who would rather not go through a level.

level=0 writes stored blocks, which is what "no compression" means everywhere; blocks then has nothing to choose between.

parameters
BytesView—positional
levelInt32labelled, optional
windowInt32labelled, optional
chainInt32labelled, optional
blocksBlockslabelled, optional

returns Bytes

fninflate

pub fn inflate(BytesView, limit? : Int32) -> Bytes raise @moonzip.Refused

Decompress a raw DEFLATE stream.

All three block types: stored, fixed Huffman, and the dynamic Huffman blocks that carry their own code tables — which is what zlib, gzip and every browser actually emit, so a reader without them reads almost nothing.

limit bounds the output. A compressed stream says nothing about how much it expands to, and a small one can expand to more memory than the machine has; a reader handling input from elsewhere should say how much it is prepared to hold. Unbounded by default, which is what every library does.

parameters
BytesView—positional
limitInt32labelled, optional

returns Bytes · raises @moonzip.Refused

gzip — RFC 1952

typeReader

A gzip reader that takes the stream a piece at a time.

The header is gathered across pushes, because a chunk boundary can fall anywhere in it, and the trailer is checked when the body ends. One member only: a stream of concatenated members is what gunzip walks, since it has the whole thing in hand and knows where the next member starts.

The checks are the ones gunzip offers, and each has the same default and the same reason.

methods

finish

pub fn Reader::finish(Self) -> Bytes raise @moonzip.Refused

Say the stream is complete: check the trailer, or say why it cannot be.

returns Bytes · raises @moonzip.Refused

new

pub fn Reader::new(checksum? : Bool, length? : Bool, header_crc? : Bool, limit? : Int32) -> Self

A reader with the same checks gunzip offers.

parameters
checksumBoollabelled, optional
lengthBoollabelled, optional
header_crcBoollabelled, optional
limitInt32labelled, optional

returns Self

push

pub fn Reader::push(Self, BytesView) -> Bytes raise @moonzip.Refused

Feed a chunk of the stream, take the bytes it decompressed to.

parameters
BytesView—positional

returns Bytes · raises @moonzip.Refused

typeWriter

A gzip writer that takes the body a piece at a time.

This is the shape a response compressor wants: the header goes out with the first chunk, each chunk is compressed and handed straight back, and the trailer is written when the body is declared over. Neither the body nor the compressed form is ever held whole — only the 32 KiB window the compressor needs for its back-references and the four bytes of running checksum.

 mbt nochecklet w = @gzip.Writer::new(level=9)
let mut out : Array[Byte] = []
for part in parts {
for b in w.push(part[:]) {
out.push(b)
}
}
for b in w.finish() {
out.push(b)
}
methods

finish

pub fn Writer::finish(Self) -> Bytes

Finish the stream: whatever the compressor still holds, then the trailer. Nothing may be pushed afterwards.

returns Bytes

new

pub fn Writer::new(level? : Int32, window? : Int32, chain? : Int32, mtime? : UInt32, name? : String, comment? : String, os? : Int32, blocks? : @deflate.Blocks) -> Self

A writer whose header says what gzip would have been told.

parameters
levelInt32labelled, optional
windowInt32labelled, optional
chainInt32labelled, optional
mtimeUInt32labelled, optional
nameStringlabelled, optional
commentStringlabelled, optional
osInt32labelled, optional
blocks@deflate.Blockslabelled, optional

returns Self

push

pub fn Writer::push(Self, BytesView) -> Bytes

Compress a chunk and take the compressed bytes it produced, the header included the first time anything comes out.

Nothing may come back for a while on a small body: the compressor holds input until it has a block's worth, which is what makes the output small. All of it comes back by finish.

parameters
BytesView—positional

returns Bytes

fncrc32

pub fn crc32(BytesView) -> UInt32

CRC-32, where the container that specifies it names it.

The implementation is @moonzip.crc32: zip writes the same checksum, and neither container should have to import the other to reach it.

parameters
BytesView—positional

returns UInt32

fngunzip

pub fn gunzip(BytesView, checksum? : Bool, length? : Bool, header_crc? : Bool, limit? : Int32) -> Bytes raise @moonzip.Refused

Read a gzip container and decompress what is in it.

The trailing CRC-32 and length are checked, because a container that carries a checksum and does not look at it is not a container. Each check is a parameter for the cases where a stream is known to be truncated and the part that arrived is still wanted.

limit bounds the output, for a stream that came from elsewhere.

parameters
BytesView—positional
checksumBoollabelled, optional
lengthBoollabelled, optional
header_crcBoollabelled, optional
limitInt32labelled, optional

returns Bytes · raises @moonzip.Refused

fngzip

pub fn gzip(BytesView, level? : Int32, window? : Int32, chain? : Int32, mtime? : UInt32, name? : String, comment? : String, os? : Int32) -> Bytes

Wrap DEFLATE output in a gzip container.

The modification time is zero and the name is absent unless asked for, so the same input gives the same bytes. A compressor that stamped the clock would make a build unreproducible and a cache entry never match — which is why Go's gzip.Writer also writes zero unless told otherwise. Set mtime when the output is a file that should carry one.

level, window and chain go straight through to the compressor.

parameters
BytesView—positional
levelInt32labelled, optional
windowInt32labelled, optional
chainInt32labelled, optional
mtimeUInt32labelled, optional
nameStringlabelled, optional
commentStringlabelled, optional
osInt32labelled, optional

returns Bytes

zlib — RFC 1950

typeReader

A zlib reader that takes the stream a piece at a time.

Two bytes of header, then DEFLATE, then four bytes of checksum — and the header can be split across two pushes, which is why the reader keeps them rather than assuming a chunk is at least that long.

methods

finish

pub fn Reader::finish(Self) -> Bytes raise @moonzip.Refused

Say the stream is complete: check the checksum, or say why it cannot be.

returns Bytes · raises @moonzip.Refused

new

pub fn Reader::new(checksum? : Bool, limit? : Int32) -> Self

A reader with the same checks unzlib offers.

parameters
checksumBoollabelled, optional
limitInt32labelled, optional

returns Self

push

pub fn Reader::push(Self, BytesView) -> Bytes raise @moonzip.Refused

Feed a chunk of the stream, take the bytes it decompressed to.

parameters
BytesView—positional

returns Bytes · raises @moonzip.Refused

window

pub fn Reader::window(Self) -> Int32

The window the stream's header declared, in bytes.

returns Int32

typeWriter

A zlib writer that takes the body a piece at a time.

The two-byte header goes out with the first chunk, each chunk is compressed and handed straight back, and the big-endian Adler-32 goes out when the body is declared over. zlib is the container HTTP calls deflate, so this is what a server compressing a response body wants beside it.

methods

finish

pub fn Writer::finish(Self) -> Bytes

Finish the stream: what the compressor still holds, then the checksum.

returns Bytes

new

pub fn Writer::new(level? : Int32, window? : Int32, chain? : Int32, blocks? : @deflate.Blocks) -> Self

A writer whose header says what zlib would have been told.

parameters
levelInt32labelled, optional
windowInt32labelled, optional
chainInt32labelled, optional
blocks@deflate.Blockslabelled, optional

returns Self

push

pub fn Writer::push(Self, BytesView) -> Bytes

Compress a chunk and take what it produced, the header included the first time anything comes out.

parameters
BytesView—positional

returns Bytes

fnadler32

pub fn adler32(BytesView) -> UInt32

Adler-32, where the container that specifies it names it.

The implementation is @moonzip.adler32; keeping it here as well means a caller who has the container in hand does not have to reach for the root package to checksum what it read.

parameters
BytesView—positional

returns UInt32

fnunzlib

pub fn unzlib(BytesView, checksum? : Bool, limit? : Int32) -> Bytes raise @moonzip.Refused

Read a zlib container and decompress what is in it.

checksum is what makes it a container rather than a wrapper; limit bounds the output, for a stream that came from elsewhere.

parameters
BytesView—positional
checksumBoollabelled, optional
limitInt32labelled, optional

returns Bytes · raises @moonzip.Refused

fnzlib

pub fn zlib(BytesView, level? : Int32, window? : Int32, chain? : Int32) -> Bytes

Wrap DEFLATE output in a zlib container.

This, not raw DEFLATE, is what HTTP's Content-Encoding: deflate was specified to mean — a detail that half the servers on the web got wrong, which is why gzip won. level, window and chain go straight through to the compressor; the header says which window was used, so a reader knows how much history it must keep.

parameters
BytesView—positional
levelInt32labelled, optional
windowInt32labelled, optional
chainInt32labelled, optional

returns Bytes

zip — APPNOTE 6.3

traitCodec

required methods
fn id(Self) -> Int32
fn pack(Self, BytesView) -> Bytes
fn unpack(Self, BytesView, Int32?) -> Bytes

structEntry

One member of an archive.

data is the content itself: decompressed when read, as-is when written. An archive is a directory of whole files, so handing back half of one would be handing back something that is not the file.

fields
nameString

The path inside the archive, with forward slashes — APPNOTE.TXT §4.4.17 says so, on every platform.

dataBytes

The content.

kindKind

How it is held.

mtimeInt64

Last modification, in Unix seconds. ZIP records MS-DOS date and time, which has two-second resolution and no zone, so a value read back is the one that was written rounded down to an even second.

commentString

The per-entry comment, which most archives leave empty.

methods

equal

pub fn Entry::equal(Self, Self) -> Bool

Derived trait glue.

returns Bool

new

pub fn Entry::new(String, Bytes, kind? : Kind, mtime? : Int64, comment? : String) -> Self

An entry with only a name and its content, deflated, stamped at the epoch.

The epoch rather than the clock: a library has no clock, and an archive that stamped the time would give different bytes for the same input and make a build unreproducible. A caller with a real modification time passes it.

parameters
String—positional
Bytes—positional
kindKindlabelled, optional
mtimeInt64labelled, optional
commentStringlabelled, optional

returns Self

not_equal

pub fn Entry::not_equal(Self, Self) -> Bool

Derived trait glue.

returns Bool

to_repr

pub fn Entry::to_repr(Self) -> @debug.Repr

Derived trait glue.

returns @debug.Repr

implements Debug, Eq

enumKind

How an entry's bytes are held in the archive.

The format calls it the compression method; the field is kind, which is what this family calls "which variant of this enum" and is not a word MoonBit reserves.

The two that are actually in use. APPNOTE.TXT names a dozen more — shrink, implode, six reduce levels — and no tool has written one since the 1990s; a reader that meets one refuses it rather than guessing.

variants
Stored

Not compressed. What an already-compressed member should use: running DEFLATE over a JPEG costs time and makes it bigger.

Deflated

DEFLATE, which is what every archive in the wild uses for everything else.

OtherInt32

A method this package does not implement, by its APPNOTE.TXT number — bzip2 is 12, LZMA is 14. A Codec handles it; without one, reading such an entry is refused rather than guessed at.

methods

equal

pub fn Kind::equal(Self, Self) -> Bool

Derived trait glue.

returns Bool

not_equal

pub fn Kind::not_equal(Self, Self) -> Bool

Derived trait glue.

returns Bool

to_repr

pub fn Kind::to_repr(Self) -> @debug.Repr

Derived trait glue.

returns @debug.Repr

implements Debug, Eq

typeReader

Walk an archive from the front, one member at a time.

unzip reads the central directory at the end, which is what a file on disk allows and what makes an entry addressable by name. A reader that is *receiving* an archive — from a socket, from a pipe, out of a decompressor — has no end to seek to, and the local headers are all it has. This is that reader.

It also reads what a streaming writer writes: an entry whose checksum and sizes sit in a data descriptor after its data rather than in its header. That case is why this reader feeds DEFLATE until the stream says it has ended instead of trusting a length in front of it.

methods

new

pub fn Reader::new(BytesView) -> Self

A reader at the start of archive.

parameters
BytesView—positional

returns Self

next

pub fn Reader::next(Self, limit? : Int32, codecs? : ArrayView[&Codec], password? : String) -> Entry? raise @moonzip.Refused

The next member, or None when the members have run out.

limit bounds what one member may expand to, as everywhere else.

parameters
limitInt32labelled, optional
codecsArrayView[&Codec]labelled, optional
passwordStringlabelled, optional

returns Entry? · raises @moonzip.Refused

typeWriter

Build an archive a member at a time.

zip is this writer with every entry added and then finished, which is the shape a caller with the whole archive in hand wants. A caller that is receiving members one at a time — a backup running, a directory being walked — wants this instead: add hands back the bytes for that member immediately, and only the central directory waits for the end.

Three knobs decide the bytes beyond the content itself:

zip64 widens the size and offset fields to sixty-four bits. It turns itself on when an entry or an offset does not fit in thirty-two, which is the only reason it exists; forcing it is for a caller whose reader expects the wider form.

descriptor puts each entry's checksum and sizes after its data instead of in its local header (APPNOTE.TXT §4.3.9). That is what a writer that streams a member it has not finished reading must do — it cannot know the sizes when it writes the header — and it costs twelve bytes a member.

password? encrypts each member. With no aes strength it uses ZipCrypto, which the format called encryption for fifteen years and which is not a security boundary (see zipcrypt_encrypt). With an aes strength of 128, 192 or 256 it uses WinZip AES instead (see aes_encrypt), which is.

methods

add

pub fn Writer::add(Self, Entry, codecs? : ArrayView[&Codec]) -> Bytes

Add a member and take the bytes it occupies: a local header, its data, and a data descriptor if one was asked for.

The compressed form is kept only if it is smaller than the input. A member that grew by being compressed is stored instead, which is what zip itself does and what makes an archive of already-compressed files the size of the files.

parameters
Entry—positional
codecsArrayView[&Codec]labelled, optional

returns Bytes

finish

pub fn Writer::finish(Self) -> Bytes

Close the archive: the central directory, then the end record — and the Zip64 end record and its locator as well if any entry needed widening.

returns Bytes

new

pub fn Writer::new(level? : Int32, zip64? : Bool, descriptor? : Bool, password? : String, aes? : Int32, ae? : Int32) -> Self

A writer that has written nothing yet.

parameters
levelInt32labelled, optional
zip64Boollabelled, optional
descriptorBoollabelled, optional
passwordStringlabelled, optional
aesInt32labelled, optional
aeInt32labelled, optional

returns Self

fnaes_decrypt

pub fn aes_decrypt(BytesView, String, Int32) -> Bytes raise @moonzip.Refused

Decrypt what aes_encrypt wrote.

The salt names the key, the verification value rejects a wrong password before any ciphertext is touched, and the authentication tag is checked before the bytes are handed back — a truncated HMAC is a message authentication code, and skipping it would turn a tampered archive into a silent decompression error three layers down.

parameters
BytesView—positional
String—positional
Int32—positional

returns Bytes · raises @moonzip.Refused

fnaes_encrypt

pub fn aes_encrypt(BytesView, String, String, Int64, Int64, strength? : Int32) -> Bytes

Encrypt data under password with WinZip AES.

strength is 128, 192 or 256. Returns the bytes that go where the payload used to: salt, verification value, ciphertext, authentication tag. The AE-1 versus AE-2 choice belongs to the writer, because it is a choice about the CRC in the header rather than about the ciphertext.

parameters
BytesView—positional
String—positional
String—positional
Int64—positional
Int64—positional
strengthInt32labelled, optional

returns Bytes

fnentry

pub fn entry(BytesView, String, limit? : Int32, codecs? : ArrayView[&Codec], password? : String) -> Entry? raise @moonzip.Refused

One entry by name, expanding only that one.

None for a name the archive does not hold — asking whether something is in an archive is a question, not a failure. A name that is there but whose content is damaged still raises: that is a broken archive, not an absent member.

parameters
BytesView—positional
String—positional
limitInt32labelled, optional
codecsArrayView[&Codec]labelled, optional
passwordStringlabelled, optional

returns Entry? · raises @moonzip.Refused

fnjoin

pub fn join(ArrayView[Bytes]) -> Bytes

Put the volumes back together, in the order they were split.

The result is the archive the volumes were cut from, which is what unzip and names expect.

parameters
ArrayView[Bytes]—positional

returns Bytes

valuelevel

pub let level : Int32

fnnames

pub fn names(BytesView) -> Array[String] raise @moonzip.Refused

Every entry's name, without decompressing anything.

The central directory is a table at the end of the archive that names every member; reading it costs nothing next to expanding them. This is what a caller listing an archive wants, and what zipfile.namelist() is.

parameters
BytesView—positional

returns Array[String] · raises @moonzip.Refused

fnsplit

pub fn split(BytesView, Int32) -> Array[Bytes]

Cut a finished archive into volumes of at most size bytes, in order.

Every volume but the last is exactly size bytes; the last holds the rest, which may be less. An archive that fits in one volume comes back as a single volume, and an empty one as an empty array. size must be positive: a volume that holds nothing would be a loop that never advances.

parameters
BytesView—positional
Int32—positional

returns Array[Bytes]

fnunzip

pub fn unzip(BytesView, limit? : Int32, codecs? : ArrayView[&Codec], password? : String) -> Array[Entry] raise @moonzip.Refused

Every entry, expanded.

limit bounds what one entry may expand to. An archive is worth bounding even more than a single stream is: a few kilobytes of archive can declare a hundred members that each claim to be a gigabyte.

parameters
BytesView—positional
limitInt32labelled, optional
codecsArrayView[&Codec]labelled, optional
passwordStringlabelled, optional

returns Array[Entry] · raises @moonzip.Refused

fnunzip_spanned

pub fn unzip_spanned(ArrayView[Bytes], limit? : Int32, codecs? : ArrayView[&Codec], password? : String) -> Array[Entry] raise @moonzip.Refused

Read an archive that arrived as spanned volumes.

The volumes are joined, each directory record's disk and relative offset are translated back to one coordinate space, and the result is read exactly as unzip reads a single buffer. Volumes must be in disk order.

parameters
ArrayView[Bytes]—positional
limitInt32labelled, optional
codecsArrayView[&Codec]labelled, optional
passwordStringlabelled, optional

returns Array[Entry] · raises @moonzip.Refused

fnunzip_split

pub fn unzip_split(ArrayView[Bytes], limit? : Int32, codecs? : ArrayView[&Codec], password? : String) -> Array[Entry] raise @moonzip.Refused

Read an archive that arrived as several volumes.

The volumes are joined first — a split archive is the same bytes cut at boundaries, so the pieces in order are the archive — and then read exactly as unzip reads one buffer.

parameters
ArrayView[Bytes]—positional
limitInt32labelled, optional
codecsArrayView[&Codec]labelled, optional
passwordStringlabelled, optional

returns Array[Entry] · raises @moonzip.Refused

fnvolume_names

pub fn volume_names(String, Int32) -> Array[String]

The file names a split archive conventionally uses.

For an archive called base, the first count - 1 volumes are base.z01, base.z02, … and the last is base.zip — the extension the tools look for, which is why the final volume carries the one that says "zip" rather than another number. A single volume is just base.zip.

parameters
String—positional
Int32—positional

returns Array[String]

fnzip

pub fn zip(ArrayView[Entry], level? : Int32, codecs? : ArrayView[&Codec], zip64? : Bool, descriptor? : Bool, password? : String, aes? : Int32, ae? : Int32) -> Bytes

Write an archive.

Entries are written in the order they are given, each with a local header, then the central directory, then the end record — the layout APPNOTE.TXT describes and the one every reader expects.

level reaches the compressor for the deflated entries; Stored ones carry their bytes unchanged. An entry that deflates to no less than it started as is stored instead, which is what zip itself does: a member should never grow by being compressed.

zip64, descriptor, password, aes and ae are the Writer's knobs, and they mean the same thing here. aes is 0 for no AES (ZipCrypto if a password is given), or 128, 192 or 256 for WinZip AES at that strength. This function is that writer with every entry in hand at once.

parameters
ArrayView[Entry]—positional
levelInt32labelled, optional
codecsArrayView[&Codec]labelled, optional
zip64Boollabelled, optional
descriptorBoollabelled, optional
passwordStringlabelled, optional
aesInt32labelled, optional
aeInt32labelled, optional

returns Bytes

fnzip_spanned

pub fn zip_spanned(ArrayView[Entry], Int32, level? : Int32, codecs? : ArrayView[&Codec], descriptor? : Bool, password? : String, aes? : Int32, ae? : Int32) -> Array[Bytes]

Write an archive as spanned volumes of at most size bytes each.

Each member's central-directory record is rewritten with the disk it starts on and its offset from that disk's first byte, and the end record is rewritten with the last disk's number and the directory's disk-relative offset. The arguments after size are zip's.

A caller with a file system writes volume i under the name volume_names gives it. The result differs from a split only in those header fields: the bytes of the members are the same, so a mode that has no need of disk numbers should use the simpler one.

parameters
ArrayView[Entry]—positional
Int32—positional
levelInt32labelled, optional
codecsArrayView[&Codec]labelled, optional
descriptorBoollabelled, optional
passwordStringlabelled, optional
aesInt32labelled, optional
aeInt32labelled, optional

returns Array[Bytes]

fnzip_split

pub fn zip_split(ArrayView[Entry], Int32, level? : Int32, codecs? : ArrayView[&Codec], zip64? : Bool, descriptor? : Bool, password? : String, aes? : Int32, ae? : Int32) -> Array[Bytes]

Write an archive and split it in one step.

The arguments are zip's, plus the volume size. This is the call a caller with a large archive and a size limit wants: it never builds the whole stream twice, and the volumes are the ones unzip_split reads back.

parameters
ArrayView[Entry]—positional
Int32—positional
levelInt32labelled, optional
codecsArrayView[&Codec]labelled, optional
zip64Boollabelled, optional
descriptorBoollabelled, optional
passwordStringlabelled, optional
aesInt32labelled, optional
aeInt32labelled, optional

returns Array[Bytes]

fnzipcrypt_decrypt

pub fn zipcrypt_decrypt(BytesView, String, Byte) -> Bytes raise @moonzip.Refused

Decrypt what zipcrypt_encrypt wrote.

The check byte is compared before any of the body is handed back, so a wrong password says so instead of turning into a decompressor error three layers further in — which is the difference between "you typed the password wrong" and "this archive is damaged".

parameters
BytesView—positional
String—positional
Byte—positional

returns Bytes · raises @moonzip.Refused

fnzipcrypt_encrypt

pub fn zipcrypt_encrypt(BytesView, String, String, Int64, Byte) -> Bytes

Encrypt data under password.

check is the byte a reader compares after decrypting the header: the high byte of the CRC-32, or — when the entry's sizes are in a data descriptor rather than its local header — the high byte of the MS-DOS time. The caller knows which, because the caller owns the flags.

parameters
BytesView—positional
String—positional
String—positional
Int64—positional
Byte—positional

returns Bytes

tar — POSIX pax

structEntry

One member of an archive.

fields
nameString

The path inside the archive, with forward slashes.

dataBytes

The content. Empty for anything that is not a file.

kindTarball

What it is.

modeInt32

Permission bits, which tar keeps as a whole 07777 — setuid and sticky included, because a tar that dropped them could not restore /usr/bin.

mtimeInt64

Last modification, in Unix seconds.

uidInt32
gidInt32
unameString

The owner's name, which is what a reader prefers over the number when the archive is restored on a different machine.

gnameString

The group's name.

linkString

The target of a link.

methods

directory

pub fn Entry::directory(String, mode? : Int32) -> Self

A directory entry.

parameters
String—positional
modeInt32labelled, optional

returns Self

equal

pub fn Entry::equal(Self, Self) -> Bool

Derived trait glue.

returns Bool

new

pub fn Entry::new(String, Bytes, kind? : Tarball, mode? : Int32, mtime? : Int64) -> Self

A file with only a name and its content.

parameters
String—positional
Bytes—positional
kindTarballlabelled, optional
modeInt32labelled, optional
mtimeInt64labelled, optional

returns Self

not_equal

pub fn Entry::not_equal(Self, Self) -> Bool

Derived trait glue.

returns Bool

symlink

pub fn Entry::symlink(String, String) -> Self

A symbolic link.

parameters
String—positional
String—positional

returns Self

to_repr

pub fn Entry::to_repr(Self) -> @debug.Repr

Derived trait glue.

returns @debug.Repr

implements Debug, Eq

enumTarball

What a member of an archive is.

tar carries more than files: a directory, a symbolic link, a hard link to a member further up, and the three device nodes. The ones a caller is likely to meet are here; the rest are kept as Other so that reading an archive with a device node in it does not fail, and so that writing one back out produces what came in.

variants
File

A regular file. The only one with bytes in it.

Directory

A directory. Its name ends with a slash and its size is zero.

Symlink

A symbolic link, whose target is in the entry's link field.

Hardlink

A hard link to a member already in the archive, by the name in link.

OtherByte

A character or block device, a FIFO, or a type this package does not name.

methods

equal

pub fn Tarball::equal(Self, Self) -> Bool

Derived trait glue.

returns Bool

not_equal

pub fn Tarball::not_equal(Self, Self) -> Bool

Derived trait glue.

returns Bool

to_repr

pub fn Tarball::to_repr(Self) -> @debug.Repr

Derived trait glue.

returns @debug.Repr

implements Debug, Eq

fnnames

pub fn names(BytesView) -> Array[String] raise @moonzip.Refused

Every entry's name, which still has to walk the archive: tar has no index.

parameters
BytesView—positional

returns Array[String] · raises @moonzip.Refused

fntar

pub fn tar(ArrayView[Entry]) -> Bytes

Write an archive.

Members go out in the order they are given, each as a header, its bytes padded to a block boundary, then two empty blocks to say the archive is over.

A name longer than a hundred bytes is carried in a pax extended header, which is a member of its own whose content is a list of keyword=value records. That is the POSIX answer, and it is what GNU tar and bsdtar both read; the older GNU L record is read but not written, because one way to say a thing is enough.

The archive is reproducible: the modification time comes from the entry and the owner is nobody, so the same entries always give the same bytes. Set mtime and uid on an entry when it should carry something else.

parameters
ArrayView[Entry]—positional

returns Bytes

fntargz

pub fn targz(ArrayView[Entry], level? : Int32) -> Bytes

A tarball: the archive gzipped, which is what .tar.gz and .tgz are.

The two formats stack cleanly — gzip compresses the bytes, and the bytes are a tar archive — so this is three lines rather than a format of its own. The compression is of the whole archive, which is why a tarball compresses far better than the same files compressed one at a time: gzip sees every member's redundancy, not just each member's own.

parameters
ArrayView[Entry]—positional
levelInt32labelled, optional

returns Bytes

fnuntar

pub fn untar(BytesView, limit? : Int32) -> Array[Entry] raise @moonzip.Refused

Read an archive.

Both ustar and pax come out, and the GNU long-name records that older archives use are understood on the way. limit bounds what one member may expand to, and tar needs it more than most: an archive is a header claiming a length, and a header can claim anything.

parameters
BytesView—positional
limitInt32labelled, optional

returns Array[Entry] · raises @moonzip.Refused

fnuntargz

pub fn untargz(BytesView, limit? : Int32) -> Array[Entry] raise @moonzip.Refused

Read a tarball.

parameters
BytesView—positional
limitInt32labelled, optional

returns Array[Entry] · raises @moonzip.Refused

lz4 — block + frame

typeReader

A frame reader that is fed a piece at a time.

The unit is again one block: the reader holds the bytes of the block it is waiting for and decodes it the moment it is complete, so the memory it needs is the frame's declared block size whatever the body's length. A block that arrives in pieces produces nothing until it is whole, and one that arrives whole is handed back by the same push.

methods

finish

pub fn Reader::finish(Self) -> Bytes raise @moonzip.Refused

returns Bytes · raises @moonzip.Refused

new

pub fn Reader::new(checksum? : Bool, limit? : Int32) -> Self
parameters
checksumBoollabelled, optional
limitInt32labelled, optional

returns Self

push

pub fn Reader::push(Self, BytesView) -> Bytes raise @moonzip.Refused
parameters
BytesView—positional

returns Bytes · raises @moonzip.Refused

typeWriter

A frame writer that takes the body a piece at a time.

A frame is a sequence of independent blocks, so the unit of work is one block: what is held between calls is the sixty-four kilobytes being filled and the running content checksum, never the body. A push that does not fill a block produces nothing, which is the format's own granularity rather than a choice made here.

 moonbit nochecklet w = @lz4.Writer::new(level=0)
let mut out : Array[Byte] = []
for part in parts {
for b in w.push(part[:]) {
out.push(b)
}
}
for b in w.finish() {
out.push(b)
}
methods

finish

pub fn Writer::finish(Self) -> Bytes

returns Bytes

new

pub fn Writer::new(level? : Int32) -> Self
parameters
levelInt32labelled, optional

returns Self

push

pub fn Writer::push(Self, BytesView) -> Bytes
parameters
BytesView—positional

returns Bytes

structXxh32

The same hash, fed a piece at a time.

A frame's content checksum covers the bytes before they were compressed, so a writer that never holds the whole body needs the hash as a state machine. What is left between calls is the four lanes, the length so far, and the bytes that did not fill a sixteen-byte stripe — which is what the four-byte and one-byte loops of the one-shot form would have read.

 moonbit nochecklet h = @lz4.Xxh32::new()
h.push(part_one[:])
h.push(part_two[:])
let sum = h.finish()
fields
seedUInt32
v1UInt32
v2UInt32
v3UInt32
v4UInt32
totalInt32
heldArray[Byte]
methods

finish

pub fn Xxh32::finish(Self) -> UInt32

returns UInt32

new

pub fn Xxh32::new(seed? : UInt32) -> Self
parameters
seedUInt32labelled, optional

returns Self

push

pub fn Xxh32::push(Self, BytesView) -> Unit
parameters
BytesView—positional

returns Unit

fnframe

pub fn frame(BytesView, level? : Int32) -> Bytes

Wrap LZ4 blocks in a frame: a header, the blocks, an end mark and a content checksum.

Blocks are independent, so a decoder can start at any of them; the content checksum is over the original bytes rather than the compressed ones, so it catches a decoder that produced the wrong thing rather than only a link that flipped a bit.

A block that does not compress is stored as it is with the high bit of its length set, which is how the format says "this one is verbatim".

parameters
BytesView—positional
levelInt32labelled, optional

returns Bytes

fnlz4

pub fn lz4(BytesView, level? : Int32) -> Bytes

Compress into the LZ4 block format.

A block is a run of sequences. Each is a token whose high nibble is how many literals follow and whose low nibble is the match length minus four, then the literals, then a two-byte back-reference, then the match length's overflow bytes. The last sequence carries literals only, and the format reserves the last five bytes of the input for them — a decoder relies on both, which is why match_start stops twelve bytes short of the end.

level picks the search depth: 0 uses one candidate per position, which is what LZ4's own "fast" mode does; higher values walk a chain. The ratio gain above the default is small and the cost is real — this is a format chosen for speed.

parameters
BytesView—positional
levelInt32labelled, optional

returns Bytes

fnunframe

pub fn unframe(BytesView, checksum? : Bool, limit? : Int32) -> Bytes raise @moonzip.Refused

Read a frame back.

checksum is what turns "some bytes came out" into "the right bytes came out"; a caller recovering what it can from a damaged transfer turns it off. A skippable frame — the format's way of saying "not mine, step over it" — is stepped over rather than refused.

parameters
BytesView—positional
checksumBoollabelled, optional
limitInt32labelled, optional

returns Bytes · raises @moonzip.Refused

fnunlz4

pub fn unlz4(BytesView, limit? : Int32) -> Bytes raise @moonzip.Refused

Decompress an LZ4 block.

There is no length in the format, so limit is the only thing standing between a hostile block and the heap.

parameters
BytesView—positional
limitInt32labelled, optional

returns Bytes · raises @moonzip.Refused

fnxxh32

pub fn xxh32(BytesView, seed? : UInt32) -> UInt32

The hash itself.

parameters
BytesView—positional
seedUInt32labelled, optional

returns UInt32

snappy — block + framing v2

typeReader

A framed reader that is fed a piece at a time.

A chunk is the largest thing held: its header says its length, so a reader waits for exactly that many bytes and then decodes, and the memory it needs is one chunk whatever the body's length.

methods

finish

pub fn Reader::finish(Self) -> Bytes raise @moonzip.Refused

returns Bytes · raises @moonzip.Refused

new

pub fn Reader::new(checksum? : Bool, limit? : Int32) -> Self
parameters
checksumBoollabelled, optional
limitInt32labelled, optional

returns Self

push

pub fn Reader::push(Self, BytesView) -> Bytes raise @moonzip.Refused
parameters
BytesView—positional

returns Bytes · raises @moonzip.Refused

typeWriter

A framed writer that takes the body a piece at a time.

The framing format's unit is a sixty-four-kilobyte chunk with its own checksum, so a writer can hand each one over as soon as it fills and hold nothing else. That is why the format exists: a receiver reads a chunk, checks it, and appends.

 moonbit nochecklet w = @snappy.Writer::new()
let mut out : Array[Byte] = []
for part in parts {
for b in w.push(part[:]) {
out.push(b)
}
}
for b in w.finish() {
out.push(b)
}
methods

finish

pub fn Writer::finish(Self) -> Bytes

returns Bytes

new

pub fn Writer::new() -> Self

returns Self

push

pub fn Writer::push(Self, BytesView) -> Bytes
parameters
BytesView—positional

returns Bytes

fnframe

pub fn frame(BytesView) -> Bytes

Wrap Snappy blocks in the framing format.

The stream identifier comes first, then a chunk per sixty-four kilobytes — compressed with a masked CRC-32C when compressing helped, verbatim with one when it did not. A chunk that grew would be a chunk that cost time to decode and bytes to store, so the format has a way to say "verbatim" and this uses it.

parameters
BytesView—positional

returns Bytes

fnsnappy

pub fn snappy(BytesView) -> Bytes

Compress into the Snappy block format.

A block starts with the uncompressed length as a varint — the one thing a decoder is told up front, and the reason Snappy needs no bound of its own to be safe — and then a run of tags. A tag is a literal or a copy, and its two low bits say which: 00 a literal, 01 a copy with an eleven-bit offset, 10 one with a sixteen-bit offset, 11 one with a thirty-two-bit offset. The three copy forms exist so that the common cases cost two bytes.

The compressor is greedy: one candidate per position from a hash of four bytes, extended as far as it runs. Snappy's own encoder is not much cleverer, because the format's whole point is that decompression be quick.

parameters
BytesView—positional

returns Bytes

fnunframe

pub fn unframe(BytesView, checksum? : Bool, limit? : Int32) -> Bytes raise @moonzip.Refused

Read a framed stream back.

checksum is what makes the CRC-32C in each chunk mean something; a caller recovering what arrived from a broken transfer turns it off. A skippable chunk — the format's way of letting a caller put its own data in the middle — is stepped over, and an unskippable one is refused, because that is the difference the two ranges of type numbers exist to make.

parameters
BytesView—positional
checksumBoollabelled, optional
limitInt32labelled, optional

returns Bytes · raises @moonzip.Refused

fnunsnappy

pub fn unsnappy(BytesView, limit? : Int32) -> Bytes raise @moonzip.Refused

Decompress a Snappy block.

The length in front is a claim, not a fact, so limit still applies: a sixteen-byte block can claim four gigabytes. The claim is also what lets this reader refuse a stream that ends early rather than return what it got.

parameters
BytesView—positional
limitInt32labelled, optional

returns Bytes · raises @moonzip.Refused

lzma — LZMA SDK

typeDecoder

A decoder that can be fed a stream in pieces, which is what LZMA2 needs: the dictionary and the learned probabilities survive across chunks, and only the range coder restarts.

methods

append

pub fn Decoder::append(Self, BytesView) -> Unit

Append bytes decoded elsewhere — an LZMA2 chunk that was stored rather than ranged-coded still belongs in the dictionary, or the next chunk's matches would land on the wrong bytes.

parameters
BytesView—positional

returns Unit

feed

pub fn Decoder::feed(Self, BytesView, Int32) -> Int32 raise @moonzip.Refused

Decode until want bytes have been produced, or — when want is negative — until the end marker. Returns the number produced by this call.

Everything is measured against out, which is both the answer and the dictionary: a back-reference is an index into what has already been produced, so there is nothing else to keep.

parameters
BytesView—positional
Int32—positional

returns Int32 · raises @moonzip.Refused

length

pub fn Decoder::length(Self) -> Int32

How much has been produced — the mark a caller keeps before a chunk.

returns Int32

new

pub fn Decoder::new(limit? : Int32) -> Self

A decoder with the default probabilities and nothing decoded.

parameters
limitInt32labelled, optional

returns Self

output

pub fn Decoder::output(Self) -> Bytes

The bytes decoded so far, which for a container is also the dictionary.

returns Bytes

properties

pub fn Decoder::properties(Self, Int32) -> Unit raise @moonzip.Refused

Adopt a properties byte: literally pb * 5 * 9 + lp * 9 + lc, which is how LZMA packs three small numbers into one.

parameters
Int32—positional

returns Unit · raises @moonzip.Refused

reset

pub fn Decoder::reset(Self) -> Unit

Forget everything but the model's shape: the start of a stream, or a dictionary reset in LZMA2.

returns Unit

since

pub fn Decoder::since(Self, Int32) -> Bytes

The bytes produced since mark, which is how a chunk's output is taken without copying the whole dictionary.

parameters
Int32—positional

returns Bytes

trim

pub fn Decoder::trim(Self, Int32) -> Unit

Drop everything a back-reference can no longer reach, keeping keep bytes. This is what makes a streaming reader's memory the dictionary rather than the stream.

parameters
Int32—positional

returns Unit

typeReader

A .lzma reader that is fed a piece at a time.

There are no blocks to wait for, so the reader stops wherever a token ends and keeps the range coder's three registers — the range, the code and the read position — to carry on from. What it will not do is stop in the middle of a token: the probabilities have been updated by then, and a token cannot be decoded twice. A token can consume at most a few hundred bytes, which is why the reader waits until that much is in hand rather than trying and rewinding.

methods

finish

pub fn Reader::finish(Self) -> Bytes raise @moonzip.Refused

returns Bytes · raises @moonzip.Refused

new

pub fn Reader::new(limit? : Int32) -> Self
parameters
limitInt32labelled, optional

returns Self

push

pub fn Reader::push(Self, BytesView) -> Bytes raise @moonzip.Refused
parameters
BytesView—positional

returns Bytes · raises @moonzip.Refused

typeWriter

An .lzma writer that takes the body a piece at a time.

The format is a single range-coded stream with no block structure, so there is no boundary to reset at: what is held between calls is the dictionary a match may reach into, the hash chains over it, and the learned probabilities. That is a window, not a body — which is what lets a two-gigabyte file be written by a process that never holds two gigabytes.

The size in the header is left unknown and the stream ends with an end marker, because a writer that has not seen the end of the body cannot promise a length.

 moonbit nochecklet w = @lzma.Writer::new(level=6)
let mut out : Array[Byte] = []
for part in parts {
for b in w.push(part[:]) {
out.push(b)
}
}
for b in w.finish() {
out.push(b)
}
methods

finish

pub fn Writer::finish(Self) -> Bytes

returns Bytes

new

pub fn Writer::new(level? : Int32) -> Self
parameters
levelInt32labelled, optional

returns Self

push

pub fn Writer::push(Self, BytesView) -> Bytes
parameters
BytesView—positional

returns Bytes

valuechunk_size

pub let chunk_size : Int32

fndecoded_dict

pub fn decoded_dict(Int32) -> Int32
parameters
Int32—positional

returns Int32

fndict_code

pub fn dict_code(Int32) -> Int32

The LZMA2 dictionary-size code: the smallest property whose decoded size is at least what was asked for, because a decoder has to know how much memory to reserve before it starts.

parameters
Int32—positional

returns Int32

fndictionary_for

pub fn dictionary_for(Int32) -> Int32

The dictionary size a level asks for. It bounds how far back a match may reach, and therefore how much a decoder must hold; 8 MiB at the top is what xz -9 uses.

parameters
Int32—positional

returns Int32

fnencode

pub fn encode(BytesView, Int32, Int32, level? : Int32) -> Bytes

Compress with LZMA, using the given properties and dictionary size.

The match finder is a hash chain over three bytes, walked greedily: the first match of three or more bytes wins, and the repeat distance is tried before the chain because it costs a handful of bits instead of a distance. That is a long way from an optimal parse, and it is the same order of effort a mainstream level-six encoder makes — the point here is a stream that real decoders read, not the last three percent.

parameters
BytesView—positional
Int32—positional
Int32—positional
levelInt32labelled, optional

returns Bytes

fnlzma

pub fn lzma(BytesView, level? : Int32) -> Bytes

Compress into the .lzma "alone" container: thirteen bytes of header, then the range-coded stream.

The header says the properties (how many context bits the literals use), the dictionary size, and the uncompressed size — which is written, so the stream does not need an end marker. level maps to the same presets xz uses, and the dictionary is capped at what a decoder can be asked to hold.

parameters
BytesView—positional
levelInt32labelled, optional

returns Bytes

fnlzma2

pub fn lzma2(BytesView, Int32, Int32) -> Bytes

LZMA2: LZMA in chunks, each with its own range coder, and a control byte that says how much state to throw away first.

Every chunk resets the state and the properties, which costs a few hundred forgettable decisions per megabyte and makes each chunk independent; the dictionary is the only thing carried across. The first chunk must reset the dictionary too — a decoder has none to start from.

parameters
BytesView—positional
Int32—positional
Int32—positional

returns Bytes

fnproperties_of

pub fn properties_of(Int32, Int32, Int32) -> Int32

The properties byte for a set of parameters.

parameters
Int32—positional
Int32—positional
Int32—positional

returns Int32

fnunlzma

pub fn unlzma(BytesView, limit? : Int32) -> Bytes raise @moonzip.Refused

Read a .lzma "alone" container.

A stream may leave its length unknown and end with a marker instead, which is what every encoder did before sizes could be trusted; both are read here.

parameters
BytesView—positional
limitInt32labelled, optional

returns Bytes · raises @moonzip.Refused

fnunlzma2

pub fn unlzma2(BytesView, Int32, Decoder) -> (Bytes, Int32) raise @moonzip.Refused

Decode the LZMA2 chunks starting at at, and say where they ended.

parameters
BytesView—positional
Int32—positional
Decoder—positional

returns (Bytes, Int32) · raises @moonzip.Refused

xz — xz file format

enumCheck

The check an xz block carries: a checksum over the uncompressed data, after the block's padding.

CRC-64 is xz's default; CRC-32 and none exist for streams that want them. SHA-256 is in the format and is read here without being verified, because verifying it would mean carrying a hash function this library does not have — the stream still decodes, and the gap is stated rather than hidden.

variants
None
Crc32
Crc64
methods

equal

pub fn Check::equal(Self, Self) -> Bool

Derived trait glue.

returns Bool

not_equal

pub fn Check::not_equal(Self, Self) -> Bool

Derived trait glue.

returns Bool

to_repr

pub fn Check::to_repr(Self) -> @debug.Repr

Derived trait glue.

returns @debug.Repr

implements Debug, Eq

structCrc64

CRC-64 as xz uses it: the reflected ECMA-182 polynomial, starting and ending complemented. Sixty-four bits because a block can be a gigabyte, and a thirty-two-bit checksum over that much is a weaker claim than the format wanted to make.

The struct is the same checksum kept as a state, which is what a block that is still arriving needs: the check covers the bytes before they were compressed, and a streaming writer never holds them all.

fields
sumUInt64
methods

finish

pub fn Crc64::finish(Self) -> UInt64

returns UInt64

new

pub fn Crc64::new() -> Self

returns Self

push

pub fn Crc64::push(Self, BytesView) -> Unit
parameters
BytesView—positional

returns Unit

typeReader

A reader that is fed an xz stream a piece at a time.

A chunk states both sizes, so the reader waits for exactly the bytes it needs and decodes one chunk at a time; the dictionary it keeps is the one the block header named. The index and the footer are checked at the end, which is the only place a truncated file can be told from a whole one.

methods

finish

pub fn Reader::finish(Self) -> Bytes raise @moonzip.Refused

returns Bytes · raises @moonzip.Refused

new

pub fn Reader::new(limit? : Int32) -> Self
parameters
limitInt32labelled, optional

returns Self

push

pub fn Reader::push(Self, BytesView) -> Bytes raise @moonzip.Refused
parameters
BytesView—positional

returns Bytes · raises @moonzip.Refused

typeWriter

An xz writer that takes the body a piece at a time.

The container is built for this: a block holds a run of LZMA2 chunks, each with its own range coder and its own sizes, so a chunk can be handed over as soon as it fills. What is held between calls is one chunk — a megabyte — and the two counters the index and the check need. The index is written at the end because that is where the format puts it, and because its sizes are only known then.

 moonbit nochecklet w = @xz.Writer::new(level=6)
let mut out : Array[Byte] = []
for part in parts {
for b in w.push(part[:]) {
out.push(b)
}
}
for b in w.finish() {
out.push(b)
}
methods

finish

pub fn Writer::finish(Self) -> Bytes

returns Bytes

new

pub fn Writer::new(level? : Int32, check? : Check) -> Self
parameters
levelInt32labelled, optional
checkChecklabelled, optional

returns Self

push

pub fn Writer::push(Self, BytesView) -> Bytes
parameters
BytesView—positional

returns Bytes

fnunxz

pub fn unxz(BytesView, limit? : Int32) -> Bytes raise @moonzip.Refused

Read an xz stream.

The header, the block header, the index and the footer are each checked against their own CRC, and the block against the check the header named. A stream that disagrees with itself is refused rather than decoded into something plausible.

parameters
BytesView—positional
limitInt32labelled, optional

returns Bytes · raises @moonzip.Refused

fnxz

pub fn xz(BytesView, level? : Int32, check? : Check) -> Bytes

Compress into an xz stream.

One block holds everything; a stream of several blocks is what a parallel compressor produces and a reader does not care either way. The block names LZMA2 with a stated dictionary, the index follows it, and the footer closes it — all three of which a reader checks before trusting a byte.

The block's data is padded to a four-byte boundary *before* its check, not after: that is where the specification puts it, and a stream that pads afterwards reads its own check three bytes early and is rejected by every other implementation.

parameters
BytesView—positional
levelInt32labelled, optional
checkChecklabelled, optional

returns Bytes

bzip2 — bzip2

typeReader

A reader that is fed a piece at a time.

A bzip2 block cannot be decoded until all of it has arrived, so a push that does not complete a block produces nothing. What survives between pushes is the bit position of the block that is being waited for — a *bit* position, not a byte one, because the format does not pad between blocks: the next marker starts at the next bit, which is why this reader keeps an accumulator rather than an offset.

methods

finish

pub fn Reader::finish(Self) -> Bytes raise @moonzip.Refused

returns Bytes · raises @moonzip.Refused

new

pub fn Reader::new(checksum? : Bool, limit? : Int32) -> Self
parameters
checksumBoollabelled, optional
limitInt32labelled, optional

returns Self

push

pub fn Reader::push(Self, BytesView) -> Bytes raise @moonzip.Refused
parameters
BytesView—positional

returns Bytes · raises @moonzip.Refused

typeWriter

A compressor that is fed a piece at a time.

bzip2 cannot emit anything until a block is complete — the sort needs all of it — so what is held between calls is one block's worth of the run-length stage plus a block of output. That is the format's own memory bound, not an arbitrary one, and it is why the level is a memory setting.

 moonbit nochecklet w = @bzip2.Writer::new(level=9)
let mut out = w.push(part_one[:])
out = out + w.push(part_two[:])
out = out + w.finish()
methods

finish

pub fn Writer::finish(Self) -> Bytes

returns Bytes

new

pub fn Writer::new(level? : Int32) -> Self
parameters
levelInt32labelled, optional

returns Self

push

pub fn Writer::push(Self, BytesView) -> Bytes
parameters
BytesView—positional

returns Bytes

fnbunzip2

pub fn bunzip2(BytesView, limit? : Int32) -> Bytes raise @moonzip.Refused

Decompress a bzip2 stream.

limit is how much output the caller is prepared to hold; a compressed stream says nothing about how far it expands, so a reader of untrusted input should say. Past it, TooBig rather than an allocation.

 moonbit nochecklet plain = @bzip2.bunzip2(packed[:])
parameters
BytesView—positional
limitInt32labelled, optional

returns Bytes · raises @moonzip.Refused

fnbzip2

pub fn bzip2(BytesView, level? : Int32) -> Bytes

Compress into bzip2.

level is one to nine and means what it means everywhere else: how much memory the compressor may use on a block, and so how far back a match can reach. Nine is the default because the format's own default is nine, and a caller who cares has an opinion.

 moonbit nochecklet packed = @bzip2.bzip2(body[:], level=9)
parameters
BytesView—positional
levelInt32labelled, optional

returns Bytes

sevenz — 7z format

structEntry

One member of an archive.

data is empty for a directory and for a file that had no content, which are two different things: kind tells them apart, and size does not.

fields
nameString
dataBytes
kindKind
mtimeInt64

Last modification, in Unix seconds. 7-Zip stores a Windows FILETIME, which is a hundred-nanosecond count since 1601, so a value read back is exact and a value that was never recorded is zero.

attributesUInt32

The Windows attribute word, kept because it is the only place 7-Zip records "hidden", "read-only" and "directory".

methods

directory

pub fn Entry::directory(String, mtime? : Int64) -> Self
parameters
String—positional
mtimeInt64labelled, optional

returns Self

equal

pub fn Entry::equal(Self, Self) -> Bool

Derived trait glue.

returns Bool

new

pub fn Entry::new(String, Bytes, mtime? : Int64, attributes? : UInt32) -> Self
parameters
String—positional
Bytes—positional
mtimeInt64labelled, optional
attributesUInt32labelled, optional

returns Self

not_equal

pub fn Entry::not_equal(Self, Self) -> Bool

Derived trait glue.

returns Bool

to_repr

pub fn Entry::to_repr(Self) -> @debug.Repr

Derived trait glue.

returns @debug.Repr

implements Debug, Eq

enumKind

What an entry is. 7-Zip records it in the file attributes rather than in a type field, which is why this is derived rather than read.

variants
File
Directory
methods

equal

pub fn Kind::equal(Self, Self) -> Bool

Derived trait glue.

returns Bool

not_equal

pub fn Kind::not_equal(Self, Self) -> Bool

Derived trait glue.

returns Bool

to_repr

pub fn Kind::to_repr(Self) -> @debug.Repr

Derived trait glue.

returns @debug.Repr

implements Debug, Eq

enumMethod

How the writer compresses. 7z is a container, so this is a choice rather than a property of the format: the same archive can hold a folder of LZMA and a folder of copies.

variants
Copy

Nothing at all. The folder exists so the file table has something to point at, which is what makes a .7z a .7z even uncompressed.

Lzma

LZMA, which is what 7-Zip's own default is for text.

Lzma2

LZMA2, the chunked form. Same codec, resettable state.

methods

equal

pub fn Method::equal(Self, Self) -> Bool

Derived trait glue.

returns Bool

label

pub fn Method::label(Self) -> String

The name a log line wants.

returns String

not_equal

pub fn Method::not_equal(Self, Self) -> Bool

Derived trait glue.

returns Bool

to_repr

pub fn Method::to_repr(Self) -> @debug.Repr

Derived trait glue.

returns @debug.Repr

implements Debug, Eq

fnnames

pub fn names(BytesView) -> Array[String] raise @moonzip.Refused

The names in an archive, without decompressing anything.

Reading a directory should not cost the directory's contents, which for a solid archive would be the whole thing.

parameters
BytesView—positional

returns Array[String] · raises @moonzip.Refused

fnsevenz

pub fn sevenz(ArrayView[Entry], level? : Int32, how? : Method, solid? : Bool) -> Bytes

Build a 7z archive.

 moonbit nochecklet archive = @sevenz.sevenz(members[:], level=6)

solid is 7-Zip's own default and the format's reason for existing: one folder holds every file, so the compressor sees them all at once and a repeated phrase across two files is paid for once. The cost is that reading the last file means decompressing the first ones, which is why it is an argument.

parameters
ArrayView[Entry]—positional
levelInt32labelled, optional
howMethodlabelled, optional
solidBoollabelled, optional

returns Bytes

fnun7z

pub fn un7z(BytesView, limit? : Int32) -> Array[Entry] raise @moonzip.Refused

Read a 7z archive.

 moonbit nochecklet members = @sevenz.un7z(archive[:])

limit is how much output the caller is prepared to hold. An archive declares its unpacked sizes, so this is checked before the allocation rather than after.

parameters
BytesView—positional
limitInt32labelled, optional

returns Array[Entry] · raises @moonzip.Refused

zstd — RFC 8878

typeReader

A frame reader that is fed a piece at a time.

The blocks of a frame are not independent — the repeat offsets and the four entropy tables carry from one to the next — so what a reader keeps is the frame's state, trimmed to its window: a back-reference reaches at most that far, and the bytes past it can never be asked for again.

methods

finish

pub fn Reader::finish(Self) -> Bytes raise @moonzip.Refused

returns Bytes · raises @moonzip.Refused

new

pub fn Reader::new(checksum? : Bool, limit? : Int32) -> Self
parameters
checksumBoollabelled, optional
limitInt32labelled, optional

returns Self

push

pub fn Reader::push(Self, BytesView) -> Bytes raise @moonzip.Refused
parameters
BytesView—positional

returns Bytes · raises @moonzip.Refused

typeWriter

A frame writer that takes the body a piece at a time.

A zstd frame is a run of blocks, each with its own literals and sequences, so the unit of work is one block: what is held between calls is the sixty-four kilobytes being filled. The frame says its window in the header instead of its content size, because a writer that has not seen the body cannot promise one.

 moonbit nochecklet w = @zstd.Writer::new(level=3)
let mut out : Array[Byte] = []
for part in parts {
for b in w.push(part[:]) {
out.push(b)
}
}
for b in w.finish() {
out.push(b)
}
methods

finish

pub fn Writer::finish(Self) -> Bytes

returns Bytes

new

pub fn Writer::new(level? : Int32) -> Self
parameters
levelInt32labelled, optional

returns Self

push

pub fn Writer::push(Self, BytesView) -> Bytes
parameters
BytesView—positional

returns Bytes

fnunzstd

pub fn unzstd(BytesView, limit? : Int32) -> Bytes raise @moonzip.Refused

Decompress a zstd frame.

 moonbit nochecklet plain = @zstd.unzstd(packed[:])
parameters
BytesView—positional
limitInt32labelled, optional

returns Bytes · raises @moonzip.Refused

fnzstd

pub fn zstd(BytesView, level? : Int32) -> Bytes

Compress into a zstd frame.

 moonbit nochecklet packed = @zstd.zstd(body[:])

The block is an LZ77 parse coded with the *predefined* FSE tables, and the literals are sent raw. That is a legal frame that any decoder reads, and it compresses: what it gives up against zstd -19 is the per-block table selection and the Huffman coding of literals, which are the two parts of the reference encoder that are about ratio rather than about the format.

parameters
BytesView—positional
levelInt32labelled, optional

returns Bytes

brotli — RFC 7932

typeReader

A reader that is fed a piece at a time.

A meta-block cannot be decoded in halves, so the reader keeps the bytes it was given and tries again when more arrive — starting from the last meta-block boundary, because the bit cursor, the distance ring and the byte count are all only consistent there. The retry threshold doubles what has to arrive before the next attempt, so a body delivered a byte at a time is not quadratic.

methods

finish

pub fn Reader::finish(Self) -> Bytes raise @moonzip.Refused

returns Bytes · raises @moonzip.Refused

new

pub fn Reader::new(limit? : Int32) -> Self
parameters
limitInt32labelled, optional

returns Self

push

pub fn Reader::push(Self, BytesView) -> Bytes raise @moonzip.Refused
parameters
BytesView—positional

returns Bytes · raises @moonzip.Refused

typeWriter

A writer that takes the body a piece at a time.

A stream is a run of meta-blocks, and a meta-block is a megabyte, so the unit of work is one of those: what is held between calls is that megabyte and the window a copy may reach back into — the window is why a match found in one meta-block can still be used in the next, which is what a periodic file needs.

 moonbit nochecklet w = @brotli.Writer::new(quality=9)
let mut out : Array[Byte] = []
for part in parts {
for b in w.push(part[:]) {
out.push(b)
}
}
for b in w.finish() {
out.push(b)
}
methods

finish

pub fn Writer::finish(Self) -> Bytes

returns Bytes

new

pub fn Writer::new(window? : Int32, quality? : Int32) -> Self
parameters
windowInt32labelled, optional
qualityInt32labelled, optional

returns Self

push

pub fn Writer::push(Self, BytesView) -> Bytes
parameters
BytesView—positional

returns Bytes

fnbrotli

pub fn brotli(BytesView, window? : Int32, quality? : Int32) -> Bytes

Brotli, RFC 7932.

The writer does all of what earns the format its ratio: literal context modelling, block types for the literal and command streams, the static dictionary, and short distance codes. At quality 9 — the default — it is level with the reference encoder on this repository's own documentation, and somewhat ahead of it below that.

window is the log of the window size, 10 to 24: how far back a copy may reach. Larger windows find more matches and cost nothing but memory while writing, since the reader keeps whatever it needs.

quality is 0 to 11 and is how hard the writer looks, not which features it uses. Up to 8 it sets the number of hash-chain candidates walked and whether a lazy step is taken, which is the whole of a greedy parse. From 10 the parse is chosen rather than taken: an optimal walk over the block keeps the cheapest price of reaching every position, prices come from the codes the previous round produced, and each round's result is encoded to see which is genuinely smaller. That is seven times the work of quality 9 on a 35 KB body — 193 ms against 28 — and four and a half per cent off the output, because the parse is where the last of the ratio lives. Ten and eleven differ only in how many candidates the search walks, which on text changes almost nothing once the parse is chosen.

A higher quality never produces more bytes and always takes longer.

parameters
BytesView—positional
windowInt32labelled, optional
qualityInt32labelled, optional

returns Bytes

fnunbrotli

pub fn unbrotli(BytesView, limit? : Int32) -> Bytes raise @moonzip.Refused

Un-brotli a complete stream.

parameters
BytesView—positional
limitInt32labelled, optional

returns Bytes · raises @moonzip.Refused