# "Compression" _Path: en/lua/data/compress_ > "Compress and decompress data using gzip, deflate, zlib, brotli, and zstd algorithms." ## Table of Contents - Compression ## Content # Compression The `compress` module encodes and decodes strings with gzip, Brotli, Zstandard, raw DEFLATE, and zlib. This is an API reference with partial HTTP and storage recipes. Every operation materializes its complete input and output as Lua strings; use the archive or stream APIs when data must remain streaming. The examples assume the entry enables `compress` and any separately required modules such as `json` or `http`. ## Loading ```lua local compress = require("compress") ``` Add `compress` to the executable entry's `modules:` list before requiring it. ## GZIP Gzip is defined by RFC 1952. ### Compress {id="gzip-compress"} ```lua -- Compress for HTTP response local body, json_err = json.encode(large_response) if json_err then return nil, json_err end local compressed, err = compress.gzip.encode(body) if err then return nil, err end -- Set Content-Encoding header local header_err = res:set_header("Content-Encoding", "gzip") if header_err then return nil, header_err end local write_err = res:write(compressed) if write_err then return nil, write_err end -- Maximum compression for storage local archived, archive_err = compress.gzip.encode(data, {level = 9}) if archive_err then return nil, archive_err end -- Fast compression for real-time local fast, fast_err = compress.gzip.encode(data, {level = 1}) if fast_err then return nil, fast_err end ``` | Parameter | Type | Description | |-----------|------|-------------| | `data` | string | Data to compress | | `options` | table? | Optional encoding options | #### Options {id="gzip-compress-options"} | Field | Type | Description | |-------|------|-------------| | `level` | integer | Compression level 1-9 (default: 6) | **Returns:** `string, error` ### Decompress {id="gzip-decompress"} ```lua -- Decompress HTTP request local content_encoding, header_err = req:header("Content-Encoding") if header_err then return nil, header_err end if content_encoding == "gzip" then local body, body_err = req:body() if body_err then return nil, body_err end local decompressed, err = compress.gzip.decode(body) if err then return nil, errors.new("Invalid gzip data"):kind(errors.INVALID) end body = decompressed end -- Decompress with size limit (prevent zip bombs) local decompressed, err = compress.gzip.decode(data, {max_size = 10 * 1024 * 1024}) if err then return nil, errors.new("Decompressed size exceeds 10MB limit"):kind(errors.INVALID) end ``` | Parameter | Type | Description | |-----------|------|-------------| | `data` | string | GZIP compressed data | | `options` | table? | Optional decoding options | #### Options {id="gzip-decompress-options"} | Field | Type | Description | |-------|------|-------------| | `max_size` | integer | Max decompressed size in bytes (default: 128MB, max: 1GB) | **Returns:** `string, error` ## Brotli Brotli is defined by RFC 7932 and is commonly used for compressed text content. ### Compress {id="brotli-compress"} ```lua -- Best for static assets and text content local compressed, err = compress.brotli.encode(html_content, {level = 11}) if err then return nil, err end -- Store `compressed` through the application's cache contract if needed. -- Moderate compression for API responses local compressed, err = compress.brotli.encode(json_data, {level = 4}) if err then return nil, err end ``` | Parameter | Type | Description | |-----------|------|-------------| | `data` | string | Data to compress | | `options` | table? | Optional encoding options | #### Options {id="brotli-compress-options"} | Field | Type | Description | |-------|------|-------------| | `level` | integer | Compression level 0-11 (default: 6) | **Returns:** `string, error` ### Decompress {id="brotli-decompress"} ```lua local decompressed, err = compress.brotli.decode(compressed_data) if err then return nil, err end -- With size limit local decompressed, err = compress.brotli.decode(data, {max_size = 50 * 1024 * 1024}) if err then return nil, err end ``` | Parameter | Type | Description | |-----------|------|-------------| | `data` | string | Brotli compressed data | | `options` | table? | Optional decoding options | #### Options {id="brotli-decompress-options"} | Field | Type | Description | |-------|------|-------------| | `max_size` | integer | Max decompressed size in bytes (default: 128MB, max: 1GB) | **Returns:** `string, error` ## Zstandard Zstandard is a general-purpose compression format defined by RFC 8878. ### Compress {id="zstd-compress"} ```lua -- Good balance of speed and ratio local compressed, err = compress.zstd.encode(binary_data) if err then return nil, err end -- Higher compression for archival local archived, archive_err = compress.zstd.encode(data, {level = 19}) if archive_err then return nil, archive_err end -- Fast mode for latency-sensitive payloads local fast, fast_err = compress.zstd.encode(data, {level = 1}) if fast_err then return nil, fast_err end ``` | Parameter | Type | Description | |-----------|------|-------------| | `data` | string | Data to compress | | `options` | table? | Optional encoding options | #### Options {id="zstd-compress-options"} | Field | Type | Description | |-------|------|-------------| | `level` | integer | Compression level 1-22 (default: 3) | | `dict` | string? | Zstd dictionary bytes from `train_dict` (default: none) | **Returns:** `string, error` ### Decompress {id="zstd-decompress"} ```lua local decompressed, err = compress.zstd.decode(compressed_data) if err then return nil, err end ``` | Parameter | Type | Description | |-----------|------|-------------| | `data` | string | Zstandard compressed data | | `options` | table? | Optional decoding options | #### Options {id="zstd-decompress-options"} | Field | Type | Description | |-------|------|-------------| | `max_size` | integer | Max decompressed size in bytes (default: 128MB, max: 1GB) | | `dict` | string? | Zstd dictionary bytes (must match the dict used to encode) | **Returns:** `string, error` ### Dictionaries {id="zstd-dictionaries"} Train a dictionary from similar sample payloads, then pass it through the `dict` option to `encode` and `decode`. Decoding requires the same dictionary used for encoding. ```lua local dict, err = compress.zstd.train_dict(samples, { size = 112640 }) if err then return nil, err end local packed, pack_err = compress.zstd.encode(data, { dict = dict }) if pack_err then return nil, pack_err end local original, decode_err = compress.zstd.decode(packed, { dict = dict }) if decode_err then return nil, decode_err end ``` #### train_dict(samples, options?) | Parameter | Type | Description | |-----------|------|-------------| | `samples` | string[] | Training samples (at least one >= 8 bytes) | | `options` | table? | `size` (integer, target dict bytes, 256-1048576, default 114688), `id` (integer, default 0), `level` (integer, 1-22) | **Returns:** `string, error` (the dictionary bytes) #### inspect_dict(dict) | Parameter | Type | Description | |-----------|------|-------------| | `dict` | string | Dictionary bytes | **Returns:** `table, error` — `{id: integer, content_size: integer}` ## Deflate Raw DEFLATE is defined by RFC 1951 and is also used inside other formats. ### Compress {id="deflate-compress"} ```lua local compressed, err = compress.deflate.encode(data, {level = 6}) if err then return nil, err end ``` | Parameter | Type | Description | |-----------|------|-------------| | `data` | string | Data to compress | | `options` | table? | Optional encoding options | #### Options {id="deflate-compress-options"} | Field | Type | Description | |-------|------|-------------| | `level` | integer | Compression level 1-9 (default: 6) | **Returns:** `string, error` ### Decompress {id="deflate-decompress"} ```lua local decompressed, err = compress.deflate.decode(compressed) if err then return nil, err end ``` | Parameter | Type | Description | |-----------|------|-------------| | `data` | string | DEFLATE compressed data | | `options` | table? | Optional decoding options | #### Options {id="deflate-decompress-options"} | Field | Type | Description | |-------|------|-------------| | `max_size` | integer | Max decompressed size in bytes (default: 128MB, max: 1GB) | **Returns:** `string, error` ## Zlib Zlib wraps DEFLATE data with a header and checksum as defined by RFC 1950. ### Compress {id="zlib-compress"} ```lua local compressed, err = compress.zlib.encode(data, {level = 6}) if err then return nil, err end ``` | Parameter | Type | Description | |-----------|------|-------------| | `data` | string | Data to compress | | `options` | table? | Optional encoding options | #### Options {id="zlib-compress-options"} | Field | Type | Description | |-------|------|-------------| | `level` | integer | Compression level 1-9 (default: 6) | **Returns:** `string, error` ### Decompress {id="zlib-decompress"} ```lua local decompressed, err = compress.zlib.decode(compressed) if err then return nil, err end ``` | Parameter | Type | Description | |-----------|------|-------------| | `data` | string | Zlib compressed data | | `options` | table? | Optional decoding options | #### Options {id="zlib-decompress-options"} | Field | Type | Description | |-------|------|-------------| | `max_size` | integer | Max decompressed size in bytes (default: 128MB, max: 1GB) | **Returns:** `string, error` ## Choosing an Algorithm | Algorithm | Best For | Speed | Ratio | Level Range | |-----------|----------|-------|-------|-------------| | gzip | HTTP, wide compatibility | Medium | Good | 1-9 | | brotli | Static assets, text | Slow | Best | 0-11 | | zstd | Binary payloads, fast compression | Fast | Good | 1-22 | | deflate/zlib | Low-level, specific protocols | Medium | Good | 1-9 | ```lua -- HTTP response based on Accept-Encoding local accept, header_err = req:header("Accept-Encoding") if header_err then return nil, header_err end accept = accept or "" local body, json_err = json.encode(response_data) if json_err then return nil, json_err end local qualities = {} for item in accept:gmatch("[^,]+") do local coding = item:match("^%s*([^;%s]+)") local has_q = item:match(";%s*[qQ]%s*=") ~= nil local q_text = item:match(";%s*[qQ]%s*=%s*([^;%s,]+)") local q if not has_q then q = 1 elseif q_text == "0" or q_text == "1" or (q_text and q_text:match("^0%.%d?%d?%d?$")) or (q_text and q_text:match("^1%.0?0?0?$")) then q = tonumber(q_text) end if coding and q and q >= 0 and q <= 1 then coding = coding:lower() qualities[coding] = math.max(qualities[coding] or 0, q) end end local function quality(coding) if qualities[coding] ~= nil then return qualities[coding] end if coding == "identity" then return qualities["*"] == 0 and 0 or 1 end return qualities["*"] or 0 end local selected, selected_q = nil, -1 for _, coding in ipairs({"br", "gzip", "identity"}) do local q = quality(coding) if q > selected_q then selected, selected_q = coding, q end end -- Include every field used by this handler or its surrounding middleware. local vary_fields = {"Accept-Encoding"} local vary_err = res:set_header("Vary", table.concat(vary_fields, ", ")) if vary_err then return nil, vary_err end if selected_q <= 0 then local status_err = res:set_status(http.STATUS.NOT_ACCEPTABLE) if status_err then return nil, status_err end local write_err = res:write("No acceptable content encoding") if write_err then return nil, write_err end elseif selected == "br" then local compressed, compress_err = compress.brotli.encode(body) if compress_err then return nil, compress_err end local set_err = res:set_header("Content-Encoding", "br") if set_err then return nil, set_err end local write_err = res:write(compressed) if write_err then return nil, write_err end elseif selected == "gzip" then local compressed, compress_err = compress.gzip.encode(body) if compress_err then return nil, compress_err end local set_err = res:set_header("Content-Encoding", "gzip") if set_err then return nil, set_err end local write_err = res:write(compressed) if write_err then return nil, write_err end else local write_err = res:write(body) if write_err then return nil, write_err end end ``` This partial handler parses exact coding tokens and RFC q-values, honors explicit rejections such as `br;q=0`, and emits `Vary: Accept-Encoding`. `set_header` replaces an existing `Vary` value, so add every other field used by surrounding middleware to `vary_fields` before setting it. A full HTTP stack may provide a shared negotiation helper instead. ## Errors | Condition | Kind | Retryable | |-----------|------|-----------| | Empty input | `errors.INVALID` | no | | Level out of range | `errors.INVALID` | no | | Invalid compressed data | `errors.INVALID` | no | | Decompressed size exceeds limit | `errors.INTERNAL` (gzip, zlib, zstd) / `errors.INVALID` (deflate, brotli) | no | See [Error Handling](lua/core/errors.md) for working with errors. ## Navigation Previous: "Base64 Encoding" (lua/data/base64) Next: "Archive" (lua/data/archive)