moonzip

A MoonBit compression library compatible with fflate

compression
deflate
gzip
zlib
zip
moon add ivgtr/moonzip@0.2.4
Download zip
Author
Version
0.2.4
License
MIT
Last updated
5 months ago
Downloads
67
README

#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

Powered by MoonBit

Site sourceReport issuePackagesBuild queueSkillsStatistics

© 2026 mooncakes.io