moonzip

    A MoonBit compression library compatible with fflate

    compression
    deflate
    gzip
    zlib
    zip
    Download zip
    Author
    Version
    0.2.4
    License
    MIT
    Last updated
    8 months ago
    Downloads
    208

    No dependencies

    #ivgtr/moonzip

    A MoonBit compression library compatible with fflate.

    fflate Sync API compatibility: 100% (27/27)

    CategoryCoverage
    Sync functions12 / 12
    Sync streaming13 / 13
    Utility2 / 2

    Async APIs (Web Worker-based) are out of scope for MoonBit/WASM runtime.

    #Supported Formats

    FormatRFC/SpecCompressDecompressStreaming
    DEFLATERFC 1951YesYesYes
    GZIPRFC 1952YesYesYes
    ZlibRFC 1950YesYesYes
    ZIPPKZIP APPNOTEYesYesYes

    #Features

    • LZ77 + Dynamic Huffman compression (levels 0-9)
    • Dictionary support (DEFLATE / Zlib FDICT)
    • Multi-member GZIP decompression
    • ZIP64 read support, per-entry options, archive comments
    • Incremental streaming with ondata callbacks
    • UTF-8 / Latin-1 string conversion, custom filename decoder for non-UTF-8 archives

    #Installation

    moon add ivgtr/moonzip

    #API

    #DEFLATE

    // Compress (level 0-9, mem 0-12)
    let compressed = @moonzip.deflate_sync(data, opts={ level: 6, mem: 4, dictionary: None })

    // Decompress
    let decompressed = @moonzip.inflate_sync(compressed)

    #GZIP

    Supports concatenated multi-member GZIP streams (RFC 1952 section 2.2).

    // Compress
    let compressed = @moonzip.gzip_sync(data, opts={
    level: 6,
    mem: 4,
    filename: Some("hello.txt"),
    mtime: Some(1700000000),
    comment: None,
    extra: None,
    })

    // Decompress (handles single and multi-member streams)
    let decompressed = @moonzip.gunzip_sync(compressed, opts={ dictionary: None })

    // Alias: compress_sync = gzip_sync
    let compressed = @moonzip.compress_sync(data)

    #Zlib

    Supports preset dictionaries via FDICT flag.

    // Compress
    let compressed = @moonzip.zlib_sync(data, opts={ level: 6, mem: 4, dictionary: None })

    // Decompress
    let decompressed = @moonzip.unzlib_sync(compressed, opts={ dictionary: None })

    // With dictionary
    let dict = @moonzip.str_to_u8("common words here")
    let compressed = @moonzip.zlib_sync(data, opts={ level: 6, mem: 4, dictionary: Some(dict) })
    let decompressed = @moonzip.unzlib_sync(compressed, opts={ dictionary: Some(dict) })

    #ZIP

    // Create archive
    let files : Array[(String, FixedArray[Byte])] = [
    ("hello.txt", data1),
    ("dir/world.txt", data2),
    ]
    let archive = @moonzip.zip_sync(files, opts={ level: 6, mtime: None, comment: None })

    // Per-entry options
    let entry_opts : Array[@types.ZipEntryOptions?] = [
    Some({ level: 0, mtime: None, comment: None, extra: None }), // Store without compression
    None, // Use global opts
    ]
    let archive = @moonzip.zip_sync(files, opts={ level: 6, mtime: None, comment: None }, entry_opts~)

    // List entries
    let entries = @moonzip.unzip_list(archive)
    // entries[i].name, entries[i].size, entries[i].original_size, entries[i].compression,
    // entries[i].mtime, entries[i].attrs, entries[i].comment, entries[i].extra

    // Extract all
    let extracted = @moonzip.unzip_sync(archive)
    // extracted[i].0 = filename, extracted[i].1 = data

    // Extract with filter
    let txt_only = @moonzip.unzip_sync(archive, filter=fn(info) { info.name.contains(".txt") })

    // Custom filename decoder (for non-UTF-8 archives like Shift_JIS)
    let decoded = @moonzip.unzip_list(archive, decode=fn(raw) {
    // raw contains the non-UTF-8 bytes; return decoded String
    my_shift_jis_decoder(raw)
    })

    let extracted = @moonzip.unzip_sync(archive, decode=fn(raw) {
    my_shift_jis_decoder(raw)
    })
    // decode is only called for entries without the UTF-8 flag (bit 11)

    #Auto-detect Decompression

    // Automatically detects GZIP, Zlib, or raw DEFLATE
    let decompressed = @moonzip.decompress_sync(compressed_data)

    #Streaming

    // DEFLATE streaming with ondata callback let stream = @stream.DeflateStream::new(level=6, ondata=fn(chunk, final) { // handle partial output }) stream.push(chunk1, false) stream.push(chunk2, true) // true = final chunk let result = stream.result() // GZIP streaming let gz = @stream.GzipStream::new(level=6) gz.push(data, true) let compressed = gz.result() // Gunzip with multi-member callback let gunz = @stream.GunzipStream::new(onmember=fn(member_data) { // called for each GZIP member }) gunz.push(compressed, true) let decompressed = gunz.result() // Zlib / Inflate / Decompress streaming let zs = @stream.ZlibStream::new(level=6) let uzs = @stream.UnzlibStream::new() let inf = @stream.InflateStream::new() let dec = @stream.DecompressStream::new() // auto-detect format // ZIP streaming creation let zip = @stream.Zip::new() let entry = @stream.ZipDeflate::new("file.txt", level=6) entry.push(data, true) zip.add_deflate(entry) let archive = zip.finish() // UTF-8 streaming let enc = @stream.EncodeUTF8::new(ondata=fn(bytes, final) { }) let dec = @stream.DecodeUTF8::new(ondata=fn(text, final) { })

    #Utilities

    // UTF-8 string <-> bytes conversion
    let bytes = @moonzip.str_to_u8("Hello")
    let text = @moonzip.str_from_u8(bytes)

    // Latin-1 mode
    let latin1_bytes = @moonzip.str_to_u8("café", latin1=true)

    // Checksums
    let crc = @moonzip.crc32(data)
    let adl = @moonzip.adler32(data)

    #Error Handling

    Decompression functions (inflate_sync, gunzip_sync, decompress_sync, unzlib_sync, unzip_sync, unzip_list, str_from_u8) raise @types.FlateError. Compression functions are infallible. Error codes (0-14) match fflate's FlateErrorCode:

    CodeName
    0UnexpectedEOF
    1InvalidBlockType
    2InvalidLengthLiteral
    3InvalidDistance
    4StreamFinished
    5NoStreamHandler
    6InvalidHeader
    7NoCallback
    8InvalidUTF8
    9ExtraFieldTooLong
    10InvalidDate
    11FilenameTooLong
    12StreamFinishing
    13InvalidZipData
    14UnknownCompressionMethod

    try {
    let result = @moonzip.gunzip_sync(data)
    } catch {
    @types.FlateError(code) => println("Error: \{code}")
    }

    #Demo

    # CLI demo moon run cmd/ # Web demo moon build --target js && cp _build/js/debug/build/demo/demo.js demo/demo.js # Open demo/index.html in a browser

    #License

    MIT

    adler32

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

    Compute Adler-32 checksum.

    compress_sync

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

    Compress data using GZIP format. Alias for gzip_sync, compatible with fflate's compressSync.

    crc32

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

    Compute CRC-32 checksum.

    decompress_sync

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

    Auto-detect format and decompress data. Tries GZIP (0x1F 0x8B), then Zlib (valid CMF/FLG), then raw DEFLATE.

    deflate_sync

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

    Compress data using raw DEFLATE.

    gunzip_sync

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

    Decompress GZIP data.

    gzip_sync

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

    Compress data using GZIP format.

    inflate_sync

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

    Decompress raw DEFLATE data.

    str_from_u8

    fn str_from_u8(data : FixedArray[Byte], latin1? : Bool) -> String raise
    FlateError

    Convert a byte array back to a string. When latin1 is true, each byte is treated as a Latin-1 character. When latin1 is false (default), the data is decoded as UTF-8 with validation.

    str_to_u8

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

    Convert a string to a byte array. When latin1 is true, each character is truncated to its low 8 bits (Latin-1). When latin1 is false (default), full UTF-8 encoding is used.

    unzip_list

    fn unzip_list(data : FixedArray[Byte], decode? : (FixedArray[Byte]) -> String) -> Array[
    UnzipFileInfo
    ] raise
    FlateError

    List files in a ZIP archive without extracting.

    decode is called for entry-level filenames and comments when the UTF-8 flag (bit 11) is not set. Pass a decoder such as Shift_JIS → String to handle non-UTF-8 archives. Entries with the UTF-8 flag are always decoded as UTF-8 regardless of this parameter.

    unzip_sync

    fn unzip_sync(data : FixedArray[Byte], filter? : (
    UnzipFileInfo
    ) -> Bool, decode? : (FixedArray[Byte]) -> String) -> Array[(String, FixedArray[Byte])] raise
    FlateError

    Extract files from a ZIP archive.

    decode is called for entry-level filenames and comments when the UTF-8 flag (bit 11) is not set. Pass a decoder such as Shift_JIS → String to handle non-UTF-8 archives. Entries with the UTF-8 flag are always decoded as UTF-8 regardless of this parameter.

    unzlib_sync

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

    Decompress Zlib data.

    zip_sync

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

    Create a ZIP archive from files.

    zlib_sync

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

    Compress data using Zlib format.

    Source Files