chore: import upstream snapshot with attribution
CI / Deep Native Runtime Cases (1/6) (push) Has been skipped
CI / Native Preflight (push) Failing after 1s
CI / Native Runtime Cases (1/2) (push) Failing after 0s
CI / Native Runtime Cases (2/2) (push) Failing after 1s
CI / Native Metadata Reports (push) Failing after 0s
CI / Native Direct Backend Artifacts (push) Failing after 0s
CI / Native Sanitizer Smoke (push) Failing after 1s
CI / Command Contract Snapshots (push) Failing after 1s
CI / Deep Conformance Suite (push) Has been skipped
CI / Graph Build Perf (push) Failing after 1s
CI / Deep Native Preflight (push) Has been skipped
CI / Deep Native Runtime Cases (2/6) (push) Has been skipped
CI / Deep Native Runtime Cases (3/6) (push) Has been skipped
CI / Conformance Suite (push) Failing after 1s
CI / Workspace Checks (push) Failing after 0s
CI / Deep Native Runtime Cases (5/6) (push) Has been skipped
CI / Deep Native Runtime Cases (6/6) (push) Has been skipped
CI / Deep Native Runtime Cases (4/6) (push) Has been skipped
CI / Deep Graph Build Perf (push) Has been skipped

This commit is contained in:
wehub-resource-sync
2026-07-13 12:29:30 +08:00
commit e7738de6d2
1723 changed files with 216139 additions and 0 deletions
+46
View File
@@ -0,0 +1,46 @@
## When To Use std.args
In Zerolang, use `std.args` for hosted command-line programs that need positional
arguments, option lookup, or simple numeric argument parsing.
Runnable today:
| API | Return | Notes |
| --- | --- | --- |
| `std.args.len()` | `usize` | Returns the process argument count. |
| `std.args.get(index)` | `Maybe<String>` | Returns the argument at `index` when present. |
| `std.args.has(index)` | `Bool` | Reports whether `index` has an argument. |
| `std.args.getOr(index, fallback)` | `String` | Returns the argument or a caller-provided fallback. |
| `std.args.find(name)` | `Maybe<usize>` | Finds the first exact argument match after the executable path. |
| `std.args.valueAfter(name)` | `Maybe<String>` | Returns the argument immediately after a matched option name. |
| `std.args.parseU32(index)` | `Maybe<u32>` | Parses an indexed argument as `u32`. |
Current limits:
- Iterator-style argument APIs.
- Target diagnostics for platforms without process arguments.
## Example
```zero
pub fn main(world: World) -> Void raises {
let count: usize = std.args.len()
let first: String = std.args.getOr(1, "default")
let maybe_count: Maybe<u32> = std.args.parseU32(2)
if count > 2 && maybe_count.has {
check world.out.write(first)
check world.out.write("\n")
}
}
```
## Design Notes
The module is hosted-only. Freestanding, edge, and embedded targets should
reject it unless they explicitly provide an argument capability.
On native Windows-style targets, `std.args` is byte-oriented process input. It
is not a Unicode argv normalization layer.
Programs that need portable argument semantics should keep target-specific
decoding outside the target-neutral core.
+38
View File
@@ -0,0 +1,38 @@
## When To Use std.ascii
In Zerolang, use `std.ascii` when a program needs byte-level ASCII predicates, case
conversion, or digit values without Unicode normalization.
Runnable today:
| API | Return | Notes |
| --- | --- | --- |
| `std.ascii.isDigit(byte)` | `Bool` | Checks `0` through `9`. |
| `std.ascii.isAlpha(byte)` | `Bool` | Checks `A` through `Z` or `a` through `z`. |
| `std.ascii.isAlnum(byte)` | `Bool` | Checks ASCII alphabetic or digit bytes. |
| `std.ascii.isWhitespace(byte)` | `Bool` | Checks space, tab, line feed, and carriage return. |
| `std.ascii.isLower(byte)` / `std.ascii.isUpper(byte)` | `Bool` | Checks ASCII case ranges. |
| `std.ascii.isHexDigit(byte)` | `Bool` | Checks decimal digits and `a-f` / `A-F`. |
| `std.ascii.toLower(byte)` / `std.ascii.toUpper(byte)` | `u8` | Converts ASCII letters and leaves other bytes unchanged. |
| `std.ascii.digitValue(byte)` | `Maybe<u8>` | Converts an ASCII decimal digit to `0..9`. |
| `std.ascii.hexValue(byte)` | `Maybe<u8>` | Converts an ASCII hexadecimal digit to `0..15`. |
## Example
```zero
pub fn main(world: World) -> Void raises {
let digit: Maybe<u8> = std.ascii.digitValue(55_u8)
let hex: Maybe<u8> = std.ascii.hexValue(70_u8)
if std.ascii.isAlpha(65_u8) && std.ascii.toLower(90_u8) == 122_u8 && digit.has && digit.value == 7_u8 && hex.has && hex.value == 15_u8 {
check world.out.write("ascii ok\n")
}
}
```
Effects: none.
Allocation behavior: no allocation.
Error behavior: value helpers return `null` when the byte is outside the accepted range.
Target support: current compiler targets.
+87
View File
@@ -0,0 +1,87 @@
## When To Use std.cli
In Zerolang, use `std.cli` for hosted command-line flag and option helpers that sit one
level above raw `std.args` access.
Runnable today:
| API | Return | Notes |
| --- | --- | --- |
| `std.cli.argEquals(index, expected)` | `Bool` | Checks one argument against an exact string. |
| `std.cli.command()` | `Maybe<String>` | Returns argument 1 as the command name. |
| `std.cli.commandOr(fallback)` | `String` | Returns the command name or a fallback. |
| `std.cli.commandEquals(expected)` | `Bool` | Checks argument 1 against an exact command string. |
| `std.cli.argOr(index, fallback)` | `String` | Returns an argument or a fallback. |
| `std.cli.argU32Or(index, fallback)` | `u32` | Parses an argument as `u32` or returns a fallback. |
| `std.cli.hasFlag(name)` | `Bool` | Reports whether an exact flag is present. |
| `std.cli.optionValue(name)` | `Maybe<String>` | Returns the value immediately after an option name. |
| `std.cli.optionValueOr(name, fallback)` | `String` | Returns the option value or a fallback. |
| `std.cli.optionU32(name)` | `Maybe<u32>` | Parses an option value as `u32`. |
| `std.cli.successExitCode()` | `i32` | Returns the conventional success exit code. |
| `std.cli.usageExitCode()` | `i32` | Returns the conventional command-line usage error code. |
| `std.cli.isHelp(command)` | `Bool` | Recognizes `help`, `--help`, and `-h`. |
| `std.cli.needsHelp()` | `Bool` | Reports whether the current invocation has no command or asks for help. |
| `std.cli.commandIn2(command, first, second)` | `Bool` | Checks a command against two accepted command names. |
| `std.cli.commandIn3(command, first, second, third)` | `Bool` | Checks a command against three accepted command names. |
| `std.cli.formatUsage(buffer, program, syntax)` | `Maybe<Span<u8>>` | Writes `usage: <program> <syntax>` into caller storage. |
| `std.cli.formatCommand(buffer, name, syntax, summary)` | `Maybe<Span<u8>>` | Writes one indented command help row. |
| `std.cli.formatOption(buffer, name, valueName, summary)` | `Maybe<Span<u8>>` | Writes one indented option help row. |
| `std.cli.formatSection(buffer, title)` | `Maybe<Span<u8>>` | Writes a section heading such as `Options:\n`. |
| `std.cli.formatHelpRow(buffer, label, summary)` | `Maybe<Span<u8>>` | Writes one padded, newline-terminated help row using the default label width. |
| `std.cli.formatHelpRowCustom(buffer, label, summary, indent, width)` | `Maybe<Span<u8>>` | Writes one padded help row using caller-supplied indentation and label width. |
| `std.cli.formatHelpRowWithWidth(buffer, label, summary, width)` | `Maybe<Span<u8>>` | Writes one padded help row using a caller-supplied label width. |
| `std.cli.formatHelp(buffer, usage, description)` | `Maybe<Span<u8>>` | Writes a help header with `Usage:` and an optional description. |
| `std.cli.formatError(buffer, message)` | `Maybe<Span<u8>>` | Writes an `error: ...` line. |
| `std.cli.formatUnknownCommand(buffer, command)` | `Maybe<Span<u8>>` | Writes a conventional unknown-command error. |
| `std.cli.formatMissingOperand(buffer, operand)` | `Maybe<Span<u8>>` | Writes a conventional missing-operand error. |
| `std.cli.formatInvalidOption(buffer, option)` | `Maybe<Span<u8>>` | Writes a conventional invalid-option error. |
Current limits:
- Table-driven command schemas.
- Bool, signed integer, and `usize` option shortcuts; compose `optionValue` with `std.parse`.
- Process exit from inside `std.cli`; use the returned exit-code constants with host process handling.
## Example
```zero
pub fn main(world: World) -> Void raises {
if std.cli.needsHelp() {
var command_storage: [96]u8 = [0_u8; 96]
let commands: Maybe<Span<u8>> = std.cli.formatHelpRow(command_storage, "hello [name]", "print a greeting")
if commands.has {
var help_storage: [256]u8 = [0_u8; 256]
let help: Maybe<Span<u8>> = std.cli.formatHelp(help_storage, "zero-test [command]", "Small hosted CLI.")
if help.has {
check world.out.write(help.value)
check world.out.write("\nCommands:\n")
check world.out.write(commands.value)
}
}
return
}
let command: String = std.cli.commandOr("")
if std.mem.eql(command, "hello") {
let name: String = std.cli.argOr(2, "world")
check world.out.write("hello ")
check world.out.write(name)
check world.out.write("\n")
return
}
var error_storage: [64]u8 = [0_u8; 64]
let error: Maybe<Span<u8>> = std.cli.formatUnknownCommand(error_storage, command)
if error.has {
check world.err.write(error.value)
check world.err.write("\n")
}
}
```
## Design Notes
`std.cli` is a thin, hosted layer over `std.args`. It keeps subcommand, fallback,
typed argument, help row, and usage error patterns regular without hiding
process arguments behind a global parser or allocating command tables. For
custom help layouts, compose `formatHelp`, `formatSection`, `formatHelpRow`,
`formatHelpRowWithWidth`, and `formatHelpRowCustom` rather than relying on a
global formatter state.
+82
View File
@@ -0,0 +1,82 @@
## When To Use std.codec
In Zerolang, use `std.codec` for byte encodings: endian integer reads/writes,
varints, base32, base64, hex, and checksums over caller-owned storage.
Runnable today:
| API | Return | Notes |
| --- | --- | --- |
| `std.codec.crc32(bytes)` | `u32` | Computes CRC-32 for a string-backed byte input. |
| `std.codec.crc32Bytes(bytes)` | `u32` | Computes CRC-32 for a span or mutable span without allocation. |
| `std.codec.encodedVarintLen(value)` | `usize` | Returns the byte length of an unsigned varint encoding. |
| `std.codec.encodedVarintLen64(value)` | `usize` | Returns the byte length of an unsigned 64-bit varint encoding. |
| `std.codec.encodedSignedVarintLen(value)` / `std.codec.encodedSignedVarintLen64(value)` | `usize` | Returns the byte length of a ZigZag signed varint encoding. |
| `std.codec.varintEncode(buffer, value)` | `Maybe<Span<u8>>` | Writes an unsigned varint into caller storage. |
| `std.codec.varintDecode(bytes)` | `Maybe<u32>` | Decodes a bounded unsigned varint. |
| `std.codec.varintEncode64(buffer, value)` / `std.codec.varintDecode64(bytes)` | `Maybe<Span<u8>>` / `Maybe<u64>` | Writes and reads bounded unsigned 64-bit varints. |
| `std.codec.signedVarintEncode(buffer, value)` / `std.codec.signedVarintDecode(bytes)` | `Maybe<Span<u8>>` / `Maybe<i32>` | Writes and reads ZigZag signed 32-bit varints. |
| `std.codec.signedVarintEncode64(buffer, value)` / `std.codec.signedVarintDecode64(bytes)` | `Maybe<Span<u8>>` / `Maybe<i64>` | Writes and reads ZigZag signed 64-bit varints. |
| `std.codec.readU16Le(bytes)` | `Maybe<u16>` | Bounds-checked little-endian read from a byte span. |
| `std.codec.readU16Be(bytes)` | `Maybe<u16>` | Bounds-checked big-endian read from a byte span. |
| `std.codec.readU32Le(bytes)` | `Maybe<u32>` | Bounds-checked little-endian read from a byte span. |
| `std.codec.readU32Be(bytes)` | `Maybe<u32>` | Bounds-checked big-endian read from a byte span. |
| `std.codec.readU64Le(bytes)` | `Maybe<u64>` | Bounds-checked little-endian read from a byte span. |
| `std.codec.readU64Be(bytes)` | `Maybe<u64>` | Bounds-checked big-endian read from a byte span. |
| `std.codec.writeU16Le(buffer, value)` | `Maybe<Span<u8>>` | Writes little-endian bytes into caller storage. |
| `std.codec.writeU16Be(buffer, value)` | `Maybe<Span<u8>>` | Writes big-endian bytes into caller storage. |
| `std.codec.writeU32Le(buffer, value)` | `Maybe<Span<u8>>` | Writes little-endian bytes into caller storage. |
| `std.codec.writeU32Be(buffer, value)` | `Maybe<Span<u8>>` | Writes big-endian bytes into caller storage. |
| `std.codec.writeU64Le(buffer, value)` | `Maybe<Span<u8>>` | Writes little-endian `u64` bytes into caller storage. |
| `std.codec.writeU64Be(buffer, value)` | `Maybe<Span<u8>>` | Writes big-endian `u64` bytes into caller storage. |
| `std.codec.base32EncodedLen(len)` / `std.codec.base32RawEncodedLen(len)` | `usize` | Returns padded or unpadded base32 encoded length. |
| `std.codec.base32Encode(buffer, bytes)` / `std.codec.base32RawEncode(buffer, bytes)` | `Maybe<Span<u8>>` | Writes RFC 4648 base32 text into caller storage. |
| `std.codec.base32DecodedLen(bytes)` / `std.codec.base32RawDecodedLen(bytes)` | `Maybe<usize>` | Validates padded or unpadded base32 text and returns decoded length. |
| `std.codec.base32Decode(buffer, bytes)` / `std.codec.base32RawDecode(buffer, bytes)` | `Maybe<Span<u8>>` | Writes decoded base32 bytes into caller storage. |
| `std.codec.base64EncodedLen(len)` | `usize` | Returns the encoded length for a base64 payload. |
| `std.codec.base64Encode(buffer, bytes)` | `Maybe<String>` | Writes base64 text into caller storage. |
| `std.codec.base64DecodedLen(bytes)` | `Maybe<usize>` | Validates padded base64 text and returns decoded length. |
| `std.codec.base64Decode(buffer, bytes)` | `Maybe<Span<u8>>` | Writes decoded base64 bytes into caller storage. |
| `std.codec.base64RawEncodedLen(len)` / `std.codec.base64RawEncode(buffer, bytes)` | `usize` / `Maybe<Span<u8>>` | Uses the standard base64 alphabet without padding. |
| `std.codec.base64RawDecodedLen(bytes)` / `std.codec.base64RawDecode(buffer, bytes)` | `Maybe<usize>` / `Maybe<Span<u8>>` | Validates and decodes unpadded standard base64. |
| `std.codec.base64UrlEncodedLen(len)` / `std.codec.base64UrlEncode(buffer, bytes)` | `usize` / `Maybe<Span<u8>>` | Uses the URL-safe base64 alphabet without padding. |
| `std.codec.base64UrlDecodedLen(bytes)` / `std.codec.base64UrlDecode(buffer, bytes)` | `Maybe<usize>` / `Maybe<Span<u8>>` | Validates and decodes unpadded URL-safe base64. |
| `std.codec.hexEncode(buffer, bytes)` | `Maybe<String>` | Writes lowercase hexadecimal text into caller storage. |
| `std.codec.hexDecodedLen(bytes)` | `Maybe<usize>` | Validates hex text and returns decoded length. |
| `std.codec.hexDecode(buffer, bytes)` | `Maybe<Span<u8>>` | Writes decoded hex bytes into caller storage. |
| `std.codec.utf8Valid(bytes)` | `Bool` | Validates a byte span as UTF-8. |
| `std.codec.urlEncode(buffer, text)` | `Maybe<String>` | Percent-encodes a string into caller storage. |
Current limits:
- Buffer-backed write APIs.
- Streaming encoders and decoders.
## Example
```zero
use std.codec
use std.mem
pub fn main(world: World) -> Void raises {
let len: usize = std.codec.encodedVarintLen(300)
let checksum: u32 = std.codec.crc32("zero")
let bytes: Span<u8> = std.mem.span("zero")
let byte_checksum: u32 = std.codec.crc32Bytes(bytes)
var encoded: [5]u8 = [0_u8; 5]
let varint: Maybe<Span<u8>> = std.codec.varintEncode(encoded, 300_u32)
var decoded: [4]u8 = [0_u8; 4]
let text: Maybe<Span<u8>> = std.codec.base64Decode(decoded, "emVybw==")
var base32_storage: [8]u8 = [0_u8; 8]
let base32: Maybe<Span<u8>> = std.codec.base32Encode(base32_storage, "zero")
if len == 2 && checksum == byte_checksum && varint.has && text.has && std.mem.eql(text.value, "zero") && base32.has && std.mem.eql(base32.value, "PJSXE3Y=") {
check world.out.write("codec primitives ok\n")
}
}
```
## Design Notes
Codec helpers are byte-oriented and allocation-free. Decoders return
`Maybe<T>` on malformed input or insufficient caller storage.
+256
View File
@@ -0,0 +1,256 @@
## When To Use std.collections
In Zerolang, use `std.collections` for fixed-capacity collection operations where the caller
owns the storage and growth must be explicit.
Runnable today:
The collection helpers operate over caller-owned fixed arrays or `MutSpan<T>`
storage plus an explicit live length. They do not allocate, grow, or retain
hidden state. Generic helpers currently support the same non-owned scalar item
types as the generic `std.mem` item helpers: `Bool`, `u8`, `u16`, `usize`,
`i32`, `u32`, `i64`, and `u64`.
Use `FixedSet<T>`, `FixedDeque<T>`, `FixedRingBuffer<T>`, or
`FixedMap<K, V>` when it is clearer to carry storage and live length as one
value. These resource values still borrow caller-owned mutable storage;
inserting, removing, clearing, or truncating values never allocates.
For byte collections, storage can come from a fixed array or from an explicit
allocator. Use `std.mem.allocBytes(alloc, capacity)` to request a
`MutSpan<u8>`, then pass that mutable span to `FixedSet<u8>`,
`FixedDeque<u8>`, or `FixedMap<u8, u8>`. The wrapper does not own the storage;
the allocator-backed span must remain live for as long as the collection value
is used.
| API | Return | Notes |
| --- | --- | --- |
| `std.collections.push(items, len, value)` | `usize` | Writes `value` at `len` when capacity remains and returns the next length. Returns the unchanged length on overflow. |
| `std.collections.append(items, len, values)` | `usize` | Copies all non-overlapping `values` into `items` at `len` when the full append fits. Returns the unchanged length on overflow or invalid length. |
| `std.collections.clear(items, len)` | `usize` | Returns `0` as the next live length. Storage contents are left unchanged. |
| `std.collections.truncate(items, len, newLen)` | `usize` | Returns the smaller of the current live length and `newLen`, after clamping invalid `len` to storage capacity. Storage contents are left unchanged. |
| `std.collections.first(items, len)` | `Maybe<T>` | Returns the first live item, or `null` when the live prefix is empty. |
| `std.collections.last(items, len)` | `Maybe<T>` | Returns the last live item, or `null` when the live prefix is empty. |
| `std.collections.pop(items, len)` | `usize` | Returns the next live length after removing the last item. Returns `0` when empty. Storage contents are left unchanged. |
| `std.collections.dequePushBack(items, len, value)` | `usize` | Writes `value` at the back when capacity remains and returns the next length. |
| `std.collections.dequePushFront(items, len, value)` | `usize` | Inserts `value` at the front by shifting the live prefix right. Returns the unchanged length when full or invalid. |
| `std.collections.dequeFront(items, len)` | `Maybe<T>` | Returns the first live deque item, or `null` when empty. |
| `std.collections.dequeBack(items, len)` | `Maybe<T>` | Returns the last live deque item, or `null` when empty. |
| `std.collections.dequePopBack(items, len)` | `usize` | Returns the next live length after removing the back item. Storage contents are left unchanged. |
| `std.collections.dequePopFront(items, len)` | `usize` | Removes the front item by shifting the live suffix left and returns the next length. Returns the unchanged length when empty or invalid. |
| `std.collections.fixedDeque(items, len)` | `FixedDeque<T>` | Builds a fixed-capacity deque value over caller-owned mutable storage and clamps the initial live length to capacity. |
| `std.collections.fixedDequeBack(deque)` | `Maybe<T>` | Returns the last live deque item, or `null` when empty. |
| `std.collections.fixedDequeClear(deque)` | `usize` | Clears a `FixedDeque<T>` and returns `0` as the next live length. |
| `std.collections.fixedDequeFront(deque)` | `Maybe<T>` | Returns the first live deque item, or `null` when empty. |
| `std.collections.fixedDequeIsFull(deque)` | `Bool` | Reports whether a `FixedDeque<T>` has no remaining capacity. |
| `std.collections.fixedDequeLen(deque)` | `usize` | Returns the current live length of a `FixedDeque<T>`. |
| `std.collections.fixedDequePopBack(deque)` | `Maybe<T>` | Removes and returns the back item when present. |
| `std.collections.fixedDequePopFront(deque)` | `Maybe<T>` | Removes and returns the front item when present, shifting the live suffix left. |
| `std.collections.fixedDequePushBack(deque, value)` | `Bool` | Appends `value` at the back when capacity remains. Returns whether the live deque changed. |
| `std.collections.fixedDequePushFront(deque, value)` | `Bool` | Inserts `value` at the front when capacity remains. Returns whether the live deque changed. |
| `std.collections.fixedDequeRemaining(deque)` | `usize` | Returns remaining storage capacity for a `FixedDeque<T>`. |
| `std.collections.fixedDequeTruncate(deque, newLen)` | `usize` | Updates a `FixedDeque<T>` live prefix to the smaller of the current live length and `newLen`, clamped to storage. |
| `std.collections.fixedDequeView(deque)` | `Span<T>` | Returns a read-only prefix view over live `FixedDeque<T>` items. |
| `std.collections.fixedRingBuffer(items, head, len)` | `FixedRingBuffer<T>` | Builds a fixed-capacity ring buffer over caller-owned mutable storage, normalizing `head` and clamping `len` to capacity. |
| `std.collections.fixedRingBufferBack(ring)` | `Maybe<T>` | Returns the last live logical item, or `null` when empty. |
| `std.collections.fixedRingBufferCapacity(ring)` | `usize` | Returns the storage capacity. |
| `std.collections.fixedRingBufferClear(ring)` | `usize` | Clears a `FixedRingBuffer<T>`, resets its head to `0`, and returns `0` as the next live length. |
| `std.collections.fixedRingBufferFront(ring)` | `Maybe<T>` | Returns the first live logical item, or `null` when empty. |
| `std.collections.fixedRingBufferGet(ring, index)` | `Maybe<T>` | Reads a logical index through wrap-around storage, or returns `null` outside the live length. |
| `std.collections.fixedRingBufferIsFull(ring)` | `Bool` | Reports whether a `FixedRingBuffer<T>` has no remaining capacity. |
| `std.collections.fixedRingBufferLen(ring)` | `usize` | Returns the current live length. |
| `std.collections.fixedRingBufferPopBack(ring)` | `Maybe<T>` | Removes and returns the back logical item when present. |
| `std.collections.fixedRingBufferPopFront(ring)` | `Maybe<T>` | Removes and returns the front logical item when present, advancing the stored head. |
| `std.collections.fixedRingBufferPushBack(ring, value)` | `Bool` | Appends `value` at the logical back when capacity remains. |
| `std.collections.fixedRingBufferPushFront(ring, value)` | `Bool` | Inserts `value` at the logical front when capacity remains. |
| `std.collections.fixedRingBufferRemaining(ring)` | `usize` | Returns remaining storage capacity. |
| `std.collections.fixedRingBufferTruncate(ring, newLen)` | `usize` | Updates the live logical prefix to the smaller of the current live length and `newLen`, clamped to storage. |
| `std.collections.fill(items, len, value)` | `Bool` | Writes `value` across the live prefix. Returns `false` for invalid length. |
| `std.collections.insertAt(items, len, index, value)` | `usize` | Inserts `value` at `index` by shifting the live suffix right. Returns the unchanged length when full or invalid. |
| `std.collections.replaceAt(items, len, index, value)` | `Bool` | Replaces the live item at `index`. Returns `false` for invalid length or index. |
| `std.collections.swapAt(items, len, left, right)` | `Bool` | Swaps two live items. Returns `false` for invalid length or index. |
| `std.collections.insertUnique(items, len, value)` | `usize` | Treats the live prefix as a fixed-capacity set. Appends `value` only when absent and capacity remains. |
| `std.collections.setClear(items, len)` | `usize` | Returns `0` as the next live set length. Storage contents are left unchanged. |
| `std.collections.setContains(items, len, value)` | `Bool` | Reports whether the live prefix contains `value` as a fixed-capacity set. |
| `std.collections.setInsert(items, len, value)` | `usize` | Appends `value` only when absent and capacity remains. |
| `std.collections.setRemaining(items, len)` | `usize` | Returns remaining fixed-capacity set storage. Returns `0` when `len` is at or past capacity. |
| `std.collections.setIsFull(items, len)` | `Bool` | Reports whether the fixed-capacity set has no remaining storage. |
| `std.collections.setRemove(items, len, value)` | `usize` | Removes the first matching set value with swap-remove and returns the next length. |
| `std.collections.setTruncate(items, len, newLen)` | `usize` | Returns the smaller of the current live set length and `newLen`, after clamping invalid `len` to storage capacity. |
| `std.collections.setView(items, len)` | `Span<T>` | Returns a clamped read-only prefix view over the live fixed-capacity set items. |
| `std.collections.fixedSet(items, len)` | `FixedSet<T>` | Builds a fixed-capacity set value over caller-owned mutable storage and clamps the initial live length to capacity. |
| `std.collections.fixedSetClear(set)` | `usize` | Clears a `FixedSet<T>` and returns `0` as the next live length. |
| `std.collections.fixedSetContains(set, value)` | `Bool` | Reports whether a `FixedSet<T>` contains `value` in its live prefix. |
| `std.collections.fixedSetInsert(set, value)` | `Bool` | Inserts `value` when absent and capacity remains. Returns whether the live set changed. |
| `std.collections.fixedSetIsFull(set)` | `Bool` | Reports whether a `FixedSet<T>` has no remaining capacity. |
| `std.collections.fixedSetLen(set)` | `usize` | Returns the current live length of a `FixedSet<T>`. |
| `std.collections.fixedSetRemaining(set)` | `usize` | Returns remaining storage capacity for a `FixedSet<T>`. |
| `std.collections.fixedSetRemove(set, value)` | `Bool` | Removes `value` with swap-remove when present. Returns whether the live set changed. |
| `std.collections.fixedSetTruncate(set, newLen)` | `usize` | Updates a `FixedSet<T>` live prefix to the smaller of the current live length and `newLen`, clamped to storage. |
| `std.collections.fixedSetView(set)` | `Span<T>` | Returns a read-only prefix view over live `FixedSet<T>` items. |
| `std.collections.fixedMap(keys, values, len)` | `FixedMap<K, V>` | Builds a fixed-capacity map value over caller-owned mutable key/value storage and clamps the initial live length to the shorter storage. |
| `std.collections.fixedMapClear(map)` | `usize` | Clears a `FixedMap<K, V>` and returns `0` as the next live length. |
| `std.collections.fixedMapContains(map, key)` | `Bool` | Reports whether a `FixedMap<K, V>` contains `key` in its live key prefix. |
| `std.collections.fixedMapGet(map, key)` | `Maybe<V>` | Returns the value for `key` when present. |
| `std.collections.fixedMapIndex(map, key)` | `usize` | Searches live keys and returns the matching index, or the live length when absent. |
| `std.collections.fixedMapIsFull(map)` | `Bool` | Reports whether a `FixedMap<K, V>` has no remaining capacity in the shorter storage. |
| `std.collections.fixedMapKeys(map)` | `Span<K>` | Returns a read-only prefix view over live `FixedMap<K, V>` keys. |
| `std.collections.fixedMapLen(map)` | `usize` | Returns the current live length of a `FixedMap<K, V>`. |
| `std.collections.fixedMapPut(map, key, value)` | `Bool` | Updates an existing key's value or appends a key/value pair when capacity remains. Returns whether the key exists afterward. |
| `std.collections.fixedMapRemaining(map)` | `usize` | Returns remaining storage capacity in the shorter key/value storage. |
| `std.collections.fixedMapRemove(map, key)` | `Bool` | Removes a key/value pair with swap-remove when present. Returns whether the live map changed. |
| `std.collections.fixedMapTruncate(map, newLen)` | `usize` | Updates the live prefix to the smaller of the current live length and `newLen`, clamped to storage. |
| `std.collections.fixedMapValues(map)` | `Span<V>` | Returns a read-only prefix view over live `FixedMap<K, V>` values. |
| `std.collections.mapClear(keys, values, len)` | `usize` | Returns `0` as the next live map length. Key/value storage contents are left unchanged. |
| `std.collections.mapContains(keys, len, key)` | `Bool` | Reports whether the live key prefix contains `key`. |
| `std.collections.mapIndex(keys, len, key)` | `usize` | Searches the live key prefix and returns the matching index. Returns the live length when absent. |
| `std.collections.mapIsFull(keys, values, len)` | `Bool` | Reports whether the shorter of key/value storage has no remaining capacity. |
| `std.collections.mapKeys(keys, len)` | `Span<K>` | Returns a clamped read-only prefix view over the live map keys. |
| `std.collections.mapGet(keys, values, len, key)` | `Maybe<V>` | Treats parallel key/value storage as a fixed-capacity map and returns the value for `key` when present. |
| `std.collections.mapPut(keys, values, len, key, value)` | `usize` | Updates an existing key's value or appends a key/value pair when capacity remains. Returns the unchanged length on overflow or invalid length. |
| `std.collections.mapRemaining(keys, values, len)` | `usize` | Returns remaining capacity in the shorter of key/value storage. |
| `std.collections.mapRemove(keys, values, len, key)` | `usize` | Removes the first matching key/value pair with swap-remove and returns the next length. Returns the unchanged length when absent or invalid. |
| `std.collections.mapTruncate(keys, values, len, newLen)` | `usize` | Returns the smaller of the live map length and `newLen`, clamped to key and value storage. |
| `std.collections.mapValues(keys, values, len)` | `Span<V>` | Returns a clamped read-only prefix view over live map values, bounded by both key and value storage. |
| `std.collections.view(items, len)` | `Span<T>` | Returns a clamped read-only prefix view over the live collection items. |
| `std.collections.remaining(items, len)` | `usize` | Returns remaining capacity after the live prefix. Returns `0` when `len` is at or past capacity. |
| `std.collections.isFull(items, len)` | `Bool` | Reports whether no capacity remains. Invalid lengths at or past capacity count as full. |
| `std.collections.contains(items, len, needle)` | `Bool` | Searches only the live prefix for `needle`. |
| `std.collections.count(items, len, needle)` | `usize` | Counts matching values in the live prefix. |
| `std.collections.removeAt(items, len, index)` | `usize` | Removes `index` by shifting the live suffix left and returns the next length. Returns the unchanged length for invalid length or index. |
| `std.collections.removeValue(items, len, value)` | `usize` | Removes the first matching live value with swap-remove and returns the next length. Returns the unchanged length when absent. |
| `std.collections.removeSwap(items, len, index)` | `usize` | Replaces `index` with the last live item and returns the next length. Returns the unchanged length for invalid length or index. |
| `std.collections.reverse(items, len)` | `Bool` | Reverses the live prefix in place. Returns `false` for invalid length. |
| `std.collections.rotateLeft(items, len, count)` | `Bool` | Rotates the live prefix left by `count`. Returns `false` for invalid length. |
| `std.collections.rotateRight(items, len, count)` | `Bool` | Rotates the live prefix right by `count`. Returns `false` for invalid length. |
| `std.collections.moveToFront(items, len, index)` | `usize` | Moves the item at `index` to the front by shifting the live prefix. Returns the unchanged length for invalid length or index. |
## Example
```zero
pub fn main(world: World) -> Void raises {
var values: [5]i32 = [0, 0, 0, 0, 0]
let extra: [2]i32 = [4, 1]
var len: usize = 0
len = std.collections.push(values, len, 3)
len = std.collections.push(values, len, 1)
len = std.collections.setInsert(values, len, 3)
len = std.collections.setInsert(values, len, 2)
let has_three: Bool = std.collections.setContains(values, len, 3)
let set_live: Span<i32> = std.collections.setView(values, len)
let set_remaining: usize = std.collections.setRemaining(values, len)
len = std.collections.setRemove(values, len, 2)
let set_truncated: usize = std.collections.setTruncate(values, len, 1)
var fixed_storage: [4]i32 = [1, 2, 0, 0]
var fixed_set: FixedSet<i32> = std.collections.fixedSet(fixed_storage, 2_usize)
let fixed_inserted: Bool = std.collections.fixedSetInsert(&mut fixed_set, 3)
let fixed_removed: Bool = std.collections.fixedSetRemove(&mut fixed_set, 1)
let fixed_live: Span<i32> = std.collections.fixedSetView(&fixed_set)
let fixed_remaining: usize = std.collections.fixedSetRemaining(&fixed_set)
let fixed_len: usize = std.collections.fixedSetLen(&fixed_set)
let fixed_truncated: usize = std.collections.fixedSetTruncate(&mut fixed_set, 1_usize)
var fixed_keys: [3]u8 = [1_u8, 2_u8, 0_u8]
var fixed_scores: [3]u16 = [10_u16, 20_u16, 0_u16]
var fixed_map: FixedMap<u8, u16> = std.collections.fixedMap(fixed_keys, fixed_scores, 2_usize)
let fixed_map_added: Bool = std.collections.fixedMapPut(&mut fixed_map, 3_u8, 30_u16)
let fixed_map_updated: Bool = std.collections.fixedMapPut(&mut fixed_map, 2_u8, 25_u16)
let fixed_map_score: Maybe<u16> = std.collections.fixedMapGet(&fixed_map, 2_u8)
let fixed_map_keys: Span<u8> = std.collections.fixedMapKeys(&fixed_map)
let fixed_map_values: Span<u16> = std.collections.fixedMapValues(&fixed_map)
let fixed_map_index: usize = std.collections.fixedMapIndex(&fixed_map, 2_u8)
let fixed_map_full: Bool = std.collections.fixedMapIsFull(&fixed_map)
let fixed_map_removed: Bool = std.collections.fixedMapRemove(&mut fixed_map, 1_u8)
let inserted_len: usize = std.collections.insertAt(values, len, 1_usize, 2)
let replaced: Bool = std.collections.replaceAt(values, inserted_len, 1_usize, 6)
let swapped: Bool = std.collections.swapAt(values, inserted_len, 0_usize, 1_usize)
len = std.collections.removeAt(values, inserted_len, 1_usize)
let first: Maybe<i32> = std.collections.first(values, len)
let last: Maybe<i32> = std.collections.last(values, len)
let popped_len: usize = std.collections.pop(values, len)
len = std.collections.truncate(values, len, 2)
len = std.collections.append(values, len, extra)
let live: Span<i32> = std.collections.view(values, len)
var deque_values: [4]i32 = [0, 0, 0, 0]
var deque_len: usize = 0
deque_len = std.collections.dequePushBack(deque_values, deque_len, 2)
deque_len = std.collections.dequePushFront(deque_values, deque_len, 1)
deque_len = std.collections.dequePushBack(deque_values, deque_len, 3)
let deque_front: Maybe<i32> = std.collections.dequeFront(deque_values, deque_len)
let deque_back: Maybe<i32> = std.collections.dequeBack(deque_values, deque_len)
let deque_after_pop_back: usize = std.collections.dequePopBack(deque_values, deque_len)
deque_len = std.collections.dequePopFront(deque_values, deque_after_pop_back)
var fixed_deque_values: [4]i32 = [0, 0, 0, 0]
var fixed_deque: FixedDeque<i32> = std.collections.fixedDeque(fixed_deque_values, 0_usize)
let fixed_deque_pushed: Bool = std.collections.fixedDequePushBack(&mut fixed_deque, 2)
let fixed_deque_front_pushed: Bool = std.collections.fixedDequePushFront(&mut fixed_deque, 1)
let fixed_deque_back: Maybe<i32> = std.collections.fixedDequeBack(&fixed_deque)
let fixed_deque_front: Maybe<i32> = std.collections.fixedDequeFront(&fixed_deque)
let fixed_deque_live: Span<i32> = std.collections.fixedDequeView(&fixed_deque)
let fixed_deque_removed: Maybe<i32> = std.collections.fixedDequePopFront(&mut fixed_deque)
var fixed_ring_values: [4]i32 = [0, 0, 0, 0]
var fixed_ring: FixedRingBuffer<i32> = std.collections.fixedRingBuffer(fixed_ring_values, 0_usize, 0_usize)
let fixed_ring_back_pushed: Bool = std.collections.fixedRingBufferPushBack(&mut fixed_ring, 2)
let fixed_ring_front_pushed: Bool = std.collections.fixedRingBufferPushFront(&mut fixed_ring, 1)
let fixed_ring_middle: Maybe<i32> = std.collections.fixedRingBufferGet(&fixed_ring, 1_usize)
let fixed_ring_front: Maybe<i32> = std.collections.fixedRingBufferPopFront(&mut fixed_ring)
let fixed_ring_wrapped: Bool = std.collections.fixedRingBufferPushBack(&mut fixed_ring, 3)
var transform: [4]i32 = [1, 2, 3, 4]
let reversed: Bool = std.collections.reverse(transform, 4_usize)
let filled: Bool = std.collections.fill(transform, 2_usize, 9)
let rotated_left: Bool = std.collections.rotateLeft(transform, 4_usize, 1_usize)
let rotated_right: Bool = std.collections.rotateRight(transform, 4_usize, 2_usize)
var keys: [3]u8 = [1_u8, 2_u8, 3_u8]
var scores: [3]u16 = [10_u16, 20_u16, 30_u16]
var map_len: usize = 2
map_len = std.collections.mapPut(keys, scores, map_len, 3_u8, 30_u16)
map_len = std.collections.mapPut(keys, scores, map_len, 2_u8, 25_u16)
let has_score: Bool = std.collections.mapContains(keys, map_len, 2_u8)
let score: Maybe<u16> = std.collections.mapGet(keys, scores, map_len, 2_u8)
let live_keys: Span<u8> = std.collections.mapKeys(keys, map_len)
let live_scores: Span<u16> = std.collections.mapValues(keys, scores, map_len)
let map_remaining: usize = std.collections.mapRemaining(keys, scores, map_len)
let map_full: Bool = std.collections.mapIsFull(keys, scores, map_len)
map_len = std.collections.mapRemove(keys, scores, map_len, 2_u8)
let removed_index: usize = std.collections.mapIndex(keys, map_len, 2_u8)
if len == 4 && inserted_len == 3 && replaced && swapped && std.collections.clear(values, len) == 0 && std.collections.setClear(values, len) == 0 && first.has && first.value == 6 && last.has && last.value == 1 && popped_len == 1 && std.collections.remaining(values, len) == 1 && !std.collections.isFull(values, len) && has_three && std.mem.len(set_live) == 3 && set_remaining == 2 && set_truncated == 1 && fixed_inserted && fixed_removed && std.mem.len(fixed_live) == 2 && fixed_remaining == 2 && fixed_len == 2 && fixed_truncated == 1 && std.collections.fixedSetClear(&mut fixed_set) == 0 && fixed_map_added && fixed_map_updated && fixed_map_score.has && fixed_map_score.value == 25_u16 && std.mem.len(fixed_map_keys) == 3 && std.mem.len(fixed_map_values) == 3 && fixed_map_index == 1 && fixed_map_full && fixed_map_removed && std.collections.fixedMapClear(&mut fixed_map) == 0 && std.collections.contains(values, len, 4) && std.collections.count(values, len, 1) == 2 && std.mem.len(live) == 4 && deque_front.has && deque_front.value == 1 && deque_back.has && deque_back.value == 3 && deque_after_pop_back == 2 && deque_len == 1 && deque_values[0] == 2 && fixed_deque_pushed && fixed_deque_front_pushed && fixed_deque_back.has && fixed_deque_back.value == 2 && fixed_deque_front.has && fixed_deque_front.value == 1 && std.mem.len(fixed_deque_live) == 2 && fixed_deque_removed.has && fixed_deque_removed.value == 1 && fixed_ring_back_pushed && fixed_ring_front_pushed && fixed_ring_middle.has && fixed_ring_middle.value == 2 && fixed_ring_front.has && fixed_ring_front.value == 1 && fixed_ring_wrapped && reversed && filled && rotated_left && rotated_right && transform[0] == 1 && transform[1] == 9 && transform[2] == 9 && transform[3] == 2 && has_score && score.has && score.value == 25_u16 && std.mem.len(live_keys) == 3 && std.mem.len(live_scores) == 3 && map_remaining == 0 && map_full && std.collections.mapClear(keys, scores, map_len) == 0 && std.collections.mapTruncate(keys, scores, map_len, 2_usize) == 2 && map_len == 2 && removed_index == 2 {
check world.out.write("collections ok\n")
}
}
```
## Allocator-Backed Storage
```zero
pub fn main(world: World) -> Void raises {
var key_storage: [4]u8 = [0_u8; 4]
var value_storage: [4]u8 = [0_u8; 4]
var key_alloc: FixedBufAlloc = std.mem.fixedBufAlloc(key_storage)
var value_alloc: FixedBufAlloc = std.mem.fixedBufAlloc(value_storage)
let keys_maybe: Maybe<MutSpan<u8>> = std.mem.allocBytes(key_alloc, 4_usize)
let values_maybe: Maybe<MutSpan<u8>> = std.mem.allocBytes(value_alloc, 4_usize)
if keys_maybe.has && values_maybe.has {
var map: FixedMap<u8, u8> = std.collections.fixedMap(keys_maybe.value, values_maybe.value, 0_usize)
let stored: Bool = std.collections.fixedMapPut(&mut map, 7_u8, 42_u8)
let value: Maybe<u8> = std.collections.fixedMapGet(&map, 7_u8)
if stored && value.has && value.value == 42_u8 {
check world.out.write("allocator-backed map ok\n")
}
}
}
```
Effects: writes to caller-provided mutable storage.
Allocation behavior: no allocation.
Error behavior: capacity and index failures are value-level. Helpers return the
unchanged length instead of growing or raising.
`append` rejects source spans that the checker can prove overlap the destination
storage. Use separate storage when copying a live prefix back into the same
collection.
Ownership: helpers reject owned item elements; move or transfer owned values
explicitly.
Target support: current compiler targets.
+58
View File
@@ -0,0 +1,58 @@
## When To Use std.crypto
In Zerolang, use `std.crypto` for small hashes, SHA-256 digests, keyed hashes,
constant-time equality, and target entropy helpers with explicit capability
boundaries.
Runnable today:
| API | Return | Notes |
| --- | --- | --- |
| `std.crypto.hash32(bytes)` | `u32` | Computes the current 32-bit hash helper over bytes. |
| `std.crypto.hmac32(key, bytes)` | `u32` | Computes the current keyed 32-bit helper over bytes. |
| `std.crypto.constantTimeEql(a, b)` | `Bool` | Compares byte spans without data-dependent early exit. |
| `std.crypto.secureRandomU32()` | `u32` | Reads target entropy where the target provides it. |
| `std.crypto.fixedHex32(buffer, value)` | `Maybe<Span<u8>>` | Writes an 8-byte lowercase hex value into caller storage. |
| `std.crypto.hashHex32(buffer, bytes)` | `Maybe<Span<u8>>` | Writes the 32-bit hash as fixed-width lowercase hex. |
| `std.crypto.hmacHex32(buffer, key, bytes)` | `Maybe<Span<u8>>` | Writes the keyed 32-bit helper as fixed-width lowercase hex. |
| `std.crypto.stableId32(buffer, bytes)` | `Maybe<Span<u8>>` | Writes a deterministic 8-byte ID from input bytes. |
| `std.crypto.randomId32(buffer)` | `Maybe<Span<u8>>` | Writes an 8-byte random ID from target entropy. |
| `std.crypto.sha256(buffer, bytes)` | `Maybe<Span<u8>>` | Writes the 32-byte SHA-256 digest into caller storage. |
| `std.crypto.sha256Hex(buffer, bytes)` | `Maybe<Span<u8>>` | Writes the SHA-256 digest as 64 lowercase hex bytes. |
| `std.crypto.hmacSha256(buffer, key, bytes)` | `Maybe<Span<u8>>` | Writes the 32-byte HMAC-SHA256 digest into caller storage. |
| `std.crypto.hmacSha256Hex(buffer, key, bytes)` | `Maybe<Span<u8>>` | Writes the HMAC-SHA256 digest as 64 lowercase hex bytes. |
Metadata labels:
- effects: codec, memory, or rand
- allocation behavior: no allocation; text helpers write caller-provided buffers
- target support: hash helpers are target-neutral; secure random requires a rand-capable target
- error behavior: caller-buffer helpers return `null` when storage is too small
- ownership notes: borrows caller-provided byte spans
- example: `examples/std-platform.graph`
## Example
```zero
pub fn main(world: World) -> Void raises {
let hash: u32 = std.crypto.hash32(std.mem.span("message"))
let hmac: u32 = std.crypto.hmac32(std.mem.span("key"), std.mem.span("message"))
var id_buf: [8]u8 = [0_u8; 8]
var sha_buf: [64]u8 = [0_u8; 64]
var hmac_buf: [64]u8 = [0_u8; 64]
let id: Maybe<Span<u8>> = std.crypto.stableId32(id_buf, std.mem.span("message"))
let sha: Maybe<Span<u8>> = std.crypto.sha256Hex(sha_buf, std.mem.span("abc"))
let hmac_sha: Maybe<Span<u8>> = std.crypto.hmacSha256Hex(hmac_buf, std.mem.span("key"), std.mem.span("message"))
if hash > 0 && hmac > 0 && id.has && sha.has && hmac_sha.has && std.crypto.constantTimeEql(std.mem.span("same"), std.mem.span("same")) {
check world.out.write("crypto ok\n")
}
}
```
## Design Notes
`std.crypto` is a small helper surface. The SHA-256 and HMAC-SHA256 helpers
cover common digest, keyed digest, and fixture needs without allocation. The
fixed-width ID helpers are useful for deterministic labels, cache keys,
fixtures, and examples. The module is not a TLS stack, certificate store,
password hashing API, or secret-management API.
+53
View File
@@ -0,0 +1,53 @@
## When To Use std.csv
In Zerolang, use `std.csv` for allocation-free CSV validation, record scanning,
field decoding, and small fixed-arity CSV writers.
Runnable today:
| API | Return | Notes |
| --- | --- | --- |
| `std.csv.valid(bytes)` | `Bool` | Validates bounded CSV input with quoted fields and CRLF or LF records. |
| `std.csv.recordCount(bytes)` | `Maybe<usize>` | Counts valid records, returning null on malformed input. |
| `std.csv.record(bytes, index)` | `Maybe<Span<u8>>` | Borrows one record slice by ordinal, excluding the line terminator. |
| `std.csv.fieldCount(record)` | `Maybe<usize>` | Counts fields in one valid record. |
| `std.csv.field(buffer, record, index)` | `Maybe<Span<u8>>` | Decodes one field into caller storage. |
| `std.csv.encodedFieldLen(field)` | `usize` | Computes the bytes needed to write one CSV field. |
| `std.csv.writeField(buffer, field)` | `Maybe<Span<u8>>` | Writes one CSV field with quotes when required. |
| `std.csv.writeRecord2(buffer, left, right)` | `Maybe<Span<u8>>` | Writes a two-field record ending in `\n`. |
| `std.csv.writeRecord3(buffer, first, second, third)` | `Maybe<Span<u8>>` | Writes a three-field record ending in `\n`. |
Metadata labels:
- effects: parse
- allocation behavior: no allocation; decoded fields and writer output use caller storage
- target support: target-neutral
- error behavior: `Maybe` helpers return null on malformed input or insufficient storage
- ownership notes: records borrow from the input; fields and writer output borrow from caller buffers
- examples: `conformance/native/pass/std-csv.graph`
## Example
```zero
pub fn main(world: World) -> Void raises {
let input: Span<u8> = "name,quote\nAda,\"a,b\"\n"
let record: Maybe<Span<u8>> = std.csv.record(input, 1_usize)
var field_buf: [16]u8 = [0_u8; 16]
var quote: Maybe<Span<u8>> = null
if record.has {
quote = std.csv.field(field_buf, record.value, 1_usize)
}
if std.csv.valid(input) && quote.has && std.mem.eql(quote.value, "a,b") {
check world.out.write("csv ok\n")
}
}
```
## Design Notes
CSV helpers follow the common RFC 4180 field rules: comma-separated fields,
quoted fields, doubled quotes inside quoted fields, and CRLF or LF record
separators. Quoted fields may contain newlines.
The writer surface is fixed-arity. Use `writeField` when a custom writer loop
is needed.
+47
View File
@@ -0,0 +1,47 @@
## When To Use std.diag
In Zerolang, use `std.diag` to turn byte offsets into source locations and
small diagnostic snippets without allocating.
Runnable today:
| API | Return | Notes |
| --- | --- | --- |
| `std.diag.line(bytes, offset)` | `usize` | Returns the 1-based line for a byte offset, clamping offsets past the end. |
| `std.diag.column(bytes, offset)` | `usize` | Returns the 1-based byte column for a byte offset. |
| `std.diag.lineStart(bytes, offset)` | `usize` | Returns the byte index where the containing line starts. |
| `std.diag.lineEnd(bytes, offset)` | `usize` | Returns the byte index where the containing line ends, trimming a trailing CR before LF. |
| `std.diag.lineText(bytes, offset)` | `Span<u8>` | Borrows the containing line without its newline. |
| `std.diag.rangeLen(bytes, start, end)` | `usize` | Returns the clamped byte length for a half-open range. |
| `std.diag.rangeText(bytes, start, end)` | `Span<u8>` | Borrows the clamped half-open byte range. |
| `std.diag.formatLocation(buffer, path, line, column)` | `Maybe<Span<u8>>` | Writes `path:line:column` into caller storage. |
| `std.diag.formatOffsetLocation(buffer, path, bytes, offset)` | `Maybe<Span<u8>>` | Computes line and column from an offset, then writes `path:line:column`. |
Metadata labels:
- effects: parse for source offset scanning; memory for caller-buffer formatting
- allocation behavior: no allocation, except formatting writes into caller storage
- target support: target-neutral
- error behavior: formatting returns `null` when the buffer is too small
- ownership notes: text helpers return borrowed views into the input bytes
- example: `conformance/native/pass/std-diag.graph`
## Example
```zero
pub fn main(world: World) -> Void raises {
let source: Span<u8> = "one\ntwo\nthree"
var storage: [32]u8 = [0_u8; 32]
let location: Maybe<Span<u8>> = std.diag.formatOffsetLocation(storage, "input.0", source, 5)
if location.has && std.mem.eql(location.value, "input.0:2:2") && std.mem.eql(std.diag.lineText(source, 5), "two") {
check world.out.write("diag ok\n")
}
}
```
## Design Notes
Offsets are byte offsets, not Unicode scalar indexes or terminal display
columns. That keeps parser diagnostics deterministic and cheap across targets.
Line and column numbers are 1-based for user-facing output. Range helpers use
half-open byte ranges and clamp both ends to the input length.
+52
View File
@@ -0,0 +1,52 @@
## When To Use std.env
In Zerolang, use `std.env` for hosted environment variable lookup and simple typed
configuration values.
Runnable today:
| API | Return | Notes |
| --- | --- | --- |
| `std.env.get(name)` | `Maybe<String>` | Returns a hosted process environment value when present. |
| `std.env.has(name)` | `Bool` | Reports whether a hosted environment variable exists. |
| `std.env.getOr(name, fallback)` | `String` | Returns the environment value or a caller-provided fallback. |
| `std.env.equals(name, expected)` | `Bool` | Compares an environment value with expected text without exposing a missing value as success. |
| `std.env.parseBool(name)` | `Maybe<Bool>` | Parses an environment value as `Bool`. |
| `std.env.parseBoolOr(name, fallback)` | `Bool` | Parses a boolean environment value or returns a caller-provided fallback. |
| `std.env.parseI32(name)` | `Maybe<i32>` | Parses an environment value as `i32`. |
| `std.env.parseI32Or(name, fallback)` | `i32` | Parses an `i32` environment value or returns a caller-provided fallback. |
| `std.env.parseU32(name)` | `Maybe<u32>` | Parses an environment value as `u32`. |
| `std.env.parseU32Or(name, fallback)` | `u32` | Parses a `u32` environment value or returns a caller-provided fallback. |
| `std.env.parseUsize(name)` | `Maybe<usize>` | Parses an environment value as `usize`. |
| `std.env.parseUsizeOr(name, fallback)` | `usize` | Parses a `usize` environment value or returns a caller-provided fallback. |
Current limits:
- Dotenv/source composition.
- Secret redaction metadata.
- Rich diagnostics for missing keys, invalid values, and source precedence.
## Example
```zero
pub fn main(world: World) -> Void raises {
let mode: String = std.env.getOr("ZERO_MODE", "default")
let verbose: Bool = std.env.parseBoolOr("ZERO_VERBOSE", false)
let delta: i32 = std.env.parseI32Or("ZERO_DELTA", 0)
let limit: u32 = std.env.parseU32Or("ZERO_LIMIT", 10)
let workers: usize = std.env.parseUsizeOr("ZERO_WORKERS", 1)
if std.env.equals("ZERO_MODE", "debug") && verbose && delta >= 0 && limit > 0_u32 && workers > 0 {
check world.out.write(mode)
check world.out.write("\n")
} else {
check world.out.write("default\n")
}
}
```
## Design Notes
Environment access is a hosted capability. Non-host targets reject `std.env`
unless they explicitly provide an environment capability.
Diagnostics name the selected target context.
+62
View File
@@ -0,0 +1,62 @@
## When To Use std.fmt
In Zerolang, use `std.fmt` when a program needs to format booleans or integers into a
caller-owned buffer instead of allocating text.
Runnable today:
Formatting helpers write into caller-provided `MutSpan<u8>` storage and return a
borrowed prefix on success.
| API | Return | Notes |
| --- | --- | --- |
| `std.fmt.bool(buffer, value)` | `Maybe<Span<u8>>` | Writes `true` or `false`. |
| `std.fmt.u32(buffer, value)` | `Maybe<Span<u8>>` | Writes decimal unsigned 32-bit text. |
| `std.fmt.u32Base(buffer, value, base)` | `Maybe<Span<u8>>` | Writes unsigned 32-bit text in base 2 through 36. |
| `std.fmt.u64(buffer, value)` | `Maybe<Span<u8>>` | Writes decimal unsigned 64-bit text. |
| `std.fmt.u64Base(buffer, value, base)` | `Maybe<Span<u8>>` | Writes unsigned 64-bit text in base 2 through 36. |
| `std.fmt.usize(buffer, value)` | `Maybe<Span<u8>>` | Writes decimal `usize` text. |
| `std.fmt.usizeBase(buffer, value, base)` | `Maybe<Span<u8>>` | Writes `usize` text in base 2 through 36. |
| `std.fmt.i32(buffer, value)` | `Maybe<Span<u8>>` | Writes decimal signed 32-bit text, including the minimum value. |
| `std.fmt.i32Base(buffer, value, base)` | `Maybe<Span<u8>>` | Writes signed 32-bit text in base 2 through 36. |
| `std.fmt.i32Sign(buffer, value)` | `Maybe<Span<u8>>` | Writes decimal signed 32-bit text with an explicit `+` for non-negative values. |
| `std.fmt.i64(buffer, value)` | `Maybe<Span<u8>>` | Writes decimal signed 64-bit text, including the minimum value. |
| `std.fmt.i64Base(buffer, value, base)` | `Maybe<Span<u8>>` | Writes signed 64-bit text in base 2 through 36. |
| `std.fmt.i64Sign(buffer, value)` | `Maybe<Span<u8>>` | Writes decimal signed 64-bit text with an explicit `+` for non-negative values. |
| `std.fmt.hexLowerU32(buffer, value)` | `Maybe<Span<u8>>` | Writes lowercase hexadecimal without a prefix. |
| `std.fmt.padLeft(buffer, text, width, pad)` | `Maybe<Span<u8>>` | Left-pads `text` with `pad` until `width`, or copies `text` when already wide enough. |
| `std.fmt.padRight(buffer, text, width, pad)` | `Maybe<Span<u8>>` | Right-pads `text` with `pad` until `width`, or copies `text` when already wide enough. |
| `std.fmt.writeSpan(writer, text)` | `Bool` | Writes bytes into a `FixedWriter`. |
| `std.fmt.writeBool(writer, value)` | `Bool` | Formats a boolean into a `FixedWriter`. |
| `std.fmt.writeU32(writer, value)` / `std.fmt.writeU64(writer, value)` / `std.fmt.writeUsize(writer, value)` | `Bool` | Formats unsigned decimal text into a `FixedWriter`. |
| `std.fmt.writeI32(writer, value)` / `std.fmt.writeI64(writer, value)` | `Bool` | Formats signed decimal text into a `FixedWriter`. |
| `std.fmt.writeI32Sign(writer, value)` / `std.fmt.writeI64Sign(writer, value)` | `Bool` | Formats signed decimal text with an explicit `+` for non-negative values into a `FixedWriter`. |
## Example
```zero
pub fn main(world: World) -> Void raises {
var number_buf: [12]u8 = [0_u8; 12]
var big_buf: [20]u8 = [0_u8; 20]
var hex_buf: [8]u8 = [0_u8; 8]
var padded_buf: [6]u8 = [0_u8; 6]
let number: Maybe<Span<u8>> = std.fmt.i32(number_buf, -42)
let big: Maybe<Span<u8>> = std.fmt.u64(big_buf, 18446744073709551615_u64)
let hex: Maybe<Span<u8>> = std.fmt.hexLowerU32(hex_buf, 48879_u32)
let padded: Maybe<Span<u8>> = std.fmt.padLeft(padded_buf, "42", 5, 48_u8)
var writer_storage: [24]u8 = [0_u8; 24]
var writer: FixedWriter = std.io.fixedWriter(writer_storage, 0)
let wrote: Bool = std.fmt.writeSpan(&mut writer, "n=") && std.fmt.writeI32(&mut writer, -42)
if number.has && big.has && hex.has && padded.has && wrote && std.mem.eql(number.value, "-42") && std.mem.eql(big.value, "18446744073709551615") && std.mem.eql(hex.value, "beef") && std.mem.eql(padded.value, "00042") && std.mem.eql(std.io.fixedWriterView(&writer), "n=-42") {
check world.out.write("fmt ok\n")
}
}
```
Effects: writes to caller-provided mutable storage or an explicit fixed writer.
Allocation behavior: no allocation.
Error behavior: returns `null` when the buffer is too small.
Target support: current compiler targets.
+82
View File
@@ -0,0 +1,82 @@
## When To Use std.fs
In Zerolang, use `std.fs` for hosted file reads, writes, existence checks, copies, renames,
and explicit file-resource cleanup.
Runnable today:
| API | Return | Notes |
| --- | --- | --- |
| `std.fs.read(path, buf)` | `usize` | Reads bytes from a hosted path into a caller-provided `MutSpan<u8>` buffer. |
| `std.fs.write(path, bytes)` | `usize` | Writes bytes to a hosted path and returns the byte count. |
| `std.fs.host()` | `Fs` | Creates the hosted filesystem capability. |
| `std.fs.open(fs, path)` | `Maybe<owned<File>>` | Opens a file and returns `null` when unavailable. |
| `std.fs.openOrRaise(fs, path)` | `owned<File>` | Opens a file or raises `raises [NotFound, TooLarge, Io]`. |
| `std.fs.create(fs, path)` | `Maybe<owned<File>>` | Creates a file and returns `null` when unavailable. |
| `std.fs.createOrRaise(fs, path)` | `owned<File>` | Creates a file or raises `raises [NotFound, TooLarge, Io]`. |
| `std.fs.readOrRaise(&mut file, buf)` | `usize` | Reads into caller storage or raises. |
| `std.fs.writeAll(&mut file, bytes)` | `Bool` | Writes bytes to an owned file handle. |
| `std.fs.writeAllOrRaise(&mut file, bytes)` | `Void` | Writes all bytes or raises. |
| `std.fs.fileLen(&mut file)` | `Maybe<usize>` | Reports file length when available. |
| `std.fs.fileLenOrRaise(&mut file)` | `usize` | Reports the file length or raises. |
| `std.fs.fileSize(fs, path)` | `Maybe<usize>` | Opens a hosted path through `fs` and reports file length when available. |
| `std.fs.readAll(alloc, fs, path, limit)` | `Maybe<owned<ByteBuf>>` | Reads through an explicit allocator and size limit. |
| `std.fs.readAllOrRaise(alloc, fs, path, limit)` | `owned<ByteBuf>` | Reads through an explicit allocator and size limit. |
| `std.fs.readBytes(path, buf)` | `Maybe<usize>` | Fills caller storage and returns the total file size; a value above `len(buf)` means the buffer holds only the first `len(buf)` bytes. |
| `std.fs.readBytesAt(path, offset, buf)` | `Maybe<usize>` | Fills caller storage starting at a byte offset and returns the total file size, so bounded buffers can process larger files in chunks. |
| `std.fs.writeBytes(path, bytes)` | `Maybe<usize>` | Writes byte spans to a hosted path. |
| `std.fs.appendBytes(path, bytes)` | `Maybe<usize>` | Appends byte spans to a hosted path, creating the file when missing. |
| `std.fs.exists(path)` | `Bool` | Checks whether a hosted path exists. |
| `std.fs.isFile(path)` | `Bool` | Checks whether a hosted path opens and reports a file length. |
| `std.fs.isDir(path)` | `Bool` | Checks whether a hosted path is a directory. |
| `std.fs.makeDir(path)` | `Bool` | Creates a hosted directory. |
| `std.fs.ensureDir(path)` | `Bool` | Succeeds when a hosted directory already exists or can be created. |
| `std.fs.removeDir(path)` | `Bool` | Removes a hosted directory. |
| `std.fs.remove(path)` | `Bool` | Removes a hosted file path. |
| `std.fs.rename(old, new)` | `Bool` | Renames a hosted file path. |
| `std.fs.dirEntryCount(path)` | `Maybe<usize>` | Counts entries in a hosted directory. |
| `std.fs.dirEntryName(buffer, path, index)` | `Maybe<Span<u8>>` | Writes one hosted directory entry name into caller storage. |
| `std.fs.tempName(buffer, prefix)` | `Maybe<String>` | Writes a temporary path into caller storage. |
| `std.fs.atomicWrite(path, temp, bytes)` | `Bool` | Writes through a caller-provided temporary path and renames. |
| `std.fs.close(&mut file)` | `Void` | Closes an owned file handle explicitly; remaining owned files are cleaned up deterministically. |
| `std.fs.readFile(fs, path, buffer)` | `Maybe<usize>` | Opens, fills caller storage, and closes through explicit `Fs`; returns the total file size, so a value above `len(buffer)` signals truncation. |
| `std.fs.writeFile(fs, path, bytes)` | `Bool` | Creates, writes all bytes, and closes through explicit `Fs`. |
| `std.fs.appendFile(fs, path, bytes)` | `Bool` | Opens, appends all bytes, and closes through explicit `Fs`. |
| `std.fs.readFileBytes(fs, path, buffer)` | `Maybe<Span<u8>>` | Opens, reads a full file, closes it, and returns the live prefix of caller storage; `null` when the file exceeds the buffer. |
| `std.fs.readFileEquals(fs, path, buffer, expected)` | `Bool` | Reads a full file through caller storage and compares the bytes with an expected span. |
| `std.fs.copyFile(from, to, buffer)` | `Bool` | Copies a hosted file through caller-provided scratch storage. |
Current limits:
- Richer permissions and platform-specific file modes.
- Recursive directory walking helpers.
- Async or nonblocking I/O.
## Example
```zero
pub fn main(world: World) -> Void raises [NotFound, TooLarge, Io] {
let fs: Fs = std.fs.host()
var buf: [32]u8 = [0_u8; 32]
if std.fs.ensureDir(".zero/out") && std.fs.writeFile(fs, ".zero/out/example.txt", "hello\n") {
let bytes: Maybe<Span<u8>> = std.fs.readFileBytes(fs, ".zero/out/example.txt", buf)
let size: Maybe<usize> = std.fs.fileSize(fs, ".zero/out/example.txt")
if bytes.has && size.has && size.value == 6 && std.fs.isFile(".zero/out/example.txt") && std.fs.readFileEquals(fs, ".zero/out/example.txt", buf, "hello\n") && std.fs.rename(".zero/out/example.txt", ".zero/out/example-renamed.txt") {
if std.fs.remove(".zero/out/example-renamed.txt") {
check world.out.write("fs ok\n")
}
}
}
}
```
## Design Notes
The path helpers are a small current API, not a hidden global filesystem.
Stable file APIs make effects, ownership, and cleanup visible through
capabilities.
Hosted filesystem APIs are denied on non-host targets with `TAR002`.
Target-neutral packages should keep filesystem code outside their cross-target
entry point.
+406
View File
@@ -0,0 +1,406 @@
## When To Use std.http
In Zerolang, use `std.http` for HTTP request parsing, response envelope writing, hosted
fetch, local listen support, and web API helpers.
Runnable today:
| API | Return | Notes |
| --- | --- | --- |
| `std.http.parseMethod(text)` | `HttpMethod` | Parses a small HTTP method token. |
| `std.http.client(net)` | `HttpClient` | Creates hosted client metadata from a network capability. |
| `std.http.server(net, address)` | `HttpServer` | Creates hosted server metadata from a network capability and address. |
| `std.http.listen(world)` | `Void raises [Io]` | Starts a loopback HTTP listener from `zero run`, auto-selecting a development port from 3000 upward. |
| `std.http.listen(world, port)` | `Void raises [Io]` | Starts a loopback HTTP listener on exactly `port`, failing if the port is occupied. |
| `std.http.fetch(client, request, response, timeout)` | `HttpResult` | Performs a hosted HTTP(S) request with a `Duration` timeout and writes response metadata, headers, and body into caller-owned storage. |
| `std.http.resultOk(result)` | `Bool` | True when transport succeeded and the status is 2xx. |
| `std.http.resultStatus(result)` | `u16` | Reads the HTTP status, or `0` when no status was available. |
| `std.http.resultBodyLen(result)` | `usize` | Reads the number of response body bytes written into the response buffer. |
| `std.http.resultError(result)` | `HttpError` | Reads the transport/provider error. |
| `std.http.errorNone()` | `HttpError` | Transport succeeded. |
| `std.http.errorInvalidUrl()` | `HttpError` | The request URL was invalid. |
| `std.http.errorUnsupportedProtocol()` | `HttpError` | The URL protocol is not supported. |
| `std.http.errorDns()` | `HttpError` | DNS lookup failed. |
| `std.http.errorConnect()` | `HttpError` | Connecting to the remote host failed. |
| `std.http.errorTls()` | `HttpError` | TLS verification or connection setup failed. |
| `std.http.errorTimeout()` | `HttpError` | The request timed out. |
| `std.http.errorTooLarge()` | `HttpError` | The response did not fit in the caller-owned buffer. |
| `std.http.errorProviderUnavailable()` | `HttpError` | The hosted HTTP provider is unavailable. |
| `std.http.errorIo()` | `HttpError` | The provider reported an I/O failure. |
| `std.http.errorInvalidRequest()` | `HttpError` | The request envelope was invalid. |
| `std.http.errorName(error)` | `String` | Returns a stable label for a transport error. |
| `std.http.responseLen(response)` | `usize` | Reads the response byte count written after the internal metadata prefix. |
| `std.http.responseHeadersLen(response)` | `usize` | Reads the raw response header byte count. |
| `std.http.responseBodyOffset(response)` | `usize` | Reads the body start offset within the response buffer. |
| `std.http.headerValue(response, name)` | `HttpHeaderValue` | Locates a response header value by case-insensitive header name. |
| `std.http.headerFound(value)` | `Bool` | True when `headerValue` found a matching header. |
| `std.http.headerOffset(value)` | `usize` | Reads the header value byte offset within the response buffer. |
| `std.http.headerLen(value)` | `usize` | Reads the header value byte length. |
| `std.http.tlsBoundary()` | `String` | Names the platform or C-library TLS boundary. |
| `std.http.statusReason(status)` | `String` | Returns a common reason phrase for status-line writing. |
| `std.http.statusIsInformational(status)` | `Bool` | True for 1xx statuses. |
| `std.http.statusIsSuccess(status)` | `Bool` | True for 2xx statuses. |
| `std.http.statusIsRedirect(status)` | `Bool` | True for 3xx statuses. |
| `std.http.statusIsClientError(status)` | `Bool` | True for 4xx statuses. |
| `std.http.statusIsServerError(status)` | `Bool` | True for 5xx statuses. |
| `std.http.writeRequest(buffer, startLine, body)` | `Maybe<Span<u8>>` | Writes `METHOD URL`, optional content length, blank line, and body into caller storage. |
| `std.http.writeRequestWithHeader(buffer, startLine, headerLine, body)` | `Maybe<Span<u8>>` | Writes a request envelope with one validated `name: value` header line. |
| `std.http.writeRequestWithHeaders(buffer, startLine, headers, body)` | `Maybe<Span<u8>>` | Writes a request envelope with a validated newline-separated header block. |
| `std.http.writeMethodRequest(buffer, method, target, body)` | `Maybe<Span<u8>>` | Writes a request envelope from separate method and target spans. |
| `std.http.writeGetRequest(buffer, target)` | `Maybe<Span<u8>>` | Writes an empty GET request envelope. |
| `std.http.writeHeadRequest(buffer, target)` | `Maybe<Span<u8>>` | Writes an empty HEAD request envelope. |
| `std.http.writeDeleteRequest(buffer, target)` | `Maybe<Span<u8>>` | Writes an empty DELETE request envelope. |
| `std.http.writeJsonRequest(buffer, startLine, body)` | `Maybe<Span<u8>>` | Writes a JSON request envelope with `content-type` and `content-length`. |
| `std.http.writeJsonMethodRequest(buffer, method, target, body)` | `Maybe<Span<u8>>` | Writes a JSON request envelope from separate method and target spans. |
| `std.http.writeJsonRequestWithHeader(buffer, startLine, headerLine, body)` | `Maybe<Span<u8>>` | Writes a JSON request envelope with one validated extra header line. |
| `std.http.writeJsonRequestWithHeaders(buffer, startLine, headers, body)` | `Maybe<Span<u8>>` | Writes a JSON request envelope with a validated extra header block. |
| `std.http.writePostJsonRequest(buffer, target, body)` | `Maybe<Span<u8>>` | Writes a POST JSON request envelope. |
| `std.http.writePutJsonRequest(buffer, target, body)` | `Maybe<Span<u8>>` | Writes a PUT JSON request envelope. |
| `std.http.writePatchJsonRequest(buffer, target, body)` | `Maybe<Span<u8>>` | Writes a PATCH JSON request envelope. |
| `std.http.writeResponse(buffer, status, body)` | `Maybe<Span<u8>>` | Writes an HTTP/1.1 response envelope into caller storage. |
| `std.http.writeResponseWithHeader(buffer, status, headerLine, body)` | `Maybe<Span<u8>>` | Writes a response envelope with one validated `name: value` header line. |
| `std.http.writeResponseWithHeaders(buffer, status, headers, body)` | `Maybe<Span<u8>>` | Writes a response envelope with a validated newline-separated header block. |
| `std.http.writeJsonResponse(buffer, status, body)` | `Maybe<Span<u8>>` | Writes a JSON HTTP/1.1 response envelope into caller storage. |
| `std.http.writeJsonResponseWithHeader(buffer, status, headerLine, body)` | `Maybe<Span<u8>>` | Writes a JSON response envelope with one validated `name: value` header line. |
| `std.http.writeJsonResponseWithHeaders(buffer, status, headers, body)` | `Maybe<Span<u8>>` | Writes a JSON response envelope with a validated extra header block. |
| `std.http.writeJsonResponseWithCookie(buffer, status, cookie, body)` | `Maybe<Span<u8>>` | Writes a JSON response envelope with one `Set-Cookie` header value. |
| `std.http.writeJsonError(buffer, status, code)` | `Maybe<Span<u8>>` | Writes `{"error":"code"}` after validating the code is JSON-safe lower-case ASCII, digits, `_`, or `-`. |
| `std.http.writeCorsPreflight(buffer, allowOrigin, allowMethods, allowHeaders)` | `Maybe<Span<u8>>` | Writes a 204 CORS preflight response with caller-provided allow headers. |
| `std.http.writeCorsJsonResponse(buffer, statusLine, body, allowOrigin)` | `Maybe<Span<u8>>` | Writes a JSON response with `access-control-allow-origin`; `statusLine` is a fragment such as `"200 OK"`. |
| `std.http.writeTextResponse(buffer, status, body)` | `Maybe<Span<u8>>` | Writes a `text/plain; charset=utf-8` response envelope into caller storage. |
| `std.http.writeTextOk(buffer, body)` | `Maybe<Span<u8>>` | Writes a 200 plain-text response envelope into caller storage. |
| `std.http.writeHtmlResponse(buffer, status, body)` | `Maybe<Span<u8>>` | Writes a `text/html; charset=utf-8` response envelope into caller storage. |
| `std.http.writeHtmlOk(buffer, body)` | `Maybe<Span<u8>>` | Writes a 200 HTML response envelope into caller storage. |
| `std.http.writeRedirect(buffer, status, location)` | `Maybe<Span<u8>>` | Writes a redirect response with a safe `Location` header; rejects non-3xx statuses and empty or control-character locations. |
| `std.http.writeFound(buffer, location)` | `Maybe<Span<u8>>` | Writes a 302 redirect response. |
| `std.http.writeSeeOther(buffer, location)` | `Maybe<Span<u8>>` | Writes a 303 redirect response. |
| `std.http.writeMovedPermanently(buffer, location)` | `Maybe<Span<u8>>` | Writes a 301 redirect response. |
| `std.http.writePermanentRedirect(buffer, location)` | `Maybe<Span<u8>>` | Writes a 308 redirect response. |
| `std.http.contentTypeForPath(path)` | `String` | Returns a small static-file media type from a path suffix. |
| `std.http.writeStaticResponse(buffer, status, path, body)` | `Maybe<Span<u8>>` | Writes a response with `content-type` inferred from the path suffix. |
| `std.http.writeJsonOk(buffer, body)` | `Maybe<Span<u8>>` | Writes a 200 JSON response envelope into caller storage. |
| `std.http.writeJsonCreated(buffer, body)` | `Maybe<Span<u8>>` | Writes a 201 JSON response envelope into caller storage. |
| `std.http.writeJsonBadRequest(buffer, body)` | `Maybe<Span<u8>>` | Writes a 400 JSON response envelope into caller storage. |
| `std.http.writeJsonUnauthorized(buffer, body)` | `Maybe<Span<u8>>` | Writes a 401 JSON response envelope into caller storage. |
| `std.http.writeJsonForbidden(buffer, body)` | `Maybe<Span<u8>>` | Writes a 403 JSON response envelope into caller storage. |
| `std.http.writeJsonNotFound(buffer, body)` | `Maybe<Span<u8>>` | Writes a 404 JSON response envelope into caller storage. |
| `std.http.writeJsonMethodNotAllowed(buffer, body)` | `Maybe<Span<u8>>` | Writes a 405 JSON response envelope into caller storage. |
| `std.http.writeJsonConflict(buffer, body)` | `Maybe<Span<u8>>` | Writes a 409 JSON response envelope into caller storage. |
| `std.http.writeJsonUnprocessable(buffer, body)` | `Maybe<Span<u8>>` | Writes a 422 JSON response envelope into caller storage. |
| `std.http.writeJsonTooManyRequests(buffer, body)` | `Maybe<Span<u8>>` | Writes a 429 JSON response envelope into caller storage. |
| `std.http.writeJsonInternalServerError(buffer, body)` | `Maybe<Span<u8>>` | Writes a 500 JSON response envelope into caller storage. |
| `std.http.writeNoContent(buffer)` | `Maybe<Span<u8>>` | Writes a 204 response envelope with an empty body. |
| `std.http.requestMethodName(request)` | `Maybe<Span<u8>>` | Borrows the method token from a request envelope. |
| `std.http.requestTarget(request)` | `Maybe<Span<u8>>` | Borrows the raw target from a request envelope. |
| `std.http.requestPath(request)` | `Maybe<Span<u8>>` | Borrows the path from an absolute or origin-form request target. |
| `std.http.pathSegmentCount(path)` | `usize` | Counts non-empty path segments in a path span. |
| `std.http.pathSegment(path, index)` | `Maybe<Span<u8>>` | Borrows a zero-based non-empty path segment from a path span. |
| `std.http.pathMatchesPattern(path, pattern)` | `Bool` | Matches path segments against literals, `:params`, and a trailing `*` wildcard. |
| `std.http.pathParam(path, pattern, name)` | `Maybe<Span<u8>>` | Borrows a named path parameter from a matched path pattern. |
| `std.http.requestPathSegmentCount(request)` | `usize` | Counts non-empty path segments from a request envelope path. |
| `std.http.requestPathSegment(request, index)` | `Maybe<Span<u8>>` | Borrows a zero-based non-empty request path segment. |
| `std.http.requestQuery(request)` | `Maybe<Span<u8>>` | Borrows the query string from a request target. |
| `std.http.requestQueryValue(request, name)` | `Maybe<Span<u8>>` | Borrows a query value by name from a request envelope. |
| `std.http.requestHeader(request, name)` | `Maybe<Span<u8>>` | Borrows a case-insensitive request header value. |
| `std.http.requestBearerToken(request)` | `Maybe<Span<u8>>` | Borrows the bearer token from the `Authorization` request header. |
| `std.http.requestCookie(request, name)` | `Maybe<Span<u8>>` | Borrows a named cookie value from the `Cookie` request header. |
| `std.http.requestContentLength(request)` | `Maybe<usize>` | Parses the `Content-Length` request header. |
| `std.http.requestContentType(request)` | `Maybe<Span<u8>>` | Borrows the media type from `Content-Type`, excluding parameters. |
| `std.http.requestAccepts(request, media)` | `Bool` | Checks whether `Accept` allows an exact media type, type wildcard, or `*/*`; absent `Accept` allows any media and `q=0` rejects a range. |
| `std.http.requestAcceptsJson(request)` | `Bool` | Checks whether `Accept` allows `application/json`. |
| `std.http.requestBody(request)` | `Maybe<Span<u8>>` | Borrows the request body after the blank line. |
| `std.http.requestBodyWithin(request, max)` | `Maybe<Span<u8>>` | Borrows the request body only when it is at most `max` bytes. |
| `std.http.requestHasJsonContentType(request)` | `Bool` | True when the request content type is `application/json`, ignoring ASCII case and allowing parameters. |
| `std.http.requestJsonBodyWithin(request, max)` | `Maybe<Span<u8>>` | Borrows the body only when content type is JSON, body length is within `max`, and bytes validate as JSON. |
| `std.http.requestJsonField(request, name, max)` | `Maybe<Span<u8>>` | Borrows a top-level JSON field from a bounded JSON request body. |
| `std.http.requestMatches(request, method, path)` | `Bool` | True when a request envelope has the exact method and normalized path. |
| `std.http.methodAllowed(method, allowed)` | `Bool` | Checks a method against a comma-separated allow list. |
| `std.http.requestMethodAllowed(request, allowed)` | `Bool` | Checks a request method against a comma-separated allow list. |
| `std.http.requestMethodIs(request, method)` | `Bool` | True when a request envelope has the exact method. |
| `std.http.requestIsGet(request, path)` | `Bool` | True when a request envelope is `GET` for the normalized path. |
| `std.http.requestIsHead(request, path)` | `Bool` | True when a request envelope is `HEAD` for the normalized path. |
| `std.http.requestIsOptions(request, path)` | `Bool` | True when a request envelope is `OPTIONS` for the normalized path. |
| `std.http.requestIsPost(request, path)` | `Bool` | True when a request envelope is `POST` for the normalized path. |
| `std.http.requestIsPut(request, path)` | `Bool` | True when a request envelope is `PUT` for the normalized path. |
| `std.http.requestIsPatch(request, path)` | `Bool` | True when a request envelope is `PATCH` for the normalized path. |
| `std.http.requestIsDelete(request, path)` | `Bool` | True when a request envelope is `DELETE` for the normalized path. |
| `std.http.requestPathStartsWith(request, prefix)` | `Bool` | True when the normalized request path starts with `prefix`. |
| `std.http.requestPathTailAfter(request, prefix)` | `Maybe<Span<u8>>` | Borrows the normalized request path after `prefix`, or `null` when it does not match. |
| `std.http.requestRouteMatches(request, method, pattern)` | `Bool` | True when the method matches and the path matches a segment pattern. |
| `std.http.requestRouteMethodAllowed(request, pattern, allowed)` | `Bool` | True when the path pattern matches and the method is in a comma-separated allow list. |
| `std.http.requestPathParam(request, pattern, name)` | `Maybe<Span<u8>>` | Borrows a named request path parameter from a matched pattern. |
| `std.http.headerBlockSafe(headers)` | `Bool` | Validates a newline-separated header block before writing it. |
| `std.http.headerBytes(response, value)` | `Maybe<Span<u8>>` | Borrows a response header value after validating packed metadata. |
| `std.http.responseBody(response, result)` | `Maybe<Span<u8>>` | Borrows the response body when the transport result succeeded. |
| `std.http.responseBodyBytes(response)` | `Maybe<Span<u8>>` | Borrows the body bytes from a local response envelope written by response helpers. |
| `std.http.responseStatus(response)` | `Maybe<u16>` | Parses the status code from a local response envelope. |
| `std.http.responseStatusIs(response, status)` | `Bool` | Checks the status code in a local response envelope. |
| `std.http.responseHeader(response, name)` | `Maybe<Span<u8>>` | Borrows a case-insensitive header value from a local response envelope. |
| `std.http.responseContentType(response)` | `Maybe<Span<u8>>` | Borrows the media type from a local response envelope. |
| `std.http.responseRedirectLocation(response)` | `Maybe<Span<u8>>` | Borrows a redirect `Location` header from a local response envelope. |
| `std.http.responseBodyEquals(response, expected)` | `Bool` | Compares a local response body with expected bytes. |
| `std.http.responseMatches(response, status, contentType, body)` | `Bool` | Checks local response status, optional content type, and body bytes. |
| `std.http.testRequest(buffer, method, target, body)` | `Maybe<Span<u8>>` | Writes a synthetic request envelope for handler tests. |
| `std.http.testJsonRequest(buffer, method, target, body)` | `Maybe<Span<u8>>` | Writes a synthetic JSON request envelope for handler tests. |
The `WithHeader` and `WithHeaders` writers reject header names they manage
themselves. Request writers reject `content-length` and `transfer-encoding`;
JSON request writers also reject `content-type`; response writers reject
`content-length`, `transfer-encoding`, and `connection`; JSON response writers
also reject `content-type`.
Metadata labels:
- effects: net, memory, parse, or none for named error constants
- allocation behavior: metadata helpers do not allocate; `fetch` writes into a
caller-owned response buffer and uses the provider runtime internally
- target support: request/response parsing and writing helpers are
target-neutral; client/server require a net-capable target; `fetch` and
`listen` run on supported Darwin arm64 and Linux x64 host executable targets
- error behavior: metadata helpers are infallible; `fetch` returns status,
body length, and error metadata so non-2xx responses are distinguishable from
transport failures
- ownership notes: HTTP helpers borrow network capability metadata and write
only to caller-owned buffers
- examples: `conformance/native/pass/std-http-metadata-neutral.graph`,
`conformance/native/pass/std-http-fetch.graph`,
`conformance/native/pass/std-http-errors.graph`,
`conformance/native/pass/std-http-response-helpers.graph`,
`conformance/native/pass/std-http-api-helpers.graph`,
`conformance/native/pass/std-http-cors-helpers.graph`,
`conformance/native/pass/std-http-auth-helpers.graph`,
`conformance/native/pass/std-http-path-segments.graph`,
`examples/json-api-client.graph`,
`examples/json-api-router.graph`,
`examples/std-http-json.graph`,
`examples/std-http-request.graph`,
`examples/std-http-headers.graph`
## Example
Metadata helpers:
```zero
pub fn main(world: World) -> Void raises {
let net: Net = std.net.host()
let addr: Address = std.net.address("localhost", 8080_u16)
let _client: HttpClient = std.http.client(net)
let _server: HttpServer = std.http.server(net, addr)
let method: HttpMethod = std.http.parseMethod("GET")
if method == std.http.parseMethod("GET") && std.mem.len(std.mem.span("body")) == 4 {
check world.out.write("http ok\n")
}
}
```
GET request:
```zero
pub fn main(world: World) -> Void raises {
let net: Net = std.net.host()
let client: HttpClient = std.http.client(net)
var response: [512]u8 = [0_u8; 512]
let request: Span<u8> = std.mem.span("GET https://example.com\n\n")
let result: HttpResult = std.http.fetch(client, request, response, std.time.ms(1000))
if std.http.resultOk(result) {
check world.out.write("http get ok\n")
return
}
check world.err.write("http get failed\n")
}
```
Request with headers and body:
```zero
pub fn main(world: World) -> Void raises {
let net: Net = std.net.host()
let client: HttpClient = std.http.client(net)
var request_buf: [256]u8 = [0_u8; 256]
let request: Maybe<Span<u8>> = std.http.writeJsonRequestWithHeader(request_buf, "POST https://example.com/api", "accept: application/json", "{\"ping\":1}")
var response: [512]u8 = [0_u8; 512]
if request.has {
let result: HttpResult = std.http.fetch(client, request.value, response, std.time.ms(1000))
if std.http.resultOk(result) {
check world.out.write("http post ok\n")
return
}
}
check world.err.write("http post failed\n")
}
```
Request routing:
```zero
pub fn main(world: World) -> Void raises {
let request: Span<u8> = std.mem.span("POST /users/7?tenant=demo\naccept: application/json\ncontent-type: application/json; charset=utf-8\n\n{\"id\":7}")
var response: [256]u8 = [0_u8; 256]
let id: Maybe<Span<u8>> = std.http.requestPathParam(request, "/users/:id", "id")
let body_id: Maybe<Span<u8>> = std.http.requestJsonField(request, "id", 64)
let tenant: Maybe<Span<u8>> = std.http.requestQueryValue(request, "tenant")
if std.http.requestRouteMatches(request, "POST", "/users/:id") && id.has && tenant.has && body_id.has && std.http.requestAcceptsJson(request) {
let written: Maybe<Span<u8>> = std.http.writeJsonResponseWithHeader(response, 201_u16, "location: /users/7", "{\"created\":true}")
if written.has {
check world.out.write("http route ok\n")
return
}
}
let failed: Maybe<Span<u8>> = std.http.writeJsonError(response, 400, "bad_request")
if failed.has {
check world.err.write("http route failed\n")
}
}
```
CORS preflight and JSON response:
```zero
pub fn main(world: World) -> Void raises {
let request: Span<u8> = std.mem.span("OPTIONS /users\naccess-control-request-method: POST\n\n")
var response: [256]u8 = [0_u8; 256]
if std.http.requestIsOptions(request, "/users") {
let written: Maybe<Span<u8>> = std.http.writeCorsPreflight(response, "*", "GET, POST, OPTIONS", "content-type, authorization")
if written.has {
check world.out.write("http cors preflight ok\n")
return
}
}
let failed: Maybe<Span<u8>> = std.http.writeCorsJsonResponse(response, "400 Bad Request", "{\"error\":\"bad_request\"}", "*")
if failed.has {
check world.err.write("http cors failed\n")
}
}
```
Authorization and session cookie helpers:
```zero
pub fn main(world: World) -> Void raises {
let request: Span<u8> = std.mem.span("GET /me\nauthorization: Bearer token-123\ncookie: sid=abc; theme=dark\n\n")
let token: Maybe<Span<u8>> = std.http.requestBearerToken(request)
let session: Maybe<Span<u8>> = std.http.requestCookie(request, "sid")
if token.has && session.has {
check world.out.write("http auth ok\n")
}
}
```
Response body:
```zero
pub fn main(world: World) -> Void raises {
let maybe_request: Maybe<String> = std.args.get(1)
if !maybe_request.has {
check world.err.write("usage: pass HTTP request envelope\n")
return
}
let net: Net = std.net.host()
let client: HttpClient = std.http.client(net)
var response: [512]u8 = [0_u8; 512]
let result: HttpResult = std.http.fetch(client, std.mem.span(maybe_request.value), response, std.time.ms(5000))
let bytes: Maybe<Span<u8>> = std.http.responseBody(response, result)
var arena_buf: [16]u8 = [0_u8; 16]
var arena: FixedBufAlloc = std.mem.fixedBufAlloc(arena_buf)
var parsed: Maybe<JsonDoc> = null
if bytes.has {
parsed = std.json.parseBytes(arena, bytes.value)
}
if std.http.resultOk(result) && bytes.has && parsed.has {
check world.out.write("http response json ok\n")
return
}
check world.err.write("http response json failed\n")
}
```
Loopback API server:
```zero
pub fn main(world: World) -> Void raises {
check std.http.listen(world)
}
fn handle(request: Span<u8>, response: MutSpan<u8>) -> Maybe<Span<u8>> {
if std.http.requestIsGet(request, "/ping") {
return std.http.writeJsonOk(response, "{\"message\":\"pong\"}")
}
if std.http.requestIsGet(request, "/robots.txt") {
return std.http.writeTextOk(response, "user-agent: *\nallow: /\n")
}
if std.http.requestIsGet(request, "/old") {
return std.http.writeMovedPermanently(response, "/ping")
}
return std.http.writeJsonError(response, 404, "not_found")
}
```
Run it and use the port printed by the listener:
```sh
zero run .
# listening on http://127.0.0.1:3001
curl -sS -i http://127.0.0.1:3001/ping
```
When no port is passed, `listen(world)` starts at `3000` and increments by one
until it finds a free loopback port. This lets small local services coexist on a
development machine. When the program passes an explicit port, such as
`std.http.listen(world, 3000_u16)`, Zero tries exactly that port and reports the
bind failure instead of auto-incrementing.
Synthetic handler check:
```zero
fn handle(request: Span<u8>, response: MutSpan<u8>) -> Maybe<Span<u8>> {
if std.http.requestRouteMethodAllowed(request, "/users/:id", "GET, HEAD") {
return std.http.writeJsonResponseWithHeaders(response, 200_u16, "cache-control: no-store", "{\"ok\":true}")
}
return std.http.writeJsonError(response, 404_u16, "not_found")
}
pub fn main(world: World) -> Void raises {
var request_storage: [128]u8 = [0_u8; 128]
var response_storage: [256]u8 = [0_u8; 256]
let request: Maybe<Span<u8>> = std.http.testRequest(request_storage, "GET", "/users/7", "")
if request.has {
let response: Maybe<Span<u8>> = handle(request.value, response_storage)
if response.has && std.http.responseMatches(response.value, 200_u16, "application/json", "{\"ok\":true}") {
check world.out.write("handler ok\n")
return
}
}
check world.err.write("handler failed\n")
}
```
## Design Notes
`std.http.fetch` is the outbound HTTP client primitive. The request argument is
one byte envelope: `METHOD URL`, followed by zero or more `Header-Name: value`
lines, a blank line, and optional request body bytes. Use `writeRequest` and
`writeJsonRequest` when you want the standard library to write that envelope
into caller storage. The timeout is a `Duration`, typically built with
`std.time.ms(...)`.
`fetch` supports `http://` and `https://` URLs on supported host executable
targets, uses the C-library curl provider, does not follow redirects, and
verifies TLS through the provider. TLS verification is always enabled.
`ZERO_HTTP_TEST_CA_BUNDLE` can point the provider at an explicit CA bundle
while keeping certificate verification on.
The response buffer starts with internal metadata, followed by raw response
headers and then the response body. Use `responseHeadersLen` and
`responseBodyOffset` rather than hard-coding offsets. `headerValue` scans the
response buffer and returns packed offset/length metadata for the matching
value; `headerFound`, `headerOffset`, and `headerLen` inspect that metadata
without allocating. Prefer `headerBytes` and `responseBody` when you need a
borrowed byte span and want metadata bounds checked in one call.
Compare `resultError` with the named `HttpError` helpers rather than raw
numbers. Use `errorName(resultError(result))` for diagnostics and logs.
`errorNone` means the transport succeeded; HTTP non-2xx statuses still carry
`errorNone` and can be inspected with `resultStatus`.
The module does not expose raw socket read/write APIs, streaming bodies, a
global router, or a heap-allocated response object.
+56
View File
@@ -0,0 +1,56 @@
## When To Use std.inet
In Zerolang, use `std.inet` to validate and parse internet address literals:
IPv4, IPv6, and RFC 1123 hostnames. These helpers are target-neutral and need
no network capability, so they work in validators and parsers on any compiler
target. Use `std.net` when a program actually opens connections or listeners.
Runnable today:
| API | Return | Notes |
| --- | --- | --- |
| `std.inet.isIpv4(text)` | `Bool` | Validates a dotted-quad IPv4 literal: four decimal octets 0-255 with no leading zeros. |
| `std.inet.parseIpv4(text)` | `Maybe<u32>` | Parses an IPv4 literal into a big-endian packed `u32`. |
| `std.inet.writeIpv4(buffer, value)` | `Maybe<Span<u8>>` | Writes a packed IPv4 value as dotted-quad text into caller storage. |
| `std.inet.isIpv4Unspecified(value)` | `Bool` | Checks `0.0.0.0/32`. |
| `std.inet.isIpv4Loopback(value)` | `Bool` | Checks `127.0.0.0/8`. |
| `std.inet.isIpv4Private(value)` | `Bool` | Checks RFC 1918 private ranges. |
| `std.inet.isIpv4LinkLocal(value)` | `Bool` | Checks `169.254.0.0/16`. |
| `std.inet.isIpv4Multicast(value)` | `Bool` | Checks `224.0.0.0/4`. |
| `std.inet.isIpv6(text)` | `Bool` | Validates an RFC 4291 IPv6 literal, including `::` compression and embedded IPv4. |
| `std.inet.parseIpv6(buffer, text)` | `Maybe<Span<u8>>` | Parses an IPv6 literal into 16 network-order bytes in a caller buffer. |
| `std.inet.isIp(text)` | `Bool` | Validates either a strict IPv4 literal or an RFC 4291 IPv6 literal. |
| `std.inet.parseIp(buffer, text)` | `Maybe<Span<u8>>` | Parses IPv4 into 4 bytes or IPv6 into 16 bytes in caller storage. |
| `std.inet.isIpv6Unspecified(bytes)` | `Bool` | Checks `::/128` over a 16-byte IPv6 span. |
| `std.inet.isIpv6Loopback(bytes)` | `Bool` | Checks `::1/128` over a 16-byte IPv6 span. |
| `std.inet.isIpv6Multicast(bytes)` | `Bool` | Checks `ff00::/8`. |
| `std.inet.isIpv6LinkLocal(bytes)` | `Bool` | Checks `fe80::/10`. |
| `std.inet.isIpv6Private(bytes)` | `Bool` | Checks the `fc00::/7` unique-local range. |
| `std.inet.isIpv6UniqueLocal(bytes)` | `Bool` | Alias for the `fc00::/7` unique-local range. |
| `std.inet.isIpv6MappedIpv4(bytes)` | `Bool` | Checks `::ffff:0:0/96` IPv4-mapped addresses. |
| `std.inet.ipv6MappedIpv4(bytes)` | `Maybe<u32>` | Extracts the packed IPv4 value from an IPv4-mapped IPv6 span. |
| `std.inet.isHostname(text)` | `Bool` | Validates an RFC 1123 hostname: dot-separated 1-63 byte alphanumeric/hyphen labels, 253 bytes total. |
## Example
```zero
pub fn main(world: World) -> Void raises {
var storage: [16]u8 = [0; 16]
let buffer: MutSpan<u8> = storage
let quad: Maybe<u32> = std.inet.parseIpv4("192.168.0.1")
let mapped: Maybe<Span<u8>> = std.inet.parseIpv6(buffer, "::ffff:192.168.1.1")
if std.inet.isHostname("example.com") && (quad.has && mapped.has) && (std.inet.isIpv4Private(quad.value) && std.inet.isIpv6MappedIpv4(mapped.value)) {
check world.out.write("inet ok\n")
}
}
```
Effects: none.
Allocation behavior: `parseIp` and `parseIpv6` write into caller buffers;
`writeIpv4` writes into caller storage. The other helpers allocate nothing.
Error behavior: validators return `Bool`; parsers return `null` for invalid
literals or undersized buffers.
Target support: current compiler targets; no network capability required.
+128
View File
@@ -0,0 +1,128 @@
## When To Use std.io
In Zerolang, use `std.io` for byte reads and writes over caller-owned storage.
The module provides explicit cursor helpers for spans plus fixed reader and
writer values for sequential stream-style code.
Span helpers:
| API | Return | Notes |
| --- | --- | --- |
| `std.io.copy(dst, src)` | `usize` | Copies as many bytes as fit and returns the count. |
| `std.io.copyN(dst, src, count)` | `Maybe<usize>` | Copies exactly `count` bytes when both spans are large enough. |
| `std.io.read(bytes, offset, dst)` | `Maybe<usize>` | Copies available bytes and returns the next cursor. |
| `std.io.readExact(bytes, offset, dst)` | `Maybe<usize>` | Fills the destination only when enough input bytes remain. |
| `std.io.readAt(bytes, offset, dst)` | `Maybe<usize>` | Reads from an explicit offset without owning state. |
| `std.io.readAll(bytes, dst)` | `Maybe<Span<u8>>` | Copies all input bytes and returns the written prefix. |
| `std.io.readByte(bytes, offset)` | `Maybe<u8>` | Reads one byte at an explicit cursor. |
| `std.io.writeByte(buffer, offset, byte)` | `Maybe<usize>` | Writes one byte and returns the next cursor. |
| `std.io.writeSpan(buffer, offset, bytes)` | `Maybe<usize>` | Writes bytes and returns the next cursor. |
| `std.io.writeAll(buffer, offset, bytes)` | `Maybe<usize>` | Writes all bytes only when the full input fits. |
| `std.io.writeAt(buffer, offset, bytes)` | `Maybe<usize>` | Writes at an explicit offset. |
| `std.io.written(buffer, len)` | `Span<u8>` | Borrows the written prefix, clamped to buffer length. |
| `std.io.remaining(buffer, offset)` | `usize` | Reports remaining byte capacity from an explicit cursor. |
Line and delimiter helpers:
| API | Return | Notes |
| --- | --- | --- |
| `std.io.nextLine(bytes, start)` | `Maybe<Span<u8>>` | Borrows the next line without `\n` or trailing `\r`. |
| `std.io.nextLineStart(bytes, start)` | `usize` | Advances to the next line start or end of input. |
| `std.io.readLine(bytes, start)` | `Maybe<Span<u8>>` | Alias for line-oriented reads using the same line rules. |
| `std.io.readLineStart(bytes, start)` | `usize` | Advances to the next line start using the same line rules. |
| `std.io.readUntilDelimiter(bytes, start, delimiter)` | `Maybe<Span<u8>>` | Borrows bytes up to the delimiter or end of input. |
| `std.io.readUntilDelimiterStart(bytes, start, delimiter)` | `usize` | Advances past the delimiter when found, otherwise to end of input. |
| `std.io.countLines(bytes)` | `usize` | Counts lines using the same next-line rules. |
Fixed stream helpers:
| API | Return | Notes |
| --- | --- | --- |
| `std.io.fixedReader(bytes, cursor)` | `FixedReader` | Builds a sequential reader over borrowed bytes. |
| `std.io.fixedReaderRead(&mut reader, dst)` | `usize` | Reads up to `dst` length and advances the reader cursor. |
| `std.io.fixedReaderReadExact(&mut reader, dst)` | `Bool` | Reads exactly `dst` length or leaves the cursor unchanged. |
| `std.io.fixedReaderReadLine(&mut reader)` | `Maybe<Span<u8>>` | Borrows the next line and advances the cursor. |
| `std.io.fixedReaderReadUntilDelimiter(&mut reader, delimiter)` | `Maybe<Span<u8>>` | Borrows bytes up to a delimiter and advances the cursor. |
| `std.io.fixedReaderReadAll(&mut reader, dst)` | `Maybe<Span<u8>>` | Copies remaining bytes into caller storage. |
| `std.io.fixedReaderReadAt(&reader, offset, dst)` | `Maybe<usize>` | Reads from an offset without moving the cursor. |
| `std.io.fixedReaderReadByte(&mut reader)` | `Maybe<u8>` | Reads one byte and advances the cursor. |
| `std.io.fixedReaderLimit(&reader, count)` | `FixedReader` | Creates a bounded reader view from the current cursor. |
| `std.io.fixedReaderSeek(&mut reader, cursor)` | `Bool` | Moves the cursor when the target is in range. |
| `std.io.fixedReaderCursor(&reader)` | `usize` | Reports the cursor. |
| `std.io.fixedReaderLen(&reader)` | `usize` | Reports input length. |
| `std.io.fixedReaderRemaining(&reader)` | `usize` | Reports unread bytes. |
| `std.io.fixedReaderDone(&reader)` | `Bool` | Reports whether the cursor reached the end. |
| `std.io.fixedWriter(buffer, cursor)` | `FixedWriter` | Builds a sequential writer over caller storage. |
| `std.io.fixedWriterWrite(&mut writer, bytes)` | `Bool` | Writes bytes and advances the writer cursor. |
| `std.io.fixedWriterWriteAll(&mut writer, bytes)` | `Bool` | Writes all bytes only when they fit. |
| `std.io.fixedWriterWriteByte(&mut writer, byte)` | `Bool` | Writes one byte and advances the cursor. |
| `std.io.fixedWriterWriteAt(&mut writer, offset, bytes)` | `Bool` | Writes at an offset without moving the cursor. |
| `std.io.fixedWriterView(&writer)` | `Span<u8>` | Borrows the live writer prefix. |
| `std.io.fixedWriterSeek(&mut writer, cursor)` | `Bool` | Moves the cursor when the target is in range. |
| `std.io.fixedWriterTruncate(&mut writer, len)` | `usize` | Clamps the live prefix length. |
| `std.io.fixedWriterClear(&mut writer)` | `usize` | Resets the cursor to zero. |
| `std.io.fixedWriterCursor(&writer)` | `usize` | Reports the cursor. |
| `std.io.fixedWriterCapacity(&writer)` | `usize` | Reports storage length. |
| `std.io.fixedWriterRemaining(&writer)` | `usize` | Reports unwritten capacity. |
Stream composition helpers:
| API | Return | Notes |
| --- | --- | --- |
| `std.io.copyBuffer(&mut reader, &mut writer, scratch)` | `usize` | Copies until EOF or writer capacity using caller scratch storage. |
| `std.io.copyReaderN(&mut reader, &mut writer, count, scratch)` | `Maybe<usize>` | Copies exactly `count` bytes through scratch storage. |
| `std.io.discard(&mut reader, scratch)` | `usize` | Reads and drops bytes through scratch storage. |
| `std.io.teeRead(&mut reader, &mut writer, dst)` | `Maybe<usize>` | Reads into `dst` and writes the same bytes to the writer. |
| `std.io.multiRead(&mut first, &mut second, dst)` | `usize` | Fills `dst` from the first reader, then the second. |
Error labels:
| API | Return | Notes |
| --- | --- | --- |
| `std.io.errorNone()` | `u32` | No IO error. |
| `std.io.errorEof()` | `u32` | End of input. |
| `std.io.errorShortRead()` | `u32` | Input ended before an exact read completed. |
| `std.io.errorShortWrite()` | `u32` | Output capacity ended before an exact write completed. |
| `std.io.errorCapacity()` | `u32` | Caller storage was too small. |
| `std.io.errorPermission()` | `u32` | Permission was denied. |
| `std.io.errorTimeout()` | `u32` | Operation timed out. |
| `std.io.errorIo()` | `u32` | General IO failure. |
| `std.io.errorName(code)` | `String` | Returns a stable label for a known code. |
Buffered capacity helpers:
| API | Return | Notes |
| --- | --- | --- |
| `std.io.bufferedReader(buffer)` | `BufferedReader` | Builds a reader descriptor over caller storage. |
| `std.io.bufferedWriter(buffer)` | `BufferedWriter` | Builds a writer descriptor over caller storage. |
| `std.io.readerCapacity(&reader)` | `usize` | Reports reader storage capacity. |
| `std.io.writerCapacity(&writer)` | `usize` | Reports writer storage capacity. |
Metadata labels:
- effects: memory
- allocation behavior: uses caller buffer; no hidden heap
- target support: target-neutral
- error behavior: exact cursor reads and writes return `Maybe.none` or `false` on overflow or insufficient input
- ownership notes: borrows or writes caller-owned storage
## Example
```zero
pub fn main(world: World) -> Void raises {
var scratch: [4]u8 = [0_u8; 4]
var output: [16]u8 = [0_u8; 16]
var reader: FixedReader = std.io.fixedReader("one\ntwo", 0)
var writer: FixedWriter = std.io.fixedWriter(output, 0)
let line: Maybe<Span<u8>> = std.io.fixedReaderReadLine(&mut reader)
let copied: usize = std.io.copyBuffer(&mut reader, &mut writer, scratch)
if line.has && copied == 3 && std.mem.eql(std.io.fixedWriterView(&writer), "two") {
check world.out.write("io ok\n")
}
}
```
## Design Notes
`std.io` is a caller-owned buffer surface, not an ambient process I/O layer.
Process stdin/stdout stay behind explicit capabilities such as `World` streams.
+179
View File
@@ -0,0 +1,179 @@
## When To Use std.json
In Zerolang, use `std.json` for validation, shallow field lookup, object and
array cursor access, explicit-allocator parsing, and caller-buffer JSON writing.
Runnable today:
| API | Return | Notes |
| --- | --- | --- |
| `std.json.validate(text)` | `Bool` | Checks the current JSON subset without allocation. |
| `std.json.validateBytes(bytes)` | `Bool` | Checks a `Span<u8>` JSON payload without allocation. |
| `std.json.parse(alloc, text)` | `Maybe<JsonDoc>` | Parses with an explicit allocator and returns `null` on failure. |
| `std.json.parseBytes(alloc, bytes)` | `Maybe<JsonDoc>` | Parses a `Span<u8>` payload with an explicit allocator and returns `null` on failure. |
| `std.json.streamTokens(text)` | `usize` | Counts stream tokens without building an owned tree. |
| `std.json.streamTokensBytes(bytes)` | `usize` | Counts stream tokens from a `Span<u8>` payload. |
| `std.json.writeString(buffer, text)` | `Maybe<String>` | Writes an escaped JSON string into caller storage. |
| `std.json.decodeBoundary()` | `String` | Documents the typed decode boundary exposed by current metadata. |
| `std.json.errorNone()` | `u32` | Validation status for a clean payload. |
| `std.json.errorInvalid()` | `u32` | Validation status for malformed JSON. |
| `std.json.errorTrailing()` | `u32` | Validation status for trailing non-whitespace bytes. |
| `std.json.errorName(code)` | `String` | Returns a stable label for a validation status. |
| `std.json.errorExpected(code)` | `String` | Returns stable expected-token text for a validation status. |
| `std.json.validateError(bytes)` | `u32` | Validates a byte span and returns a structured status code. |
| `std.json.errorOffset(bytes)` | `usize` | Returns the byte offset where validation fails, or the input length for valid JSON. |
| `std.json.errorLine(bytes)` | `usize` | Returns the one-based line for the validation error offset. |
| `std.json.errorColumn(bytes)` | `usize` | Returns the one-based column for the validation error offset. |
| `std.json.field(bytes, key)` | `Maybe<Span<u8>>` | Returns the raw top-level object field value. |
| `std.json.objectFieldCount(bytes)` | `Maybe<usize>` | Counts fields in a JSON object slice. |
| `std.json.objectKey(buffer, bytes, ordinal)` | `Maybe<Span<u8>>` | Decodes an ordinal object key into caller storage. |
| `std.json.objectValue(bytes, ordinal)` | `Maybe<Span<u8>>` | Returns an ordinal object value as a raw JSON slice. |
| `std.json.arrayCount(bytes)` | `Maybe<usize>` | Counts items in a JSON array slice. |
| `std.json.arrayValue(bytes, ordinal)` | `Maybe<Span<u8>>` | Returns an ordinal array value as a raw JSON slice. |
| `std.json.path(bytes, path)` | `Maybe<Span<u8>>` | Returns a raw value for a dot-separated object path. |
| `std.json.pathString(buffer, bytes, path)` | `Maybe<Span<u8>>` | Looks up and decodes a string at a dot-separated object path. |
| `std.json.pathU32(bytes, path)` | `Maybe<u32>` | Looks up and decodes a `u32` at a dot-separated object path. |
| `std.json.pathBool(bytes, path)` | `Maybe<Bool>` | Looks up and decodes a bool at a dot-separated object path. |
| `std.json.stringDecode(buffer, value)` | `Maybe<Span<u8>>` | Decodes a JSON string value, including Unicode escapes as UTF-8, into caller storage. |
| `std.json.string(buffer, bytes, key)` | `Maybe<Span<u8>>` | Looks up and decodes a top-level string field. |
| `std.json.u32(bytes, key)` | `Maybe<u32>` | Looks up and decodes a top-level unsigned integer field. |
| `std.json.bool(bytes, key)` | `Maybe<Bool>` | Looks up and decodes a top-level boolean field. |
| `std.json.writeStringBytes(buffer, text)` | `Maybe<Span<u8>>` | Writes an escaped JSON string from byte input. |
| `std.json.writeObject1String(buffer, key, value)` | `Maybe<Span<u8>>` | Writes a one-field object with a string value. |
| `std.json.writeObject1U32(buffer, key, value)` | `Maybe<Span<u8>>` | Writes a one-field object with a `u32` value. |
| `std.json.writeObject1Bool(buffer, key, value)` | `Maybe<Span<u8>>` | Writes a one-field object with a bool value. |
| `std.json.writeFieldRaw(buffer, key, value)` | `Maybe<Span<u8>>` | Writes one object field from a key and validated raw JSON value. |
| `std.json.writeFieldString(buffer, key, value)` | `Maybe<Span<u8>>` | Writes one object field with an escaped string value. |
| `std.json.writeFieldU32(buffer, key, value)` | `Maybe<Span<u8>>` | Writes one object field with a `u32` value. |
| `std.json.writeFieldBool(buffer, key, value)` | `Maybe<Span<u8>>` | Writes one object field with a bool value. |
| `std.json.writeObject2Fields(buffer, field0, field1)` | `Maybe<Span<u8>>` | Writes a two-field object from field fragments and validates the final object. |
| `std.json.writeObject2StringField(buffer, key, value, field1)` | `Maybe<Span<u8>>` | Writes a two-field object from a string field and a prebuilt field fragment. |
| `std.json.writeObject2U32Field(buffer, key, value, field1)` | `Maybe<Span<u8>>` | Writes a two-field object from a `u32` field and a prebuilt field fragment. |
| `std.json.writeObject2BoolField(buffer, key, value, field1)` | `Maybe<Span<u8>>` | Writes a two-field object from a bool field and a prebuilt field fragment. |
| `std.json.writeArray2Strings(buffer, value0, value1)` | `Maybe<Span<u8>>` | Writes a two-item array with escaped string values. |
| `std.json.writeArray2U32(buffer, value0, value1)` | `Maybe<Span<u8>>` | Writes a two-item array with `u32` values. |
| `std.json.writeArray2Bools(buffer, value0, value1)` | `Maybe<Span<u8>>` | Writes a two-item array with bool values. |
Metadata labels:
- effects: memory, parse, or alloc
- allocation behavior: validation and streaming are allocation-free; parse uses explicit allocator only; direct writers write caller buffers
- target support: target-neutral
- error behavior: `Maybe` helpers return null on failure
- diagnostics: `errorOffset`, `errorLine`, and `errorColumn` locate validation failures; valid JSON reports the end of input
- ownership notes: parsed documents are owned by explicit allocator storage in this compiler slice
- examples: `examples/std-data-formats.graph`, `examples/std-json-bytes.graph`, `conformance/native/pass/std-codec-json-url.graph`
## Example
```zero
pub fn main(world: World) -> Void raises {
var arena_buf: [16]u8 = [0_u8; 16]
var arena: FixedBufAlloc = std.mem.fixedBufAlloc(arena_buf)
let parsed: Maybe<JsonDoc> = std.json.parse(arena, "{\"ok\":true}")
var out: [16]u8 = [0_u8; 16]
let text: Maybe<String> = std.json.writeString(out, "zero")
if parsed.has && text.has && std.json.streamTokens("{\"ok\":true}") == 3 {
check world.out.write("json ok\n")
}
}
```
Byte-span parse form:
```zero
pub fn main(world: World) -> Void raises {
let bytes: Span<u8> = std.mem.span("{\"ok\":1}")
var arena_buf: [16]u8 = [0_u8; 16]
var arena: FixedBufAlloc = std.mem.fixedBufAlloc(arena_buf)
let parsed: Maybe<JsonDoc> = std.json.parseBytes(arena, bytes)
if parsed.has && std.json.validateBytes(bytes) && std.json.streamTokensBytes(bytes) == 3 {
check world.out.write("json bytes ok\n")
return
}
check world.err.write("json bytes failed\n")
}
```
Top-level object lookup and caller-buffer writing:
```zero
pub fn main(world: World) -> Void raises {
let input: Span<u8> = "{\"name\":\"zero\",\"count\":42,\"ok\":true}"
var name_buf: [8]u8 = [0_u8; 8]
let name: Maybe<Span<u8>> = std.json.string(name_buf, input, "name")
let count: Maybe<u32> = std.json.u32(input, "count")
var count_field_buf: [24]u8 = [0_u8; 24]
let count_field: Maybe<Span<u8>> = std.json.writeFieldU32(count_field_buf, "count", 42_u32)
var out: [48]u8 = [0_u8; 48]
var written: Maybe<Span<u8>> = null
if count_field.has {
written = std.json.writeObject2StringField(out, "name", "zero", count_field.value)
}
if name.has && count.has && written.has && std.json.validateError(written.value) == std.json.errorNone() {
check world.out.write("json lookup ok\n")
}
}
```
Object and array cursors return borrowed raw JSON slices. Object keys are
decoded into caller-owned storage:
```zero
pub fn main(world: World) -> Void raises {
let input: Span<u8> = "{\"user\":{\"name\":\"zero\",\"count\":42},\"items\":[1,2]}"
let field_count: Maybe<usize> = std.json.objectFieldCount(input)
var key_buf: [8]u8 = [0_u8; 8]
let first_key: Maybe<Span<u8>> = std.json.objectKey(key_buf, input, 0)
let items: Maybe<Span<u8>> = std.json.field(input, "items")
var name_buf: [8]u8 = [0_u8; 8]
let name: Maybe<Span<u8>> = std.json.pathString(name_buf, input, "user.name")
if field_count.has && first_key.has && items.has && std.json.arrayCount(items.value).has && name.has {
check world.out.write("json cursors ok\n")
}
}
```
JSON validation diagnostics are allocation-free. Use `std.diag` when a
human-readable file location is needed:
```zero
pub fn main(world: World) -> Void raises {
let input: Span<u8> = "{\n \"ok\": tru\n}"
var location_buf: [24]u8 = [0_u8; 24]
let location: Maybe<Span<u8>> = std.diag.formatOffsetLocation(location_buf, "config.json", input, std.json.errorOffset(input))
if std.json.validateError(input) == std.json.errorInvalid() && std.json.errorLine(input) == 2 && location.has {
check world.out.write(location.value)
check world.out.write("\n")
}
}
```
Small array responses use the same caller-buffer pattern:
```zero
pub fn main(world: World) -> Void raises {
var out: [32]u8 = [0_u8; 32]
let tags: Maybe<Span<u8>> = std.json.writeArray2Strings(out, "api", "agent")
if tags.has {
check world.out.write(tags.value)
}
}
```
## Design Notes
JSON should not fake allocation-free semantics. Validation, field lookup,
string decode, and writing stay allocation-free. String decode writes UTF-8 for
Unicode escapes and rejects malformed surrogate pairs.
Parsing into an owned document requires an explicit allocator. The current
`JsonDoc` value is opaque; examples inspect `Maybe.has` and use token streaming
for allocation-free summaries. Field lookup is intentionally shallow: it reads
top-level object fields and returns raw slices or typed scalar decodes. Object
path lookup follows dot-separated object keys; array indexing remains explicit
through `arrayValue`. When an object contains duplicate keys, name-based lookup
returns the first matching value, while ordinal object cursors preserve the
source order and expose every field. Validation diagnostics report byte offsets
in the source payload; line and column helpers treat lines and columns as
one-based byte positions.
+65
View File
@@ -0,0 +1,65 @@
## When To Use std.log
In Zerolang, use `std.log` for explicit-buffer structured log record formatting.
Runnable today:
| API | Return | Notes |
| --- | --- | --- |
| `std.log.levelDebug()` | `String` | Static `debug` level text. |
| `std.log.levelInfo()` | `String` | Static `info` level text. |
| `std.log.levelWarn()` | `String` | Static `warn` level text. |
| `std.log.levelError()` | `String` | Static `error` level text. |
| `std.log.message(buffer, level, message)` | `Maybe<Span<u8>>` | Writes one newline-terminated JSON Lines record with `level` and `message`. |
| `std.log.keyValue(buffer, level, key, value)` | `Maybe<Span<u8>>` | Writes one newline-terminated JSON Lines record with `level`, `key`, and `value`. |
| `std.log.stringField(buffer, key, value)` | `Maybe<Span<u8>>` | Writes one JSON string field fragment. |
| `std.log.messageField(buffer, level, message, field)` | `Maybe<Span<u8>>` | Writes one JSON Lines message record with one field fragment. |
| `std.log.redacted(buffer, level, key)` | `Maybe<Span<u8>>` | Writes one JSON Lines record marking a field name as redacted. |
Metadata labels:
- effects: memory; `messageField` also validates JSON field fragments
- allocation behavior: writes caller buffer; no hidden heap
- target support: target-neutral
- error behavior: returns `null` when the buffer is too small or a value cannot be JSON-escaped
- ownership notes: borrows returned bytes from caller-owned storage
- example: `examples/std-testing-log.graph`
## Example
```zero
pub fn main(world: World) -> Void raises {
var storage: [128]u8 = [0_u8; 128]
var field_storage: [64]u8 = [0_u8; 64]
let field: Maybe<Span<u8>> = std.log.stringField(field_storage, "event", "startup")
if field.has {
let entry: Maybe<Span<u8>> = std.log.messageField(storage, std.log.levelInfo(), "started", field.value)
if entry.has {
check world.out.write(entry.value)
}
}
}
```
Expected output:
```json
{"level":"info","message":"started","event":"startup"}
```
## Design Notes
`std.log` is a formatting surface, not a global logger. The caller owns the
storage and chooses where to write the resulting span, such as `World.out`, a
file handle, or a test assertion.
Records use JSON Lines so downstream tools can parse them without guessing at
ad hoc separators. The helpers write exactly one record and include a trailing
newline.
`messageField` validates the final JSON object before returning it. Build field
fragments with `stringField` unless the field fragment is already known to be
valid JSON.
Use `redacted` for logs that need to state which field was intentionally
withheld without writing the sensitive value.
+86
View File
@@ -0,0 +1,86 @@
## When To Use std.math
In Zerolang, use `std.math` for pure fixed-width integer helpers, checked/saturating
arithmetic, and small number-theory routines.
Runnable today:
| API | Return | Notes |
| --- | --- | --- |
| `std.math.minU32(left, right)` | `u32` | Returns the smaller unsigned value. |
| `std.math.minI32(left, right)` | `i32` | Returns the smaller signed 32-bit value. |
| `std.math.minUsize(left, right)` | `usize` | Returns the smaller pointer-width unsigned value. |
| `std.math.minI64(left, right)` | `i64` | Returns the smaller signed 64-bit value. |
| `std.math.minU64(left, right)` | `u64` | Returns the smaller unsigned 64-bit value. |
| `std.math.maxU32(left, right)` | `u32` | Returns the larger unsigned value. |
| `std.math.maxI32(left, right)` | `i32` | Returns the larger signed 32-bit value. |
| `std.math.maxUsize(left, right)` | `usize` | Returns the larger pointer-width unsigned value. |
| `std.math.maxI64(left, right)` | `i64` | Returns the larger signed 64-bit value. |
| `std.math.maxU64(left, right)` | `u64` | Returns the larger unsigned 64-bit value. |
| `std.math.clampU32(value, low, high)` | `u32` | Clamps between the two bounds; swapped bounds are normalized. |
| `std.math.clampI32(value, low, high)` | `i32` | Clamps a signed 32-bit value; swapped bounds are normalized. |
| `std.math.clampUsize(value, low, high)` | `usize` | Clamps a pointer-width unsigned value; swapped bounds are normalized. |
| `std.math.clampI64(value, low, high)` | `i64` | Clamps a signed 64-bit value; swapped bounds are normalized. |
| `std.math.clampU64(value, low, high)` | `u64` | Clamps an unsigned 64-bit value; swapped bounds are normalized. |
| `std.math.absI32(value)` | `u32` | Returns the unsigned magnitude of a signed 32-bit value. |
| `std.math.absI64(value)` | `u64` | Returns the unsigned magnitude of a signed 64-bit value. |
| `std.math.checkedAddU32(left, right)` | `Maybe<u32>` | Adds only when the result fits in `u32`. |
| `std.math.checkedSubU32(left, right)` | `Maybe<u32>` | Subtracts only when the result fits in `u32`. |
| `std.math.checkedMulU32(left, right)` | `Maybe<u32>` | Multiplies only when the result fits in `u32`. |
| `std.math.checkedAddUsize(left, right)` | `Maybe<usize>` | Adds only when the result fits in `usize`. |
| `std.math.checkedSubUsize(left, right)` | `Maybe<usize>` | Subtracts only when the result fits in `usize`. |
| `std.math.checkedMulUsize(left, right)` | `Maybe<usize>` | Multiplies only when the result fits in `usize`. |
| `std.math.checkedAddI32(left, right)` | `Maybe<i32>` | Adds only when the result fits in `i32`. |
| `std.math.checkedSubI32(left, right)` | `Maybe<i32>` | Subtracts only when the result fits in `i32`. |
| `std.math.checkedMulI32(left, right)` | `Maybe<i32>` | Multiplies only when the result fits in `i32`. |
| `std.math.saturatingAddU32(left, right)` | `u32` | Adds and clamps overflow to `u32` max. |
| `std.math.saturatingSubU32(left, right)` | `u32` | Subtracts and clamps underflow to `0`. |
| `std.math.saturatingMulU32(left, right)` | `u32` | Multiplies and clamps overflow to `u32` max. |
| `std.math.saturatingAddUsize(left, right)` | `usize` | Adds and clamps overflow to `usize` max. |
| `std.math.saturatingSubUsize(left, right)` | `usize` | Subtracts and clamps underflow to `0`. |
| `std.math.saturatingMulUsize(left, right)` | `usize` | Multiplies and clamps overflow to `usize` max. |
| `std.math.saturatingAddI32(left, right)` | `i32` | Adds and clamps overflow to the nearest `i32` bound. |
| `std.math.saturatingSubI32(left, right)` | `i32` | Subtracts and clamps overflow to the nearest `i32` bound. |
| `std.math.saturatingMulI32(left, right)` | `i32` | Multiplies and clamps overflow to the nearest `i32` bound. |
| `std.math.gcdU32(left, right)` | `u32` | Euclidean greatest common divisor. |
| `std.math.lcmU32(left, right)` | `u32` | Least common multiple; returns `0` when either input is `0`. |
| `std.math.checkedLcmU32(left, right)` | `Maybe<u32>` | Least common multiple only when the result fits in `u32`. |
| `std.math.powU32(base, exponent)` | `u32` | Fixed-width exponentiation by squaring. |
| `std.math.checkedPowU32(base, exponent)` | `Maybe<u32>` | Exponentiation only when the result fits in `u32`. |
| `std.math.modPowU32(base, exponent, modulus)` | `u32` | Modular exponentiation; returns `0` for modulus `0`. |
| `std.math.isPrimeU32(value)` | `Bool` | Trial division primality for unsigned integers. |
| `std.math.isEvenU32(value)` | `Bool` | Reports whether a `u32` value is even. |
| `std.math.isOddU32(value)` | `Bool` | Reports whether a `u32` value is odd. |
| `std.math.sqrtFloorU32(value)` | `u32` | Integer square root rounded down. |
| `std.math.factorialU32(value)` | `Maybe<u32>` | Factorial only when the result fits in `u32`. |
| `std.math.binomialU32(n, k)` | `Maybe<u32>` | Binomial coefficient only when the result fits in `u32`. |
| `std.math.divisorCountU32(value)` | `u32` | Counts positive divisors; returns `0` for `0`. |
| `std.math.properDivisorSumU32(value)` | `u32` | Sums positive divisors smaller than `value`. |
Current scope:
- Helpers are pure, target-neutral fixed-width integer operations.
- Checked helpers return `Maybe<T>` instead of wrapping or trapping.
- Saturating helpers clamp to documented integer bounds.
- The module does not provide floating-point math, big integers, or arbitrary-precision number theory.
## Example
```zero
pub fn main(world: World) -> Void raises {
if std.math.gcdU32(84, 30) == 6 && std.math.isPrimeU32(31) {
check world.out.write("math helper ok\n")
}
}
```
## Design Notes
`std.math` does not allocate and does not require a hosted runtime capability.
Names include the integer width because Zero does not overload standard-library
helpers by argument type.
Number-theory helpers are intentionally simple and deterministic. They are
suitable for small fixed-width tasks, examples, and compiler-portable checks.
Large-number algorithms should be added as explicit APIs with documented bounds
instead of hidden heap allocation or implicit widening.
+172
View File
@@ -0,0 +1,172 @@
## When To Use std.mem
In Zerolang, use `std.mem` for spans, clamped span views, copy/fill,
fixed-buffer allocators, explicit byte buffers, and memory-budget-visible
collection foundations.
Runnable today:
The item-generic helpers currently support these direct-backed scalar element
types: `Bool`, `u8`, `u16`, `usize`, `i32`, `u32`, `i64`, and `u64`.
| API | Return | Notes |
| --- | --- | --- |
| `std.mem.copy(dst, src)` | `usize` | Copies from `Span<u8>` into caller-owned `MutSpan<u8>` storage and returns the copied byte count. |
| `std.mem.copyItems(dst, src)` | `usize` | Copies matching scalar `Span<T>` items into caller-owned mutable item storage and returns the copied item count. |
| `std.mem.fill(dst, value)` | `usize` | Fills caller-owned `MutSpan<u8>` storage with a `u8` byte and returns the filled byte count. |
| `std.mem.fillItems(dst, value)` | `usize` | Fills caller-owned mutable scalar item storage with a matching `T` value and returns the filled item count. |
| `std.mem.eql(a, b)` | `Bool` | Compares string-backed byte inputs. |
| `std.mem.span(value)` | `Span<u8>` | Builds a native `Span<u8>` view over a string literal. |
| `std.mem.contains(items, needle)` | `Bool` | Searches readable contiguous non-owned scalar `T` storage for a matching value. |
| `std.mem.compareI32(left, right)` | `i32` | Lexicographically compares two `Span<i32>` values and returns `-1`, `0`, or `1`. |
| `std.mem.compareU8(left, right)` | `i32` | Lexicographically compares two `Span<u8>` values and returns `-1`, `0`, or `1`. |
| `std.mem.compareBytes(left, right)` | `i32` | Alias-style byte lexicographic comparison for `Span<u8>` values. |
| `std.mem.compareU32(left, right)` | `i32` | Lexicographically compares two `Span<u32>` values and returns `-1`, `0`, or `1`. |
| `std.mem.compareUsize(left, right)` | `i32` | Lexicographically compares two `Span<usize>` values and returns `-1`, `0`, or `1`. |
| `std.mem.startsWith(items, prefix)` | `Bool` | Checks whether a scalar span begins with a matching prefix span. |
| `std.mem.endsWith(items, suffix)` | `Bool` | Checks whether a scalar span ends with a matching suffix span. |
| `std.mem.splitBefore(items, delimiter)` | `Span<T>` | Returns the read-only scalar item prefix before the first delimiter, or the full span when absent. |
| `std.mem.splitAfter(items, delimiter)` | `Span<T>` | Returns the read-only scalar item suffix after the first delimiter, or an empty span when absent. |
| `std.mem.isEmpty(items)` | `Bool` | Reports whether readable contiguous scalar item storage has zero items. |
| `std.mem.chunkCount(items, chunkSize)` | `usize` | Returns the number of non-overlapping chunks needed to cover the span, or `0` when `chunkSize` is zero. |
| `std.mem.chunk(items, chunkIndex, chunkSize)` | `Span<T>` | Returns a clamped read-only scalar item chunk by zero-based chunk index. |
| `std.mem.windowCount(items, windowSize)` | `usize` | Returns the number of overlapping fixed-size windows available in a span, or `0` when the size is zero or larger than the span. |
| `std.mem.window(items, windowIndex, windowSize)` | `Span<T>` | Returns an overlapping read-only scalar item window by zero-based window index. |
| `std.mem.advance(items, cursor, count)` | `usize` | Returns a clamped cursor advanced by at most `count` items. |
| `std.mem.cursorDone(items, cursor)` | `Bool` | Reports whether a cursor is at or past the end of a span. |
| `std.mem.remaining(items, cursor)` | `Span<T>` | Returns the clamped read-only scalar item view from `cursor` to the end. |
| `std.mem.cursorChunk(items, cursor, count)` | `Span<T>` | Returns a clamped read-only scalar item window beginning at `cursor`. |
| `std.mem.prefix(items, count)` | `Span<T>` | Returns a clamped read-only scalar item prefix view. |
| `std.mem.dropPrefix(items, count)` | `Span<T>` | Returns a clamped read-only scalar item view after the first `count` items. |
| `std.mem.suffix(items, count)` | `Span<T>` | Returns a clamped read-only scalar item suffix view. |
| `std.mem.dropSuffix(items, count)` | `Span<T>` | Returns a clamped read-only scalar item view before the last `count` items. |
| `std.mem.slice(items, start, count)` | `Span<T>` | Returns a clamped read-only scalar item window beginning at `start`. |
| `std.mem.len(bytes)` | `usize` | Returns the length of a fixed array, `Span<T>`, or `MutSpan<T>`. |
| `std.mem.get(bytes, index)` | `Maybe<T>` | Reads one indexed element from an array/span-like value when the index is in bounds. |
| `std.mem.eqlBytes(a, b)` | `Bool` | Compares two `Span<T>`/`MutSpan<T>` values with the same element type. |
| `std.mem.nullAlloc()` | `NullAlloc` | Creates an allocator that always returns `null`, useful for proving code does not allocate. |
| `std.mem.fixedBufAlloc(buffer)` | `FixedBufAlloc` | Creates a mutable fixed-buffer allocator from caller-owned `MutSpan<u8>` bytes. |
| `std.mem.arena(buffer)` | `FixedBufAlloc` | Arena-style alias over the fixed-buffer allocator model; `reset` rewinds the caller-owned storage. |
| `std.mem.pageAlloc()` | `PageAlloc` | Explicit host allocator handle metadata; it never creates an ambient global allocator. |
| `std.mem.generalAlloc()` | `GeneralAlloc` | Explicit general allocator handle metadata; callers still pass allocator state deliberately. |
| `std.mem.allocBytes(alloc, len)` | `Maybe<MutSpan<u8>>` | Allocates bytes from `NullAlloc` or a mutable `FixedBufAlloc` binding. |
| `std.mem.byteBuf(alloc, len)` | `Maybe<owned<ByteBuf>>` | Creates an owned byte buffer backed by explicit caller-provided allocator storage. |
| `std.mem.bufBytes(&buf)` | `MutSpan<u8>` | Borrows writable bytes from an owned `ByteBuf`. |
| `std.mem.bufLen(&buf)` | `usize` | Returns the live length of a `ByteBuf`. |
| `std.mem.reset(&mut arena)` | `Void` | Resets caller-owned arena/fixed-buffer allocation state. |
| `std.mem.capacity(arena)` | `usize` | Reports fixed-buffer capacity. |
| `std.mem.vec(storage)` | `Vec` | Monomorphic byte vector over caller-owned mutable storage. |
| `std.mem.vecPush(&mut vec, value)` | `Bool` | Appends one byte when capacity remains; returns `false` instead of growing implicitly. |
| `std.mem.vecBytes(&vec)` | `Span<u8>` | Borrows the live bytes in the vector without copying. |
| `std.mem.vecGet(&vec, index)` | `Maybe<u8>` | Reads one live vector byte when `index` is in bounds. |
| `std.mem.vecSet(&mut vec, index, value)` | `Bool` | Replaces one live vector byte when `index` is in bounds; returns `false` out of bounds. |
| `std.mem.vecClear(&mut vec)` | `usize` | Resets live length to zero and keeps caller-owned storage available for reuse. |
| `std.mem.vecPop(&mut vec)` | `Bool` | Removes one live byte when the vector is non-empty. |
| `std.mem.vecTruncate(&mut vec, len)` | `usize` | Shrinks the live length to at most `len` and returns the resulting length. |
| `std.mem.vecRemoveSwap(&mut vec, index)` | `Bool` | Removes one live byte by replacing it with the last live byte. Returns `false` out of bounds. |
| `std.mem.vecIndex(&vec, value)` | `usize` | Returns the first live index for `value`, or the current live length when absent. |
| `std.mem.vecContains(&vec, value)` | `Bool` | Reports whether a byte value is present in the live vector prefix. |
| `std.mem.vecInsertUnique(&mut vec, value)` | `Bool` | Appends `value` only when it is absent and capacity remains. |
| `std.mem.vecRemoveValue(&mut vec, value)` | `Bool` | Swap-removes the first matching live byte by value. |
| `std.mem.vecLen(&vec)` | `usize` | Reports current vector length. |
| `std.mem.vecCapacity(&vec)` | `usize` | Reports caller-provided vector capacity. |
| `std.mem.vecRemaining(&vec)` | `usize` | Reports remaining byte-vector capacity. Returns `0` when the vector is full. |
| `std.mem.vecIsEmpty(&vec)` | `Bool` | Reports whether the vector has no live bytes. |
| `std.mem.vecIsFull(&vec)` | `Bool` | Reports whether the vector has no remaining capacity. |
## Example
```zero
type SliceView {
bytes: Span<u8>,
values: Span<i32>,
}
pub fn main(world: World) -> Void raises {
let bytes: Span<u8> = std.mem.span("zero-memory")
let same: Span<u8> = std.mem.span("zero-memory")
var scratch: [11]u8 = [0_u8; 11]
let copied: usize = std.mem.copy(scratch, bytes)
var ints: [3]i32 = [1, 2, 3]
let intSpan: MutSpan<i32> = ints
let filled: usize = std.mem.fillItems(intSpan, 7)
let prefix: Span<i32> = std.mem.prefix(intSpan, 2)
let suffix: Span<i32> = std.mem.suffix(intSpan, 2)
let middle: Span<i32> = std.mem.slice(intSpan, 1, 1)
let before: Span<i32> = std.mem.splitBefore(intSpan, 7)
let after: Span<i32> = std.mem.splitAfter(intSpan, 7)
let chunk: Span<i32> = std.mem.chunk(intSpan, 1_usize, 2_usize)
let sliding: Span<i32> = std.mem.window(intSpan, 1_usize, 2_usize)
let cursor: usize = std.mem.advance(intSpan, 0_usize, 2_usize)
let rest: Span<i32> = std.mem.remaining(intSpan, cursor)
let view: SliceView = SliceView { bytes: bytes, values: intSpan }
let ordered: Bool = std.mem.compareI32(prefix, suffix) == 0
let starts: Bool = std.mem.startsWith(view.bytes, std.mem.span("zero"))
let ends: Bool = std.mem.endsWith(view.bytes, std.mem.span("memory"))
if copied == 11 && filled == 3 && ordered && starts && ends && std.mem.len(view.bytes) == 11 && std.mem.eqlBytes(view.bytes, same) && std.mem.len(view.values) == 3 && std.mem.contains(view.values, 7) && std.mem.isEmpty(before) && std.mem.len(after) == 2 && std.mem.len(prefix) == 2 && std.mem.len(suffix) == 2 && std.mem.len(middle) == 1 && std.mem.len(chunk) == 1 && std.mem.len(sliding) == 2 && std.mem.len(rest) == 1 && !std.mem.cursorDone(intSpan, cursor) {
check world.out.write("memory type forms runnable\n")
}
}
```
## Allocator Example
```zero
pub fn main(world: World) -> Void raises {
var storage: [8]u8 = [0, 0, 0, 0, 0, 0, 0, 0]
var alloc: FixedBufAlloc = std.mem.fixedBufAlloc(storage)
let bytes: Maybe<MutSpan<u8>> = std.mem.allocBytes(alloc, 4)
if bytes.has {
bytes.value[0] = 90
check world.out.write("fixed buffer allocated\n")
}
}
```
Effects: none beyond writes performed by caller code.
Allocation behavior:
- `NullAlloc` always returns `null`.
- `FixedBufAlloc` and `Arena` return `MutSpan<u8>` views into caller-owned
storage.
- `ByteBuf` owns a slice of explicit allocator storage and never reaches for a
global heap.
Use `std.mem.allocBytes(alloc, capacity)` when a byte-oriented API needs
allocator-backed mutable storage directly. `std.collections.fixedSet`,
`std.collections.fixedDeque`, and `std.collections.fixedMap` can use those
returned spans as caller-owned backing storage.
Ownership: returned spans borrow from the original fixed buffer; no heap
ownership is created.
Target support: current compiler targets. Direct native builds lower
`FixedBufAlloc` locals only; `PageAlloc`, `GeneralAlloc`, and `NullAlloc`
locals type-check but fail `zero build` with a `BLD004` diagnostic that points
back to `std.mem.fixedBufAlloc`.
## Reporting Contract
`zero mem --json [graph-input]` reports the allocator contract in machine-readable form:
- `memoryBudgets`: stack, static, heap, arena, fixed-buffer, collection-capacity, allocator-capacity, requested-allocation, and linear-memory floor budgets.
- `allocatorFacts`: `NullAlloc`, `FixedBufAlloc`, `Arena`, `PageAlloc`, and
`GeneralAlloc` usage, capacity, failure behavior, and
hidden-global-allocator status.
- `allocationInstrumentation`: pay-as-used hooks for attempts, successes, failures, requested bytes, granted bytes, and peak live bytes.
- `collectionFacts`: fixed-capacity `Vec`, fixed-storage `std.collections`
helpers, and owned `ByteBuf`, including growth/failure/cleanup behavior.
- `safetyFacts`: the selected profile plus the current bounds,
initialization, aliasing, lifetime, ownership, span, MIR, literal integer
range-check, runtime arithmetic, and unchecked-surface facts.
All heap budgets are explicit. A program that only uses `std.mem` remains at
`heapBytes: 0`, `globalHeapBytes: 0`, and `hiddenHeapAllocation: false` unless
an allocator API documents otherwise.
## Design Notes
No standard collection may silently allocate from a global heap. Heap-owning APIs
will require an allocator capability and document ownership, capacity, and
cleanup.
+47
View File
@@ -0,0 +1,47 @@
## When To Use std.net
In Zerolang, use `std.net` for network capability metadata, local address construction,
timeouts, and bootstrap client/listener handles.
Runnable today:
| API | Return | Notes |
| --- | --- | --- |
| `std.net.host()` | `Net` | Creates the hosted network capability. |
| `std.net.address(host, port)` | `Address` | Builds an address value without allocation. |
| `std.net.dnsName(address)` | `String` | Reads the address host name. |
| `std.net.withTimeout(address, duration)` | `Address` | Returns address metadata with a timeout. |
| `std.net.localhost(port)` | `Address` | Builds a `localhost` address with the requested port. |
| `std.net.loopback(port)` | `Address` | Builds a `127.0.0.1` loopback address with the requested port. |
| `std.net.connect(net, address)` | `Maybe<Conn>` | Returns a bootstrap connection handle when available. |
| `std.net.listen(net, address)` | `Maybe<Listener>` | Returns a bootstrap listener handle when available. |
Metadata labels:
- effects: net
- allocation behavior: no allocation
- target support: address helpers are target-neutral; host/connect/listen require a net-capable target
- error behavior: connection helpers return `Maybe`
- ownership notes: no stream ownership transfer in the current handle model
- example: `conformance/native/pass/std-net-http-breadth.graph`
## Example
```zero
pub fn main(world: World) -> Void raises {
let net: Net = std.net.host()
let addr: Address = std.net.withTimeout(std.net.localhost(8080_u16), std.time.ms(250))
let loopback: Address = std.net.loopback(8080_u16)
let conn: Maybe<Conn> = std.net.connect(net, addr)
if conn.has && std.mem.eql(std.net.dnsName(addr), "localhost") && std.mem.eql(std.net.dnsName(loopback), "127.0.0.1") {
check world.out.write("net ok\n")
}
}
```
## Design Notes
`std.net` exposes network capability metadata and bootstrap handles. Current
fixtures expect connection and listener handles to be absent. It does not
provide socket read/write APIs in the current public surface. Outbound HTTP is
exposed through `std.http.fetch(...)` rather than through raw sockets.
+78
View File
@@ -0,0 +1,78 @@
## When To Use std.parse
In Zerolang, use `std.parse` for allocation-free byte scanners and scalar parsers.
Runnable today:
| API | Return | Notes |
| --- | --- | --- |
| `std.parse.isAsciiDigit(value)` | `Bool` | Checks whether the first byte is `0` through `9`. |
| `std.parse.isAsciiAlpha(value)` | `Bool` | Checks whether the first byte is ASCII alphabetic. |
| `std.parse.isIdentifierStart(value)` | `Bool` | Checks whether the first byte can start a Zero identifier. |
| `std.parse.isWhitespace(value)` | `Bool` | Checks ASCII whitespace. |
| `std.parse.scanDigits(value)` | `usize` | Counts a leading run of ASCII digits. |
| `std.parse.scanIdentifier(value)` | `usize` | Counts a leading identifier token. |
| `std.parse.scanWhitespace(value)` | `usize` | Counts leading spaces, tabs, line feeds, and carriage returns. |
| `std.parse.scanUntilByte(value, byte)` | `usize` | Returns the first matching byte index or the input length. |
| `std.parse.tokenAscii(value)` | `Span<u8>` | Borrows the first non-whitespace ASCII token. |
| `std.parse.parseBool(value)` | `Maybe<Bool>` | Parses `true` or `false`. |
| `std.parse.parseDuration(value)` | `Maybe<Duration>` | Parses signed `ns`, `us`, `ms`, `s`, `m`, and `h` duration components such as `1h30m`. |
| `std.parse.parseByteSize(value)` | `Maybe<usize>` | Parses bytes plus `KB`, `MB`, `GB`, `KiB`, `MiB`, and `GiB` suffixes. |
| `std.parse.parseI32(value)` | `Maybe<i32>` | Parses a full signed 32-bit decimal value. |
| `std.parse.parseI32Base(value, base)` | `Maybe<i32>` | Parses a full signed 32-bit value in base 2 through 36. |
| `std.parse.parseI32Prefix(value)` | `Maybe<i32>` | Parses signed decimal, `0x`, `0o`, or `0b` input. |
| `std.parse.parseI64(value)` | `Maybe<i64>` | Parses a full signed 64-bit decimal value. |
| `std.parse.parseI64Base(value, base)` | `Maybe<i64>` | Parses a full signed 64-bit value in base 2 through 36. |
| `std.parse.parseI64Prefix(value)` | `Maybe<i64>` | Parses signed decimal, `0x`, `0o`, or `0b` input. |
| `std.parse.parseU8(value)` | `Maybe<u8>` | Parses a full decimal byte value. |
| `std.parse.parseU16(value)` | `Maybe<u16>` | Parses a full decimal unsigned 16-bit value. |
| `std.parse.parseU32(value)` | `Maybe<u32>` | Parses a full decimal unsigned 32-bit value. |
| `std.parse.parseU32Base(value, base)` | `Maybe<u32>` | Parses a full unsigned 32-bit value in base 2 through 36. |
| `std.parse.parseU32Prefix(value)` | `Maybe<u32>` | Parses unsigned decimal, `0x`, `0o`, or `0b` input. |
| `std.parse.parseU64(value)` | `Maybe<u64>` | Parses a full decimal unsigned 64-bit value. |
| `std.parse.parseU64Base(value, base)` | `Maybe<u64>` | Parses a full unsigned 64-bit value in base 2 through 36. |
| `std.parse.parseU64Prefix(value)` | `Maybe<u64>` | Parses unsigned decimal, `0x`, `0o`, or `0b` input. |
| `std.parse.parseUsize(value)` | `Maybe<usize>` | Parses a full decimal `usize` value. |
| `std.parse.parseUsizeBase(value, base)` | `Maybe<usize>` | Parses a full `usize` value in base 2 through 36. |
| `std.parse.parseUsizePrefix(value)` | `Maybe<usize>` | Parses `usize` decimal, `0x`, `0o`, or `0b` input. |
Current limits:
- Source position and span types.
- Rich cursor objects beyond the current allocation-free scanner primitives.
- Token and diagnostic data shared by language and data parsers.
## Example
```zero
use std.parse
pub fn main(world: World) -> Void raises {
let digit: Bool = std.parse.isAsciiDigit("7")
let ident: Bool = std.parse.isIdentifierStart("_")
let scanned: usize = std.parse.scanDigits("123abc")
let parsed: Maybe<u16> = std.parse.parseU16("8080")
let signed: Maybe<i32> = std.parse.parseI32Prefix("-0x2a")
let signed64: Maybe<i64> = std.parse.parseI64("-9223372036854775808")
let hex: Maybe<u32> = std.parse.parseU32Base("ff", 16_u32)
let hex64: Maybe<u64> = std.parse.parseU64Prefix("0xffffffffffffffff")
let duration: Maybe<Duration> = std.parse.parseDuration("1h30m")
let size: Maybe<usize> = std.parse.parseByteSize("2MiB")
let token: Span<u8> = std.parse.tokenAscii(" zero text")
if digit && ident && scanned == 3 && parsed.has && parsed.value == 8080 && signed.has && signed.value == -42 && signed64.has && signed64.value == 0 - 9223372036854775807 - 1 && hex.has && hex.value == 255_u32 && hex64.has && hex64.value == 18446744073709551615_u64 && duration.has && std.time.asSecondsFloor(duration.value) == 5400_i64 && size.has && size.value == 2097152 && std.mem.eql(token, "zero") {
check world.out.write("parse primitives ok\n")
}
}
```
## Design Notes
The module stays byte-oriented so compiler, config, and codec code can parse
without Unicode scalar handling or heap allocation. Public helpers accept byte
spans, so callers can parse string literals, fixed arrays, and runtime buffers.
Integer parsers return `Maybe<T>` instead of allocating diagnostics. Base parsers
accept bases 2 through 36, consume the full input, and reject overflow. Prefix
parsers recognize decimal by default plus `0x`, `0o`, and `0b` for 32-bit,
64-bit, and `usize` widths. Duration and byte-size parsers also consume the
full input and reject overflow.
+51
View File
@@ -0,0 +1,51 @@
## When To Use std.path
In Zerolang, use `std.path` for lexical path operations that borrow from input
paths or write into caller-owned buffers.
Runnable today:
| API | Return | Notes |
| --- | --- | --- |
| `std.path.basename(path)` | `String` | Borrows the final lexical component of `path`. |
| `std.path.dirname(path)` | `String` | Borrows or returns the lexical parent portion of `path`. |
| `std.path.extension(path)` | `String` | Borrows the suffix after the last `.` in the final component. |
| `std.path.stem(path)` | `String` | Borrows the final component without its extension. |
| `std.path.splitDir(path)` | `String` | Borrows the directory side of the final split. |
| `std.path.splitBase(path)` | `String` | Borrows the basename side of the final split. |
| `std.path.isAbs(path)` | `Bool` | Returns true for paths that begin with a path separator. |
| `std.path.componentCount(path)` | `usize` | Counts non-empty lexical path components. |
| `std.path.component(path, index)` | `Maybe<String>` | Borrows one non-empty lexical component by index. |
| `std.path.abs(buffer, base, target)` | `Maybe<String>` | Copies `target` when already absolute, or joins `base` and `target` into caller storage. |
| `std.path.join(buffer, left, right)` | `Maybe<String>` | Joins two path fragments into caller-provided fixed buffer storage. |
| `std.path.normalize(buffer, path)` | `Maybe<String>` | Collapses repeated `/`, `.`, and lexical `..` segments into caller-provided storage. |
| `std.path.relative(buffer, base, target)` | `Maybe<String>` | Produces a target-relative lexical path when possible, or copies `target`. |
Current scope:
- Helpers are target-neutral lexical operations over `/` and `\` separators.
- Buffer-writing helpers return `null` when caller storage is too small.
- The module does not implement platform-specific path rules, drive prefixes, or filesystem access.
## Example
```zero
pub fn main(world: World) -> Void raises {
var storage: [64]u8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
let path: Maybe<String> = std.path.join(storage, ".zero", "example.txt")
if path.has {
check world.out.write(path.value)
check world.out.write("\n")
}
}
```
## Design Notes
`std.path.basename`, `dirname`, `extension`, `stem`, `splitDir`, `splitBase`,
and `component` return borrowed views into the input path. `std.path.abs`,
`join`, `normalize`, and `relative` write into caller storage and return `null`
when the buffer is too small. They do not allocate.
The current behavior uses `/` as the portable package/example separator. These
helpers are lexical string helpers, not target-specific filesystem resolvers.
+122
View File
@@ -0,0 +1,122 @@
## When To Use std.proc
In Zerolang, use `std.proc` for hosted process helpers behind explicit process
capability boundaries. The surface is host-only and supports both status-style
helpers and owned child handles for incremental I/O.
Runnable today:
| API | Return | Notes |
| --- | --- | --- |
| `std.proc.spawn(command)` | `ProcStatus` | Runs an argv-style command with inherited stdio through the explicit proc capability surface and returns its status. |
| `std.proc.spawnInherit(command)` | `ProcStatus` | Runs an argv-style command while inheriting stdin, stdout, and stderr from the parent process. |
| `std.proc.spawnInheritArgs(program, args, cwd, env)` | `ProcStatus` | Runs a program path plus newline-separated argv entries while inheriting stdio, using a working directory and newline-separated `KEY=value` environment bindings. |
| `std.proc.exitCode(status)` | `i32` | Reads the process status code. |
| `std.proc.succeeded(status)` | `Bool` | Reports whether the status exit code is `0`. |
| `std.proc.failed(status)` | `Bool` | Reports whether the status exit code is nonzero. |
| `std.proc.runOk(command)` | `Bool` | Spawns a hosted command and reports whether the resulting status succeeded. |
| `std.proc.runCode(command)` | `i32` | Spawns a hosted command and returns its exit code. |
| `std.proc.capture(command, buffer)` | `Maybe<usize>` | Runs an argv-style command and captures stdout into caller storage. Returns `null` on parse failure, spawn failure, nonzero exit, unsupported target, or output truncation. |
| `std.proc.captureArgs(program, args, buffer)` | `Maybe<usize>` | Runs a program path plus newline-separated argv entries and captures stdout into caller storage. |
| `std.proc.captureFiles(command, stdoutPath, stderrPath)` | `ProcStatus` | Runs an argv-style command and writes stdout and stderr to hosted paths. Returns `127` when the command cannot be parsed, spawned, waited on, or the output files cannot be opened. |
| `std.proc.captureFilesArgs(program, args, stdoutPath, stderrPath)` | `ProcStatus` | Runs a program path plus newline-separated argv entries and redirects stdout and stderr to hosted paths. |
| `std.proc.spawnChild(command)` | `ProcChild` | Starts a hosted child process with piped stdin, stdout, and stderr. Returns an invalid handle when the process cannot be created. |
| `std.proc.spawnChildIn(command, cwd)` | `ProcChild` | Starts a hosted child process in a working directory with piped stdin, stdout, and stderr. Returns an invalid handle when the cwd is invalid or the process cannot be created. |
| `std.proc.spawnChildInEnv(command, cwd, env)` | `ProcChild` | Starts a hosted child process in a working directory with piped stdin/stdout/stderr and explicit newline-separated `KEY=value` environment bindings. |
| `std.proc.spawnChildArgs(program, args, cwd, env)` | `ProcChild` | Starts a hosted child process from a program path plus newline-separated argv entries, working directory, and newline-separated `KEY=value` environment bindings. |
| `std.proc.childValid(child)` | `Bool` | Reports whether the handle currently names an open child slot. |
| `std.proc.running(child)` | `Bool` | Polls the child process without blocking. |
| `std.proc.wait(child)` | `ProcStatus` | Waits for process exit and returns its status. |
| `std.proc.kill(child)` | `Bool` | Sends the child a termination signal on supported hosts. |
| `std.proc.interrupt(child)` | `Bool` | Sends the child an interrupt signal on supported hosts. |
| `std.proc.close(child)` | `Bool` | Closes the handle and any remaining pipes. |
| `std.proc.closeStdin(child)` | `Bool` | Closes the child stdin pipe while keeping stdout, stderr, and status available. |
| `std.proc.pid(child)` | `i32` | Returns the hosted process id for a child handle, or `0` when unavailable. |
| `std.proc.pidRunning(pid)` | `Bool` | Reports whether a hosted process id appears to be running. |
| `std.proc.killPid(pid)` | `Bool` | Sends a termination signal to a hosted process id on supported hosts. |
| `std.proc.interruptPid(pid)` | `Bool` | Sends an interrupt signal to a hosted process id on supported hosts. |
| `std.proc.killGroupPid(pid)` | `Bool` | Sends a termination signal to the hosted process group whose id is `pid` on supported hosts. |
| `std.proc.interruptGroupPid(pid)` | `Bool` | Sends an interrupt signal to the hosted process group whose id is `pid` on supported hosts. |
| `std.proc.readStdout(child, buffer)` | `Maybe<usize>` | Nonblocking read from the child's stdout pipe into caller storage. Returns `null` when no bytes are currently available or the stream is closed. |
| `std.proc.readStderr(child, buffer)` | `Maybe<usize>` | Nonblocking read from the child's stderr pipe into caller storage. Returns `null` when no bytes are currently available or the stream is closed. |
| `std.proc.writeStdin(child, bytes)` | `Maybe<usize>` | Nonblocking write to the child's stdin pipe. Returns `null` when the stream is not writable. |
Metadata labels:
- effects: proc
- allocation behavior: child handles may keep runtime-owned pipe buffers while
`wait` drains stdout, stderr, or PTY output
- target support: hosted targets with the `proc` capability
- error behavior: `spawn`, `spawnInherit`, `captureFiles`, and `wait` return `ProcStatus`; `exitCode` is infallible; `capture` and child I/O helpers return `null` when they cannot produce a complete result
- ownership notes: `ProcChild` values name runtime-owned process slots; call `wait` when process status matters and `close` when the handle is no longer needed
- example: `examples/std-platform.graph`
## Example
```zero
pub fn main(world: World) -> Void raises {
let status: ProcStatus = std.proc.spawn("zero-noop")
var storage: [64]u8 = [0_u8; 64]
let captured: Maybe<usize> = std.proc.capture("printf proc-capture", storage)
let files: ProcStatus = std.proc.captureFiles("sh -c 'printf proc-out; printf proc-err >&2'", ".zero/proc.out", ".zero/proc.err")
if std.proc.succeeded(status) && std.proc.succeeded(files) && std.proc.runOk("sh -c true") && std.proc.runCode("sh -c true") == 0 && captured.has {
check world.out.write("proc ok\n")
}
}
```
Incremental child I/O uses caller-owned buffers:
```zero
pub fn main(world: World) -> Void raises {
let child: ProcChild = std.proc.spawnChild("zero-noop")
var stdoutStorage: [64]u8 = [0_u8; 64]
let out: Maybe<usize> = std.proc.readStdout(child, stdoutStorage)
if out.has {
check world.out.write("child stdout available\n")
}
let pid: i32 = std.proc.pid(child)
let status: ProcStatus = std.proc.wait(child)
let closed: Bool = std.proc.close(child)
if pid > 0 && std.proc.succeeded(status) && closed {
check world.out.write("child ok\n")
}
}
```
## Design Notes
`std.proc` requires a hosted target that advertises the `proc` capability.
Targets without process support, including Windows hosts until their process
runtime is implemented, must reject process helpers before code generation.
They should not compile a placeholder process implementation.
`capture` and `captureFiles` do not invoke a shell. They split simple
argv-style command text and support quoted arguments. `capture` captures stdout
only into caller storage. `captureFiles` redirects stdout and stderr to
separate hosted paths.
`spawnInherit` uses the same argv-style parser and leaves stdin, stdout, and
stderr connected to the parent process. `spawnInheritArgs` uses an explicit
program plus newline-separated argv entries, working directory, and environment
block. Use inherited stdio for editor, pager, and terminal program launches
where captured pipes would be the wrong interface.
Child handles use the same command parser when created from command text. The
`*Args` helpers avoid command-text parsing: `program` is argv[0], and each
non-empty line in `args` becomes one following argv entry with spaces preserved.
`captureArgs` captures stdout into caller storage, `captureFilesArgs` redirects
stdout and stderr to hosted paths, and `spawnChildArgs` returns nonblocking
pipes so event loops can poll process state and terminal input without owning
threads. `wait` drains child output into the handle before reaping so post-wait
`readStdout` and `readStderr` calls can still consume buffered data.
Hosted child helpers start children in their own process group where the target
platform supports it. Use `killGroupPid` or `interruptGroupPid` when a stored
pid should stop a command tree instead of only the direct parent process.
`spawnChildInEnv`, `spawnInheritArgs`, and `spawnChildArgs` accept a
newline-separated env block such as `"TOKEN=...\nMODE=batch"`. Empty lines are
ignored. Invalid entries or oversized env blocks make the helper fail: status
helpers return an error status and child helpers return an invalid handle.
+78
View File
@@ -0,0 +1,78 @@
## When To Use std.pty
In Zerolang, use `std.pty` for hosted child processes that need terminal
semantics instead of plain pipes. PTY children are useful for interactive CLIs,
REPLs, shells, editors, pagers, and tools that change behavior when stdout is a
terminal.
Runnable today:
| API | Return | Notes |
| --- | --- | --- |
| `std.pty.spawn(command)` | `ProcChild` | Starts an argv-style command attached to a pseudoterminal. |
| `std.pty.spawnIn(command, cwd)` | `ProcChild` | Starts a PTY child in a hosted working directory. |
| `std.pty.spawnInEnv(command, cwd, env)` | `ProcChild` | Starts a PTY child with a working directory and newline-separated `KEY=value` environment bindings. |
| `std.pty.spawnArgs(program, args, cwd, env)` | `ProcChild` | Starts a program path plus newline-separated argv entries attached to a pseudoterminal. |
| `std.pty.valid(child)` | `Bool` | Reports whether the handle currently names an open child slot. |
| `std.pty.childValid(child)` | `Bool` | Alias for `std.pty.valid`. |
| `std.pty.running(child)` | `Bool` | Polls the PTY child without blocking. |
| `std.pty.wait(child)` | `ProcStatus` | Waits for process exit and returns its status. |
| `std.pty.kill(child)` | `Bool` | Sends the child a termination signal on supported hosts. |
| `std.pty.interrupt(child)` | `Bool` | Sends the child an interrupt signal on supported hosts. |
| `std.pty.close(child)` | `Bool` | Closes the handle and remaining terminal resources. |
| `std.pty.pid(child)` | `i32` | Returns the hosted process id for the child, or `0` when unavailable. |
| `std.pty.read(child, buffer)` | `Maybe<usize>` | Nonblocking read from the PTY master into caller storage. |
| `std.pty.write(child, bytes)` | `Maybe<usize>` | Nonblocking write to the PTY master. |
| `std.pty.resize(child, columns, rows)` | `Bool` | Sets the child terminal size on supported hosts. |
Metadata labels:
- effects: proc
- allocation behavior: child handles may keep runtime-owned PTY output buffers while `wait` drains terminal output
- target support: hosted targets with the `proc` capability
- error behavior: spawn helpers return an invalid `ProcChild` handle on failure; `read` and `write` return `null` when no bytes can be transferred
- ownership notes: `ProcChild` values name runtime-owned process slots; call `wait` when process status matters and `close` when the handle is no longer needed
## Example
```zero
pub fn main(world: World) -> Void raises {
let child: ProcChild = std.pty.spawnArgs("printf", "hello pty\n", ".", "")
let resized: Bool = std.pty.resize(child, 80_usize, 24_usize)
var storage: [64]u8 = [0_u8; 64]
var saw_output: Bool = false
var attempts: usize = 0
while attempts < 20_usize && !saw_output {
let read: Maybe<usize> = std.pty.read(child, storage)
if read.has {
let bytes: Span<u8> = std.io.written(storage, read.value)
saw_output = std.str.contains(bytes, "hello pty")
}
if !saw_output {
let slept: Bool = std.time.sleep(std.time.ms(10))
}
attempts = attempts + 1_usize
}
let status: ProcStatus = std.pty.wait(child)
if resized && saw_output && std.proc.succeeded(status) {
check world.out.write("pty ok\n")
}
let closed: Bool = std.pty.close(child)
}
```
## Design Notes
`std.pty` returns the same `ProcChild` handle shape used by `std.proc`, but the
underlying child is connected to a pseudoterminal master. PTY output is a single
terminal byte stream: stderr is merged by the terminal, and `std.pty.read`
reads from that stream.
Targets without process support, including Windows hosts until their process
runtime is implemented, reject PTY helpers before code generation.
For short-lived programs, drain the PTY with `std.pty.read` before `wait`; some
hosts report the terminal as closed once the child exits.
Use `std.proc.spawnChild*` for programs where separate stdout and stderr pipes
matter. Use `std.pty.spawn*` for programs where terminal behavior matters.
+50
View File
@@ -0,0 +1,50 @@
## When To Use std.rand
In Zerolang, use `std.rand` for deterministic random sources and target-gated entropy.
Runnable today:
| API | Return | Notes |
| --- | --- | --- |
| `std.rand.seed(value)` | `RandSource` | Creates a deterministic test source. |
| `std.rand.nextU32(&mut source)` | `u32` | Advances an explicit random source. |
| `std.rand.nextBool(&mut source)` | `Bool` | Advances an explicit random source and returns one random bit. |
| `std.rand.nextBelow(&mut source, bound)` | `Maybe<u32>` | Returns a value in `[0, bound)` using rejection sampling, or null when bound is zero. |
| `std.rand.rangeU32(&mut source, low, high)` | `Maybe<u32>` | Returns a value in `[low, high)` using rejection sampling, or null for an empty range. |
| `std.rand.entropyU32()` | `u32` | Reads target entropy where the target provides it. |
| `std.rand.entropySeed()` | `RandSource` | Creates a `RandSource` from target entropy where available. |
| `std.rand.entropyHex32(buffer)` | `Maybe<Span<u8>>` | Writes an 8-byte lowercase entropy ID into caller storage. |
Metadata labels:
- effects: rand
- allocation behavior: no allocation; `entropyHex32` writes caller-provided storage
- target support: deterministic source is target-neutral; entropy requires a rand-capable target
- error behavior: bounded helpers return null for invalid bounds; `entropyHex32` returns null when storage is too small
- ownership notes: deterministic helpers mutate the caller-owned source
- example: `examples/std-platform.graph`
## Example
```zero
pub fn main(world: World) -> Void raises {
var rng: RandSource = std.rand.seed(7_u32)
let first: u32 = std.rand.nextU32(&mut rng)
let second: Bool = std.rand.nextBool(&mut rng)
let bounded: Maybe<u32> = std.rand.nextBelow(&mut rng, 10_u32)
let ranged: Maybe<u32> = std.rand.rangeU32(&mut rng, 40_u32, 50_u32)
var id_buf: [8]u8 = [0_u8; 8]
let entropy_id: Maybe<Span<u8>> = std.rand.entropyHex32(id_buf)
if first == 1025555898_u32 && second && bounded.has && ranged.has && entropy_id.has {
check world.out.write("rand ok\n")
}
}
```
## Design Notes
Zero keeps random sources explicit. Deterministic tests use `std.rand.seed`;
bounded helpers use rejection sampling so the range is not modulo-biased.
Caller-facing IDs use `entropyHex32` when target entropy is available.
Production entropy stays target-capability-gated.
+100
View File
@@ -0,0 +1,100 @@
## When To Use std.regex
In Zerolang, use `std.regex` to match text against a documented ECMA-262-leaning
regular expression subset, such as JSON Schema `pattern` checks.
Supported syntax: literals, `.`, character classes with negation, ranges, and
`\d \D \w \W \s \S`, anchors `^` `$` and word boundaries `\b` `\B`, greedy
quantifiers `*` `+` `?` `{m}` `{m,}` `{m,n}`, alternation `|`, and capturing or
`(?:...)` non-capturing groups (matching only; no capture extraction). Matching
is by Unicode codepoint over UTF-8 text and searches anywhere in the text unless
the pattern is anchored, like ECMAScript `RegExp.prototype.test`. When multiple
matches start at the same byte, span-returning helpers use the longest end
position, so `a|ab` finds `ab` in `ab`.
Unsupported constructs never misparse silently. Compilation fails with a
structured status code: `1` backreference, `2` lookahead, `3` lookbehind,
`4` named group, `5` lazy quantifier, `6` group modifier or inline flags,
`7` unicode property escape, `8` invalid syntax, `9` invalid quantifier range,
`10` program over the buffer or 2048-byte limit, `11` pattern is not valid
UTF-8, `12` group nesting over depth 32.
Runnable today:
| API | Return | Notes |
| --- | --- | --- |
| `std.regex.compile(buffer, pattern)` | `Maybe<Span<u8>>` | Compiles a pattern into a caller-owned buffer; returns the compiled program span or `null` on any compile failure. |
| `std.regex.compileStatus(buffer, pattern)` | `u32` | Compiles and returns `0` or the structured status code for diagnostics. |
| `std.regex.compileErrorOffset(buffer, pattern)` | `Maybe<usize>` | Returns the pattern byte offset for a compile failure, or `null` when the pattern compiles. |
| `std.regex.statusName(status)` | `String` | Names a status code, such as `unsupported backreference`. |
| `std.regex.isMatch(program, text)` | `Bool` | Tests text against a compiled program. Compile once, then match many times. |
| `std.regex.matches(pattern, text)` | `Maybe<Bool>` | One-shot compile and match with an internal 1024-byte program buffer; returns `null` when the pattern does not compile. |
| `std.regex.contains(pattern, text)` | `Maybe<Bool>` | Alias-shaped one-shot search helper; returns `null` when the pattern does not compile. |
| `std.regex.findIndex(pattern, text)` | `Maybe<usize>` | Returns the first matching byte index, the input length when absent, or `null` when the pattern does not compile. |
| `std.regex.find(pattern, text)` | `Maybe<Span<u8>>` | Borrows the first matching span, or returns `null` when absent or invalid. |
| `std.regex.findCount(pattern, text)` | `Maybe<usize>` | Counts non-overlapping matches, or returns `null` when the pattern does not compile. |
| `std.regex.findNth(pattern, text, index)` | `Maybe<Span<u8>>` | Borrows the zero-based non-overlapping match at `index`, or returns `null` when absent or invalid. |
| `std.regex.findNthIndex(pattern, text, index)` | `Maybe<usize>` | Returns the byte index of the zero-based non-overlapping match, the input length when absent, or `null` when invalid. |
| `std.regex.replace(buffer, pattern, text, replacement)` | `Maybe<Span<u8>>` | Replaces non-overlapping matches with literal replacement bytes into caller storage. |
| `std.regex.splitCount(pattern, text)` | `Maybe<usize>` | Counts fields separated by non-empty regex matches, or returns `null` when the pattern does not compile. |
| `std.regex.split(pattern, text, index)` | `Maybe<Span<u8>>` | Borrows the zero-based field separated by non-empty regex matches, or returns `null` when absent or invalid. |
## Example
```zero
pub fn main(world: World) -> Void raises {
var storage: [512]u8 = [0; 512]
let buffer: MutSpan<u8> = storage
let compiled: Maybe<Span<u8>> = std.regex.compile(buffer, "^[a-z]+-\\d{2,4}$")
if !compiled.has {
return
}
let program: Span<u8> = compiled.value
let quick: Maybe<Bool> = std.regex.matches("^(cat|dog)s?$", "dogs")
let first: Maybe<Span<u8>> = std.regex.find("\\d+", "build-2048")
let second: Maybe<Span<u8>> = std.regex.findNth("\\d+", "a1 b22 c333", 1)
var replaced_storage: [16]u8 = [0; 16]
let replaced: Maybe<Span<u8>> = std.regex.replace(replaced_storage, "\\d+", "a1 b22", "#")
let fields: Maybe<usize> = std.regex.splitCount("[,;]", "red,green;blue")
let middle: Maybe<Span<u8>> = std.regex.split("[,;]", "red,green;blue", 1)
if std.regex.isMatch(program, "build-2048") && !std.regex.isMatch(program, "build-1") && (quick.has && quick.value) && first.has && std.mem.eql(first.value, "2048") && second.has && std.mem.eql(second.value, "22") && replaced.has && std.mem.eql(replaced.value, "a# b#") && fields.has && fields.value == 3 && middle.has && std.mem.eql(middle.value, "green") {
check world.out.write("regex ok\n")
}
}
```
Diagnosing a rejected pattern:
```zero
pub fn main(world: World) -> Void raises {
var storage: [128]u8 = [0; 128]
let buffer: MutSpan<u8> = storage
let status: u32 = std.regex.compileStatus(buffer, "(?=lookahead)")
let offset: Maybe<usize> = std.regex.compileErrorOffset(buffer, "(?=lookahead)")
if status != 0 {
check world.out.write(std.regex.statusName(status))
check world.out.write("\n")
}
}
```
Effects: writes to caller-provided mutable storage for `compile`,
`compileStatus`, `compileErrorOffset`, and `replace`; other helpers only borrow
input spans or return scalar results.
Allocation behavior: `compile`, `compileStatus`, and `compileErrorOffset` write
the caller program buffer. `replace` writes the caller output buffer. One-shot
search, split, and match helpers use fixed internal program storage and allocate
nothing on the heap.
Error behavior: `compile` returns `null`, `compileStatus` returns a status code
naming the construct, and `compileErrorOffset` returns the byte offset for a
failed compile. One-shot helpers return `null` for invalid patterns; `isMatch`
returns `false` for malformed program spans or invalid UTF-8 text.
`find`, `findNth`, `replace`, `split`, and their index/count variants use the
leftmost start and longest end for each match. `split` and `splitCount` use
non-empty regex matches as separators. Zero-length matches are ignored as
separators so callers get deterministic field traversal without a cursor object.
Target support: current compiler targets.
+104
View File
@@ -0,0 +1,104 @@
## When To Use std.search
In Zerolang, use `std.search` for scalar span lookup and binary-search helpers
over sorted caller-owned data.
Runnable today:
| API | Return | Notes |
| --- | --- | --- |
| `std.search.indexOf(items, needle)` | `usize` | Returns the first matching index or `std.mem.len(items)` when absent. |
| `std.search.lastIndexOf(items, needle)` | `usize` | Returns the last matching index or `std.mem.len(items)` when absent. |
| `std.search.lowerBoundI32(items, needle)` | `usize` | Returns the insertion point in sorted `Span<i32>` input. |
| `std.search.upperBoundI32(items, needle)` | `usize` | Returns the insertion point after existing equal values in sorted `Span<i32>` input. |
| `std.search.binaryI32(items, needle)` | `usize` | Returns the matching sorted `Span<i32>` index or `std.mem.len(items)` when absent. |
| `std.search.containsSortedI32(items, needle)` | `Bool` | Checks whether sorted `Span<i32>` input contains the needle. |
| `std.search.countSortedI32(items, needle)` | `usize` | Counts equal values in sorted `Span<i32>` input. |
| `std.search.equalRangeI32(items, needle)` | `Span<i32>` | Borrows the equal-value range in sorted `Span<i32>` input. |
| `std.search.partitionPointI32(items, pivot)` | `usize` | Returns the split index where values stop being below `pivot`. |
| `std.search.lowerBoundDescI32(items, needle)` | `usize` | Returns the insertion point before equal values in descending sorted `Span<i32>` input. |
| `std.search.upperBoundDescI32(items, needle)` | `usize` | Returns the insertion point after equal values in descending sorted `Span<i32>` input. |
| `std.search.binaryDescI32(items, needle)` | `usize` | Returns the matching descending sorted `Span<i32>` index or `std.mem.len(items)` when absent. |
| `std.search.containsSortedDescI32(items, needle)` | `Bool` | Checks whether descending sorted `Span<i32>` input contains the needle. |
| `std.search.countSortedDescI32(items, needle)` | `usize` | Counts equal values in descending sorted `Span<i32>` input. |
| `std.search.equalRangeDescI32(items, needle)` | `Span<i32>` | Borrows the equal-value range in descending sorted `Span<i32>` input. |
| `std.search.partitionPointDescI32(items, pivot)` | `usize` | Returns the split index where descending values stop being above `pivot`. |
| `std.search.minI32(items)` | `Maybe<i32>` | Returns the minimum value in `Span<i32>` input or `null` for an empty span. |
| `std.search.maxI32(items)` | `Maybe<i32>` | Returns the maximum value in `Span<i32>` input or `null` for an empty span. |
| `std.search.minIndexI32(items)` | `usize` | Returns the first minimum-value index in `Span<i32>` input or `std.mem.len(items)` for an empty span. |
| `std.search.maxIndexI32(items)` | `usize` | Returns the first maximum-value index in `Span<i32>` input or `std.mem.len(items)` for an empty span. |
| `std.search.lowerBoundU32(items, needle)` | `usize` | Returns the insertion point in sorted `Span<u32>` input. |
| `std.search.upperBoundU32(items, needle)` | `usize` | Returns the insertion point after existing equal values in sorted `Span<u32>` input. |
| `std.search.binaryU32(items, needle)` | `usize` | Returns the matching sorted `Span<u32>` index or `std.mem.len(items)` when absent. |
| `std.search.containsSortedU32(items, needle)` | `Bool` | Checks whether sorted `Span<u32>` input contains the needle. |
| `std.search.countSortedU32(items, needle)` | `usize` | Counts equal values in sorted `Span<u32>` input. |
| `std.search.equalRangeU32(items, needle)` | `Span<u32>` | Borrows the equal-value range in sorted `Span<u32>` input. |
| `std.search.partitionPointU32(items, pivot)` | `usize` | Returns the split index where values stop being below `pivot`. |
| `std.search.lowerBoundDescU32(items, needle)` | `usize` | Returns the insertion point before equal values in descending sorted `Span<u32>` input. |
| `std.search.upperBoundDescU32(items, needle)` | `usize` | Returns the insertion point after equal values in descending sorted `Span<u32>` input. |
| `std.search.binaryDescU32(items, needle)` | `usize` | Returns the matching descending sorted `Span<u32>` index or `std.mem.len(items)` when absent. |
| `std.search.containsSortedDescU32(items, needle)` | `Bool` | Checks whether descending sorted `Span<u32>` input contains the needle. |
| `std.search.countSortedDescU32(items, needle)` | `usize` | Counts equal values in descending sorted `Span<u32>` input. |
| `std.search.equalRangeDescU32(items, needle)` | `Span<u32>` | Borrows the equal-value range in descending sorted `Span<u32>` input. |
| `std.search.partitionPointDescU32(items, pivot)` | `usize` | Returns the split index where descending values stop being above `pivot`. |
| `std.search.minU32(items)` | `Maybe<u32>` | Returns the minimum value in `Span<u32>` input or `null` for an empty span. |
| `std.search.maxU32(items)` | `Maybe<u32>` | Returns the maximum value in `Span<u32>` input or `null` for an empty span. |
| `std.search.minIndexU32(items)` | `usize` | Returns the first minimum-value index in `Span<u32>` input or `std.mem.len(items)` for an empty span. |
| `std.search.maxIndexU32(items)` | `usize` | Returns the first maximum-value index in `Span<u32>` input or `std.mem.len(items)` for an empty span. |
| `std.search.lowerBoundUsize(items, needle)` | `usize` | Returns the insertion point in sorted `Span<usize>` input. |
| `std.search.upperBoundUsize(items, needle)` | `usize` | Returns the insertion point after existing equal values in sorted `Span<usize>` input. |
| `std.search.binaryUsize(items, needle)` | `usize` | Returns the matching sorted `Span<usize>` index or `std.mem.len(items)` when absent. |
| `std.search.containsSortedUsize(items, needle)` | `Bool` | Checks whether sorted `Span<usize>` input contains the needle. |
| `std.search.countSortedUsize(items, needle)` | `usize` | Counts equal values in sorted `Span<usize>` input. |
| `std.search.equalRangeUsize(items, needle)` | `Span<usize>` | Borrows the equal-value range in sorted `Span<usize>` input. |
| `std.search.partitionPointUsize(items, pivot)` | `usize` | Returns the split index where values stop being below `pivot`. |
| `std.search.lowerBoundDescUsize(items, needle)` | `usize` | Returns the insertion point before equal values in descending sorted `Span<usize>` input. |
| `std.search.upperBoundDescUsize(items, needle)` | `usize` | Returns the insertion point after equal values in descending sorted `Span<usize>` input. |
| `std.search.binaryDescUsize(items, needle)` | `usize` | Returns the matching descending sorted `Span<usize>` index or `std.mem.len(items)` when absent. |
| `std.search.containsSortedDescUsize(items, needle)` | `Bool` | Checks whether descending sorted `Span<usize>` input contains the needle. |
| `std.search.countSortedDescUsize(items, needle)` | `usize` | Counts equal values in descending sorted `Span<usize>` input. |
| `std.search.equalRangeDescUsize(items, needle)` | `Span<usize>` | Borrows the equal-value range in descending sorted `Span<usize>` input. |
| `std.search.partitionPointDescUsize(items, pivot)` | `usize` | Returns the split index where descending values stop being above `pivot`. |
| `std.search.minUsize(items)` | `Maybe<usize>` | Returns the minimum value in `Span<usize>` input or `null` for an empty span. |
| `std.search.maxUsize(items)` | `Maybe<usize>` | Returns the maximum value in `Span<usize>` input or `null` for an empty span. |
| `std.search.minIndexUsize(items)` | `usize` | Returns the first minimum-value index in `Span<usize>` input or `std.mem.len(items)` for an empty span. |
| `std.search.maxIndexUsize(items)` | `usize` | Returns the first maximum-value index in `Span<usize>` input or `std.mem.len(items)` for an empty span. |
Generic equality search supports the same non-owned scalar item types as
`std.mem.contains`.
## Example
```zero
pub fn main(world: World) -> Void raises {
let values: [5]i32 = [1, 2, 3, 5, 8]
let found: usize = std.search.binaryI32(values, 5)
let present: Bool = std.search.containsSortedI32(values, 5)
let after_five: usize = std.search.upperBoundI32(values, 5)
let fives: usize = std.search.countSortedI32(values, 5)
let five_range: Span<i32> = std.search.equalRangeI32(values, 5)
let partition_point: usize = std.search.partitionPointI32(values, 5)
let missing: usize = std.search.indexOf(values, 13)
let minimum: Maybe<i32> = std.search.minI32(values)
let maximum: Maybe<i32> = std.search.maxI32(values)
let max_index: usize = std.search.maxIndexI32(values)
let descending: [5]i32 = [9, 5, 5, 3, 1]
let descending_found: usize = std.search.binaryDescI32(descending, 5)
let descending_count: usize = std.search.countSortedDescI32(descending, 5)
if found == 3 && present && after_five == 4 && fives == 1 && std.mem.len(five_range) == 1 && partition_point == 3 && missing == std.mem.len(values) && minimum.has && minimum.value == 1 && maximum.has && maximum.value == 8 && max_index == 4 && descending_found == 1 && descending_count == 2 {
check world.out.write("search ok\n")
}
}
```
Effects: none.
Allocation behavior: no allocation.
Error behavior: absent values return the input length; sorted-count helpers
return `0`; min/max helpers return `null` for empty input; min/max index
helpers return the input length for empty input.
Ownership: generic equality search rejects owned item elements.
Target support: current compiler targets.
+114
View File
@@ -0,0 +1,114 @@
## When To Use std.sort
In Zerolang, use `std.sort` for in-place sorting and scalar ordering helpers
over caller-owned storage.
Runnable today:
| API | Return | Notes |
| --- | --- | --- |
| `std.sort.insertionI32(items)` | `Void` | Sorts caller-owned mutable `i32` storage in ascending order. |
| `std.sort.insertionDescI32(items)` | `Void` | Sorts caller-owned mutable `i32` storage in descending order. |
| `std.sort.stableI32(items)` | `Void` | Stable ascending sort over caller-owned mutable `i32` storage. |
| `std.sort.unstableI32(items)` | `Void` | Unstable ascending sort over caller-owned mutable `i32` storage. |
| `std.sort.stableDescI32(items)` | `Void` | Stable descending sort over caller-owned mutable `i32` storage. |
| `std.sort.unstableDescI32(items)` | `Void` | Unstable descending sort over caller-owned mutable `i32` storage. |
| `std.sort.reverseI32(items)` | `Void` | Reverses caller-owned mutable `i32` storage in place. |
| `std.sort.swapI32(items, left, right)` | `Bool` | Swaps two in-bounds `i32` elements and returns `false` for invalid indexes. |
| `std.sort.rotateLeftI32(items, amount)` | `Void` | Rotates caller-owned mutable `i32` storage left by `amount` positions. |
| `std.sort.rotateRightI32(items, amount)` | `Void` | Rotates caller-owned mutable `i32` storage right by `amount` positions. |
| `std.sort.isSortedI32(items)` | `Bool` | Checks whether `Span<i32>` input is sorted ascending. |
| `std.sort.isSortedDescI32(items)` | `Bool` | Checks whether `Span<i32>` input is sorted descending. |
| `std.sort.partitionI32(items, pivot)` | `usize` | Moves values below `pivot` before the rest and returns the split index. |
| `std.sort.partitionDescI32(items, pivot)` | `usize` | Moves values above `pivot` before the rest and returns the split index. |
| `std.sort.isPartitionedI32(items, pivot)` | `Bool` | Checks whether all values below `pivot` are before the remaining `i32` values. |
| `std.sort.isPartitionedDescI32(items, pivot)` | `Bool` | Checks whether all values above `pivot` are before the remaining `i32` values. |
| `std.sort.dedupeSortedI32(items)` | `usize` | Compacts sorted mutable `i32` storage in place and returns the unique prefix length. |
| `std.sort.selectNthI32(items, index)` | `Bool` | Partially reorders mutable `i32` storage so `items[index]` contains the nth ascending value. |
| `std.sort.selectNthDescI32(items, index)` | `Bool` | Partially reorders mutable `i32` storage so `items[index]` contains the nth descending value. |
| `std.sort.mergeSortedI32(dst, left, right)` | `usize` | Merges ascending sorted `i32` inputs into non-overlapping caller storage and returns the written count. |
| `std.sort.mergeSortedDescI32(dst, left, right)` | `usize` | Merges descending sorted `i32` inputs into non-overlapping caller storage and returns the written count. |
| `std.sort.insertionU32(items)` | `Void` | Sorts caller-owned mutable `u32` storage in ascending order. |
| `std.sort.insertionDescU32(items)` | `Void` | Sorts caller-owned mutable `u32` storage in descending order. |
| `std.sort.stableU32(items)` | `Void` | Stable ascending sort over caller-owned mutable `u32` storage. |
| `std.sort.unstableU32(items)` | `Void` | Unstable ascending sort over caller-owned mutable `u32` storage. |
| `std.sort.stableDescU32(items)` | `Void` | Stable descending sort over caller-owned mutable `u32` storage. |
| `std.sort.unstableDescU32(items)` | `Void` | Unstable descending sort over caller-owned mutable `u32` storage. |
| `std.sort.reverseU32(items)` | `Void` | Reverses caller-owned mutable `u32` storage in place. |
| `std.sort.swapU32(items, left, right)` | `Bool` | Swaps two in-bounds `u32` elements and returns `false` for invalid indexes. |
| `std.sort.rotateLeftU32(items, amount)` | `Void` | Rotates caller-owned mutable `u32` storage left by `amount` positions. |
| `std.sort.rotateRightU32(items, amount)` | `Void` | Rotates caller-owned mutable `u32` storage right by `amount` positions. |
| `std.sort.isSortedU32(items)` | `Bool` | Checks whether `Span<u32>` input is sorted ascending. |
| `std.sort.isSortedDescU32(items)` | `Bool` | Checks whether `Span<u32>` input is sorted descending. |
| `std.sort.partitionU32(items, pivot)` | `usize` | Moves values below `pivot` before the rest and returns the split index. |
| `std.sort.partitionDescU32(items, pivot)` | `usize` | Moves values above `pivot` before the rest and returns the split index. |
| `std.sort.isPartitionedU32(items, pivot)` | `Bool` | Checks whether all values below `pivot` are before the remaining `u32` values. |
| `std.sort.isPartitionedDescU32(items, pivot)` | `Bool` | Checks whether all values above `pivot` are before the remaining `u32` values. |
| `std.sort.dedupeSortedU32(items)` | `usize` | Compacts sorted mutable `u32` storage in place and returns the unique prefix length. |
| `std.sort.selectNthU32(items, index)` | `Bool` | Partially reorders mutable `u32` storage so `items[index]` contains the nth ascending value. |
| `std.sort.selectNthDescU32(items, index)` | `Bool` | Partially reorders mutable `u32` storage so `items[index]` contains the nth descending value. |
| `std.sort.mergeSortedU32(dst, left, right)` | `usize` | Merges ascending sorted `u32` inputs into non-overlapping caller storage and returns the written count. |
| `std.sort.mergeSortedDescU32(dst, left, right)` | `usize` | Merges descending sorted `u32` inputs into non-overlapping caller storage and returns the written count. |
| `std.sort.insertionUsize(items)` | `Void` | Sorts caller-owned mutable `usize` storage in ascending order. |
| `std.sort.insertionDescUsize(items)` | `Void` | Sorts caller-owned mutable `usize` storage in descending order. |
| `std.sort.stableUsize(items)` | `Void` | Stable ascending sort over caller-owned mutable `usize` storage. |
| `std.sort.unstableUsize(items)` | `Void` | Unstable ascending sort over caller-owned mutable `usize` storage. |
| `std.sort.stableDescUsize(items)` | `Void` | Stable descending sort over caller-owned mutable `usize` storage. |
| `std.sort.unstableDescUsize(items)` | `Void` | Unstable descending sort over caller-owned mutable `usize` storage. |
| `std.sort.reverseUsize(items)` | `Void` | Reverses caller-owned mutable `usize` storage in place. |
| `std.sort.swapUsize(items, left, right)` | `Bool` | Swaps two in-bounds `usize` elements and returns `false` for invalid indexes. |
| `std.sort.rotateLeftUsize(items, amount)` | `Void` | Rotates caller-owned mutable `usize` storage left by `amount` positions. |
| `std.sort.rotateRightUsize(items, amount)` | `Void` | Rotates caller-owned mutable `usize` storage right by `amount` positions. |
| `std.sort.isSortedUsize(items)` | `Bool` | Checks whether `Span<usize>` input is sorted ascending. |
| `std.sort.isSortedDescUsize(items)` | `Bool` | Checks whether `Span<usize>` input is sorted descending. |
| `std.sort.partitionUsize(items, pivot)` | `usize` | Moves values below `pivot` before the rest and returns the split index. |
| `std.sort.partitionDescUsize(items, pivot)` | `usize` | Moves values above `pivot` before the rest and returns the split index. |
| `std.sort.isPartitionedUsize(items, pivot)` | `Bool` | Checks whether all values below `pivot` are before the remaining `usize` values. |
| `std.sort.isPartitionedDescUsize(items, pivot)` | `Bool` | Checks whether all values above `pivot` are before the remaining `usize` values. |
| `std.sort.dedupeSortedUsize(items)` | `usize` | Compacts sorted mutable `usize` storage in place and returns the unique prefix length. |
| `std.sort.selectNthUsize(items, index)` | `Bool` | Partially reorders mutable `usize` storage so `items[index]` contains the nth ascending value. |
| `std.sort.selectNthDescUsize(items, index)` | `Bool` | Partially reorders mutable `usize` storage so `items[index]` contains the nth descending value. |
| `std.sort.mergeSortedUsize(dst, left, right)` | `usize` | Merges ascending sorted `usize` inputs into non-overlapping caller storage and returns the written count. |
| `std.sort.mergeSortedDescUsize(dst, left, right)` | `usize` | Merges descending sorted `usize` inputs into non-overlapping caller storage and returns the written count. |
The first sort surface is deliberately small and typed. Generic comparator
sorting should wait for stronger comparator contracts instead of smuggling an
untyped callback convention into the standard library.
## Example
```zero
pub fn main(world: World) -> Void raises {
var values: [5]i32 = [5, 1, 4, 2, 3]
std.sort.stableI32(values)
let unique_len: usize = std.sort.dedupeSortedI32(values)
std.sort.unstableI32(values)
std.sort.reverseI32(values)
let swapped: Bool = std.sort.swapI32(values, 0_usize, 4_usize)
std.sort.rotateLeftI32(values, 2_usize)
std.sort.rotateRightI32(values, 2_usize)
let high_len: usize = std.sort.partitionDescI32(values, 2)
let high_partitioned: Bool = std.sort.isPartitionedDescI32(values, 2)
std.sort.stableDescI32(values)
std.sort.unstableDescI32(values)
var selected: [5]i32 = [9, 1, 4, 7, 2]
let selected_ok: Bool = std.sort.selectNthI32(selected, 2_usize)
let left_sorted: [2]i32 = [1, 3]
let right_sorted: [3]i32 = [2, 4, 5]
var merged: [5]i32 = [0, 0, 0, 0, 0]
let merged_len: usize = std.sort.mergeSortedI32(merged, left_sorted, right_sorted)
if std.sort.isSortedDescI32(values) && swapped && unique_len == 5 && high_len == 3 && high_partitioned && selected_ok && selected[2] == 4 && merged_len == 5 && values[0] == 5 && values[4] == 1 {
check world.out.write("sort ok\n")
}
}
```
Effects: writes to caller-provided mutable storage.
Allocation behavior: no allocation.
Error behavior: none.
Ownership: sort helpers are typed scalar helpers and do not move owned values.
Target support: current compiler targets.
+73
View File
@@ -0,0 +1,73 @@
## When To Use std.str
In Zerolang, use `std.str` for allocation-free byte-string helpers over spans and
caller-owned storage.
Runnable today:
| API | Return | Notes |
| --- | --- | --- |
| `std.str.copy(buffer, text)` | `Maybe<Span<u8>>` | Copies `text` into caller storage. |
| `std.str.concat(buffer, left, right)` | `Maybe<Span<u8>>` | Writes `left` followed by `right`. |
| `std.str.repeat(buffer, text, count)` | `Maybe<Span<u8>>` | Repeats `text` into caller storage. |
| `std.str.replace(buffer, text, old, replacement)` | `Maybe<Span<u8>>` | Replaces non-overlapping `old` byte substrings into caller storage; empty `old` returns `null`. |
| `std.str.reverse(buffer, text)` | `Maybe<Span<u8>>` | Writes reversed bytes into non-overlapping caller-provided storage. |
| `std.str.countByte(text, byte)` | `usize` | Counts exact byte matches. |
| `std.str.count(text, needle)` | `usize` | Counts non-overlapping byte substring matches; the empty needle returns `len + 1`. |
| `std.str.splitCount(text, separator)` | `usize` | Counts byte-separator split parts; an empty separator returns `0`. |
| `std.str.split(text, separator, index)` | `Maybe<Span<u8>>` | Borrows a zero-based split part. |
| `std.str.fieldCountAscii(text)` | `usize` | Counts non-empty ASCII-whitespace separated fields. |
| `std.str.fieldAscii(text, index)` | `Maybe<Span<u8>>` | Borrows a zero-based ASCII-whitespace field. |
| `std.str.lineCount(text)` | `usize` | Counts LF-delimited lines; a trailing LF does not add a final empty line. |
| `std.str.line(text, index)` | `Maybe<Span<u8>>` | Borrows a zero-based line and strips `\r` before `\n`. |
| `std.str.indexOf(text, needle)` / `std.str.lastIndexOf(text, needle)` | `usize` | Returns a matching byte index or the input length when absent. |
| `std.str.startsWith(text, prefix)` | `Bool` | Checks a byte prefix. |
| `std.str.endsWith(text, suffix)` | `Bool` | Checks a byte suffix. |
| `std.str.contains(text, needle)` | `Bool` | Checks for a byte substring; the empty needle is present. |
| `std.str.trimAscii(text)` | `Span<u8>` | Borrows `text` without leading or trailing ASCII space bytes. |
| `std.str.trimStartAscii(text)` / `std.str.trimEndAscii(text)` | `Span<u8>` | Borrows one-sided trimmed views. |
| `std.str.toLowerAscii(buffer, text)` / `std.str.toUpperAscii(buffer, text)` | `Maybe<Span<u8>>` | Writes ASCII case-converted bytes into caller storage. |
| `std.str.eqlIgnoreAsciiCase(left, right)` | `Bool` | Compares ASCII case-insensitively. |
| `std.str.wordCountAscii(text)` | `usize` | Counts non-empty runs separated by ASCII space bytes. |
Current scope:
- Helpers operate on byte spans and ASCII delimiter rules for space, tab, line feed, and carriage return.
- `reverse`, `repeat`, `replace`, `copy`, and `concat` write into caller storage and return `null` when the buffer is too small. The destination buffer must not overlap the input.
- The module does not implement Unicode case mapping, grapheme segmentation, or locale-aware text rules.
## Example
```zero
pub fn main(world: World) -> Void raises {
var storage: [6]u8 = [0_u8; 6]
let reversed: Maybe<Span<u8>> = std.str.reverse(storage, "drawer")
var repeated_storage: [8]u8 = [0_u8; 8]
let repeated: Maybe<Span<u8>> = std.str.repeat(repeated_storage, "ha", 3)
var lower_storage: [4]u8 = [0_u8; 4]
let lower: Maybe<Span<u8>> = std.str.toLowerAscii(lower_storage, "ZERO")
let field: Maybe<Span<u8>> = std.str.fieldAscii("zero text", 1)
if reversed.has && repeated.has && (lower.has && field.has) {
if std.mem.eql(reversed.value, "reward") && std.mem.eql(repeated.value, "hahaha") && (std.mem.eql(lower.value, "zero") && std.mem.eql(field.value, "text")) {
check world.out.write("string helper ok\n")
}
}
}
```
## Design Notes
`std.str` is allocation-free. Functions that create new byte sequences use
caller-provided storage, and functions that return spans borrow from an input or
that caller-provided storage.
`reverse` is a copy helper, not an in-place reversal primitive. Pass separate
destination storage when the source text comes from mutable bytes.
String literals can be passed directly to these helpers; fixed arrays and
mutable buffers can be passed as spans when the caller needs non-literal input.
The current helpers are byte-string primitives. They are suitable for protocol
tokens, Rosetta-style ASCII examples, and fixed-buffer tools. Unicode text
algorithms should be added as explicit APIs with documented behavior instead of
being implied by these byte-span helpers.
+162
View File
@@ -0,0 +1,162 @@
## When To Use std.term
In Zerolang, use `std.term` when terminal code needs ANSI output sequences,
hosted terminal metadata, nonblocking input reads, or key decoding for bytes
already read from input.
Runnable today:
| Helper | Returns | Notes |
| --- | --- | --- |
| `std.term.reset()` | `String` | ANSI SGR reset. |
| `std.term.bold()` | `String` | ANSI SGR bold style. |
| `std.term.dim()` | `String` | ANSI SGR dim style. |
| `std.term.underline()` | `String` | ANSI SGR underline style. |
| `std.term.inverse()` | `String` | ANSI SGR inverse style. |
| `std.term.fgDefault()` | `String` | Reset foreground color. |
| `std.term.fgBlack()` | `String` | Black foreground color. |
| `std.term.fgRed()` | `String` | Red foreground color. |
| `std.term.fgGreen()` | `String` | Green foreground color. |
| `std.term.fgYellow()` | `String` | Yellow foreground color. |
| `std.term.fgBlue()` | `String` | Blue foreground color. |
| `std.term.fgMagenta()` | `String` | Magenta foreground color. |
| `std.term.fgCyan()` | `String` | Cyan foreground color. |
| `std.term.fgWhite()` | `String` | White foreground color. |
| `std.term.bgDefault()` | `String` | Reset background color. |
| `std.term.bgBlack()` | `String` | Black background color. |
| `std.term.bgRed()` | `String` | Red background color. |
| `std.term.bgGreen()` | `String` | Green background color. |
| `std.term.bgYellow()` | `String` | Yellow background color. |
| `std.term.bgBlue()` | `String` | Blue background color. |
| `std.term.bgMagenta()` | `String` | Magenta background color. |
| `std.term.bgCyan()` | `String` | Cyan background color. |
| `std.term.bgWhite()` | `String` | White background color. |
| `std.term.clearScreen()` | `String` | Clear the full terminal screen. |
| `std.term.clearScreenDown()` | `String` | Clear from the cursor through the end of the screen. |
| `std.term.clearScreenUp()` | `String` | Clear from the cursor through the start of the screen. |
| `std.term.clearLine()` | `String` | Clear the current terminal line. |
| `std.term.clearLineRight()` | `String` | Clear from the cursor through the end of the line. |
| `std.term.clearLineLeft()` | `String` | Clear from the cursor through the start of the line. |
| `std.term.cursorHome()` | `String` | Move the cursor to row 1, column 1. |
| `std.term.cursorTo(buffer, row, column)` | `Maybe<Span<u8>>` | Writes a 1-based ANSI cursor-position sequence into caller storage. |
| `std.term.cursorUp(buffer, count)` | `Maybe<Span<u8>>` | Writes an ANSI cursor-up sequence into caller storage; count `0` writes an empty span. |
| `std.term.cursorDown(buffer, count)` | `Maybe<Span<u8>>` | Writes an ANSI cursor-down sequence into caller storage; count `0` writes an empty span. |
| `std.term.cursorRight(buffer, count)` | `Maybe<Span<u8>>` | Writes an ANSI cursor-right sequence into caller storage; count `0` writes an empty span. |
| `std.term.cursorLeft(buffer, count)` | `Maybe<Span<u8>>` | Writes an ANSI cursor-left sequence into caller storage; count `0` writes an empty span. |
| `std.term.saveCursor()` | `String` | Save the current cursor position. |
| `std.term.restoreCursor()` | `String` | Restore the saved cursor position. |
| `std.term.hideCursor()` | `String` | Hide the terminal cursor. |
| `std.term.showCursor()` | `String` | Show the terminal cursor. |
| `std.term.enterAltScreen()` | `String` | Enter the alternate screen buffer. |
| `std.term.leaveAltScreen()` | `String` | Leave the alternate screen buffer. |
| `std.term.enterBracketedPaste()` | `String` | Enable bracketed paste markers in supporting terminals. |
| `std.term.leaveBracketedPaste()` | `String` | Disable bracketed paste markers in supporting terminals. |
| `std.term.enterMouseCapture()` | `String` | Enable SGR mouse tracking and drag/wheel capture in supporting terminals. |
| `std.term.leaveMouseCapture()` | `String` | Disable the SGR mouse tracking modes enabled by `enterMouseCapture`. |
| `std.term.keyNone()` | `u32` | Sentinel returned for incomplete or unsupported key bytes. |
| `std.term.keyEscape()` | `u32` | Escape key code. |
| `std.term.keyEnter()` | `u32` | Enter key code for `\r` or `\n`. |
| `std.term.keyTab()` | `u32` | Tab key code. |
| `std.term.keyBackspace()` | `u32` | Backspace key code for `0x7f` or `0x08`. |
| `std.term.keyCtrlA()` | `u32` | Ctrl-A key code. |
| `std.term.keyCtrlC()` | `u32` | Ctrl-C key code. |
| `std.term.keyCtrlD()` | `u32` | Ctrl-D key code. |
| `std.term.keyCtrlE()` | `u32` | Ctrl-E key code. |
| `std.term.keyCtrlK()` | `u32` | Ctrl-K key code. |
| `std.term.keyCtrlL()` | `u32` | Ctrl-L key code. |
| `std.term.keyCtrlN()` | `u32` | Ctrl-N key code. |
| `std.term.keyCtrlP()` | `u32` | Ctrl-P key code. |
| `std.term.keyCtrlR()` | `u32` | Ctrl-R key code. |
| `std.term.keyCtrlU()` | `u32` | Ctrl-U key code. |
| `std.term.keyCtrlW()` | `u32` | Ctrl-W key code. |
| `std.term.keyArrowUp()` | `u32` | Up-arrow key code above the Unicode scalar range. |
| `std.term.keyArrowDown()` | `u32` | Down-arrow key code above the Unicode scalar range. |
| `std.term.keyArrowRight()` | `u32` | Right-arrow key code above the Unicode scalar range. |
| `std.term.keyArrowLeft()` | `u32` | Left-arrow key code above the Unicode scalar range. |
| `std.term.keyDelete()` | `u32` | Delete key code above the Unicode scalar range. |
| `std.term.keyHome()` | `u32` | Home key code above the Unicode scalar range. |
| `std.term.keyEnd()` | `u32` | End key code above the Unicode scalar range. |
| `std.term.keyPageUp()` | `u32` | Page Up key code above the Unicode scalar range. |
| `std.term.keyPageDown()` | `u32` | Page Down key code above the Unicode scalar range. |
| `std.term.keyInsert()` | `u32` | Insert key code above the Unicode scalar range. |
| `std.term.keyShiftTab()` | `u32` | Shift-Tab key code above the Unicode scalar range. |
| `std.term.keyF1()` | `u32` | F1 key code above the Unicode scalar range. |
| `std.term.keyF2()` | `u32` | F2 key code above the Unicode scalar range. |
| `std.term.keyF3()` | `u32` | F3 key code above the Unicode scalar range. |
| `std.term.keyF4()` | `u32` | F4 key code above the Unicode scalar range. |
| `std.term.keyF5()` | `u32` | F5 key code above the Unicode scalar range. |
| `std.term.keyF6()` | `u32` | F6 key code above the Unicode scalar range. |
| `std.term.keyF7()` | `u32` | F7 key code above the Unicode scalar range. |
| `std.term.keyF8()` | `u32` | F8 key code above the Unicode scalar range. |
| `std.term.keyF9()` | `u32` | F9 key code above the Unicode scalar range. |
| `std.term.keyF10()` | `u32` | F10 key code above the Unicode scalar range. |
| `std.term.keyF11()` | `u32` | F11 key code above the Unicode scalar range. |
| `std.term.keyF12()` | `u32` | F12 key code above the Unicode scalar range. |
| `std.term.keyPasteStart()` | `u32` | Bracketed paste start marker code above the Unicode scalar range. |
| `std.term.keyPasteEnd()` | `u32` | Bracketed paste end marker code above the Unicode scalar range. |
| `std.term.keyCode(bytes)` | `u32` | Decodes one key from caller-provided bytes, returning Unicode scalar values for printable UTF-8 and named constants for control keys. |
| `std.term.keyByteLen(bytes)` | `usize` | Returns the decoded key width in bytes, or `0` for incomplete or unsupported input. |
| `std.term.stdinIsTty()` | `Bool` | Reports whether standard input is attached to a terminal. |
| `std.term.stdoutIsTty()` | `Bool` | Reports whether standard output is attached to a terminal. |
| `std.term.widthOr(fallback)` | `usize` | Returns terminal columns, or `fallback` when unavailable. |
| `std.term.heightOr(fallback)` | `usize` | Returns terminal rows, or `fallback` when unavailable. |
| `std.term.enterRawMode()` | `Bool` | Puts standard input into raw, nonblocking terminal mode when supported. |
| `std.term.leaveRawMode()` | `Bool` | Restores the terminal mode saved by `enterRawMode()`. |
| `std.term.readInput(buffer)` | `Maybe<usize>` | Fills the caller buffer with currently available stdin bytes without blocking; returns `null` when no bytes are available or the input source is unsupported. |
Metadata labels:
- effects: ANSI/key helpers are pure; TTY/size helpers read hosted terminal metadata; raw-mode helpers update the hosted terminal; `readInput` reads from hosted stdin
- allocation behavior: no allocation
- target support: ANSI/key helpers are target-neutral; TTY/size/raw-mode/input helpers require hosted runtime support
- error behavior: ANSI/key helpers are infallible; hosted helpers return fallbacks, `false`, or `null` when unavailable
- ownership notes: ANSI sequences are borrowed static byte views
- example: `conformance/native/pass/std-term-ansi.graph`
Example:
```zero
pub fn main(world: World) -> Void raises {
check world.out.write(std.term.enterAltScreen())
check world.out.write(std.term.enterMouseCapture())
check world.out.write(std.term.clearScreen())
check world.out.write(std.term.cursorHome())
var cursor: [24]u8 = [0_u8; 24]
let top: Maybe<Span<u8>> = std.term.cursorTo(cursor, 1_usize, 1_usize)
if top.has {
check world.out.write(top.value)
}
check world.out.write(std.term.bold())
check world.out.write(std.term.fgCyan())
let width: usize = std.term.widthOr(80_usize)
let height: usize = std.term.heightOr(24_usize)
let raw: Bool = std.term.enterRawMode()
var input: [16]u8 = [0_u8; 16]
let pending: Maybe<usize> = std.term.readInput(input)
if pending.has {
let bytes: Span<u8> = std.mem.prefix(input, pending.value)
let key: u32 = std.term.keyCode(bytes)
if key == std.term.keyCtrlC() {
check world.out.write("cancel")
}
}
check world.out.write("ready")
if raw {
let restored: Bool = std.term.leaveRawMode()
if !restored {
return
}
}
check world.out.write(std.term.reset())
check world.out.write(std.term.leaveMouseCapture())
check world.out.write(std.term.leaveAltScreen())
}
```
Key decoding is target-neutral: it parses bytes the caller already has. TTY and
size helpers are hosted metadata calls and return caller fallbacks when a
terminal size is unavailable. Raw mode is a hosted terminal capability: call
`leaveRawMode()` before returning to normal line-oriented terminal input.
`readInput()` is nonblocking; in raw mode it can be polled by interactive
programs, and on noninteractive stdin it returns available piped bytes when the
host exposes them.
+59
View File
@@ -0,0 +1,59 @@
## When To Use std.testing
In Zerolang, use `std.testing` inside test blocks for output checks and small boolean
assertion helpers.
Runnable today:
| API | Return | Notes |
| --- | --- | --- |
| `std.testing.isTrue(value)` | `Bool` | Passes through a `Bool` for readable `expect` statements. |
| `std.testing.isFalse(value)` | `Bool` | Returns true when the value is false. |
| `std.testing.equalBool(actual, expected)` | `Bool` | Compares booleans explicitly. |
| `std.testing.equalUsize(actual, expected)` | `Bool` | Compares `usize` values explicitly. |
| `std.testing.equalU32(actual, expected)` | `Bool` | Compares `u32` values explicitly. |
| `std.testing.equalI32(actual, expected)` | `Bool` | Compares `i32` values explicitly. |
| `std.testing.equalBytes(actual, expected)` | `Bool` | Compares byte spans by value. |
| `std.testing.containsBytes(actual, needle)` | `Bool` | Checks whether a byte span contains a byte substring. |
| `std.testing.startsWith(actual, prefix)` | `Bool` | Checks a byte prefix. |
| `std.testing.endsWith(actual, suffix)` | `Bool` | Checks a byte suffix. |
| `std.testing.notEqualBytes(actual, expected)` | `Bool` | Checks byte-span inequality. |
| `std.testing.diffIndexBytes(actual, expected)` | `Maybe<usize>` | Returns the first differing byte index, or `null` when spans are equal. |
| `std.testing.jsonFieldEquals(actual, key, expected)` | `Bool` | Compares a raw top-level JSON field value. |
| `std.testing.jsonPathEquals(actual, path, expected)` | `Bool` | Compares a raw dotted JSON path value. |
| `std.testing.caseName(buffer, suite, index)` | `Maybe<Span<u8>>` | Writes a stable table-case name like `suite[3]` into caller storage. |
Metadata labels:
- effects: none for scalar comparisons; memory for byte-span/case-name checks; parse for JSON checks
- allocation behavior: no allocation
- target support: target-neutral
- error behavior: infallible
- ownership notes: no ownership transfer
- example: `examples/std-testing-log.graph`
## Example
```zero
test "testing helpers support direct test blocks" {
let diff: Maybe<usize> = std.testing.diffIndexBytes("zero", "zeta")
expect std.testing.equalU32(42_u32, 42_u32)
expect std.testing.equalBytes("zero", "zero")
expect std.testing.containsBytes("zerolang", "lang")
expect diff.has && diff.value == 2
expect std.testing.jsonPathEquals("{\"user\":{\"name\":\"zero\"}}", "user.name", "\"zero\"")
}
```
## Design Notes
`std.testing` helpers return `Bool`; they do not register tests, hide failures,
allocate output, or produce process I/O. Use them inside ordinary `expect`
statements so the compiler and `zero test` keep one visible test model.
The byte helpers are byte-span predicates. They are suitable for output checks,
protocol fixtures, and small examples where a full parser would be more complex
than the assertion.
JSON helpers compare raw JSON values, so string expectations include their JSON
quotes, such as `"\"zero\""`.
+36
View File
@@ -0,0 +1,36 @@
## When To Use std.text
In Zerolang, use `std.text` for ASCII and UTF-8 byte-backed validation.
Runnable today:
`std.text` is for byte-backed text validation and counting. It does not imply
locale-aware case mapping, grapheme segmentation, normalization, or display-width
rules.
| API | Return | Notes |
| --- | --- | --- |
| `std.text.isAscii(text)` | `Bool` | Checks that every byte is below `0x80`. |
| `std.text.utf8Valid(text)` | `Bool` | Validates UTF-8 byte structure, rejecting overlong encodings, surrogate code points, and values above `U+10FFFF`. |
| `std.text.utf8Len(text)` | `Maybe<usize>` | Counts Unicode scalar values when UTF-8 is valid; returns `null` on invalid input. |
## Example
```zero
pub fn main(world: World) -> Void raises {
let valid: [2]u8 = [195_u8, 169_u8]
let invalid: [1]u8 = [128_u8]
let len: Maybe<usize> = std.text.utf8Len(valid)
if !std.text.isAscii(valid) && std.text.utf8Valid(valid) && !std.text.utf8Valid(invalid) && len.has && len.value == 1 {
check world.out.write("text ok\n")
}
}
```
Effects: none.
Allocation behavior: no allocation.
Error behavior: `utf8Len` returns `null` for invalid UTF-8.
Target support: current compiler targets.
+102
View File
@@ -0,0 +1,102 @@
## When To Use std.time
In Zerolang, use `std.time` for duration math, RFC 3339 date and time validation
and parsing, and target-gated monotonic or wall-clock helpers.
Runnable today:
| API | Return | Notes |
| --- | --- | --- |
| `std.time.ns(value)` | `Duration` | Builds a nanosecond duration. |
| `std.time.us(value)` | `Duration` | Builds a microsecond duration. |
| `std.time.ms(value)` | `Duration` | Builds a millisecond duration. |
| `std.time.seconds(value)` | `Duration` | Builds a second duration. |
| `std.time.minutes(value)` | `Duration` | Builds a minute duration. |
| `std.time.hours(value)` | `Duration` | Builds an hour duration. |
| `std.time.zero()` | `Duration` | Returns a zero duration. |
| `std.time.add(a, b)` | `Duration` | Adds two durations. |
| `std.time.sub(a, b)` | `Duration` | Subtracts one duration from another. |
| `std.time.min(a, b)` | `Duration` | Returns the smaller duration. |
| `std.time.max(a, b)` | `Duration` | Returns the larger duration. |
| `std.time.clamp(value, low, high)` | `Duration` | Clamps a duration between normalized bounds. |
| `std.time.abs(value)` | `Duration` | Returns a non-negative duration magnitude. |
| `std.time.between(start, end)` | `Duration` | Returns the non-negative duration between two values. |
| `std.time.hasElapsed(start, now, timeout)` | `Bool` | Reports whether a timeout window has elapsed. |
| `std.time.deadlineAfter(start, timeout)` | `Duration` | Builds a deadline by adding a timeout to a start instant. |
| `std.time.remainingUntil(deadline, now)` | `Duration` | Returns remaining time or zero once the deadline has passed. |
| `std.time.deadlineExpired(deadline, now)` | `Bool` | Reports whether `now` is at or past `deadline`. |
| `std.time.sleep(duration)` | `Bool` | Sleeps for a hosted non-negative duration; returns `false` on host failure. |
| `std.time.asNs(value)` | `i64` | Converts to nanoseconds. |
| `std.time.asUsFloor(value)` | `i64` | Converts to whole microseconds. |
| `std.time.asMsFloor(value)` | `i32` | Converts to whole milliseconds. |
| `std.time.asSecondsFloor(value)` | `i64` | Converts to whole seconds. |
| `std.time.lessThan(a, b)` | `Bool` | Compares two durations. |
| `std.time.isZero(value)` | `Bool` | Reports whether a duration is zero. |
| `std.time.monotonic()` | `Duration` | Reads a monotonic target clock where available. |
| `std.time.wallSeconds()` | `i64` | Reads target wall-clock seconds where available. |
| `std.time.isRfc3339Date(text)` | `Bool` | Validates an RFC 3339 full-date with leap years and days-in-month. |
| `std.time.isRfc3339Time(text)` | `Bool` | Validates an RFC 3339 full-time with fractional seconds, numeric offsets, and the leap-second rule. |
| `std.time.isRfc3339DateTime(text)` | `Bool` | Validates an RFC 3339 date-time joined by `T` or `t`. |
| `std.time.parseRfc3339DateTimeOr(text, fallback)` | `i64` | Parses a date-time into UTC epoch seconds; returns the fallback when invalid. Fractional seconds truncate; a valid leap second maps to the same epoch second as `:59`. |
| `std.time.isLeapYear(year)` | `Bool` | Gregorian leap-year predicate. |
| `std.time.daysInMonth(year, month)` | `u32` | Days in a month; returns `0` for invalid months. |
| `std.time.writeDurationNs(buffer, value)` | `Maybe<Span<u8>>` | Writes nanoseconds with an `ns` suffix into caller storage. |
| `std.time.writeDurationMs(buffer, value)` | `Maybe<Span<u8>>` | Writes whole milliseconds with an `ms` suffix into caller storage. |
| `std.time.writeDurationSeconds(buffer, value)` | `Maybe<Span<u8>>` | Writes whole seconds with an `s` suffix into caller storage. |
Current limits:
- Target-specific clock availability diagnostics.
- Timer handles and fake-clock handles are not public APIs.
Metadata labels:
- effects: time
- allocation behavior: no allocation
- target support: duration math is target-neutral; clock reads and sleep require a time-capable target
- error behavior: infallible helpers; RFC 3339 validators return `Bool` and the epoch parser returns its fallback for invalid text
- ownership notes: no ownership transfer
- example: `examples/std-platform.graph`
## Example
```zero
pub fn main(world: World) -> Void raises {
let a: Duration = std.time.ms(250)
let b: Duration = std.time.seconds(1)
let total: Duration = std.time.add(a, b)
let span: Duration = std.time.between(std.time.seconds(2), std.time.ms(250))
let deadline: Duration = std.time.deadlineAfter(std.time.seconds(10), std.time.ms(500))
let remaining: Duration = std.time.remainingUntil(deadline, std.time.seconds(10))
let slept: Bool = std.time.sleep(std.time.zero())
var text_storage: [32]u8 = [0_u8; 32]
let text: Maybe<Span<u8>> = std.time.writeDurationMs(text_storage, total)
if slept && std.time.asMsFloor(total) == 1250 && std.time.asMsFloor(span) == 1750 && (std.time.asMsFloor(remaining) == 500 && text.has) {
check world.out.write("duration ok\n")
}
}
```
RFC 3339 validation includes the exact leap-second rule: `seconds == 60` is
valid only when the time normalized by its numeric offset equals `23:59:60`
UTC, wrapping modulo 24 hours. `00:29:60+00:30` is valid because it normalizes
to `23:59:60` UTC on the previous day, while `23:59:60-01:00` is invalid
because it normalizes to `00:59:60` UTC.
```zero
pub fn main(world: World) -> Void raises {
let wrapped: Bool = std.time.isRfc3339Time("00:29:60+00:30")
let not_leap: Bool = std.time.isRfc3339Time("23:59:60-01:00")
let epoch: i64 = std.time.parseRfc3339DateTimeOr("2000-01-01T00:00:00Z", -1)
if wrapped && !not_leap && epoch == 946684800 && std.time.daysInMonth(2024, 2) == 29 {
check world.out.write("rfc3339 ok\n")
}
}
```
## Design Notes
Time is an effect when it observes or waits on the outside world.
Pure duration math can stay allocation-free and target-independent.
Timer and fake-clock APIs are not exposed in the current public surface.
+67
View File
@@ -0,0 +1,67 @@
## When To Use std.toml
In Zerolang, use `std.toml` for TOML validation, shallow field lookup, and typed scalar
decode helpers.
Runnable today:
| API | Return | Notes |
| --- | --- | --- |
| `std.toml.validate(text)` | `Bool` | Checks the current TOML subset without allocation. |
| `std.toml.validateBytes(bytes)` | `Bool` | Checks a `Span<u8>` TOML payload without allocation. |
| `std.toml.field(bytes, key)` | `Maybe<Span<u8>>` | Returns the raw value for a direct, dotted, or shallow table field. |
| `std.toml.stringDecode(buffer, value)` | `Maybe<Span<u8>>` | Decodes a TOML string value into caller storage. |
| `std.toml.string(buffer, bytes, key)` | `Maybe<Span<u8>>` | Looks up and decodes a TOML string field. |
| `std.toml.u32(bytes, key)` | `Maybe<u32>` | Looks up and decodes an unsigned integer field. |
| `std.toml.i32(bytes, key)` | `Maybe<i32>` | Looks up and decodes a signed integer field. |
| `std.toml.bool(bytes, key)` | `Maybe<Bool>` | Looks up and decodes a boolean field. |
| `std.toml.arrayCount(value)` | `Maybe<usize>` | Counts items in a raw array value. |
| `std.toml.arrayValue(value, index)` | `Maybe<Span<u8>>` | Borrows a raw array item by ordinal. |
| `std.toml.arrayString(buffer, value, index)` | `Maybe<Span<u8>>` | Decodes a string array item into caller storage. |
| `std.toml.arrayU32(value, index)` | `Maybe<u32>` | Decodes an unsigned integer array item. |
| `std.toml.arrayI32(value, index)` | `Maybe<i32>` | Decodes a signed integer array item. |
| `std.toml.arrayBool(value, index)` | `Maybe<Bool>` | Decodes a boolean array item. |
| `std.toml.writeKeyValueString(buffer, key, value)` | `Maybe<Span<u8>>` | Writes one string key/value line. |
| `std.toml.writeKeyValueU32(buffer, key, value)` | `Maybe<Span<u8>>` | Writes one unsigned integer key/value line. |
| `std.toml.writeKeyValueBool(buffer, key, value)` | `Maybe<Span<u8>>` | Writes one boolean key/value line. |
| `std.toml.writeTableHeader(buffer, table)` | `Maybe<Span<u8>>` | Writes one table header line. |
Metadata labels:
- effects: parse
- allocation behavior: allocation-free; decoded strings and writer output use caller storage
- target support: target-neutral
- error behavior: `Maybe` helpers return null on malformed or missing fields
- ownership notes: returned raw fields borrow from the input span; decoded strings borrow from the caller buffer
- examples: `conformance/native/pass/std-toml-basic.graph`
## Example
```zero
pub fn main(world: World) -> Void raises {
let input: Span<u8> = "[package]\nname = \"demo\"\n\n[features]\ngraph = true\nlevels = [1, 2, 3]\n"
var name_buffer: [16]u8 = [0_u8; 16]
let name: Maybe<Span<u8>> = std.toml.string(name_buffer, input, "package.name")
let graph: Maybe<Bool> = std.toml.bool(input, "features.graph")
let levels: Maybe<Span<u8>> = std.toml.field(input, "features.levels")
var count: Maybe<usize> = null
if levels.has {
count = std.toml.arrayCount(levels.value)
}
if std.toml.validateBytes(input) && name.has && graph.has && graph.value && count.has && count.value == 3 {
check world.out.write("toml ok\n")
}
}
```
## Design Notes
The current TOML helper surface is deliberately narrow. It supports the package
manifest subset used by Zero packages: tables, dotted keys, strings, booleans,
integers, scalar arrays, and small writer helpers. Field lookup is shallow and table-aware, so
`std.toml.string(buffer, input, "package.name")` can read `name` inside a
`[package]` table.
The helpers avoid hidden allocation. Use `field` when a raw value slice is
enough, and use `string` or `stringDecode` when escape decoding into explicit
caller storage is required.
+60
View File
@@ -0,0 +1,60 @@
## When To Use std.unicode
In Zerolang, use `std.unicode` for UTF-8 codepoint decode/encode iteration and
codepoint-class checks. For whole-span validation and codepoint counting, use
the existing `std.text.utf8Valid` and `std.text.utf8Len` helpers; `std.unicode`
extends them with per-codepoint access.
Decoding is strict UTF-8: overlong encodings, surrogate codepoints, values
above `U+10FFFF`, and truncated sequences all return `null`.
Runnable today:
| API | Return | Notes |
| --- | --- | --- |
| `std.unicode.decodeAt(text, index)` | `Maybe<u32>` | Decodes the codepoint starting at a byte index; `null` for invalid or out-of-range positions. |
| `std.unicode.widthAt(text, index)` | `Maybe<usize>` | Byte width of the sequence at a byte index; advance `index` by this to iterate codepoints. |
| `std.unicode.nextIndex(text, index)` | `Maybe<usize>` | Next byte index after the codepoint at `index`; `null` for invalid input. |
| `std.unicode.invalidIndex(text)` | `usize` | First invalid UTF-8 byte index, or the input length when valid. |
| `std.unicode.decodeStatusAt(text, index)` | `u32` | Strict UTF-8 status code at a byte index. |
| `std.unicode.statusName(status)` | `String` | Names a status code such as `truncated sequence`. |
| `std.unicode.encode(buffer, cp)` | `Maybe<Span<u8>>` | Encodes a codepoint as UTF-8 into a caller buffer; `null` for surrogates, values above `U+10FFFF`, or a too-small buffer. |
| `std.unicode.encodedWidth(cp)` | `Maybe<usize>` | UTF-8 byte width a codepoint needs (1-4); `null` for invalid codepoints. |
| `std.unicode.isDigit(cp)` | `Bool` | ASCII digit class, matching regex `\d` semantics by codepoint. |
| `std.unicode.isWord(cp)` | `Bool` | ASCII word class `[A-Za-z0-9_]`, matching regex `\w` semantics. |
| `std.unicode.isSpace(cp)` | `Bool` | ECMA-262 whitespace plus line terminators, matching regex `\s` semantics. |
## Example
```zero
pub fn main(world: World) -> Void raises {
let text: Span<u8> = std.mem.span("aé💯")
var index: usize = 0
var count: usize = 0
while index < std.mem.len(text) {
let next: Maybe<usize> = std.unicode.nextIndex(text, index)
if !next.has {
return
}
index = next.value
count = count + 1
}
var storage: [4]u8 = [0; 4]
let buffer: MutSpan<u8> = storage
let encoded: Maybe<Span<u8>> = std.unicode.encode(buffer, 233)
if count == 3 && std.unicode.invalidIndex(text) == std.mem.len(text) && (encoded.has && std.mem.len(encoded.value) == 2) {
check world.out.write("unicode ok\n")
}
}
```
Effects: none.
Allocation behavior: `encode` writes the caller buffer; all other helpers
allocate nothing.
Error behavior: decode/encode and cursor helpers return `null` for invalid
input. `decodeStatusAt` and `statusName` provide allocation-free status details;
class helpers are infallible.
Target support: current compiler targets.
+71
View File
@@ -0,0 +1,71 @@
## When To Use std.url
In Zerolang, use `std.url` for lexical URL splitting, percent encoding, decoded
query lookup, form-urlencoded bodies, and query appending.
Runnable today:
| API | Return | Notes |
| --- | --- | --- |
| `std.url.percentEncode(buffer, bytes)` | `Maybe<Span<u8>>` | Percent-encodes bytes into caller storage. |
| `std.url.percentDecode(buffer, bytes)` | `Maybe<Span<u8>>` | Percent-decodes bytes into caller storage. |
| `std.url.queryEscape(buffer, bytes)` | `Maybe<Span<u8>>` | Query-escapes bytes, using `+` for spaces. |
| `std.url.queryUnescape(buffer, bytes)` | `Maybe<Span<u8>>` | Query-unescapes bytes, converting `+` back to space. |
| `std.url.scheme(url)` | `Maybe<Span<u8>>` | Borrows the URL scheme if present. |
| `std.url.authority(url)` | `Maybe<Span<u8>>` | Borrows the URL authority if present. |
| `std.url.host(url)` | `Maybe<Span<u8>>` | Borrows the host from the URL authority. |
| `std.url.path(url)` | `Span<u8>` | Borrows the path or an empty suffix. |
| `std.url.query(url)` | `Maybe<Span<u8>>` | Borrows the raw query string if present. |
| `std.url.fragment(url)` | `Maybe<Span<u8>>` | Borrows the raw fragment if present. |
| `std.url.queryValue(query, key)` | `Maybe<Span<u8>>` | Borrows a raw query parameter value by key. |
| `std.url.queryValueDecoded(buffer, query, key)` | `Maybe<Span<u8>>` | Looks up a raw or escaped query key and writes the decoded value. |
| `std.url.writeQueryParam(buffer, key, value)` | `Maybe<Span<u8>>` | Writes an escaped `key=value` query parameter. |
| `std.url.writeFormField(buffer, key, value)` | `Maybe<Span<u8>>` | Writes one application/x-www-form-urlencoded field. |
| `std.url.appendFormField(buffer, form, field)` | `Maybe<Span<u8>>` | Appends one encoded field to an existing form body. |
| `std.url.formValue(buffer, form, key)` | `Maybe<Span<u8>>` | Looks up a form field by raw or escaped key and writes the decoded value. |
| `std.url.appendQuery(buffer, base, query)` | `Maybe<Span<u8>>` | Writes a URL with an appended raw query segment. |
| `std.url.writeUrl(buffer, scheme, host, path)` | `Maybe<Span<u8>>` | Writes a `scheme://host/path` URL. |
| `std.url.appendFragment(buffer, base, fragment)` | `Maybe<Span<u8>>` | Writes a URL with an appended raw fragment. |
Metadata labels:
- effects: parse
- allocation behavior: no allocation; writers use caller storage
- target support: target-neutral
- error behavior: `Maybe` helpers return null on malformed input or insufficient storage
- ownership notes: borrowed slices point into the input; encoded output points into caller storage
- examples: `conformance/native/pass/std-codec-json-url.graph`
## Example
```zero
pub fn main(world: World) -> Void raises {
let url: Span<u8> = "https://example.com/path?q=zero%20lang#part"
let host: Maybe<Span<u8>> = std.url.host(url)
let query: Maybe<Span<u8>> = std.url.query(url)
let fragment: Maybe<Span<u8>> = std.url.fragment(url)
var out: [48]u8 = [0_u8; 48]
var param_buf: [16]u8 = [0_u8; 16]
let param: Maybe<Span<u8>> = std.url.writeQueryParam(param_buf, "q", "zero lang")
var decoded_buf: [16]u8 = [0_u8; 16]
var decoded: Maybe<Span<u8>> = null
var next: Maybe<Span<u8>> = null
if param.has {
next = std.url.appendQuery(out, "https://example.com/path", param.value)
}
if query.has {
decoded = std.url.queryValueDecoded(decoded_buf, query.value, "q")
}
if host.has && query.has && fragment.has && decoded.has && next.has && std.mem.eql(host.value, "example.com") && std.mem.eql(decoded.value, "zero lang") {
check world.out.write("url ok\n")
}
}
```
## Design Notes
URL helpers are lexical and byte-oriented. They do not resolve DNS, normalize
paths, or allocate. Decoding rejects malformed percent escapes. Form helpers use
the same encoding as query strings: spaces become `+`, and other non-unreserved
bytes are percent-escaped. URL builders expect path, query, and fragment bytes
that are already escaped for their position.