diff options
| author | salmonik <salmonik@tuta.io> | 2026-08-27 16:58:31 +0200 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-08-27 16:58:31 +0200 |
| commit | 300c0acbd7ab3fae8596ff839c39d9a17c70c68d (patch) | |
| tree | 5a600b24c037dc798766e3ce3fc004956e517abb /bins/count | |
| parent | exec: PE32+ loader, versioned ABI, first external binaries (v0.6.0) (diff) | |
| download | rs-dos-300c0acbd7ab3fae8596ff839c39d9a17c70c68d.tar.gz rs-dos-300c0acbd7ab3fae8596ff839c39d9a17c70c68d.zip | |
streams: per-invocation channel, pumping mode, filter pipeline wiring
exec: LineChannel bounded ring (cap=16) with abort/eof/marker fields.
Shell 'pumping mode' assembles canonical lines from its queue into the
channel while the command runs; ^C marks the channel aborted.
Shell-side grammar: USE/TIL pre-drain into the channel before spawn;
<cmd> <path> TIL <marker> sugar drains paths until the marker line.
Interactive TIL: pump_key checks each submitted line against the marker
and closes the channel when it matches.
Exports callbacks run in the command task but touch shell-owned state:
this is safe by construction — the shell blocks in exec_external until
the command completes. put_line feeds Pipeline::line directly;
get_line pops from the channel (yield-poll when empty).
load: drop NX from image mapping (single-privilege kernel, NX bit 63
reserved when EFER.NXE is set by OVMF); compute image span from section
RVAs not file size.
bins/count: gate binary — reads lines via get_line, reports count via
put_line. Verified: count USE file → 6; count <path> TIL END → 2;
interactive count TIL DONE → 3; -h filter across task boundary;
^C abort.
v1 stream exports: put_line → Pipeline, get_line ← channel, log_line
→ klog append, exit → abort_current.
Accounting: print_one_line → pipe.line() returns false (head kill /
pager ^C) → the binary should wrap up promptly; accepted: binary does
not check the return value in v1 (phase 3 simplification).
Diffstat (limited to 'bins/count')
| -rw-r--r-- | bins/count/Cargo.toml | 10 | ||||
| -rw-r--r-- | bins/count/src/main.rs | 64 |
2 files changed, 74 insertions, 0 deletions
diff --git a/bins/count/Cargo.toml b/bins/count/Cargo.toml new file mode 100644 index 0000000..5ab50cf --- /dev/null +++ b/bins/count/Cargo.toml | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | [package] | ||
| 2 | name = "count" | ||
| 3 | version.workspace = true | ||
| 4 | edition.workspace = true | ||
| 5 | |||
| 6 | [lints] | ||
| 7 | workspace = true | ||
| 8 | |||
| 9 | [dependencies] | ||
| 10 | rsapi = { path = "../../rsapi" } | ||
diff --git a/bins/count/src/main.rs b/bins/count/src/main.rs new file mode 100644 index 0000000..6fd6f17 --- /dev/null +++ b/bins/count/src/main.rs | |||
| @@ -0,0 +1,64 @@ | |||
| 1 | //! count — stream-input consumer (ADR 0006 Phase 3 gate). | ||
| 2 | //! | ||
| 3 | //! Reads lines through `get_line` until EOF or Abort, then reports how | ||
| 4 | //! many it saw via `put_line`. Proves: USE pre-drain, TIL truncation, | ||
| 5 | //! interactive pumping, and ^C abort across the task boundary. | ||
| 6 | |||
| 7 | #![no_std] | ||
| 8 | #![no_main] | ||
| 9 | |||
| 10 | use rsapi::{Exports, StreamResult}; | ||
| 11 | |||
| 12 | #[panic_handler] | ||
| 13 | fn panic(_info: &core::panic::PanicInfo) -> ! { | ||
| 14 | loop { | ||
| 15 | core::hint::spin_loop(); | ||
| 16 | } | ||
| 17 | } | ||
| 18 | |||
| 19 | fn put_u32(exports: &Exports, mut v: u32) { | ||
| 20 | // Small fixed buffer; u32 max is 10 digits. | ||
| 21 | let mut buf = [0u8; 10]; | ||
| 22 | let mut i = buf.len(); | ||
| 23 | if v == 0 { | ||
| 24 | i -= 1; | ||
| 25 | buf[i] = b'0'; | ||
| 26 | } | ||
| 27 | while v > 0 { | ||
| 28 | i -= 1; | ||
| 29 | buf[i] = b'0' + (v % 10) as u8; | ||
| 30 | v /= 10; | ||
| 31 | } | ||
| 32 | let suffix = b" line(s)"; | ||
| 33 | let mut msg = [0u8; 18]; | ||
| 34 | let n = buf.len() - i; | ||
| 35 | msg[..n].copy_from_slice(&buf[i..]); | ||
| 36 | msg[n..n + suffix.len()].copy_from_slice(suffix); | ||
| 37 | (exports.put_line)(rsapi::Line { | ||
| 38 | ptr: msg.as_ptr(), | ||
| 39 | len: n + suffix.len(), | ||
| 40 | }); | ||
| 41 | } | ||
| 42 | |||
| 43 | /// The image entry point — the loader jumps here with the ABI arguments. | ||
| 44 | #[unsafe(no_mangle)] | ||
| 45 | pub extern "C" fn efi_main( | ||
| 46 | exports: &'static Exports, | ||
| 47 | _argc: usize, | ||
| 48 | _argv: *const rsapi::Arg, | ||
| 49 | ) -> i32 { | ||
| 50 | let mut count: u32 = 0; | ||
| 51 | loop { | ||
| 52 | let mut line = rsapi::Line { | ||
| 53 | ptr: core::ptr::null(), | ||
| 54 | len: 0, | ||
| 55 | }; | ||
| 56 | match (exports.get_line)(&mut line) { | ||
| 57 | StreamResult::Line => count += 1, | ||
| 58 | StreamResult::Eof => break, | ||
| 59 | StreamResult::Abort => return 130, // 128+SIGINT convention | ||
| 60 | } | ||
| 61 | } | ||
| 62 | put_u32(exports, count); | ||
| 63 | 0 | ||
| 64 | } | ||
