Skip to main content

README.md

# Z64

A high-performance, robust Elixir library for parsing and extracting ZIP archives, with native support for PKWARE's **Deflate64 (Compression Method 9)** decompression algorithm.

Erlang's built-in `:zip` module and zlib wrapper do not support the Deflate64 compression method. This library bridges that gap by wrapping the official zlib `infback9` C extension in a safe and efficient **Zig-based NIF** using the `zigler` compiler.

Memory allocations for the sliding window and decompression buffers are managed directly through Erlang's allocator, ensuring safety, performance, and complete compatibility with the BEAM.

## Features

- **Deflate64 (Method 9) Support**: Decompress files compressed with PKWARE's Enhanced Deflate (64KB sliding window, up to 65536 distance, 65538 length).
- **Standard Deflate (Method 8) & Stored (Method 0) Support**: Can list and extract standard files in zip archives seamlessly.
- **In-Memory and File-System extraction**: Easily read archives from binary data or file paths.
- **Integrity Validation**: Automatically checks CRC-32 checksums of decompressed files to guarantee data integrity.
- **Pure Zig NIF Integration**: Safe and robust C/Zig interoperability without requiring manually written C-NIF boilerplates or makefiles.

## Installation

Add `z64` to your list of dependencies in `mix.exs`:

```elixir
def deps do
  [
    {:z64, "~> 0.1.0"}
  ]
end
```

## Quick Start

### 1. List Entries in a ZIP Archive

List all file metadata (including names, compressed size, uncompressed size, compression method, and CRC-32) within a ZIP file:

```elixir
{:ok, entries} = Z64.list_entries("path/to/archive.zip")
# Or pass a binary
{:ok, entries} = Z64.list_entries(zip_binary)

# Output structure:
# [
#   %Z64.Entry{
#     filename: "document.txt",
#     compression_method: 9,          # Method 9 is Deflate64
#     compressed_size: 4567,
#     uncompressed_size: 15432,
#     crc32: 3456789012,
#     local_header_offset: 124,
#     ...
#   }
# ]
```

### 2. Extract a Specific Entry

Retrieve the uncompressed content of a single file from the ZIP archive:

```elixir
case Z64.extract_entry("archive.zip", "large_deflate64_file.csv") do
  {:ok, binary} ->
    # Work with the decompressed binary data
    IO.puts("Extracted #{byte_size(binary)} bytes!")

  {:error, :file_not_found} ->
    IO.puts("File not found in archive")

  {:error, reason} ->
    IO.puts("Decompression failed: #{inspect(reason)}")
end
```

### 3. Extract All Files (Unzip)

Extract the entire ZIP archive to a destination directory on the disk:

```elixir
case Z64.unzip("archive.zip", "output_directory/") do
  :ok ->
    IO.puts("Archive extracted successfully!")

  {:error, reason} ->
    IO.puts("Extraction failed: #{inspect(reason)}")
end
```

## How It Works

Deflate64 differs from standard DEFLATE (RFC 1951) primarily in:
1. **Sliding Window Size**: 64KB instead of 32KB.
2. **Additional Distance Codes**: Codes 30 and 31 represent distances up to 65,536 bytes.
3. **Larger Block/Length Limits**: Support for lengths up to 65,538 bytes.

This library includes the official zlib `contrib/infback9` implementation (written by Mark Adler) inside `c_src/`. This C code is compiled alongside a Zig wrapper (`lib/z64/nif.ex`), which acts as the NIF bridging Elixir and the C library.

All buffer management and allocation use the BEAM's internal memory allocator, ensuring zero memory leaks and complete safety.

## Tests

Run the test suite using `mix`:

```bash
mix test
```