oven-sh/bun
Allocators & sys
The Zig code in Bun never calls std.fs, std.posix, or std.os directly. Instead it uses thin wrappers in bun.sys.* and a process-wide allocator policy. This page summarises both.
Allocators
src/allocators/ and src/allocators.zig provide:
| Allocator | Use case |
|---|---|
bun.default_allocator |
The 99% case. Backed by mimalloc (vendor/mimalloc/). |
MimallocArena |
A heap-aligned arena for short-lived allocations; freed in one go. |
bun.PathBuffer |
4 KB stack buffer for path manipulation. Pooled via bun.path_buffer_pool to avoid 64 KB stack allocations on Windows. |
LinearFifo (src/linear_fifo.zig) |
A fast ring buffer used by I/O code. |
MimallocPool |
Per-thread pool used by the bundler to keep parse output near the consumer thread. |
mimalloc replaces the system allocator everywhere except inside JSC (which manages its own heap) and inside the vendored libraries that link their own malloc.
src/handle_oom.zig provides bun.handleOom(expr) — wraps allocator calls so error.OutOfMemory becomes a stable crash. Use it instead of expr catch unreachable or expr catch bun.outOfMemory().
bun.sys
src/sys.zig (~165 KB) is the cross-platform syscall layer. Every operation returns Maybe(T) — a tagged union of .result: T or .err: bun.sys.Error:
const fd = switch (bun.sys.open(path, bun.O.RDONLY, 0)) {
.result => |fd| fd,
.err => |err| return .{ .err = err },
};Notable functions: open, openat, read, pread, write, pwrite, stat, fstat, lstat, mkdir, unlink, rename, symlink, chmod, dup, sendfile, mmap, getcwd, readlink, getFdPath.
A higher-level wrapper, bun.sys.File (src/sys/File.zig), handles open + read/write + close in one call:
const bytes = switch (bun.sys.File.readFrom(bun.FD.cwd(), path, allocator)) {
.result => |b| b,
.err => |err| return .{ .err = err },
};bun.FD (src/fd.zig) is the cross-platform file descriptor type. Use bun.FD.cwd() for the current working directory; fd.close() to close.
bun.sys.Error (src/sys/Error.zig) preserves the syscall name, the errno, and the path on which the call failed. Convert to a JS error with err.toSystemError().toErrorInstance(globalObject).
Why not std.posix?
Three reasons:
- Windows.
std.posixpanics on Windows for many calls.bun.sysalways works. - Error fidelity.
std.fsdiscards the path on error.bun.sys.Errorkeeps it so error messages are useful. - No
unreachable.bun.sysnever callsunreachableon unexpected errnos; it returns them as errors.
bun.strings
src/string/immutable.zig is the project's string library. SIMD-accelerated for indexOf, eql, startsWith, contains, firstNonASCII. Handles UTF-8, Latin-1, and UTF-16 (because JSC uses Latin-1 and UTF-16 internally).
bun.String (src/string.zig) is the bridge type for Zig ↔ JSC. Methods: cloneUTF8, borrowUTF8, toUTF8, toJS, createUTF8ForJS. Prefer it over ZigString in new code.
bun.path
src/resolver/resolve_path.zig is also reachable as bun.path.*. Functions: join, joinZ, joinStringBuf, joinAbsString, dirname, basename, relative, normalizeBuf. The platform parameter is .auto (current platform), .posix, .windows, or .loose (accept either separator).
bun.path.join uses a thread-local buffer; the result must be copied if it needs to outlive the call.
bun.spawn
src/bun.js/api/bun/process.zig exposes bun.spawn (async, returns a Subprocess JS object) and bun.spawnSync (synchronous, returns the result directly). Use these instead of std.process.Child — they handle Windows process trees, stdio inheritance/piping, environment maps, and timing-out.
Logging
Output.zig is the logging surface. Use bun.Output.scoped(.MyScope, .visible) to define a tagged logger; bun.Output.pretty("<green>ok<r>: {s}\n", ...) for pretty-printed output. Debug logs gated .hidden only fire when BUN_DEBUG_<scope>=1.
Environment variables
src/env_var.zig is the type-safe env accessor. bun.env_var.HOME.get() returns ?[]const u8; bun.env_var.CI.get() returns ?bool; bun.env_var.BUN_CONFIG_DNS_TIME_TO_LIVE_SECONDS.get() returns a u64 with a default. The list of declared env vars is the single source of truth for what Bun reads.
URL parsing
For WHATWG-correct URL parsing in Zig, use bun.jsc.URL.fromUTF8(...) which calls into WebKit's URL implementation. Note: WebKit swaps host and hostname semantics relative to JS — url.host() returns hostname without port, url.hostname() returns hostname with port.
Patterns to follow
The repo's authoritative reference is src/CLAUDE.md:
- Always use
bun.*instead ofstd.*. - Always handle the
Maybe(T)switch — neverunreachablefor unexpected errnos. - Always use
bun.handleOomfor allocator calls; never silently catch all errors. - Always use
bun.PathBuffer(or the pool) for path scratch space. - Imports go at the bottom of the file (the auto-formatter moves them).
- Never use
@import()inline inside functions.
Integration points
- Every Zig file in the repo. This is the foundation.
bun.spawn,bun.sys.File,bun.path,bun.stringsare imported by virtually every command.
Entry points for modification
- To add a new syscall wrapper, edit
src/sys.zigand add the Windows + POSIX implementations in lockstep. Tests undertest/sys/(or via integration tests) should cover both platforms. - To change allocator policy, edit
src/allocators.zigandsrc/allocators/— most call sites usebun.default_allocatorso global swaps are tractable. - To add a typed env var, edit
src/env_var.zig.
Key source files
| File | Purpose |
|---|---|
src/CLAUDE.md / src/AGENTS.md |
Authoritative style reference. |
src/sys.zig |
Cross-platform syscalls. |
src/sys/File.zig |
High-level file handle. |
src/fd.zig |
Cross-platform FD. |
src/string.zig, src/string/ |
String types and helpers. |
src/resolver/resolve_path.zig |
Path utilities. |
src/allocators/, src/allocators.zig |
Allocator wrappers. |
src/env_var.zig |
Type-safe env. |
src/handle_oom.zig |
OOM → crash. |
src/output.zig |
Pretty-printing and logging. |
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.