# `F3.Postscript`

The 32-byte tail of an F3 file.

Fields are little-endian, in the order fixed by `F3.Format.postscript_layout/0`.
The magic `F3` is last. This module checks the tail and the claimed sizes.
It does not decode the footer and it does not verify checksums.

`metadata_size` and `footer_size` are attacker-controlled. A claim larger
than `max_metadata_bytes` is `:resource_limit` and is not allocated.
The default cap is 16 MiB. Pass a non-negative integer to `decode/2`
to use another cap for that call.

# `checksum_type`

```elixir
@type checksum_type() :: :xxhash | {:unknown, 0..255}
```

# `compression`

```elixir
@type compression() :: :uncompressed
```

# `t`

```elixir
@type t() :: %F3.Postscript{
  checksum_type: checksum_type(),
  data_checksum: non_neg_integer(),
  file_size: non_neg_integer(),
  footer_compression: compression(),
  footer_offset: non_neg_integer(),
  footer_size: non_neg_integer(),
  major_version: non_neg_integer(),
  metadata_offset: non_neg_integer(),
  metadata_size: non_neg_integer(),
  minor_version: non_neg_integer(),
  schema_checksum: non_neg_integer()
}
```

# `decode`

```elixir
@spec decode(F3.File.t(), non_neg_integer()) :: {:ok, t()} | {:error, F3.Error.t()}
```

Reads and checks the postscript of an open file.

`max_metadata_bytes` limits `metadata_size` and `footer_size`. Checksum
type `0` is `:xxhash`. Any other byte is `{:unknown, byte}` and is not
a reason to stop. Footer compression other than uncompressed is
`:unsupported_encoding`. A file shorter than 32 bytes is `:truncated`.
`max` must be a non-negative integer. This does not raise for a short file.

## Examples

    iex> {:ok, file} = F3.open("test/fixtures/small.f3")
    iex> {:ok, post} = F3.Postscript.decode(file, F3.Postscript.max_metadata_bytes())
    iex> {post.major_version, post.minor_version, post.footer_compression}
    {0, 1, :uncompressed}

    iex> {:ok, short} = F3.open({:memory, "no"})
    iex> {:error, error} = F3.Postscript.decode(short, 32)
    iex> error.category
    :truncated

# `max_metadata_bytes`

```elixir
@spec max_metadata_bytes() :: pos_integer()
```

Default maximum metadata span, in bytes, for one `decode/2` call.

## Examples

    iex> F3.Postscript.max_metadata_bytes()
    16_777_216

---

*Consult [api-reference.md](api-reference.md) for complete listing*
