diff options
Diffstat (limited to 'src/limits.zig')
| -rw-r--r-- | src/limits.zig | 237 |
1 files changed, 0 insertions, 237 deletions
diff --git a/src/limits.zig b/src/limits.zig deleted file mode 100644 index ee94a4f6..00000000 --- a/src/limits.zig +++ /dev/null @@ -1,237 +0,0 @@ -//! Every board-shaped capacity in one table. -//! -//! These numbers used to be nine `platform == .esp32p4` tests scattered across -//! nine files, each one a separate place to forget. They are not nine -//! decisions: they are ONE decision — how much memory this build is allowed to -//! spend — taken nine times, in nine files, where no reader could see the -//! total. Here the whole budget is on one screen and every cap says what it is -//! measured against. -//! -//! Two booleans derive all of it, and nothing outside this file tests the -//! platform for a capacity again. -//! -//! WHAT DOES NOT BELONG HERE: capability switches. `terminal_panes`, -//! `board_memory.enabled`, `hosted`, `font_picker` and the rest answer "does -//! this build have the thing at all", which is a question about the platform -//! and not about a budget — they stay next to the thing they gate. That -//! division is also why a build option selecting the board's budget on a -//! desktop does not work; the note on `board` below records the attempt. -const std = @import("std"); -const builtin = @import("builtin"); -const config = @import("pardes_config"); - -/// `board` is the ESP32-P4 firmware's budget: a 384 KiB heap and a 240 KiB -/// chunk of L2MEM shared between `.bss`, `.data` and the stack. `reduced` is -/// any freestanding target with no OS under it — the browser's wasm linear -/// memory grown on demand, megabytes rather than tens, but not a desktop's -/// address space. -/// -/// TWO BOOLEANS AND NOT A PROFILE ENUM, and a build option was tried and -/// removed. `-Dmem-profile=board` was meant to let a native test runner -/// compile the board's capacities and boot the core under them; it does not -/// work, and cannot. The dominant term in a boot is `@sizeOf(Pane)`, which -/// carries the ghostty-vt Terminal — 1.1 MiB of it — and what removes that is -/// `pardes.terminal_panes`, a CAPABILITY keyed on the platform rather than a -/// capacity in this table. So the option shrank the rings and left the boot -/// six times over budget, producing a configuration nothing was designed for: -/// `zig build unit-test -Dmem-profile=board` deadlocked in a futex rather than -/// failing, because a hosted build with the board's effect ring silently drops -/// effects a hosted test is waiting on. -/// -/// What DOES test the board's memory pressure natively is in pardes.zig: the -/// grid-scaled cost and the allocation-failure sweep, both of which are -/// platform-independent and run on the ordinary build. See the comment block -/// above `board_heap_bytes` there. -const board = config.platform == .esp32p4; -/// No OS means no address space to reserve megabytes out of, whatever the -/// platform is called. `.web` is wasm32-freestanding and `.esp32p4` is -/// riscv32-freestanding, so the target answers this for both. -const reduced_target = builtin.os.tag == .freestanding; -const KiB = 1024; -const MiB = 1024 * KiB; - -/// THE NUMBER EVERY OTHER NUMBER HERE IS MEASURED AGAINST: the board's whole -/// heap, the 384 KiB chunk of L2MEM at 0x4FF40000 (`05-zig-p4`'s linker script -/// owns the split; the 128 KiB above it measured as L2 cache rather than -/// memory). Unconditional and not profile-derived, because it is a fact about -/// the silicon rather than a budget this build chose — a desktop build that -/// wants to know what the board affords is asking exactly this question, which -/// is what the memory tests in pardes.zig do with it. -pub const board_heap_bytes = 384 * KiB; - -/// How many effects the ring holds. SHRUNK, not moved to the heap, on the -/// board: `pump` drains this to empty on every iteration with an -/// unconditional `while (nextEffect())` — including effects `perform` itself -/// queues — so no capacity can deadlock the drain, and the only question a -/// capacity answers is how big a single-pump BURST may be before `emit` -/// refuses the overflow. The one producer that can burst is `emitWrite`, -/// which chunks arbitrary bytes into 64-byte `.write` effects for a pty, and -/// a build with `terminal_panes == false` has no pty to write to. Everything -/// else queues O(1) effects per event, and `in_q` holds at most 64 events per -/// pump, so 128 leaves two effects per queued event. -/// -/// A 1.0625 MiB inline ring cannot live in the board's 384 KiB heap at all; -/// 128 entries is 34 KiB. NOTE THE BEHAVIOUR CHANGE: `emit` has always -/// refused (not evicted) once full, so on the board a burst larger than 128 effects -/// now drops its tail where 4096 would have held it — reachable only through -/// `emitWrite`, i.e. only if a pty ever appears on this platform. -pub const effect_cap = if (board) 128 else 4096; - -/// Bytes of a pane's pty write that may WAIT in the core when `effect_cap` -/// chunks are already queued. `emitWrite` splits a burst into fixed 64-byte -/// effects, so without this a paste larger than `effect_cap * 64` (256 KiB on -/// a desktop) lost its tail silently — the ring refuses rather than evicts, -/// which keeps queued bytes in order but cut the new ones off. The remainder -/// parks here instead and `nextEffect` refills the ring as the host drains it, -/// so a large paste is DELAYED rather than truncated. -/// -/// 4 MiB matches `tty.max_paste_bytes`, the largest burst a host can hand the -/// core in one event, so the bound is the one the producer already enforces. -/// Zero on the board: no ptys means no `emitWrite`, and the allocation this -/// would justify cannot live in 384 KiB anyway. A zero cap parks nothing and -/// restores the old refusal exactly. -pub const pending_write_cap: usize = if (board) 0 else 4 << 20; - -/// Rows the per-pane soft-wrap map covers. `wrapWidth` refuses to wrap a pane -/// taller than this (it reads the array's own length), so shrinking it cannot -/// truncate a map — a taller pane renders unwrapped, exactly as documented on -/// `Pane.wrap_line`. A serial console is not 128 rows tall. -pub const wrap_rows = if (board) 128 else 256; - -/// A shell's reported working directory, owned inline by the pane. Zero-sized -/// where there are no processes to report one: the `PdfSlot` rule, applied to -/// a capacity whose sole producer (`Pardes.setCwd`, fed by a pty's prompt -/// report) does not exist without terminal panes. `setOwnedCwd` clamps, so a -/// zero cap reads as "no directory known" — which is the truth here. -/// -/// Keyed on the profile rather than on `pardes.terminal_panes`, which this -/// file must not import (the core imports the table, not the other way round). -/// The two agree by construction: the board is the only build with no ptys. -pub const cwd_buf_cap = if (board) 0 else 1024; - -/// EDIT BOUNDARIES REMEMBERED PER FILE PANE. Every entry owns a gpa copy of -/// the WHOLE file, so this number multiplies heap, not just the pane: 256 of -/// them is not a bound a 384 KiB board could ever reach anyway. `pushHistory` -/// evicts and frees the oldest once full, so the smaller ring loses the -/// deepest undo steps and nothing else — no truncation, no dropped edit. -pub const undo_max = if (board) 16 else 256; - -/// How many message-row lines the session keeps for `Messages`, and one of the -/// bigger fixed costs on `Pardes`: an entry is 262 bytes, so 128 of them is -/// 32.75 KiB that is allocated whether or not anybody ever reads it. That is -/// 8.5% of the board's whole 384 KiB heap and about the size of its effect -/// ring, so the board takes sixteen — enough that a failure you looked away -/// from is still there, which is the whole point, and not enough to matter -/// beside the panes. This belongs here rather than in config.zig for exactly -/// the reason the file's header gives: it is a board-shaped capacity. -pub const message_log = if (board) 16 else 128; - -/// Bounds the only user-editable, schema-owned tag fragment. It IS the storage -/// bound: `Pane.tag_tail` is `[max_tag_tail]u8`, and every writer (appendTag, -/// tagInsert, restoreDumpTail, the acmefs `tag` file) refuses input that does -/// not fit rather than truncating it, so the schema limit and the buffer can -/// never disagree — a dump reader can reject data before copying it into a -/// pane. -/// -/// 512 on the P4 firmware. A tag is ONE line — a pane's path plus its command -/// words — and 4 KiB of it is 4 KiB per pane out of a 384 KiB heap. A serial -/// console is 80 columns; 512 is six of those. -pub const max_tag_tail: usize = if (board) 512 else 4096; - -/// HOW LONG A HOST-SUPPLIED ABSOLUTE PATH MAY BE, and the only reason that -/// record was ever kilobytes: the shell a native host resolved, the font file -/// a native picker returned, and (in pardes.zig) the one watched theme file. -/// All three name something on a FILESYSTEM, and all three are retained -/// inline because the core has no allocator at the point they arrive. -/// -/// Fixed at 4095 wherever a filesystem exists — deliberately NOT derived from -/// std.fs PATH_MAX, which web has no answer for, and 4095 rather than 4096 so -/// the macOS C bridge's NUL fits without a second, subtly different limit at -/// that boundary. Zero on the P4 firmware, which has no filesystem, no -/// processes to spawn a shell for and no font picker: `Text(0)` is a -/// zero-sized field whose `set` refuses every non-empty path, so the three -/// producers report failure instead of storing 12 KiB nothing can fill. -pub const host_path_cap: usize = if (board) 0 else 4095; - -/// WHETHER PARDES'S OWN SOURCE IS EMBEDDED — the source_manifest allowlist, -/// which is a capacity spelled as rodata rather than as a number. -/// -/// ON THE P4 the allowlist is EMPTY, and that is the whole difference: the -/// table is ~0.95 MiB of rodata against a 1.5 MiB flash partition, and the -/// firmware's filesystem is the serial host's, reached through the Host -/// vtable. The API is unchanged — `all` is a zero-length array and `find` -/// answers null — so every caller compiles identically and simply finds -/// nothing embedded. -pub const embedded_sources = !board; - -/// Bytes per dumped row, and it is a different number on the board. -/// -/// `hexdump -C`'s sixteen is the layout everyone can already read, and it needs 79 columns: ten for -/// the address, forty-eight for the hex, a gap, and the eighteen-column ASCII gutter. The P4 drives -/// a 56-column grid of which seven go to the line-number gutter, so a sixteen-byte row wraps onto a -/// second display line and the columns stop lining up - which is the entire value of the layout. -/// -/// Eight fits in 46 and keeps every property that matters: address on the left, fixed-width hex -/// columns, ASCII on the right, and a gap at the halfway mark because the eye counts in fours and -/// eights rather than in sixteens. -/// -/// NO `0x` ON WHAT THESE WORDS PRINT, which is where two of those columns came from. It reads no -/// worse - every number here is hex, there is no other kind, and the words refuse a decimal one - and -/// it buys something better than the width: an address in a dump can now be typed straight back into -/// a `Peek` without editing it, because bare hex is exactly what the parser wants. Output that is -/// valid input is worth more than a prefix restating what the whole file already says. -pub const hexdump_row_bytes: u32 = if (board) 8 else 16; - -/// Three tiers, because the address space differs by four orders of magnitude. -/// `reduced_target` is the browser: a wasm linear memory it grows on demand, so -/// the static reservations are megabytes rather than tens. -/// -/// `board` is ESP32-P4 firmware, and its tier is deliberately ALL FALLBACK. Every -/// capacity here is a `StackFallbackAllocator`'s buffer, which is a static and -/// therefore lands in `.bss` — and on the P4 `.bss`, `.data` and the stack all -/// share ONE 240 KiB chunk of L2MEM at 0x4FF03000, while the heap the fallback -/// allocator hands out is the separate 384 KiB chunk at 0x4FF40000 - the 128 KiB -/// above that measured as L2 cache rather than memory. A -/// megabyte-shaped reservation here would not fit, and every byte that did fit -/// would be taken from the stack's neighbourhood to duplicate memory the heap -/// already has. So the buffers exist only because the type requires one: 4 KiB -/// absorbs the small churn, and everything else spills to the real heap on the -/// first allocation. -pub const arena = struct { - pub const pardes = if (board) 4 * KiB else if (reduced_target) 8 * MiB else 32 * MiB; - pub const frame = if (board) 4 * KiB else if (reduced_target) 4 * MiB else 16 * MiB; - // Zero is legal and always spills, which is exactly what an arena for a - // compiled-out subsystem should do. `StackFallbackAllocator(0).buffer` is - // `[0]u8`; `get()` inits the FixedBufferAllocator over an empty slice, so - // `FixedBufferAllocator.alloc` fails every nonzero request and `alloc` - // falls through to `self.fallback_allocator.rawAlloc`, while `ownsPtr` over - // an empty range is false for every pointer so `resize`/`remap`/`free` - // route to the fallback too. See lib/std/heap.zig, StackFallbackAllocator. - pub const tree_sitter = if (board) 0 else if (reduced_target) 4 * MiB else 16 * MiB; - pub const image = if (board) 0 else if (reduced_target) 64 * KiB else 32 * MiB; - pub const pdf = if (board) 0 else if (reduced_target or !config.mupdf) 64 * KiB else 64 * MiB; -}; - -// THE REGRESSION GUARD for the refactor that created this file: nine caps -// moved out of nine files, and the one thing that must not have changed is -// what a tty/gui/macos build gets. Spelling the historical desktop numbers -// here as literals is the point — a derivation would agree with itself. -test "board limits: a desktop build keeps exactly its historical capacities" { - if (board or reduced_target) return error.SkipZigTest; - try std.testing.expectEqual(4096, effect_cap); - try std.testing.expectEqual(256, wrap_rows); - try std.testing.expectEqual(1024, cwd_buf_cap); - try std.testing.expectEqual(256, undo_max); - try std.testing.expectEqual(@as(usize, 4096), max_tag_tail); - try std.testing.expectEqual(@as(usize, 4095), host_path_cap); - try std.testing.expect(embedded_sources); - try std.testing.expectEqual(@as(u32, 16), hexdump_row_bytes); - // The arena tier a desktop gets is the third one, so it is only the - // historical desktop tier when the target is not itself reduced. - if (reduced_target) return; - try std.testing.expectEqual(32 * MiB, arena.pardes); - try std.testing.expectEqual(16 * MiB, arena.frame); - try std.testing.expectEqual(16 * MiB, arena.tree_sitter); - try std.testing.expectEqual(32 * MiB, arena.image); - try std.testing.expectEqual(if (config.mupdf) 64 * MiB else 64 * KiB, arena.pdf); -} |
