defmodule LibdeflateEx do @moduledoc """ Fast deflate/zlib/gzip compression and decompression using libdeflate via Rustler NIF. ## Compression Compression functions accept data and a compression level (0-12, where 0 is no compression, 1 is fastest, and 12 is highest compression). A default level of 6 is used when not specified. ## Decompression Decompression functions require the exact uncompressed size to be known ahead of time, which is typical for formats like ZIP (stored in the central directory). """ @default_level 6 @typedoc "Compression level from 0 (no compression) to 12 (maximum compression)." @type level :: 0..12 # ── Compression ────────────────────────────────────────────────────────────── @doc """ Compress data using raw deflate (no header). `level` is the compression level (1-12). Defaults to #{@default_level}. Returns `{:ok, binary}` or `{:error, reason}`. """ @spec deflate_compress(binary(), level()) :: {:ok, binary()} | {:error, String.t()} def deflate_compress(data, level \\ @default_level) when is_binary(data) and is_integer(level) do LibdeflateEx.Native.deflate_compress(data, level) end @doc """ Compress data using zlib format (deflate with zlib header + checksum). `level` is the compression level (1-12). Defaults to #{@default_level}. Returns `{:ok, binary}` or `{:error, reason}`. """ @spec zlib_compress(binary(), level()) :: {:ok, binary()} | {:error, String.t()} def zlib_compress(data, level \\ @default_level) when is_binary(data) and is_integer(level) do LibdeflateEx.Native.zlib_compress(data, level) end @doc """ Compress data using gzip format. `level` is the compression level (1-12). Defaults to #{@default_level}. Returns `{:ok, binary}` or `{:error, reason}`. """ @spec gzip_compress(binary(), level()) :: {:ok, binary()} | {:error, String.t()} def gzip_compress(data, level \\ @default_level) when is_binary(data) and is_integer(level) do LibdeflateEx.Native.gzip_compress(data, level) end @doc """ Like `deflate_compress/2` but raises on error. """ @spec deflate_compress!(binary(), level()) :: binary() def deflate_compress!(data, level \\ @default_level) do case deflate_compress(data, level) do {:ok, result} -> result {:error, reason} -> raise "deflate compress failed: #{reason}" end end @doc """ Like `zlib_compress/2` but raises on error. """ @spec zlib_compress!(binary(), level()) :: binary() def zlib_compress!(data, level \\ @default_level) do case zlib_compress(data, level) do {:ok, result} -> result {:error, reason} -> raise "zlib compress failed: #{reason}" end end @doc """ Like `gzip_compress/2` but raises on error. """ @spec gzip_compress!(binary(), level()) :: binary() def gzip_compress!(data, level \\ @default_level) do case gzip_compress(data, level) do {:ok, result} -> result {:error, reason} -> raise "gzip compress failed: #{reason}" end end # ── Decompression ──────────────────────────────────────────────────────────── @doc """ Decompress raw deflate data (no zlib/gzip header). Returns `{:ok, binary}` or `{:error, reason}`. """ @spec deflate_decompress(binary(), non_neg_integer()) :: {:ok, binary()} | {:error, String.t()} def deflate_decompress(data, uncompressed_size) when is_binary(data) and is_integer(uncompressed_size) do LibdeflateEx.Native.deflate_decompress(data, uncompressed_size) end @doc """ Decompress zlib-wrapped data (deflate with 2-byte header + checksum). Returns `{:ok, binary}` or `{:error, reason}`. """ @spec zlib_decompress(binary(), non_neg_integer()) :: {:ok, binary()} | {:error, String.t()} def zlib_decompress(data, uncompressed_size) when is_binary(data) and is_integer(uncompressed_size) do LibdeflateEx.Native.zlib_decompress(data, uncompressed_size) end @doc """ Decompress gzip data. Returns `{:ok, binary}` or `{:error, reason}`. """ @spec gzip_decompress(binary(), non_neg_integer()) :: {:ok, binary()} | {:error, String.t()} def gzip_decompress(data, uncompressed_size) when is_binary(data) and is_integer(uncompressed_size) do LibdeflateEx.Native.gzip_decompress(data, uncompressed_size) end @doc """ Like `deflate_decompress/2` but raises on error. """ @spec deflate_decompress!(binary(), non_neg_integer()) :: binary() def deflate_decompress!(data, uncompressed_size) do case deflate_decompress(data, uncompressed_size) do {:ok, result} -> result {:error, reason} -> raise "deflate decompress failed: #{reason}" end end @doc """ Like `zlib_decompress/2` but raises on error. """ @spec zlib_decompress!(binary(), non_neg_integer()) :: binary() def zlib_decompress!(data, uncompressed_size) do case zlib_decompress(data, uncompressed_size) do {:ok, result} -> result {:error, reason} -> raise "zlib decompress failed: #{reason}" end end @doc """ Like `gzip_decompress/2` but raises on error. """ @spec gzip_decompress!(binary(), non_neg_integer()) :: binary() def gzip_decompress!(data, uncompressed_size) do case gzip_decompress(data, uncompressed_size) do {:ok, result} -> result {:error, reason} -> raise "gzip decompress failed: #{reason}" end end end