Examples
Four standalone programs live in
examples/,
each one file. Every one builds its own archive first, so any of them runs from
a fresh clone with no setup:
zig build example-01 # Pack a directory and read a file backzig build example-02 # Load assets without allocating per assetzig build example-03 # Look assets up by compile-time handlezig build example-04 # Stream an entry instead of buffering it
zig build examples # Build all four without running themEverything they write goes to zig-out/examples/, which git ignores. They build
as part of zig build test, so a stale example fails CI rather than rotting
quietly.
The output below is what these actually print.
01 - Pack and read
Section titled “01 - Pack and read”The round trip end to end: pack, Archive.open, iterating
entries, readAlloc, and verify. It also re-reads the original file from
disk and compares, so the round-trip claim is checked rather than asserted.
$ zig build example-01packed 5 files, 13309 bytes -> 12565 bytes
archive holds 5 entries: audio/blip.wav 2444 bytes deflate data/noise.bin 4096 bytes store jimmy.png 6138 bytes deflate levels/level-01.json 398 bytes deflate shaders/sprite.frag 233 bytes deflate
read jimmy.png: 6138 bytes starts with PNG signature: true identical to the file on disk
verified all 5 entriesNote data/noise.bin is store - it is incompressible, so pack fell back to
writing it verbatim rather than growing it.
02 - Load assets without allocating
Section titled “02 - Load assets without allocating”The shape a game actually wants: open the archive once at startup, allocate one
buffer sized to your largest asset, then find and read into it forever. It
also shows what a missing path and an undersized buffer do.
$ zig build example-02jimmy.png 6138 bytes deflate PNG imagelevels/level-01.json 398 bytes deflate JSONshaders/sprite.frag 233 bytes deflate GLSL sourcedata/noise.bin 4096 bytes store binary
levels/level-99.json is not in this archivereading jimmy.png into 16 bytes: BufferTooSmallNothing past the single gpa.alloc at startup touches the allocator. A missing
path is null from find, not an error; an undersized buffer is
error.BufferTooSmall from read.
03 - Compile-time asset handles
Section titled “03 - Compile-time asset handles”Turning a mistyped asset path from a runtime null into a compile error, using
assetId and findId.
$ zig build example-03handles baked into the binary: 0x40a8e93753577eb5 audio/blip.wav 0xbc5d025c01217e9b data/noise.bin 0xda77d2ecbff2fabb jimmy.png 0x3a43adffebb476d2 levels/level-01.json 0xc51bb341f35636a4 shaders/sprite.frag
loading by handle: jimmy.png 6138 bytes audio/blip.wav 2444 bytes levels/level-01.json 398 bytes
archive.find("jimy.png") -> null, discovered at run timeload(&archive, .@"jimy.png") -> caught by the compilerThe last two lines are the whole point. The enum is written by hand here so the
example stands alone; in a real project
zpack ids generates it. See
asset handles.
04 - Streaming
Section titled “04 - Streaming”A 1 MiB texture streamed through a 68 KiB buffer with
entryReader, hashing as it goes since entryReader
verifies nothing itself.
$ zig build example-04asset 1048576 bytes (116281 on disk, deflate)buffer 69632 bytes is 15x smaller than the asset
streamed 1048576 bytes in 32 chunkslargest chunk held at once: 65536 byteshash matches the one recorded at pack time
peak memory for the contents: 69632 bytes, not 1048576
entryReader with a 1 KiB buffer: BufferTooSmallstored entry through that same 1 KiB buffer: 4096 bytes
archive.verify() runs the same pass over every entryThe last two lines show the asymmetry: a deflated entry needs at least
stream_buffer_len for its history window, but a stored entry accepts any
buffer, even one far smaller than the entry.
The example generates its texture at runtime rather than checking a large binary into the repository.
The sample assets
Section titled “The sample assets”examples/example-assets/ is small and chosen to cover the cases that behave
differently, not to be realistic:
| File | Why it is there |
|---|---|
jimmy.png |
A real image, at the top level rather than in a subdirectory |
audio/blip.wav |
A real container format, one directory deep |
levels/level-01.json |
Text, so it compresses hard - 398 bytes down to 144 |
shaders/sprite.frag |
More text, and a second entry sharing no parent with the first |
data/noise.bin |
Incompressible, so pack falls back to storing it verbatim |
That last one is the point of the set: an archive built from this tree contains
both deflate and store entries, so every example exercises both paths
without having to explain the difference twice.