diff --git a/gen/rust/t27os/release.rs b/gen/rust/t27os/release.rs new file mode 100644 index 0000000000..bb45155e81 --- /dev/null +++ b/gen/rust/t27os/release.rs @@ -0,0 +1,636 @@ +// Generated from .t27 spec +// DO NOT EDIT — generated by t27c +// NOT LOWERED BY THIS BACKEND: 21 test(s), 0 invariant(s), 0 function(s) with no body. +// This backend emits declarations only. The spec's checks live in +// the Zig and Verilog outputs; do not read this file as verified. + +pub const VERSION_MAJOR: u32 = 0; + +pub const VERSION_MINOR: u32 = 1; + +pub const VERSION_PATCH: u32 = 0; + +pub const NO_VERSION: u64 = 18446744073709551615; + +pub const PART_MAX: u32 = 999999; + +pub fn pack_version(major: u32, minor: u32, patch: u32) -> u64 { + return ((((major as u64) << 40) | ((minor as u64) << 20)) | (patch as u64)); +} + +pub fn current_version() -> u64 { + return pack_version(VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH); +} + +pub fn version_major(v: u64) -> u32 { + return (((v >> 40) & 1048575) as u32); +} + +pub fn version_minor(v: u64) -> u32 { + return (((v >> 20) & 1048575) as u32); +} + +pub fn version_patch(v: u64) -> u32 { + return ((v & 1048575) as u32); +} + +pub fn put_number(out: &mut [u8], cap: usize, w: usize, v: u32) -> usize { + let mut scale: u32 = 1; + while ((v / scale) >= 10) { + scale = (scale * 10); + } + let mut k: usize = w; + let mut rest: u32 = v; + while (scale > 0) { + let d: u32 = (rest / scale); + if (k < cap) { + out[(k) as usize] = ((48 + d) as u8); + } + k += 1; + rest = (rest - (d * scale)); + scale = (scale / 10); + } + return k; +} + +pub fn put_semver(out: &mut [u8], cap: usize, w: usize, v: u64) -> usize { + let mut k: usize = put_number(out, cap, w, version_major(v)); + k = put_str(out, cap, k, ".", 1); + k = put_number(out, cap, k, version_minor(v)); + k = put_str(out, cap, k, ".", 1); + return put_number(out, cap, k, version_patch(v)); +} + +pub fn read_part(t: &[u8], n: usize, at: usize) -> u64 { + if (at >= n) { + return NO_VERSION; + } + let mut i: usize = at; + let mut v: u32 = 0; + while (((i < n) && (t[(i) as usize] >= b'0')) && (t[(i) as usize] <= b'9')) { + if ((i - at) >= 6) { + return NO_VERSION; + } + v = ((v * 10) + ((t[(i) as usize] - b'0') as u32)); + i += 1; + } + if (i == at) { + return NO_VERSION; + } + if ((t[(at) as usize] == b'0') && ((i - at) > 1)) { + return NO_VERSION; + } + return (((v as u64) << 32) | (i as u64)); +} + +pub fn semver_parse(t: &[u8], n: usize) -> u64 { + let mut a: u64 = read_part(t, n, 0); + if (a == NO_VERSION) { + return NO_VERSION; + } + let mut p: usize = ((a & 4294967295) as usize); + if ((p >= n) || (t[(p) as usize] != b'.')) { + return NO_VERSION; + } + let mut b: u64 = read_part(t, n, (p + 1)); + if (b == NO_VERSION) { + return NO_VERSION; + } + p = ((b & 4294967295) as usize); + if ((p >= n) || (t[(p) as usize] != b'.')) { + return NO_VERSION; + } + let mut c: u64 = read_part(t, n, (p + 1)); + if (c == NO_VERSION) { + return NO_VERSION; + } + p = ((c & 4294967295) as usize); + if (p != n) { + return NO_VERSION; + } + return pack_version(((a >> 32) as u32), ((b >> 32) as u32), ((c >> 32) as u32)); +} + +pub fn is_newer(candidate: u64, current: u64) -> bool { + if ((candidate == NO_VERSION) || (current == NO_VERSION)) { + return false; + } + return (candidate > current); +} + +pub const MANIFEST_URL: &'static str = "https://github.com/gHashTag/t27/releases/latest/download/t27os-release.t27"; + +pub const URL_PREFIX: &'static str = "https://github.com/gHashTag/t27/releases/"; + +pub const URL_PREFIX_LEN: usize = 41; + +pub const KEY_VERSION: &'static str = "pub const VERSION : str = "; + +pub const KEY_BUILD: &'static str = "pub const BUILD : u32 = "; + +pub const KEY_SHA: &'static str = "pub const SHA256 : str = "; + +pub const KEY_MIN: &'static str = "pub const MIN_MACOS : str = "; + +pub const KEY_URL: &'static str = "pub const URL : str = "; + +pub const KEY_NOTES: &'static str = "pub const NOTES : str = "; + +pub const M_OK: u8 = 0; + +pub const M_NO_VERSION: u8 = 1; + +pub const M_NO_BUILD: u8 = 2; + +pub const M_BAD_SHA: u8 = 3; + +pub const M_BAD_MIN: u8 = 4; + +pub const M_BAD_URL: u8 = 5; + +pub const M_BAD_NOTES: u8 = 6; + +pub const NO_FIELD: u64 = 18446744073709551615; + +pub fn at_line_start(t: &[u8], i: usize) -> bool { + if (i == 0) { + return true; + } + return (t[((i - 1)) as usize] == 10); +} + +pub fn starts_here(t: &[u8], n: usize, at: usize, key: &'static str, key_len: usize) -> bool { + if ((at + key_len) > n) { + return false; + } + let mut j: usize = 0; + while (j < key_len) { + if (t[((at + j)) as usize] != key.as_bytes()[(j) as usize]) { + return false; + } + j += 1; + } + return true; +} + +pub fn field_str(t: &[u8], n: usize, key: &'static str, key_len: usize) -> u64 { + let mut i: usize = 0; + while (i < n) { + if (at_line_start(t, i) && starts_here(t, n, i, key, key_len)) { + let q: usize = (i + key_len); + if ((q >= n) || (t[(q) as usize] != 34)) { + return NO_FIELD; + } + let mut e: usize = (q + 1); + while (((e < n) && (t[(e) as usize] != 34)) && (t[(e) as usize] != 10)) { + e += 1; + } + if ((e >= n) || (t[(e) as usize] != 34)) { + return NO_FIELD; + } + return ((((q + 1) as u64) << 32) | (((e - q) - 1) as u64)); + } + i += 1; + } + return NO_FIELD; +} + +pub fn field_num(t: &[u8], n: usize, key: &'static str, key_len: usize) -> u64 { + let mut i: usize = 0; + while (i < n) { + if (at_line_start(t, i) && starts_here(t, n, i, key, key_len)) { + let mut e: usize = (i + key_len); + let mut v: u64 = 0; + while (((e < n) && (t[(e) as usize] >= b'0')) && (t[(e) as usize] <= b'9')) { + if ((e - (i + key_len)) >= 9) { + return NO_FIELD; + } + v = ((v * 10) + ((t[(e) as usize] - b'0') as u64)); + e += 1; + } + if (e == (i + key_len)) { + return NO_FIELD; + } + return v; + } + i += 1; + } + return NO_FIELD; +} + +pub fn field_start(f: u64) -> usize { + return ((f >> 32) as usize); +} + +pub fn field_len(f: u64) -> usize { + return ((f & 4294967295) as usize); +} + +pub fn sha_ok(t: &[u8], n: usize) -> bool { + if (n != 64) { + return false; + } + let mut i: usize = 0; + while (i < n) { + let c: u8 = t[(i) as usize]; + if !((((c >= b'0') && (c <= b'9')) || ((c >= b'a') && (c <= b'f')))) { + return false; + } + i += 1; + } + return true; +} + +pub fn url_ok(t: &[u8], n: usize) -> bool { + if (n <= URL_PREFIX_LEN) { + return false; + } + if !(starts_here(t, n, 0, URL_PREFIX, URL_PREFIX_LEN)) { + return false; + } + let mut i: usize = 0; + while (i < n) { + if (((((t[(i) as usize] <= 32) || (t[(i) as usize] == 34)) || (t[(i) as usize] == 39)) || (t[(i) as usize] == 92)) || (t[(i) as usize] > 126)) { + return false; + } + i += 1; + } + return true; +} + +pub fn macos_parse(t: &[u8], n: usize) -> u32 { + let mut a: u64 = read_part(t, n, 0); + if (a == NO_VERSION) { + return 4294967295; + } + let major: u32 = ((a >> 32) as u32); + let mut p: usize = ((a & 4294967295) as usize); + if (p == n) { + return (major * 1000); + } + if (t[(p) as usize] != b'.') { + return 4294967295; + } + let mut b: u64 = read_part(t, n, (p + 1)); + if (b == NO_VERSION) { + return 4294967295; + } + p = ((b & 4294967295) as usize); + if (p != n) { + return 4294967295; + } + return ((major * 1000) + ((b >> 32) as u32)); +} + +pub fn macos_meets(min_text: &[u8], n: usize, host: u32) -> bool { + let m: u32 = macos_parse(min_text, n); + if (m == 4294967295) { + return false; + } + return (host >= m); +} + +pub fn manifest_check(t: &[u8], n: usize) -> u8 { + let v: u64 = field_str(t, n, KEY_VERSION, 26); + if (v == NO_FIELD) { + return M_NO_VERSION; + } + if (semver_parse(&t[(field_start(v)) as usize..((field_start(v) + field_len(v))) as usize], field_len(v)) == NO_VERSION) { + return M_NO_VERSION; + } + if (field_num(t, n, KEY_BUILD, 24) == NO_FIELD) { + return M_NO_BUILD; + } + let s: u64 = field_str(t, n, KEY_SHA, 25); + if (s == NO_FIELD) { + return M_BAD_SHA; + } + if !(sha_ok(&t[(field_start(s)) as usize..((field_start(s) + field_len(s))) as usize], field_len(s))) { + return M_BAD_SHA; + } + let m: u64 = field_str(t, n, KEY_MIN, 28); + if (m == NO_FIELD) { + return M_BAD_MIN; + } + if (macos_parse(&t[(field_start(m)) as usize..((field_start(m) + field_len(m))) as usize], field_len(m)) == 4294967295) { + return M_BAD_MIN; + } + let u: u64 = field_str(t, n, KEY_URL, 22); + if (u == NO_FIELD) { + return M_BAD_URL; + } + if !(url_ok(&t[(field_start(u)) as usize..((field_start(u) + field_len(u))) as usize], field_len(u))) { + return M_BAD_URL; + } + let w: u64 = field_str(t, n, KEY_NOTES, 24); + if (w == NO_FIELD) { + return M_BAD_NOTES; + } + if !(url_ok(&t[(field_start(w)) as usize..((field_start(w) + field_len(w))) as usize], field_len(w))) { + return M_BAD_NOTES; + } + return M_OK; +} + +pub fn put_line_text(out: &mut [u8], cap: usize, w: usize, s: &'static str, s_len: usize) -> usize { + let mut k: usize = put_str(out, cap, w, s, s_len); + if (k < cap) { + out[(k) as usize] = 10; + } + return (k + 1); +} + +pub fn put_slice(out: &mut [u8], cap: usize, w: usize, b: &[u8], b_len: usize) -> usize { + let mut i: usize = 0; + while (i < b_len) { + if ((w + i) < cap) { + out[((w + i)) as usize] = b[(i) as usize]; + } + i += 1; + } + return (w + b_len); +} + +pub fn put_tag(out: &mut [u8], cap: usize, w: usize, v: u64) -> usize { + let mut k: usize = put_str(out, cap, w, "v", 1); + return put_semver(out, cap, k, v); +} + +pub fn put_zip_name(out: &mut [u8], cap: usize, w: usize, v: u64) -> usize { + let mut k: usize = put_str(out, cap, w, "T27OS-", 6); + k = put_semver(out, cap, k, v); + return put_str(out, cap, k, ".zip", 4); +} + +pub fn manifest_text(out: &mut [u8], cap: usize, v: u64, build: u32, sha: &[u8], sha_len: usize) -> usize { + let mut w: usize = 0; + w = put_line_text(out, cap, w, "; T27 OS release manifest, written by specs/t27os/release.t27 manifest_text. The app reads the", 94); + w = put_line_text(out, cap, w, "; six constants below and nothing else.", 39); + w = put_line_text(out, cap, w, "module T27osReleaseManifest;", 28); + w = put_str(out, cap, w, KEY_VERSION, 26); + w = put_str(out, cap, w, "\"", 1); + w = put_semver(out, cap, w, v); + w = put_line_text(out, cap, w, "\";", 2); + w = put_str(out, cap, w, KEY_BUILD, 24); + w = put_number(out, cap, w, build); + w = put_line_text(out, cap, w, ";", 1); + w = put_str(out, cap, w, KEY_SHA, 25); + w = put_str(out, cap, w, "\"", 1); + w = put_slice(out, cap, w, sha, sha_len); + w = put_line_text(out, cap, w, "\";", 2); + w = put_str(out, cap, w, KEY_MIN, 28); + w = put_line_text(out, cap, w, "\"11.0\";", 7); + w = put_str(out, cap, w, KEY_URL, 22); + w = put_str(out, cap, w, "\"", 1); + w = put_str(out, cap, w, URL_PREFIX, 41); + w = put_str(out, cap, w, "download/", 9); + w = put_tag(out, cap, w, v); + w = put_str(out, cap, w, "/", 1); + w = put_zip_name(out, cap, w, v); + w = put_line_text(out, cap, w, "\";", 2); + w = put_str(out, cap, w, KEY_NOTES, 24); + w = put_str(out, cap, w, "\"", 1); + w = put_str(out, cap, w, URL_PREFIX, 41); + w = put_str(out, cap, w, "tag/", 4); + w = put_tag(out, cap, w, v); + w = put_line_text(out, cap, w, "\";", 2); + return w; +} + +pub const CHECK_EVERY_S: u64 = 21600; + +pub const CONFIRM_AFTER_S: u64 = 60; + +pub fn check_due(now: u64, last_check: u64) -> bool { + if (last_check == 0) { + return true; + } + if (now < last_check) { + return true; + } + return ((now - last_check) >= CHECK_EVERY_S); +} + +pub fn idle_enough(busy_panes: u32) -> bool { + return (busy_panes == 0); +} + +pub fn should_check(now: u64, last_check: u64, busy_panes: u32) -> bool { + return (check_due(now, last_check) && idle_enough(busy_panes)); +} + +pub const OFFER_NONE: u8 = 0; + +pub const OFFER_UPDATE: u8 = 1; + +pub const OFFER_BAD_MANIFEST: u8 = 2; + +pub const OFFER_OLD_MACOS: u8 = 3; + +pub const OFFER_REJECTED: u8 = 4; + +pub fn offer(check: u8, candidate: u64, current: u64, rejected: u64, os_ok: bool) -> u8 { + if (check != M_OK) { + return OFFER_BAD_MANIFEST; + } + if !(is_newer(candidate, current)) { + return OFFER_NONE; + } + if (candidate == rejected) { + return OFFER_REJECTED; + } + if !(os_ok) { + return OFFER_OLD_MACOS; + } + return OFFER_UPDATE; +} + +pub fn sha_matches(computed: &[u8], computed_len: usize, wanted: &[u8], wanted_len: usize) -> bool { + if (!(sha_ok(computed, computed_len)) || !(sha_ok(wanted, wanted_len))) { + return false; + } + let mut i: usize = 0; + while (i < 64) { + if (computed[(i) as usize] != wanted[(i) as usize]) { + return false; + } + i += 1; + } + return true; +} + +pub const U_IDLE: u8 = 0; + +pub const U_CHECKING: u8 = 1; + +pub const U_DOWNLOADING: u8 = 2; + +pub const U_VERIFYING: u8 = 3; + +pub const U_STAGED: u8 = 4; + +pub const U_SWAPPED: u8 = 5; + +pub const EV_CHECK: u8 = 0; + +pub const EV_OFFER: u8 = 1; + +pub const EV_NO_OFFER: u8 = 2; + +pub const EV_DOWNLOADED: u8 = 3; + +pub const EV_FAILED: u8 = 4; + +pub const EV_SHA_OK: u8 = 5; + +pub const EV_SHA_BAD: u8 = 6; + +pub const EV_SIGNED: u8 = 7; + +pub const EV_SWAP: u8 = 8; + +pub fn update_step(state: u8, event: u8, busy_panes: u32) -> u8 { + if (((event == EV_FAILED) || (event == EV_SHA_BAD)) || (event == EV_NO_OFFER)) { + return U_IDLE; + } + if ((state == U_IDLE) && (event == EV_CHECK)) { + if idle_enough(busy_panes) { + return U_CHECKING; + } + return U_IDLE; + } + if ((state == U_CHECKING) && (event == EV_OFFER)) { + return U_DOWNLOADING; + } + if ((state == U_DOWNLOADING) && (event == EV_DOWNLOADED)) { + return U_VERIFYING; + } + if ((state == U_VERIFYING) && (event == EV_SHA_OK)) { + return U_VERIFYING; + } + if ((state == U_VERIFYING) && (event == EV_SIGNED)) { + return U_STAGED; + } + if ((state == U_STAGED) && (event == EV_SWAP)) { + if idle_enough(busy_panes) { + return U_SWAPPED; + } + return U_STAGED; + } + return state; +} + +pub const BOOT_RUN: u8 = 0; + +pub const BOOT_ROLLBACK: u8 = 1; + +pub const BOOT_BROKEN: u8 = 2; + +pub fn boot_action(pending_marker: bool, previous_kept: bool) -> u8 { + if !(pending_marker) { + return BOOT_RUN; + } + if previous_kept { + return BOOT_ROLLBACK; + } + return BOOT_BROKEN; +} + +pub fn confirm_due(uptime_s: u64) -> bool { + return (uptime_s >= CONFIRM_AFTER_S); +} + +pub fn previous_after(swapped: bool, rolled_back: bool, previous_kept: bool) -> bool { + if rolled_back { + return false; + } + if swapped { + return true; + } + return previous_kept; +} + +pub fn rejected_after(boot: u8, running: u64, rejected: u64) -> u64 { + if (boot == BOOT_ROLLBACK) { + return running; + } + return rejected; +} + +pub const PLAN_STEPS: u32 = 6; + +pub fn plan_text(out: &mut [u8], cap: usize, v: u64) -> usize { + let mut w: usize = 0; + w = put_str(out, cap, w, "# T27 OS release ", 17); + w = put_semver(out, cap, w, v); + w = put_line_text(out, cap, w, " -- the plan only; nothing below was run", 40); + w = put_line_text(out, cap, w, "# Publishing needs the owner's yes. T27OS_SIGN_ID names a Developer ID Application identity (else ad hoc).", 106); + w = put_line_text(out, cap, w, "1. build:", 9); + w = put_line_text(out, cap, w, " make t27os T27OS_BUILD=$(git rev-list --count HEAD)", 54); + w = put_line_text(out, cap, w, "2. zip:", 7); + w = put_str(out, cap, w, " ditto -c -k --keepParent target/t27os/T27OS.app target/t27os/", 64); + w = put_zip_name(out, cap, w, v); + w = put_line_text(out, cap, w, "", 0); + w = put_line_text(out, cap, w, "3. sha256:", 10); + w = put_str(out, cap, w, " shasum -a 256 target/t27os/", 30); + w = put_zip_name(out, cap, w, v); + w = put_line_text(out, cap, w, "", 0); + w = put_line_text(out, cap, w, "4. manifest:", 12); + w = put_line_text(out, cap, w, " tri t27os manifest --build $(git rev-list --count HEAD) --sha > target/t27os/t27os-release.t27", 120); + w = put_line_text(out, cap, w, "5. notes (the commits since the last tag):", 42); + w = put_line_text(out, cap, w, " git log --format='- %s' $(git describe --tags --abbrev=0 --match 'v*' 2>/dev/null || git rev-list --max-parents=0 HEAD)..HEAD > target/t27os/notes.md", 152); + w = put_line_text(out, cap, w, "6. publish (the owner's yes first):", 35); + w = put_str(out, cap, w, " gh release create ", 21); + w = put_tag(out, cap, w, v); + w = put_str(out, cap, w, " target/t27os/", 14); + w = put_zip_name(out, cap, w, v); + w = put_str(out, cap, w, " target/t27os/t27os-release.t27 --title 'T27 OS ", 48); + w = put_semver(out, cap, w, v); + w = put_line_text(out, cap, w, "' --notes-file target/t27os/notes.md", 36); + return w; +} + +pub fn publish_allowed(plan_only: bool, owner_yes: bool) -> bool { + if plan_only { + return false; + } + return owner_yes; +} + +pub const REFUSAL: &'static str = "tri t27os release: publishing needs the owner's yes and is not done by this verb; run it with --plan and follow the steps by hand"; + +pub const REFUSAL_LEN: usize = 129; + +pub fn buf_has(buf: &[u8], buf_len: usize, needle: &'static str, needle_len: usize) -> bool { + if (needle_len > buf_len) { + return false; + } + let mut start: usize = 0; + while ((start + needle_len) <= buf_len) { + let mut k: usize = 0; + let mut diff: usize = 0; + while (k < needle_len) { + if (buf[((start + k)) as usize] != needle.as_bytes()[(k) as usize]) { + diff += 1; + } + k += 1; + } + if (diff == 0) { + return true; + } + start += 1; + } + return false; +} + +pub fn put_str(dst: &mut [u8], cap: usize, w: usize, s: &'static str, s_len: usize) -> usize { + let mut i: usize = 0; + while (i < s_len) { + if ((w + i) < cap) { + dst[((w + i)) as usize] = s.as_bytes()[(i) as usize]; + } + i += 1; + } + return (w + s_len); +} +