Open-Source Wikis

/

Bun

/

Systems

/

Allocators & sys

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:

  1. Windows. std.posix panics on Windows for many calls. bun.sys always works.
  2. Error fidelity. std.fs discards the path on error. bun.sys.Error keeps it so error messages are useful.
  3. No unreachable. bun.sys never calls unreachable on 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 of std.*.
  • Always handle the Maybe(T) switch — never unreachable for unexpected errnos.
  • Always use bun.handleOom for 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.strings are imported by virtually every command.

Entry points for modification

  • To add a new syscall wrapper, edit src/sys.zig and add the Windows + POSIX implementations in lockstep. Tests under test/sys/ (or via integration tests) should cover both platforms.
  • To change allocator policy, edit src/allocators.zig and src/allocators/ — most call sites use bun.default_allocator so 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.

Allocators & sys – Bun wiki | Factory