fzip

High-performance compression library for MoonBit, ported from fflate. (DEFLATE/GZIP/Zlib/ZIP) ๐Ÿš€

compression
deflate
gzip
zlib
zip
fflate
moon add hustcer/fzip@0.8.6
Download zip
Author
Version
0.8.6
License
Apache-2.0
Last updated
last month
Downloads
9K
README

#fzip โ€“ High-performance compression library for MoonBit

fzip (short for fast zip) is a high-performance, in-memory compression library for MoonBit, ported from the fflate JavaScript library.

This library provides:

  • Deflate, GZIP, Zlib, and ZIP compression and decompression
  • Synchronous APIs plus streaming APIs for DEFLATE/GZIP/Zlib
  • ZIP64 metadata support for in-memory ZIP archives that still fit the sync API limits
  • Validation and size limits for malformed input, zip bombs, path traversal, and corrupted data

#Benchmark

Platform: macOS (Apple Silicon), MoonBit wasm-gc target. Full results in bench.md.

#Features

  • Pure MoonBit implementation with no external dependencies (compiled to Wasm-GC)
  • Support for DEFLATE, GZIP, Zlib, and ZIP formats
  • Automatic format detection for decompression
  • Streaming APIs for chunk-based DEFLATE/GZIP/Zlib compression and decompression
  • In-memory ZIP read/write support, including ZIP64 metadata, data descriptors, listing, and CRC-32 validation
  • Configurable input and output limits
  • 360+ tests covering format correctness, edge cases, security scenarios, and malformed input

#Installation

moon add hustcer/fzip

Or add this to your moon.mod.json:

{ "deps": { "hustcer/fzip": "0.8.2" } }

#Quick Start

// Compress (GZIP)
let data = @fzip.str_to_u8("Hello, MoonBit!")
let compressed = @fzip.gzip_sync(data)

// Decompress (auto-detect format)
let original = @fzip.decompress_sync(compressed)
let text = @fzip.str_from_u8(original)

println(text) // "Hello, MoonBit!"

#Current Status

Detailed API documentation can be explored via moon ide doc or in the codebase.

#Working Features

  • DEFLATE compression/decompression - Full deflate algorithm implementation
  • GZIP format support - Deflate with GZIP headers, timestamps, metadata, and CRC-32 checksums
  • Zlib format support - Deflate with Zlib headers and Adler-32 checksums
  • ZIP archive support - Write, list, and extract ZIP archives
  • ZIP64 metadata support - Read and write ZIP64 metadata for archives that fit the current in-memory sync API limits
  • ZIP data descriptors - Read entries that use central-directory sizes and CRC-32
  • Streaming compression - Stream-based handlers for chunk-based data processing
  • Input validation - Size limits, path traversal detection, checksum validation, and malformed metadata rejection
  • Auto-detection - Decompression that recognizes GZIP, Zlib, or raw DEFLATE
  • Test coverage - Tests for encoding, decoding, edge cases, and security-related regressions

#API Compatibility

The API is inspired by the original fflate library structure and adapted for MoonBit:

  • Type-safe synchronous and stream-based representations (ondata callbacks).
  • Error propagation through MoonBit's raise Error mechanisms.
  • Zero-dependency string encoding/decoding implementations built-in.

#Detailed API

#ZIP

The ZIP APIs are synchronous and in-memory: callers pass complete archive bytes to unzip_sync/unzip_list, and zip_sync builds a complete archive in a FixedArray[Byte]. ZIP64 support makes those APIs understand ZIP64 metadata; it does not add streaming extraction or creation for archives larger than memory.

// Create ZIP archive
let files : Array[(String, FixedArray[Byte])] = [
("hello.txt", @fzip.str_to_u8("Hello!")),
("binary.dat", my_bytes),
]
let archive = @fzip.zip_sync(files)

// Extract ZIP archive
let extracted = @fzip.unzip_sync(archive)
for entry in extracted {
let (filename, content) = entry
println("\{filename}: \{content.length()} bytes")
}

// List files without extracting
let infos = @fzip.unzip_list(archive)
for info in infos {
println("\{info.name}: original size \{info.original_size} (compressed: \{info.size})")
}

// Extract ZIP archive and verify each entry CRC-32
let checked = @fzip.unzip_sync(archive, opts={ verify_checksum: true })

#ZIP64 metadata support

zip_sync, unzip_sync, and unzip_list understand ZIP64 metadata for archives and entries that still fit inside the sync API's Int/FixedArray limits. This is ZIP64 metadata compatibility for in-memory ZIP APIs, not large-file streaming.

The writer emits ZIP64 metadata when a classic ZIP field needs a sentinel value:

  • entry compressed or uncompressed size reaches 0xFFFFFFFF
  • entry local-header offset reaches 0xFFFFFFFF
  • entry count reaches 0xFFFF
  • central directory size or offset reaches 0xFFFFFFFF

ZIP64 archives include the ZIP64 EOCD record, ZIP64 EOCD locator, and per-entry ZIP64 extra fields required by the format. The classic EOCD is still written last.

The reader validates ZIP64 metadata before extraction. Metadata that is structurally valid but too large for the current sync API raises FzipErrorCode::Zip64ValueTooLarge. Malformed ZIP64 metadata, encryption, multi-disk archives, and missing required ZIP64 extras raise InvalidZipData.

For recoverable writer failures, use zip_sync_checked:

let archive = @fzip.zip_sync_checked(files) catch {
FzipError(code~, message~) => {
// Handle Zip64ValueTooLarge, ExtraFieldTooLong,
// FilenameTooLong, InvalidZipData, or another writer error.
return
}
}

zip_sync uses the same builder and aborts if the builder reports a recoverable error. It does not return a partial archive.

True large-file streaming is not implemented yet. See docs/zip64.md for the ZIP64 plan.

#Automatic Decompression

// compress_sync defaults to gzip_sync
let compressed = @fzip.compress_sync(data)

// decompress_sync automatically detects GZIP, Zlib, or DEFLATE
let original = @fzip.decompress_sync(compressed)

#Streaming

let stream = @fzip.DeflateStream::new()
stream.ondata = Some(FlateStreamHandler(fn(data, is_final) {
// handle output chunk
}))
stream.push(chunk1)
stream.push(chunk2, final_=true)

Available streams: DeflateStream, InflateStream, GzipStream, GunzipStream, ZlibStream, UnzlibStream, DecompressStream.

#Security Features

fzip includes checks for common compression and archive issues:

  • Size limits: Configurable max output (default 100MB) and input (default 1GB) sizes; ZIP extraction also caps total sync output and entry fan-out
  • Checksum verification:
    • GZIP and Zlib verify their container checksums by default
    • ZIP entry CRC-32 verification is opt-in via unzip_sync(..., opts={ verify_checksum: true }); this preserves the historical fast ZIP extraction path and avoids an extra checksum pass when callers already trust the archive source
  • Compression ratio check: ZIP files with compression ratios > 1000:1 are rejected
  • Path traversal protection: ZIP entries with unsafe paths (../, absolute paths) are rejected
  • ZIP metadata validation: ZIP filenames and extra fields are length-limited; encrypted and multi-disk ZIP archives are rejected
  • Data descriptor support: ZIP entries with general-purpose bit 3 are bounded by central-directory sizes and verified with CRC-32
  • Strict UTF-8 decoding: malformed UTF-8 is rejected unless Latin-1 decoding is explicitly requested

Configure security options per operation:

// With all security features (default)
let original = @fzip.gunzip_sync(compressed, opts={
out: None,
dictionary: None,
max_output_size: 10485760, // 10MB limit
max_input_size: 104857600, // 100MB limit
verify_checksum: true, // Verify CRC-32 (default)
})

// Skip checksum verification when integrity is checked elsewhere
let original = @fzip.gunzip_sync(compressed, opts={
verify_checksum: false, // Skip CRC-32 for ~4x faster decompression
..
})

#Development

moon check # Type check moon build # Full build moon test # Run tests moon test -v # Verbose output moon fmt # Format code moon bench # Run benchmarks

#License

#
FzipError

pub(all) suberror FzipError {
FzipError(FzipErrorCode, String)
}

Error raised by fzip decoding, decompression, and archive-reading APIs.

code is stable enough for branching in callers. message may include additional context such as the failed format check or violated safety limit.

#
DecompressStream

pub(all) struct DecompressStream {
ondata : FlateStreamHandler?
// private fields
}

Chunk-oriented decompressor with automatic format detection.

Input chunks are buffered until the final push, then decoded with decompress_sync. GZIP, Zlib, and raw DEFLATE inputs are supported.

#
DecompressStream::new

Create an auto-detecting decompression stream.

#
DecompressStream::push

fn DecompressStream::push(self : DecompressStream, chunk : FixedArray[Byte], final_? : Bool) -> Unit raise FzipError

Push one input chunk into the auto-detecting decompression stream.

#
DeflateOptions

pub(all) struct DeflateOptions {
level : Int
mem : Int
dictionary : FixedArray[Byte]?
} derive(
Debug
)

Options for raw DEFLATE compression.

level follows the usual 0-9 compression scale: 0 stores data without compression and higher values spend more CPU to improve compression ratio. mem controls the internal hash table size; leave it as 0 to let fzip choose a size from the input length. dictionary can be used when both the compressor and decompressor share the same preset dictionary.

#
DeflateOptions::default

Return the default raw DEFLATE compression options.

#
DeflateStream

pub(all) struct DeflateStream {
ondata : FlateStreamHandler?
// private fields
}

Chunk-oriented raw DEFLATE compressor.

Set ondata before calling push. Input chunks are buffered until a call with final_=true, then compressed with deflate_sync and emitted through ondata.

#
DeflateStream::new

Create a raw DEFLATE compression stream.

#
DeflateStream::push

fn DeflateStream::push(self : DeflateStream, chunk : FixedArray[Byte], final_? : Bool) -> Unit

Push one input chunk into the raw DEFLATE compression stream.

Chunks are buffered until final_ is true. On the final push, compressed output is sent to ondata if a handler is installed.

#
FlateStreamHandler

pub(all) struct FlateStreamHandler((FixedArray[Byte], Bool) -> Unit)

Callback wrapper used by stream-style APIs.

The wrapped function receives (data, is_final). data is the produced output chunk and is_final is true for the final callback of a stream.

#
FzipErrorCode

pub(all) enum FzipErrorCode {
UnexpectedEOF
InvalidBlockType
InvalidLengthLiteral
InvalidDistance
StreamFinished
NoStreamHandler
InvalidHeader
NoCallback
InvalidUTF8
ExtraFieldTooLong
InvalidDate
FilenameTooLong
StreamFinishing
InvalidZipData
UnknownCompressionMethod
InvalidChecksum
Zip64ValueTooLarge
} derive(Eq,
Debug
)

Error categories reported by fzip operations.

Most public decompression and archive-reading APIs raise FzipError with one of these codes. The code is intended for programmatic handling while the error message gives a human-readable explanation.

#
GunzipOptions

pub(all) struct GunzipOptions {
out : FixedArray[Byte]?
dictionary : FixedArray[Byte]?
max_output_size : Int
max_input_size : Int
verify_checksum : Bool
} derive(
Debug
)

Options for GZIP decompression.

By default fzip verifies the CRC-32 footer to detect corrupted data. Disable verify_checksum only when data integrity is already guaranteed elsewhere and decompression speed is more important. When out is not supplied, gunzip_sync allocates its output from the GZIP ISIZE footer after checking it against max_output_size.

#
GunzipOptions::default

fn GunzipOptions::default() -> GunzipOptions

Return the default GZIP decompression options.

#
GunzipStream

pub(all) struct GunzipStream {
ondata : FlateStreamHandler?
// private fields
}

Chunk-oriented GZIP decompressor.

Input chunks are buffered until the final push, then decoded with gunzip_sync and emitted through ondata.

#
GunzipStream::new

Create a GZIP decompression stream.

#
GunzipStream::push

fn GunzipStream::push(self : GunzipStream, chunk : FixedArray[Byte], final_? : Bool) -> Unit raise FzipError

Push one input chunk into the GZIP decompression stream.

#
GzipOptions

pub(all) struct GzipOptions {
level : Int
mem : Int
dictionary : FixedArray[Byte]?
mtime : Int
filename : String
} derive(
Debug
)

Options for GZIP compression.

These options control the inner DEFLATE stream plus GZIP metadata. mtime is a Unix timestamp in seconds; use 0 to leave the timestamp unset. filename is written into the optional GZIP original-name field as single-byte header data when it is non-empty. GZIP does not record a dictionary identifier, so decompression must be given the same dictionary out of band when one is used.

#
GzipOptions::default

fn GzipOptions::default() -> GzipOptions

Return the default GZIP compression options.

#
GzipStream

pub(all) struct GzipStream {
ondata : FlateStreamHandler?
// private fields
}

Chunk-oriented GZIP compressor.

Input chunks are buffered until the final push, then encoded with gzip_sync and emitted through ondata.

#
GzipStream::new

fn GzipStream::new(opts? : GzipOptions) -> GzipStream

Create a GZIP compression stream.

#
GzipStream::push

fn GzipStream::push(self : GzipStream, chunk : FixedArray[Byte], final_? : Bool) -> Unit

Push one input chunk into the GZIP compression stream.

#
InflateOptions

pub(all) struct InflateOptions {
out : FixedArray[Byte]?
dictionary : FixedArray[Byte]?
max_output_size : Int
max_input_size : Int
} derive(
Debug
)

Options for raw DEFLATE decompression.

out lets callers provide a reusable output buffer. The buffer must be large enough for the full uncompressed payload when it is supplied. The size limits are enforced before or during decompression to protect callers from malformed streams and zip-bomb style inputs.

#
InflateOptions::default

Return the default raw DEFLATE decompression options.

#
InflateStream

pub(all) struct InflateStream {
ondata : FlateStreamHandler?
// private fields
}

Chunk-oriented raw DEFLATE decompressor.

Set ondata before calling push. Input chunks are buffered until a call with final_=true, then decompressed with inflate_sync and emitted through ondata.

#
InflateStream::new

Create a raw DEFLATE decompression stream.

#
InflateStream::push

fn InflateStream::push(self : InflateStream, chunk : FixedArray[Byte], final_? : Bool) -> Unit raise FzipError

Push one input chunk into the raw DEFLATE decompression stream.

Chunks are buffered until final_ is true. On the final push, decompressed output is sent to ondata if a handler is installed.

#
UnzipFileInfo

pub(all) struct UnzipFileInfo {
name : String
size : Int
original_size : Int
compression : Int
} derive(
Debug
)

Metadata for an entry returned by unzip_list.

#
UnzipOptions

pub(all) struct UnzipOptions {
verify_checksum : Bool
} derive(
Debug
)

Options for ZIP extraction.

ZIP archives carry CRC-32 values in the central directory. verify_checksum enables validation of each extracted entry against those values. It is off by default to preserve the historical unzip_sync performance profile; turn it on when archive integrity is not already guaranteed by the caller.

#
UnzipOptions::default

fn UnzipOptions::default() -> UnzipOptions

Return the default ZIP extraction options.

#
UnzlibOptions

pub(all) struct UnzlibOptions {
out : FixedArray[Byte]?
dictionary : FixedArray[Byte]?
max_output_size : Int
max_input_size : Int
verify_checksum : Bool
} derive(
Debug
)

Options for Zlib decompression.

By default fzip verifies the Adler-32 footer to detect corrupted data. If a dictionary is required, the stream must advertise that requirement and the caller must provide the matching dictionary; the dictionary checksum in the header is skipped rather than compared.

#
UnzlibOptions::default

fn UnzlibOptions::default() -> UnzlibOptions

Return the default Zlib decompression options.

#
UnzlibStream

pub(all) struct UnzlibStream {
ondata : FlateStreamHandler?
// private fields
}

Chunk-oriented Zlib decompressor.

Input chunks are buffered until the final push, then decoded with unzlib_sync and emitted through ondata.

#
UnzlibStream::new

Create a Zlib decompression stream.

#
UnzlibStream::push

fn UnzlibStream::push(self : UnzlibStream, chunk : FixedArray[Byte], final_? : Bool) -> Unit raise FzipError

Push one input chunk into the Zlib decompression stream.

#
ZipEntryOptions

pub(all) struct ZipEntryOptions {
level : Int
mem : Int
os : Int
attrs : Int
extra : Array[(Int, FixedArray[Byte])]
comment : String
mtime : Int
} derive(
Debug
)

Options applied to entries written by zip_sync.

The current API applies one set of options to every entry in the archive. level = 0 stores entries without compression; other levels use DEFLATE. Extra fields are written as (header_id, data) pairs.

#
ZipEntryOptions::default

Return the default ZIP entry options.

#
ZlibOptions

pub(all) struct ZlibOptions {
level : Int
mem : Int
dictionary : FixedArray[Byte]?
} derive(
Debug
)

Options for Zlib compression.

Zlib wraps a raw DEFLATE stream with a header and Adler-32 checksum. When a dictionary is supplied, its Adler-32 value is written to the Zlib header and decompression must use the same dictionary.

#
ZlibOptions::default

fn ZlibOptions::default() -> ZlibOptions

Return the default Zlib compression options.

#
ZlibStream

pub(all) struct ZlibStream {
ondata : FlateStreamHandler?
// private fields
}

Chunk-oriented Zlib compressor.

Input chunks are buffered until the final push, then encoded with zlib_sync and emitted through ondata.

#
ZlibStream::new

fn ZlibStream::new(opts? : ZlibOptions) -> ZlibStream

Create a Zlib compression stream.

#
ZlibStream::push

fn ZlibStream::push(self : ZlibStream, chunk : FixedArray[Byte], final_? : Bool) -> Unit

Push one input chunk into the Zlib compression stream.

#
adler32

fn adler32(data : FixedArray[Byte]) -> UInt

Compute the Adler-32 checksum of data.

The returned UInt uses fzip's little-endian footer representation, so it matches the value read from Zlib streams by fzip's byte readers.

#
compress_sync

fn compress_sync(data : FixedArray[Byte], opts? : GzipOptions) -> FixedArray[Byte]

Compress data using the library default container format.

This is a convenience wrapper around gzip_sync, so the returned bytes are a complete GZIP stream with a GZIP header and CRC-32 footer. Use deflate_sync or zlib_sync when you need those formats explicitly.

#
crc32

fn crc32(data : FixedArray[Byte]) -> UInt

Compute the CRC-32 checksum of data.

The returned value uses the standard reflected CRC-32 polynomial 0xEDB88320, matching the checksum stored in GZIP and ZIP records.

#
decompress_sync

fn decompress_sync(data : FixedArray[Byte], opts? : InflateOptions) -> FixedArray[Byte] raise FzipError

Decompress GZIP, Zlib, or raw DEFLATE data.

The input format is detected from the leading bytes. GZIP and Zlib streams are decoded with checksum verification enabled and no option to disable it through this convenience API; otherwise the input is treated as a raw DEFLATE stream. The supplied InflateOptions control the output buffer, optional dictionary, and inflater size limits. For GZIP input without out, the output allocation follows the GZIP ISIZE footer.

#
default_max_input_size

let default_max_input_size : Int

Default maximum compressed input size for defensive decompression.

Used as the default value in decompression option structs.

#
default_max_output_size

let default_max_output_size : Int

Default maximum uncompressed output size for defensive decompression.

Used as the default value in decompression option structs.

#
deflate_sync

fn deflate_sync(data : FixedArray[Byte], opts? : DeflateOptions) -> FixedArray[Byte]

Compress data as a raw DEFLATE stream.

Raw DEFLATE contains only compressed blocks, with no GZIP or Zlib wrapper and no checksum footer. Use this when another protocol supplies its own framing, or when you need the exact DEFLATE payload for ZIP entries.

#
freq_skew_shift

let freq_skew_shift : Int

Frequency-skew divisor used to detect compressible high-entropy inputs.

#
full_scan_threshold

let full_scan_threshold : Int

Size below which compressibility detection scans the whole input.

#
gunzip_sync

fn gunzip_sync(data : FixedArray[Byte], opts? : GunzipOptions) -> FixedArray[Byte] raise FzipError

Decompress a GZIP stream.

The GZIP header is parsed, the inner DEFLATE payload is inflated, and the CRC-32 footer is verified by default. Set verify_checksum to false in GunzipOptions only when checksum validation is handled elsewhere. If no output buffer is provided, fzip allocates one using the GZIP ISIZE footer.

#
gzip_sync

fn gzip_sync(data : FixedArray[Byte], opts? : GzipOptions) -> FixedArray[Byte]

Compress data into a GZIP stream.

The output contains a GZIP header, a DEFLATE payload, and a footer with the CRC-32 checksum and original input size. GzipOptions controls compression level, optional dictionary use, and header metadata such as timestamp and original filename. When a dictionary is used, callers must pass the same dictionary to gunzip_sync; the GZIP format does not carry a dictionary ID.

#
high_entropy_unique_threshold

let high_entropy_unique_threshold : Int

Unique-byte threshold that marks small inputs as high entropy candidates.

#
inflate_sync

fn inflate_sync(data : FixedArray[Byte], opts? : InflateOptions) -> FixedArray[Byte] raise FzipError

Decompress a raw DEFLATE stream.

The input must be raw DEFLATE data without a GZIP or Zlib wrapper. Use decompress_sync when the format is unknown. InflateOptions can provide a preset dictionary, a caller-owned output buffer, and input/output size limits for defensive decompression.

#
max_filename_length

let max_filename_length : Int

Maximum filename length accepted while reading ZIP central directory entries.

#
min_compressibility_check_len

let min_compressibility_check_len : Int

Minimum input length before the compressor samples data for compressibility.

#
periodicity_check_samples

let periodicity_check_samples : Int

Number of positions sampled when checking high-entropy data for periodicity.

#
sampling_entropy_threshold

let sampling_entropy_threshold : Int

Unique-byte threshold in sampled large inputs that suggests incompressibility.

#
sampling_stride

let sampling_stride : Int

Sampling stride used by large-input compressibility detection.

#
small_data_mem_threshold

let small_data_mem_threshold : Int

Size below which compression uses a smaller hash table to reduce overhead.

#
str_from_u8

fn str_from_u8(data : FixedArray[Byte], latin1? : Bool, offset? : Int, len? : Int) -> String raise FzipError

Decode bytes into a String.

The selected byte range is decoded as strict UTF-8 per RFC 3629. Any of the following raise InvalidUTF8:

  • continuation bytes (0x80-0xBF) appearing as a sequence start;
  • overlong encodings (0xC0 0x80 โ†’ U+0000, etc.);
  • a continuation byte without the 10xxxxxx prefix;
  • surrogate code points (U+D800-U+DFFF);
  • code points above U+10FFFF;
  • 5- or 6-byte legacy UTF-8 sequences (start byte >= 0xF5).

Earlier versions of this function silently accepted these patterns, which produced fzip-specific decoder output that disagreed with standard libraries โ€” a parser-differential risk for ZIP filenames where strict rejection is preferred over silent corruption.

When latin1 is true, every byte is mapped directly to the same Unicode code point. offset and len select the input range.

#
str_to_u8

fn str_to_u8(s : String, latin1? : Bool) -> FixedArray[Byte]

Encode a String into bytes.

By default this returns UTF-8 bytes. When latin1 is true, each UTF-16 code unit is truncated to one byte, which is useful for legacy ZIP metadata that is explicitly stored as Latin-1 compatible byte data.

#
unzip_list

fn unzip_list(data : FixedArray[Byte]) -> Array[UnzipFileInfo] raise FzipError

List ZIP entries without extracting their contents.

This reads the central directory and returns entry names, compressed sizes, original sizes, and compression methods. It is useful for inspecting an archive before deciding whether to call unzip_sync.

#
unzip_sync

fn unzip_sync(data : FixedArray[Byte], opts? : UnzipOptions) -> Array[(String, FixedArray[Byte])] raise FzipError

Extract all supported files from a ZIP archive.

The result preserves archive order as (filename, data) pairs. fzip supports stored entries (method 0) and deflated entries (method 8), rejects unsafe paths such as absolute paths or .. components, and applies decompression ratio checks to reduce zip-bomb risk.

#
unzlib_sync

fn unzlib_sync(data : FixedArray[Byte], opts? : UnzlibOptions) -> FixedArray[Byte] raise FzipError

Decompress a Zlib stream.

The Zlib header is validated before inflating the inner DEFLATE payload. The Adler-32 footer is verified by default and can be disabled with UnzlibOptions.verify_checksum for trusted inputs. For dictionary streams, fzip checks that a dictionary is expected but does not compare the header's dictionary checksum with the provided dictionary.

#
zip64_eocd_signature

#alias(zip64_eocd_locator_signature, deprecated="`zip64_eocd_locator_signature` is deprecated, use `zip64_eocd_signature` instead")
let zip64_eocd_signature : UInt

ZIP64 end-of-central-directory record signature (PKWARE APPNOTE ยง4.3.14).

Replaces the misnamed zip64_eocd_locator_signature, which is kept as a deprecated alias for source compatibility. The actual ZIP64 EOCD locator signature is zip64_locator_signature below.

#
zip64_locator_signature

let zip64_locator_signature : UInt

ZIP64 end-of-central-directory locator signature (PKWARE APPNOTE ยง4.3.15).

#
zip_cd_signature

let zip_cd_signature : UInt

ZIP central directory file-header signature.

#
zip_eocd_signature

let zip_eocd_signature : UInt

ZIP end-of-central-directory signature.

#
zip_local_signature

let zip_local_signature : UInt

ZIP local file-header signature.

#
zip_sync

fn zip_sync(files : Array[(String, FixedArray[Byte])], opts? : ZipEntryOptions) -> FixedArray[Byte]

Create a ZIP archive from (filename, data) entries.

Each entry is written with the same ZipEntryOptions. Entry names are encoded as UTF-8 when needed, and level = 0 stores files without DEFLATE compression. The returned bytes are a complete ZIP archive containing local file headers, central directory entries, and the end-of-central-directory record. ZIP64 metadata (per-entry ZIP64 extras, ZIP64 EOCD record, ZIP64 locator) is emitted automatically when an entry crosses the classic 32-bit size limit, when the archive carries 65 535 or more entries, or when the central directory itself crosses the 32-bit size or offset limit.

This function preserves its existing non-raising signature for source compatibility. The shared raising builder it delegates to may detect metadata or layout values that exceed the sync API's Int/FixedArray budget. When that happens this wrapper traps deterministically with a stable abort message rather than returning a partial or corrupt archive. Callers that prefer to recover from such failures should use zip_sync_checked.

#
zip_sync_checked

fn zip_sync_checked(files : Array[(String, FixedArray[Byte])], opts? : ZipEntryOptions) -> FixedArray[Byte] raise FzipError

Create a ZIP archive from (filename, data) entries with recoverable failure semantics.

Behaves exactly like zip_sync but raises FzipError instead of trapping when the writer encounters a value or layout that cannot be represented in the current sync API, or when caller-provided ZIP metadata cannot be encoded safely. Common failure modes include:

  • Zip64ValueTooLarge โ€” a metadata count, size, offset, or the final archive size cannot fit in MoonBit's 32-bit signed Int or be safely indexed in a FixedArray.
  • ExtraFieldTooLong โ€” the user's opts.extra plus the writer-generated ZIP64 extra would exceed max_extra_field_length for a local or central directory entry.
  • FilenameTooLong / InvalidZipData โ€” filename, comment, or extra-field metadata cannot fit in the ZIP format's fixed-width fields.

Both APIs share the same underlying builder, so any successful build produces byte-identical output.

#
zlib_sync

fn zlib_sync(data : FixedArray[Byte], opts? : ZlibOptions) -> FixedArray[Byte]

Compress data into a Zlib stream.

The output contains a Zlib header, a raw DEFLATE payload, and an Adler-32 checksum footer. If ZlibOptions.dictionary is set, the dictionary checksum is written into the header and callers must provide the same dictionary when decompressing.

Powered by MoonBit

Site sourceReport issuePackagesBuild queueSkillsStatistics

ยฉ 2026 mooncakes.io