From 65ce9b23a0c150feead630e84ae45242fb05d8d0 Mon Sep 17 00:00:00 2001 From: midden-fabler Date: Fri, 29 Aug 2025 10:03:32 -0400 Subject: [PATCH 01/15] ford: add memo bucket for ford --- pkg/c3/motes.h | 1 + pkg/noun/allocate.h | 1 + pkg/noun/manage.c | 1 + pkg/noun/nock.c | 24 +++++++++++++++++++++--- pkg/noun/zave.c | 5 +++++ pkg/noun/zave.h | 2 +- 6 files changed, 30 insertions(+), 4 deletions(-) diff --git a/pkg/c3/motes.h b/pkg/c3/motes.h index 3ba8a700ed..21dd9dac01 100644 --- a/pkg/c3/motes.h +++ b/pkg/c3/motes.h @@ -451,6 +451,7 @@ # define c3__fond c3_s4('f','o','n','d') # define c3__for c3_s3('f','o','r') # define c3__forb c3_s4('f','o','r','b') +# define c3__ford c3_s4('f','o','r','d') # define c3__fore c3_s4('f','o','r','e') # define c3__fork c3_s4('f','o','r','k') # define c3__form c3_s4('f','o','r','m') diff --git a/pkg/noun/allocate.h b/pkg/noun/allocate.h index ea1feb8817..262720e181 100644 --- a/pkg/noun/allocate.h +++ b/pkg/noun/allocate.h @@ -181,6 +181,7 @@ struct { // memoization caches u3p(u3h_root) har_p; // transient u3p(u3h_root) per_p; // persistent + u3p(u3h_root) for_p; // ford } cax; } u3a_road; typedef u3a_road u3_road; diff --git a/pkg/noun/manage.c b/pkg/noun/manage.c index 6ce366109c..df983134e8 100644 --- a/pkg/noun/manage.c +++ b/pkg/noun/manage.c @@ -499,6 +499,7 @@ _pave_parts(void) { u3R->cax.har_p = u3h_new_cache(u3C.hap_w); // transient u3R->cax.per_p = u3h_new_cache(u3C.per_w); // persistent + u3R->cax.for_p = u3h_new_cache(u3C.per_w); // ford u3R->jed.war_p = u3h_new(); u3R->jed.cod_p = u3h_new(); u3R->jed.han_p = u3h_new(); diff --git a/pkg/noun/nock.c b/pkg/noun/nock.c index b7dc802c42..f978b76379 100644 --- a/pkg/noun/nock.c +++ b/pkg/noun/nock.c @@ -1140,7 +1140,15 @@ _n_bint(u3_noun* ops, u3_noun hif, u3_noun nef, c3_o los_o, c3_o tel_o) { u3_weak con = u3r_skip(hod); if ( (u3_none != con) && (c3y == u3du(con)) ) { - cid = u3z_memo_keep; + if ( (c3y == u3du(u3t(con))) + && (c3__clay == u3h(con)) + && (c3__ford == u3h(u3t(con))) ) + { + cid = u3z_memo_ford; + } + else { + cid = u3z_memo_keep; + } } } ++tot_w; _n_emit(ops, u3nq(op_y, cid, mem_w, u3k(nef))); @@ -2681,7 +2689,14 @@ _n_burn(u3n_prog* pog_u, u3_noun bus, c3_ys mov, c3_ys off) skim_out: o = u3k(mem_u->key); x = u3nc(x, o); - o = u3z_find_m(mem_u->cid, 144 + c3__nock, x); + switch ( mem_u->cid ) { + case u3z_memo_ford: { + o = u3z_find_m(mem_u->cid, 136 + c3__ford, x); + } break; + default: { + o = u3z_find_m(mem_u->cid, 144 + c3__nock, x); + } + } if ( u3_none == o ) { _n_push(mov, off, u3nc(mem_u->cid, x)); _n_push(mov, off, u3k(u3h(x))); @@ -2697,7 +2712,10 @@ _n_burn(u3n_prog* pog_u, u3_noun bus, c3_ys mov, c3_ys off) x = _n_pep(mov, off); // product top = _n_peek(off); o = *top; - if ( ( u3z_memo_toss == u3h(o) ) + if ( u3z_memo_ford == u3h(o) ) { + u3z_save_m(u3h(o), 136 + c3__ford, u3t(o), x); + } + else if ( ( u3z_memo_toss == u3h(o) ) ? ( &(u3H->rod_u) != u3R ) : ( 0 == u3R->ski.gul ) ) { // prevents userspace from persistence u3z_save_m(u3h(o), 144 + c3__nock, u3t(o), x); diff --git a/pkg/noun/zave.c b/pkg/noun/zave.c index 25307186b9..d1050363a4 100644 --- a/pkg/noun/zave.c +++ b/pkg/noun/zave.c @@ -46,6 +46,8 @@ _har(u3a_road* rod_u, u3z_cid cid) return rod_u->cax.har_p; case u3z_memo_keep: return rod_u->cax.per_p; + case u3z_memo_ford: + return rod_u->cax.for_p; // design cache key to handle arvo changes that invalidate } u3_assert(0); } @@ -66,6 +68,9 @@ u3z_find(u3z_cid cid, u3_noun key) while ( 1 ) { u3_weak got = u3h_get(_har(rod_u, cid), key); if ( u3_none != got ) { + if ( u3z_memo_ford == cid ) { + u3m_p("found ford", 1); + } return got; } if ( 0 == rod_u->kid_p ) { diff --git a/pkg/noun/zave.h b/pkg/noun/zave.h index f7e2be9f36..823604fb2d 100644 --- a/pkg/noun/zave.h +++ b/pkg/noun/zave.h @@ -19,7 +19,7 @@ typedef enum { u3z_memo_toss = 0, u3z_memo_keep = 1, - // u3z_memo_ford = 2, + u3z_memo_ford = 2, // u3z_memo_ames = 3, // ... } u3z_cid; From c6196bf5429283db02cdec4471ecf3e684ac04a3 Mon Sep 17 00:00:00 2001 From: midden-fabler Date: Mon, 8 Sep 2025 13:40:56 -0400 Subject: [PATCH 02/15] noun: add mass for ford cache --- pkg/noun/allocate.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/pkg/noun/allocate.c b/pkg/noun/allocate.c index fd278549a4..9dc36cc5f3 100644 --- a/pkg/noun/allocate.c +++ b/pkg/noun/allocate.c @@ -2240,7 +2240,7 @@ u3a_print_quac(FILE* fil_u, c3_w den_w, u3m_quac* mas_u) u3m_quac* u3a_mark_road() { - u3m_quac** qua_u = c3_malloc(sizeof(*qua_u) * 9); + u3m_quac** qua_u = c3_malloc(sizeof(*qua_u) * 10); qua_u[0] = c3_calloc(sizeof(*qua_u[0])); qua_u[0]->nam_c = strdup("namespace"); @@ -2274,10 +2274,14 @@ u3a_mark_road() qua_u[7]->nam_c = strdup("persistent memoization cache"); qua_u[7]->siz_w = u3h_mark(u3R->cax.per_p) * 4; - qua_u[8] = NULL; + qua_u[8] = c3_calloc(sizeof(*qua_u[8])); + qua_u[8]->nam_c = strdup("ford memoization cache"); + qua_u[8]->siz_w = u3h_mark(u3R->cax.for_p) * 4; + + qua_u[9] = NULL; c3_w sum_w = 0; - for (c3_w i_w = 0; i_w < 8; i_w++) { + for (c3_w i_w = 0; i_w < 9; i_w++) { sum_w += qua_u[i_w]->siz_w; } @@ -2313,6 +2317,7 @@ u3a_rewrite_compact(void) u3a_rewrite_noun(u3R->pro.trace); u3h_rewrite(u3R->cax.har_p); u3h_rewrite(u3R->cax.per_p); + u3h_rewrite(u3R->cax.for_p); u3R->ski.gul = u3a_rewritten_noun(u3R->ski.gul); u3R->bug.tax = u3a_rewritten_noun(u3R->bug.tax); @@ -2322,6 +2327,7 @@ u3a_rewrite_compact(void) u3R->pro.trace = u3a_rewritten_noun(u3R->pro.trace); u3R->cax.har_p = u3a_rewritten(u3R->cax.har_p); u3R->cax.per_p = u3a_rewritten(u3R->cax.per_p); + u3R->cax.for_p = u3a_rewritten(u3R->cax.for_p); } /* _ca_print_box(): heuristically print the contents of an allocation box. From 9ea894e5c55f392bb43f667e518c55eb2fe33029 Mon Sep 17 00:00:00 2001 From: midden-fabler Date: Mon, 8 Sep 2025 15:17:56 -0400 Subject: [PATCH 03/15] noun: handle ford cache in u3m_love --- pkg/noun/manage.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/noun/manage.c b/pkg/noun/manage.c index df983134e8..ec34bc1705 100644 --- a/pkg/noun/manage.c +++ b/pkg/noun/manage.c @@ -1199,6 +1199,7 @@ u3m_love(u3_noun pro) u3p(u3h_root) byc_p = u3R->byc.har_p; u3a_jets jed_u = u3R->jed; u3p(u3h_root) per_p = u3R->cax.per_p; + u3p(u3h_root) for_p = u3R->cax.for_p; // fallback to parent road (child heap on parent's stack) // @@ -1210,6 +1211,7 @@ u3m_love(u3_noun pro) jed_u = u3j_take(jed_u); byc_p = u3n_take(byc_p); per_p = u3h_take(per_p); + for_p = u3h_take(for_p); // pop the stack // @@ -1221,6 +1223,7 @@ u3m_love(u3_noun pro) u3j_reap(jed_u); u3n_reap(byc_p); u3z_reap(u3z_memo_keep, per_p); + u3z_reap(u3z_memo_ford, for_p); return pro; } From 693657e8111eb3dc62631911cb8a5faa0a98852f Mon Sep 17 00:00:00 2001 From: midden-fabler Date: Mon, 8 Sep 2025 15:19:37 -0400 Subject: [PATCH 04/15] melt: gc ford cache --- pkg/noun/urth.c | 3 +++ pkg/vere/melt.c | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/pkg/noun/urth.c b/pkg/noun/urth.c index 81c542da8a..ea482dff3e 100644 --- a/pkg/noun/urth.c +++ b/pkg/noun/urth.c @@ -552,6 +552,9 @@ u3u_melt(void) u3h_free(u3R->cax.per_p); u3R->cax.per_p = u3h_new_cache(u3C.per_w); + u3h_free(u3R->cax.for_p); + u3R->cax.for_p = u3h_new_cache(u3C.per_w); + u3h_free(u3R->jed.cod_p); u3R->jed.cod_p = u3h_new(); diff --git a/pkg/vere/melt.c b/pkg/vere/melt.c index 113a08c317..49ca349ccf 100644 --- a/pkg/vere/melt.c +++ b/pkg/vere/melt.c @@ -183,6 +183,7 @@ u3_melt_all(FILE *fil_u) u3h_walk_with(u3R->jed.cod_p, _melt_walk_hamt, &can_u); u3h_walk_with(u3R->cax.per_p, _melt_walk_hamt, &can_u); + u3h_walk_with(u3R->cax.for_p, _melt_walk_hamt, &can_u); u3j_boot(c3n); u3j_ream(); @@ -210,6 +211,9 @@ u3_meld_all(FILE *fil_u) u3h_free(u3R->cax.per_p); u3R->cax.per_p = u3h_new_cache(u3C.per_w); + u3h_free(u3R->cax.for_p); + u3R->cax.for_p = u3h_new_cache(u3C.per_w); + (void)u3_melt_all(fil_u); (void)u3m_pack(); From cc56a89bd5917651aea0522666e634e74d60ad2d Mon Sep 17 00:00:00 2001 From: midden-fabler Date: Mon, 8 Sep 2025 15:20:13 -0400 Subject: [PATCH 05/15] mars: trim ford cache --- pkg/vere/mars.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pkg/vere/mars.c b/pkg/vere/mars.c index c2acbaa1e5..8da1ec56e4 100644 --- a/pkg/vere/mars.c +++ b/pkg/vere/mars.c @@ -724,8 +724,10 @@ _mars_post(u3_mars* mar_u) if ( mar_u->fag_w & _mars_fag_hit1 ) { if ( u3C.wag_w & u3o_verbose ) { u3l_log("mars: threshold 1: %u", u3h_wyt(u3R->cax.per_p)); + u3l_log("mars: threshold 1: %u", u3h_wyt(u3R->cax.for_p)); } u3h_trim_to(u3R->cax.per_p, u3h_wyt(u3R->cax.per_p) / 2); + u3h_trim_to(u3R->cax.for_p, u3h_wyt(u3R->cax.for_p) / 2); u3m_reclaim(); } @@ -735,6 +737,7 @@ _mars_post(u3_mars* mar_u) if ( mar_u->fag_w & _mars_fag_vega ) { u3h_trim_to(u3R->cax.per_p, u3h_wyt(u3R->cax.per_p) / 2); + u3h_trim_to(u3R->cax.for_p, u3h_wyt(u3R->cax.for_p) / 2); u3m_reclaim(); } @@ -748,9 +751,12 @@ _mars_post(u3_mars* mar_u) if ( mar_u->fag_w & _mars_fag_hit0 ) { if ( u3C.wag_w & u3o_verbose ) { u3l_log("mars: threshold 0: per_p %u", u3h_wyt(u3R->cax.per_p)); + u3l_log("mars: threshold 0: for_p %u", u3h_wyt(u3R->cax.for_p)); } u3h_free(u3R->cax.per_p); u3R->cax.per_p = u3h_new_cache(u3C.per_w); + u3h_free(u3R->cax.for_p); + u3R->cax.for_p = u3h_new_cache(u3C.per_w); u3a_print_memory(stderr, "mars: pack: gained", u3m_pack()); u3l_log(""); } From 86a5d9849af7af6475cc7e60cd33c6bd1aba770c Mon Sep 17 00:00:00 2001 From: midden-fabler Date: Fri, 19 Sep 2025 10:16:43 -0400 Subject: [PATCH 06/15] zave: remove ford print --- pkg/noun/zave.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/pkg/noun/zave.c b/pkg/noun/zave.c index d1050363a4..40587ad23f 100644 --- a/pkg/noun/zave.c +++ b/pkg/noun/zave.c @@ -68,9 +68,6 @@ u3z_find(u3z_cid cid, u3_noun key) while ( 1 ) { u3_weak got = u3h_get(_har(rod_u, cid), key); if ( u3_none != got ) { - if ( u3z_memo_ford == cid ) { - u3m_p("found ford", 1); - } return got; } if ( 0 == rod_u->kid_p ) { From 4885253c402d6ae3c2d7812e0b419b20d586673f Mon Sep 17 00:00:00 2001 From: midden-fabler Date: Mon, 3 Nov 2025 14:02:15 -0500 Subject: [PATCH 07/15] allocate: fix ford --- pkg/noun/allocate.c | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/noun/allocate.c b/pkg/noun/allocate.c index 9037cb01af..eb127bfe96 100644 --- a/pkg/noun/allocate.c +++ b/pkg/noun/allocate.c @@ -1749,6 +1749,7 @@ u3a_mark_road() qua_u[11]->siz_w = wee_w * 4; } + qua_u[12] = c3_calloc(sizeof(*qua_u[12])); qua_u[12]->nam_c = strdup("ford memoization cache"); qua_u[12]->siz_w = u3h_mark(u3R->cax.for_p) * 4; From 6bb086230100533c05175e600ed4461e85584f5a Mon Sep 17 00:00:00 2001 From: midden-fabler Date: Wed, 12 Nov 2025 13:14:30 -0500 Subject: [PATCH 08/15] manage: initialize ford cache --- pkg/noun/manage.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pkg/noun/manage.c b/pkg/noun/manage.c index 42ab324cf6..0dcdba31f4 100644 --- a/pkg/noun/manage.c +++ b/pkg/noun/manage.c @@ -664,6 +664,13 @@ _find_home(void) u3j_reclaim(); u3H->pam_d = _pave_params(); } + + // if for_p is zero then it is an old pier pre ford cache, initialize the + // cache + // + if ( !u3R->cax.for_p ) { + u3R->cax.for_p = u3h_new_cache(u3C.per_w); + } } /* u3m_pave(): instantiate or activate image. From b0f0061d33dca3c8c93d18e8e989edd9384f6882 Mon Sep 17 00:00:00 2001 From: Quodss Date: Tue, 25 Nov 2025 17:38:13 +0100 Subject: [PATCH 09/15] Merge branch 'develop' into mf/lightning --- ext/libuv/build.zig | 9 +- ext/libuv/patches/libuv/src/win/tty.c | 2422 +++++++++++++++++++++++++ pkg/c3/defs.h | 3 + pkg/c3/motes.h | 1 + pkg/c3/platform/windows/wsetjmp.h | 7 - pkg/c3/portable.h | 8 +- pkg/noun/allocate.c | 25 +- pkg/noun/allocate.h | 8 +- pkg/noun/build.zig | 2 +- pkg/noun/hashtable.c | 1 + pkg/noun/imprison.c | 62 +- pkg/noun/jets/a/add.c | 69 +- pkg/noun/jets/a/dec.c | 12 +- pkg/noun/jets/a/gte.c | 25 +- pkg/noun/jets/a/gth.c | 25 +- pkg/noun/jets/a/lte.c | 25 +- pkg/noun/jets/a/lth.c | 25 +- pkg/noun/jets/a/sub.c | 65 +- pkg/noun/jets/e/blake.c | 1 + pkg/noun/jets/e/json_en.c | 10 +- pkg/noun/manage.c | 390 +++- pkg/noun/manage.h | 99 + pkg/noun/nock.c | 46 +- pkg/noun/options.h | 2 +- pkg/noun/palloc.c | 178 +- pkg/noun/platform/windows/rsignal.h | 9 +- pkg/noun/platform/windows/setjmp.h | 15 + pkg/noun/retrieve.c | 52 + pkg/noun/retrieve.h | 12 + pkg/noun/zave.c | 2 +- pkg/vere/benchmarks.c | 2 +- pkg/vere/build.zig | 1 - pkg/vere/io/ames.c | 14 +- pkg/vere/io/behn.c | 6 +- pkg/vere/io/conn.c | 2 +- pkg/vere/io/cttp.c | 2 +- pkg/vere/io/http.c | 20 +- pkg/vere/io/mesa.c | 2 +- pkg/vere/io/term.c | 17 +- pkg/vere/io/unix.c | 2 +- pkg/vere/king.c | 2 +- pkg/vere/main.c | 2 +- pkg/vere/mars.c | 12 +- pkg/vere/pier.c | 2 +- pkg/vere/platform/darwin/ptty.c | 2 +- pkg/vere/platform/linux/ptty.c | 2 +- pkg/vere/time.c | 169 -- pkg/vere/vere.h | 70 - 48 files changed, 3440 insertions(+), 499 deletions(-) create mode 100644 ext/libuv/patches/libuv/src/win/tty.c delete mode 100644 pkg/c3/platform/windows/wsetjmp.h create mode 100644 pkg/noun/platform/windows/setjmp.h delete mode 100644 pkg/vere/time.c diff --git a/ext/libuv/build.zig b/ext/libuv/build.zig index 4f6a58e351..25f77070b3 100644 --- a/ext/libuv/build.zig +++ b/ext/libuv/build.zig @@ -77,6 +77,14 @@ pub fn build(b: *std.Build) !void { .flags = uv_flags.items, }); + if (t.os.tag == .windows) { + uv.addCSourceFiles(.{ + .files = &.{"patches/libuv/src/win/tty.c"}, + .flags = uv_flags.items, + }); + uv.addIncludePath(uv_c.path("src/win")); + } + uv.installHeadersDirectory(uv_c.path("include"), "", .{}); if (t.os.tag == .windows) { @@ -162,7 +170,6 @@ const uv_srcs_windows = uv_srcs ++ [_][]const u8{ "win/snprintf.c", "win/stream.c", "win/tcp.c", - "win/tty.c", "win/udp.c", "win/util.c", "win/winapi.c", diff --git a/ext/libuv/patches/libuv/src/win/tty.c b/ext/libuv/patches/libuv/src/win/tty.c new file mode 100644 index 0000000000..34739013a3 --- /dev/null +++ b/ext/libuv/patches/libuv/src/win/tty.c @@ -0,0 +1,2422 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#ifndef COMMON_LVB_REVERSE_VIDEO +# define COMMON_LVB_REVERSE_VIDEO 0x4000 +#endif + +#include "uv.h" +#include "internal.h" +#include "handle-inl.h" +#include "stream-inl.h" +#include "req-inl.h" + +#ifndef InterlockedOr +# define InterlockedOr _InterlockedOr +#endif + +#define UNICODE_REPLACEMENT_CHARACTER (0xfffd) + +#define ANSI_NORMAL 0x0000 +#define ANSI_ESCAPE_SEEN 0x0002 +#define ANSI_CSI 0x0004 +#define ANSI_ST_CONTROL 0x0008 +#define ANSI_IGNORE 0x0010 +#define ANSI_IN_ARG 0x0020 +#define ANSI_IN_STRING 0x0040 +#define ANSI_BACKSLASH_SEEN 0x0080 +#define ANSI_EXTENSION 0x0100 +#define ANSI_DECSCUSR 0x0200 + +#define MAX_INPUT_BUFFER_LENGTH 8192 +#define MAX_CONSOLE_CHAR 8192 + +#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING +#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 +#endif + +#define CURSOR_SIZE_SMALL 25 +#define CURSOR_SIZE_LARGE 100 + +static void uv__tty_capture_initial_style( + CONSOLE_SCREEN_BUFFER_INFO* screen_buffer_info, + CONSOLE_CURSOR_INFO* cursor_info); +static void uv__tty_update_virtual_window(CONSOLE_SCREEN_BUFFER_INFO* info); +static int uv__cancel_read_console(uv_tty_t* handle); + + +/* Null uv_buf_t */ +static const uv_buf_t uv_null_buf_ = { 0, NULL }; + +enum uv__read_console_status_e { + NOT_STARTED, + IN_PROGRESS, + TRAP_REQUESTED, + COMPLETED +}; + +static volatile LONG uv__read_console_status = NOT_STARTED; +static volatile LONG uv__restore_screen_state; +static CONSOLE_SCREEN_BUFFER_INFO uv__saved_screen_state; + + +/* + * The console virtual window. + * + * Normally cursor movement in windows is relative to the console screen buffer, + * e.g. the application is allowed to overwrite the 'history'. This is very + * inconvenient, it makes absolute cursor movement pretty useless. There is + * also the concept of 'client rect' which is defined by the actual size of + * the console window and the scroll position of the screen buffer, but it's + * very volatile because it changes when the user scrolls. + * + * To make cursor movement behave sensibly we define a virtual window to which + * cursor movement is confined. The virtual window is always as wide as the + * console screen buffer, but it's height is defined by the size of the + * console window. The top of the virtual window aligns with the position + * of the caret when the first stdout/err handle is created, unless that would + * mean that it would extend beyond the bottom of the screen buffer - in that + * that case it's located as far down as possible. + * + * When the user writes a long text or many newlines, such that the output + * reaches beyond the bottom of the virtual window, the virtual window is + * shifted downwards, but not resized. + * + * Since all tty i/o happens on the same console, this window is shared + * between all stdout/stderr handles. + */ + +static int uv_tty_virtual_offset = -1; +static int uv_tty_virtual_height = -1; +static int uv_tty_virtual_width = -1; + +/* The console window size + * We keep this separate from uv_tty_virtual_*. We use those values to only + * handle signalling SIGWINCH + */ + +static HANDLE uv__tty_console_handle = INVALID_HANDLE_VALUE; +static int uv__tty_console_height = -1; +static int uv__tty_console_width = -1; +static HANDLE uv__tty_console_resized = INVALID_HANDLE_VALUE; +static uv_mutex_t uv__tty_console_resize_mutex; + +static DWORD WINAPI uv__tty_console_resize_message_loop_thread(void* param); +static void CALLBACK uv__tty_console_resize_event(HWINEVENTHOOK hWinEventHook, + DWORD event, + HWND hwnd, + LONG idObject, + LONG idChild, + DWORD dwEventThread, + DWORD dwmsEventTime); +static DWORD WINAPI uv__tty_console_resize_watcher_thread(void* param); +static void uv__tty_console_signal_resize(void); + +/* We use a semaphore rather than a mutex or critical section because in some + cases (uv__cancel_read_console) we need take the lock in the main thread and + release it in another thread. Using a semaphore ensures that in such + scenario the main thread will still block when trying to acquire the lock. */ +static uv_sem_t uv_tty_output_lock; + +static WORD uv_tty_default_text_attributes = + FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; + +static char uv_tty_default_fg_color = 7; +static char uv_tty_default_bg_color = 0; +static char uv_tty_default_fg_bright = 0; +static char uv_tty_default_bg_bright = 0; +static char uv_tty_default_inverse = 0; + +static CONSOLE_CURSOR_INFO uv_tty_default_cursor_info; + +/* Determine whether or not ANSI support is enabled. */ +static BOOL uv__need_check_vterm_state = TRUE; +static uv_tty_vtermstate_t uv__vterm_state = UV_TTY_UNSUPPORTED; +static void uv__determine_vterm_state(HANDLE handle); + +void uv__console_init(void) { + if (uv_sem_init(&uv_tty_output_lock, 1)) + abort(); + uv__tty_console_handle = CreateFileW(L"CONOUT$", + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_WRITE, + 0, + OPEN_EXISTING, + 0, + 0); + if (uv__tty_console_handle != INVALID_HANDLE_VALUE) { + CONSOLE_SCREEN_BUFFER_INFO sb_info; + uv_mutex_init(&uv__tty_console_resize_mutex); + if (GetConsoleScreenBufferInfo(uv__tty_console_handle, &sb_info)) { + uv__tty_console_width = sb_info.dwSize.X; + uv__tty_console_height = sb_info.srWindow.Bottom - sb_info.srWindow.Top + 1; + } + QueueUserWorkItem(uv__tty_console_resize_message_loop_thread, + NULL, + WT_EXECUTELONGFUNCTION); + } +} + + +int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, uv_file fd, int unused) { + BOOL readable; + DWORD NumberOfEvents; + HANDLE handle; + CONSOLE_SCREEN_BUFFER_INFO screen_buffer_info; + CONSOLE_CURSOR_INFO cursor_info; + (void)unused; + + uv__once_init(); + handle = (HANDLE) uv__get_osfhandle(fd); + if (handle == INVALID_HANDLE_VALUE) + return UV_EBADF; + + if (fd <= 2) { + /* In order to avoid closing a stdio file descriptor 0-2, duplicate the + * underlying OS handle and forget about the original fd. + * We could also opt to use the original OS handle and just never close it, + * but then there would be no reliable way to cancel pending read operations + * upon close. + */ + if (!DuplicateHandle(INVALID_HANDLE_VALUE, + handle, + INVALID_HANDLE_VALUE, + &handle, + 0, + FALSE, + DUPLICATE_SAME_ACCESS)) + return uv_translate_sys_error(GetLastError()); + fd = -1; + } + + readable = GetNumberOfConsoleInputEvents(handle, &NumberOfEvents); + if (!readable) { + /* Obtain the screen buffer info with the output handle. */ + if (!GetConsoleScreenBufferInfo(handle, &screen_buffer_info)) { + return uv_translate_sys_error(GetLastError()); + } + + /* Obtain the cursor info with the output handle. */ + if (!GetConsoleCursorInfo(handle, &cursor_info)) { + return uv_translate_sys_error(GetLastError()); + } + + /* Obtain the tty_output_lock because the virtual window state is shared + * between all uv_tty_t handles. */ + uv_sem_wait(&uv_tty_output_lock); + + if (uv__need_check_vterm_state) + uv__determine_vterm_state(handle); + + /* Remember the original console text attributes and cursor info. */ + uv__tty_capture_initial_style(&screen_buffer_info, &cursor_info); + + uv__tty_update_virtual_window(&screen_buffer_info); + + uv_sem_post(&uv_tty_output_lock); + } + + + uv__stream_init(loop, (uv_stream_t*) tty, UV_TTY); + uv__connection_init((uv_stream_t*) tty); + + tty->handle = handle; + tty->u.fd = fd; + tty->reqs_pending = 0; + tty->flags |= UV_HANDLE_BOUND; + + if (readable) { + /* Initialize TTY input specific fields. */ + tty->flags |= UV_HANDLE_TTY_READABLE | UV_HANDLE_READABLE; + /* TODO: remove me in v2.x. */ + tty->tty.rd.unused_ = NULL; + tty->tty.rd.read_line_buffer = uv_null_buf_; + tty->tty.rd.read_raw_wait = NULL; + + /* Init keycode-to-vt100 mapper state. */ + tty->tty.rd.last_key_len = 0; + tty->tty.rd.last_key_offset = 0; + tty->tty.rd.last_utf16_high_surrogate = 0; + memset(&tty->tty.rd.last_input_record, 0, sizeof tty->tty.rd.last_input_record); + } else { + /* TTY output specific fields. */ + tty->flags |= UV_HANDLE_WRITABLE; + + /* Init utf8-to-utf16 conversion state. */ + tty->tty.wr.utf8_bytes_left = 0; + tty->tty.wr.utf8_codepoint = 0; + + /* Initialize eol conversion state */ + tty->tty.wr.previous_eol = 0; + + /* Init ANSI parser state. */ + tty->tty.wr.ansi_parser_state = ANSI_NORMAL; + } + + return 0; +} + + +/* Set the default console text attributes based on how the console was + * configured when libuv started. + */ +static void uv__tty_capture_initial_style( + CONSOLE_SCREEN_BUFFER_INFO* screen_buffer_info, + CONSOLE_CURSOR_INFO* cursor_info) { + static int style_captured = 0; + + /* Only do this once. + Assumption: Caller has acquired uv_tty_output_lock. */ + if (style_captured) + return; + + /* Save raw win32 attributes. */ + uv_tty_default_text_attributes = screen_buffer_info->wAttributes; + + /* Convert black text on black background to use white text. */ + if (uv_tty_default_text_attributes == 0) + uv_tty_default_text_attributes = 7; + + /* Convert Win32 attributes to ANSI colors. */ + uv_tty_default_fg_color = 0; + uv_tty_default_bg_color = 0; + uv_tty_default_fg_bright = 0; + uv_tty_default_bg_bright = 0; + uv_tty_default_inverse = 0; + + if (uv_tty_default_text_attributes & FOREGROUND_RED) + uv_tty_default_fg_color |= 1; + + if (uv_tty_default_text_attributes & FOREGROUND_GREEN) + uv_tty_default_fg_color |= 2; + + if (uv_tty_default_text_attributes & FOREGROUND_BLUE) + uv_tty_default_fg_color |= 4; + + if (uv_tty_default_text_attributes & BACKGROUND_RED) + uv_tty_default_bg_color |= 1; + + if (uv_tty_default_text_attributes & BACKGROUND_GREEN) + uv_tty_default_bg_color |= 2; + + if (uv_tty_default_text_attributes & BACKGROUND_BLUE) + uv_tty_default_bg_color |= 4; + + if (uv_tty_default_text_attributes & FOREGROUND_INTENSITY) + uv_tty_default_fg_bright = 1; + + if (uv_tty_default_text_attributes & BACKGROUND_INTENSITY) + uv_tty_default_bg_bright = 1; + + if (uv_tty_default_text_attributes & COMMON_LVB_REVERSE_VIDEO) + uv_tty_default_inverse = 1; + + /* Save the cursor size and the cursor state. */ + uv_tty_default_cursor_info = *cursor_info; + + style_captured = 1; +} + + +int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) { + DWORD flags; + unsigned char was_reading; + uv_alloc_cb alloc_cb; + uv_read_cb read_cb; + int err; + + if (!(tty->flags & UV_HANDLE_TTY_READABLE)) { + return UV_EINVAL; + } + + if (!!mode == !!(tty->flags & UV_HANDLE_TTY_RAW)) { + return 0; + } + + switch (mode) { + case UV_TTY_MODE_NORMAL: + flags = ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT; + break; + case UV_TTY_MODE_RAW: + flags = ENABLE_WINDOW_INPUT | ENABLE_PROCESSED_INPUT; + break; + case UV_TTY_MODE_IO: + return UV_ENOTSUP; + default: + return UV_EINVAL; + } + + /* If currently reading, stop, and restart reading. */ + if (tty->flags & UV_HANDLE_READING) { + was_reading = 1; + alloc_cb = tty->alloc_cb; + read_cb = tty->read_cb; + err = uv__tty_read_stop(tty); + if (err) { + return uv_translate_sys_error(err); + } + } else { + was_reading = 0; + alloc_cb = NULL; + read_cb = NULL; + } + + uv_sem_wait(&uv_tty_output_lock); + if (!SetConsoleMode(tty->handle, flags)) { + err = uv_translate_sys_error(GetLastError()); + uv_sem_post(&uv_tty_output_lock); + return err; + } + uv_sem_post(&uv_tty_output_lock); + + /* Update flag. */ + tty->flags &= ~UV_HANDLE_TTY_RAW; + tty->flags |= mode ? UV_HANDLE_TTY_RAW : 0; + + /* If we just stopped reading, restart. */ + if (was_reading) { + err = uv__tty_read_start(tty, alloc_cb, read_cb); + if (err) { + return uv_translate_sys_error(err); + } + } + + return 0; +} + + +int uv_tty_get_winsize(uv_tty_t* tty, int* width, int* height) { + CONSOLE_SCREEN_BUFFER_INFO info; + + if (!GetConsoleScreenBufferInfo(tty->handle, &info)) { + return uv_translate_sys_error(GetLastError()); + } + + uv_sem_wait(&uv_tty_output_lock); + uv__tty_update_virtual_window(&info); + uv_sem_post(&uv_tty_output_lock); + + *width = uv_tty_virtual_width; + *height = uv_tty_virtual_height; + + return 0; +} + + +static void CALLBACK uv_tty_post_raw_read(void* data, BOOLEAN didTimeout) { + uv_loop_t* loop; + uv_tty_t* handle; + uv_req_t* req; + + assert(data); + assert(!didTimeout); + + req = (uv_req_t*) data; + handle = (uv_tty_t*) req->data; + loop = handle->loop; + + UnregisterWait(handle->tty.rd.read_raw_wait); + handle->tty.rd.read_raw_wait = NULL; + + SET_REQ_SUCCESS(req); + POST_COMPLETION_FOR_REQ(loop, req); +} + + +static void uv__tty_queue_read_raw(uv_loop_t* loop, uv_tty_t* handle) { + uv_read_t* req; + BOOL r; + + assert(handle->flags & UV_HANDLE_READING); + assert(!(handle->flags & UV_HANDLE_READ_PENDING)); + + assert(handle->handle && handle->handle != INVALID_HANDLE_VALUE); + + handle->tty.rd.read_line_buffer = uv_null_buf_; + + req = &handle->read_req; + memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped)); + + r = RegisterWaitForSingleObject(&handle->tty.rd.read_raw_wait, + handle->handle, + uv_tty_post_raw_read, + (void*) req, + INFINITE, + WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE); + if (!r) { + handle->tty.rd.read_raw_wait = NULL; + SET_REQ_ERROR(req, GetLastError()); + uv__insert_pending_req(loop, (uv_req_t*)req); + } + + handle->flags |= UV_HANDLE_READ_PENDING; + handle->reqs_pending++; +} + + +static DWORD CALLBACK uv_tty_line_read_thread(void* data) { + uv_loop_t* loop; + uv_tty_t* handle; + uv_req_t* req; + DWORD bytes; + size_t read_bytes; + WCHAR utf16[MAX_INPUT_BUFFER_LENGTH / 3]; + DWORD chars; + DWORD read_chars; + LONG status; + COORD pos; + BOOL read_console_success; + + assert(data); + + req = (uv_req_t*) data; + handle = (uv_tty_t*) req->data; + loop = handle->loop; + + assert(handle->tty.rd.read_line_buffer.base != NULL); + assert(handle->tty.rd.read_line_buffer.len > 0); + + /* ReadConsole can't handle big buffers. */ + if (handle->tty.rd.read_line_buffer.len < MAX_INPUT_BUFFER_LENGTH) { + bytes = handle->tty.rd.read_line_buffer.len; + } else { + bytes = MAX_INPUT_BUFFER_LENGTH; + } + + /* At last, unicode! One utf-16 codeunit never takes more than 3 utf-8 + * codeunits to encode. */ + chars = bytes / 3; + + status = InterlockedExchange(&uv__read_console_status, IN_PROGRESS); + if (status == TRAP_REQUESTED) { + SET_REQ_SUCCESS(req); + InterlockedExchange(&uv__read_console_status, COMPLETED); + req->u.io.overlapped.InternalHigh = 0; + POST_COMPLETION_FOR_REQ(loop, req); + return 0; + } + + read_console_success = ReadConsoleW(handle->handle, + (void*) utf16, + chars, + &read_chars, + NULL); + + if (read_console_success) { + read_bytes = bytes; + uv_utf16_to_wtf8(utf16, + read_chars, + &handle->tty.rd.read_line_buffer.base, + &read_bytes); + SET_REQ_SUCCESS(req); + req->u.io.overlapped.InternalHigh = (DWORD) read_bytes; + } else { + SET_REQ_ERROR(req, GetLastError()); + } + + status = InterlockedExchange(&uv__read_console_status, COMPLETED); + + if (status == TRAP_REQUESTED) { + /* If we canceled the read by sending a VK_RETURN event, restore the + screen state to undo the visual effect of the VK_RETURN */ + if (read_console_success && InterlockedOr(&uv__restore_screen_state, 0)) { + HANDLE active_screen_buffer; + active_screen_buffer = CreateFileA("conout$", + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (active_screen_buffer != INVALID_HANDLE_VALUE) { + pos = uv__saved_screen_state.dwCursorPosition; + + /* If the cursor was at the bottom line of the screen buffer, the + VK_RETURN would have caused the buffer contents to scroll up by one + line. The right position to reset the cursor to is therefore one line + higher */ + if (pos.Y == uv__saved_screen_state.dwSize.Y - 1) + pos.Y--; + + SetConsoleCursorPosition(active_screen_buffer, pos); + CloseHandle(active_screen_buffer); + } + } + uv_sem_post(&uv_tty_output_lock); + } + POST_COMPLETION_FOR_REQ(loop, req); + return 0; +} + + +static void uv__tty_queue_read_line(uv_loop_t* loop, uv_tty_t* handle) { + uv_read_t* req; + BOOL r; + + assert(handle->flags & UV_HANDLE_READING); + assert(!(handle->flags & UV_HANDLE_READ_PENDING)); + assert(handle->handle && handle->handle != INVALID_HANDLE_VALUE); + + req = &handle->read_req; + memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped)); + + handle->tty.rd.read_line_buffer = uv_buf_init(NULL, 0); + handle->alloc_cb((uv_handle_t*) handle, 8192, &handle->tty.rd.read_line_buffer); + if (handle->tty.rd.read_line_buffer.base == NULL || + handle->tty.rd.read_line_buffer.len == 0) { + handle->read_cb((uv_stream_t*) handle, + UV_ENOBUFS, + &handle->tty.rd.read_line_buffer); + return; + } + assert(handle->tty.rd.read_line_buffer.base != NULL); + + /* Reset flags No locking is required since there cannot be a line read + in progress. We are also relying on the memory barrier provided by + QueueUserWorkItem*/ + uv__restore_screen_state = FALSE; + uv__read_console_status = NOT_STARTED; + r = QueueUserWorkItem(uv_tty_line_read_thread, + (void*) req, + WT_EXECUTELONGFUNCTION); + if (!r) { + SET_REQ_ERROR(req, GetLastError()); + uv__insert_pending_req(loop, (uv_req_t*)req); + } + + handle->flags |= UV_HANDLE_READ_PENDING; + handle->reqs_pending++; +} + + +static void uv__tty_queue_read(uv_loop_t* loop, uv_tty_t* handle) { + if (handle->flags & UV_HANDLE_TTY_RAW) { + uv__tty_queue_read_raw(loop, handle); + } else { + uv__tty_queue_read_line(loop, handle); + } +} + + +static const char* get_vt100_fn_key(DWORD code, char shift, char ctrl, + size_t* len) { +#define VK_CASE(vk, normal_str, shift_str, ctrl_str, shift_ctrl_str) \ + case (vk): \ + if (shift && ctrl) { \ + *len = sizeof shift_ctrl_str; \ + return "\033" shift_ctrl_str; \ + } else if (shift) { \ + *len = sizeof shift_str ; \ + return "\033" shift_str; \ + } else if (ctrl) { \ + *len = sizeof ctrl_str; \ + return "\033" ctrl_str; \ + } else { \ + *len = sizeof normal_str; \ + return "\033" normal_str; \ + } + + switch (code) { + /* These mappings are the same as Cygwin's. Unmodified and alt-modified + * keypad keys comply with linux console, modifiers comply with xterm + * modifier usage. F1. f12 and shift-f1. f10 comply with linux console, f6. + * f12 with and without modifiers comply with rxvt. */ + VK_CASE(VK_INSERT, "[2~", "[2;2~", "[2;5~", "[2;6~") + VK_CASE(VK_END, "[4~", "[4;2~", "[4;5~", "[4;6~") + VK_CASE(VK_DOWN, "[B", "[1;2B", "[1;5B", "[1;6B") + VK_CASE(VK_NEXT, "[6~", "[6;2~", "[6;5~", "[6;6~") + VK_CASE(VK_LEFT, "[D", "[1;2D", "[1;5D", "[1;6D") + VK_CASE(VK_CLEAR, "[G", "[1;2G", "[1;5G", "[1;6G") + VK_CASE(VK_RIGHT, "[C", "[1;2C", "[1;5C", "[1;6C") + VK_CASE(VK_UP, "[A", "[1;2A", "[1;5A", "[1;6A") + VK_CASE(VK_HOME, "[1~", "[1;2~", "[1;5~", "[1;6~") + VK_CASE(VK_PRIOR, "[5~", "[5;2~", "[5;5~", "[5;6~") + VK_CASE(VK_DELETE, "[3~", "[3;2~", "[3;5~", "[3;6~") + VK_CASE(VK_NUMPAD0, "[2~", "[2;2~", "[2;5~", "[2;6~") + VK_CASE(VK_NUMPAD1, "[4~", "[4;2~", "[4;5~", "[4;6~") + VK_CASE(VK_NUMPAD2, "[B", "[1;2B", "[1;5B", "[1;6B") + VK_CASE(VK_NUMPAD3, "[6~", "[6;2~", "[6;5~", "[6;6~") + VK_CASE(VK_NUMPAD4, "[D", "[1;2D", "[1;5D", "[1;6D") + VK_CASE(VK_NUMPAD5, "[G", "[1;2G", "[1;5G", "[1;6G") + VK_CASE(VK_NUMPAD6, "[C", "[1;2C", "[1;5C", "[1;6C") + VK_CASE(VK_NUMPAD7, "[A", "[1;2A", "[1;5A", "[1;6A") + VK_CASE(VK_NUMPAD8, "[1~", "[1;2~", "[1;5~", "[1;6~") + VK_CASE(VK_NUMPAD9, "[5~", "[5;2~", "[5;5~", "[5;6~") + VK_CASE(VK_DECIMAL, "[3~", "[3;2~", "[3;5~", "[3;6~") + VK_CASE(VK_F1, "[[A", "[23~", "[11^", "[23^" ) + VK_CASE(VK_F2, "[[B", "[24~", "[12^", "[24^" ) + VK_CASE(VK_F3, "[[C", "[25~", "[13^", "[25^" ) + VK_CASE(VK_F4, "[[D", "[26~", "[14^", "[26^" ) + VK_CASE(VK_F5, "[[E", "[28~", "[15^", "[28^" ) + VK_CASE(VK_F6, "[17~", "[29~", "[17^", "[29^" ) + VK_CASE(VK_F7, "[18~", "[31~", "[18^", "[31^" ) + VK_CASE(VK_F8, "[19~", "[32~", "[19^", "[32^" ) + VK_CASE(VK_F9, "[20~", "[33~", "[20^", "[33^" ) + VK_CASE(VK_F10, "[21~", "[34~", "[21^", "[34^" ) + VK_CASE(VK_F11, "[23~", "[23$", "[23^", "[23@" ) + VK_CASE(VK_F12, "[24~", "[24$", "[24^", "[24@" ) + + default: + *len = 0; + return NULL; + } +#undef VK_CASE +} + + +void uv_process_tty_read_raw_req(uv_loop_t* loop, uv_tty_t* handle, + uv_req_t* req) { + /* Shortcut for handle->tty.rd.last_input_record.Event.KeyEvent. */ +#define KEV handle->tty.rd.last_input_record.Event.KeyEvent + + DWORD records_left, records_read; + uv_buf_t buf; + _off_t buf_used; + + assert(handle->type == UV_TTY); + assert(handle->flags & UV_HANDLE_TTY_READABLE); + handle->flags &= ~UV_HANDLE_READ_PENDING; + + if (!(handle->flags & UV_HANDLE_READING) || + !(handle->flags & UV_HANDLE_TTY_RAW)) { + goto out; + } + + if (!REQ_SUCCESS(req)) { + /* An error occurred while waiting for the event. */ + if ((handle->flags & UV_HANDLE_READING)) { + handle->flags &= ~UV_HANDLE_READING; + handle->read_cb((uv_stream_t*)handle, + uv_translate_sys_error(GET_REQ_ERROR(req)), + &uv_null_buf_); + } + goto out; + } + + /* Fetch the number of events */ + if (!GetNumberOfConsoleInputEvents(handle->handle, &records_left)) { + handle->flags &= ~UV_HANDLE_READING; + DECREASE_ACTIVE_COUNT(loop, handle); + handle->read_cb((uv_stream_t*)handle, + uv_translate_sys_error(GetLastError()), + &uv_null_buf_); + goto out; + } + + /* Windows sends a lot of events that we're not interested in, so buf will be + * allocated on demand, when there's actually something to emit. */ + buf = uv_null_buf_; + buf_used = 0; + + while ((records_left > 0 || handle->tty.rd.last_key_len > 0) && + (handle->flags & UV_HANDLE_READING)) { + if (handle->tty.rd.last_key_len == 0) { + /* Read the next input record */ + if (!ReadConsoleInputW(handle->handle, + &handle->tty.rd.last_input_record, + 1, + &records_read)) { + handle->flags &= ~UV_HANDLE_READING; + DECREASE_ACTIVE_COUNT(loop, handle); + handle->read_cb((uv_stream_t*) handle, + uv_translate_sys_error(GetLastError()), + &buf); + goto out; + } + records_left--; + + /* We might be not subscribed to EVENT_CONSOLE_LAYOUT or we might be + * running under some TTY emulator that does not send those events. */ + if (handle->tty.rd.last_input_record.EventType == WINDOW_BUFFER_SIZE_EVENT) { + uv__tty_console_signal_resize(); + } + + /* Ignore other events that are not key events. */ + if (handle->tty.rd.last_input_record.EventType != KEY_EVENT) { + continue; + } + + /* Ignore keyup events, unless the left alt key was held and a valid + * unicode character was emitted. */ + if (!KEV.bKeyDown && + (KEV.wVirtualKeyCode != VK_MENU || + KEV.uChar.UnicodeChar == 0)) { + continue; + } + + /* Ignore keypresses to numpad number keys if the left alt is held + * because the user is composing a character, or windows simulating this. + */ + if ((KEV.dwControlKeyState & LEFT_ALT_PRESSED) && + !(KEV.dwControlKeyState & ENHANCED_KEY) && + (KEV.wVirtualKeyCode == VK_INSERT || + KEV.wVirtualKeyCode == VK_END || + KEV.wVirtualKeyCode == VK_DOWN || + KEV.wVirtualKeyCode == VK_NEXT || + KEV.wVirtualKeyCode == VK_LEFT || + KEV.wVirtualKeyCode == VK_CLEAR || + KEV.wVirtualKeyCode == VK_RIGHT || + KEV.wVirtualKeyCode == VK_HOME || + KEV.wVirtualKeyCode == VK_UP || + KEV.wVirtualKeyCode == VK_PRIOR || + KEV.wVirtualKeyCode == VK_NUMPAD0 || + KEV.wVirtualKeyCode == VK_NUMPAD1 || + KEV.wVirtualKeyCode == VK_NUMPAD2 || + KEV.wVirtualKeyCode == VK_NUMPAD3 || + KEV.wVirtualKeyCode == VK_NUMPAD4 || + KEV.wVirtualKeyCode == VK_NUMPAD5 || + KEV.wVirtualKeyCode == VK_NUMPAD6 || + KEV.wVirtualKeyCode == VK_NUMPAD7 || + KEV.wVirtualKeyCode == VK_NUMPAD8 || + KEV.wVirtualKeyCode == VK_NUMPAD9)) { + continue; + } + + if (KEV.uChar.UnicodeChar != 0) { + int prefix_len; + size_t char_len; + char* last_key_buf; + + /* Character key pressed */ + if (KEV.uChar.UnicodeChar >= 0xD800 && + KEV.uChar.UnicodeChar < 0xDC00) { + /* UTF-16 high surrogate */ + handle->tty.rd.last_utf16_high_surrogate = KEV.uChar.UnicodeChar; + continue; + } + + /* Prefix with \u033 if alt was held, but alt was not used as part a + * compose sequence. */ + if ((KEV.dwControlKeyState & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) + && !(KEV.dwControlKeyState & (LEFT_CTRL_PRESSED | + RIGHT_CTRL_PRESSED)) && KEV.bKeyDown) { + handle->tty.rd.last_key[0] = '\033'; + prefix_len = 1; + } else { + prefix_len = 0; + } + + char_len = sizeof handle->tty.rd.last_key; + last_key_buf = &handle->tty.rd.last_key[prefix_len]; + if (handle->tty.rd.last_utf16_high_surrogate) { + /* UTF-16 surrogate pair */ + WCHAR utf16_buffer[2]; + utf16_buffer[0] = handle->tty.rd.last_utf16_high_surrogate; + utf16_buffer[1] = KEV.uChar.UnicodeChar; + if (uv_utf16_to_wtf8(utf16_buffer, + 2, + &last_key_buf, + &char_len)) + char_len = 0; + handle->tty.rd.last_utf16_high_surrogate = 0; + } else { + /* Single UTF-16 character */ + if (uv_utf16_to_wtf8(&KEV.uChar.UnicodeChar, + 1, + &last_key_buf, + &char_len)) + char_len = 0; + } + + /* If the utf16 character(s) couldn't be converted something must be + * wrong. */ + if (char_len == 0) { + handle->flags &= ~UV_HANDLE_READING; + DECREASE_ACTIVE_COUNT(loop, handle); + handle->read_cb((uv_stream_t*) handle, + uv_translate_sys_error(GetLastError()), + &buf); + goto out; + } + + handle->tty.rd.last_key_len = (unsigned char) (prefix_len + char_len); + handle->tty.rd.last_key_offset = 0; + continue; + + } else { + /* Function key pressed */ + const char* vt100; + size_t prefix_len, vt100_len; + + vt100 = get_vt100_fn_key(KEV.wVirtualKeyCode, + !!(KEV.dwControlKeyState & SHIFT_PRESSED), + !!(KEV.dwControlKeyState & ( + LEFT_CTRL_PRESSED | + RIGHT_CTRL_PRESSED)), + &vt100_len); + + /* If we were unable to map to a vt100 sequence, just ignore. */ + if (!vt100) { + continue; + } + + /* Prefix with \x033 when the alt key was held. */ + if (KEV.dwControlKeyState & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) { + handle->tty.rd.last_key[0] = '\033'; + prefix_len = 1; + } else { + prefix_len = 0; + } + + /* Copy the vt100 sequence to the handle buffer. */ + assert(prefix_len + vt100_len < sizeof handle->tty.rd.last_key); + memcpy(&handle->tty.rd.last_key[prefix_len], vt100, vt100_len); + + handle->tty.rd.last_key_len = (unsigned char) (prefix_len + vt100_len); + handle->tty.rd.last_key_offset = 0; + continue; + } + } else { + /* Copy any bytes left from the last keypress to the user buffer. */ + if (handle->tty.rd.last_key_offset < handle->tty.rd.last_key_len) { + /* Allocate a buffer if needed */ + if (buf_used == 0) { + buf = uv_buf_init(NULL, 0); + handle->alloc_cb((uv_handle_t*) handle, 1024, &buf); + if (buf.base == NULL || buf.len == 0) { + handle->read_cb((uv_stream_t*) handle, UV_ENOBUFS, &buf); + goto out; + } + assert(buf.base != NULL); + } + + buf.base[buf_used++] = handle->tty.rd.last_key[handle->tty.rd.last_key_offset++]; + + /* If the buffer is full, emit it */ + if ((size_t) buf_used == buf.len) { + handle->read_cb((uv_stream_t*) handle, buf_used, &buf); + buf = uv_null_buf_; + buf_used = 0; + } + + continue; + } + + /* Apply dwRepeat from the last input record. */ + if (--KEV.wRepeatCount > 0) { + handle->tty.rd.last_key_offset = 0; + continue; + } + + handle->tty.rd.last_key_len = 0; + continue; + } + } + + /* Send the buffer back to the user */ + if (buf_used > 0) { + handle->read_cb((uv_stream_t*) handle, buf_used, &buf); + } + + out: + /* Wait for more input events. */ + if ((handle->flags & UV_HANDLE_READING) && + !(handle->flags & UV_HANDLE_READ_PENDING)) { + uv__tty_queue_read(loop, handle); + } + + DECREASE_PENDING_REQ_COUNT(handle); + +#undef KEV +} + + + +void uv_process_tty_read_line_req(uv_loop_t* loop, uv_tty_t* handle, + uv_req_t* req) { + uv_buf_t buf; + + assert(handle->type == UV_TTY); + assert(handle->flags & UV_HANDLE_TTY_READABLE); + + buf = handle->tty.rd.read_line_buffer; + + handle->flags &= ~UV_HANDLE_READ_PENDING; + handle->tty.rd.read_line_buffer = uv_null_buf_; + + if (!REQ_SUCCESS(req)) { + /* Read was not successful */ + if (handle->flags & UV_HANDLE_READING) { + /* Real error */ + handle->flags &= ~UV_HANDLE_READING; + DECREASE_ACTIVE_COUNT(loop, handle); + handle->read_cb((uv_stream_t*) handle, + uv_translate_sys_error(GET_REQ_ERROR(req)), + &buf); + } + } else { + if (!(handle->flags & UV_HANDLE_CANCELLATION_PENDING) && + req->u.io.overlapped.InternalHigh != 0) { + /* Read successful. TODO: read unicode, convert to utf-8 */ + DWORD bytes = req->u.io.overlapped.InternalHigh; + handle->read_cb((uv_stream_t*) handle, bytes, &buf); + } + handle->flags &= ~UV_HANDLE_CANCELLATION_PENDING; + } + + /* Wait for more input events. */ + if ((handle->flags & UV_HANDLE_READING) && + !(handle->flags & UV_HANDLE_READ_PENDING)) { + uv__tty_queue_read(loop, handle); + } + + DECREASE_PENDING_REQ_COUNT(handle); +} + + +void uv__process_tty_read_req(uv_loop_t* loop, uv_tty_t* handle, + uv_req_t* req) { + assert(handle->type == UV_TTY); + assert(handle->flags & UV_HANDLE_TTY_READABLE); + + /* If the read_line_buffer member is zero, it must have been an raw read. + * Otherwise it was a line-buffered read. FIXME: This is quite obscure. Use a + * flag or something. */ + if (handle->tty.rd.read_line_buffer.len == 0) { + uv_process_tty_read_raw_req(loop, handle, req); + } else { + uv_process_tty_read_line_req(loop, handle, req); + } +} + + +int uv__tty_read_start(uv_tty_t* handle, uv_alloc_cb alloc_cb, + uv_read_cb read_cb) { + uv_loop_t* loop = handle->loop; + + if (!(handle->flags & UV_HANDLE_TTY_READABLE)) { + return ERROR_INVALID_PARAMETER; + } + + handle->flags |= UV_HANDLE_READING; + INCREASE_ACTIVE_COUNT(loop, handle); + handle->read_cb = read_cb; + handle->alloc_cb = alloc_cb; + + /* If reading was stopped and then started again, there could still be a read + * request pending. */ + if (handle->flags & UV_HANDLE_READ_PENDING) { + return 0; + } + + /* Maybe the user stopped reading half-way while processing key events. + * Short-circuit if this could be the case. */ + if (handle->tty.rd.last_key_len > 0) { + SET_REQ_SUCCESS(&handle->read_req); + uv__insert_pending_req(handle->loop, (uv_req_t*) &handle->read_req); + /* Make sure no attempt is made to insert it again until it's handled. */ + handle->flags |= UV_HANDLE_READ_PENDING; + handle->reqs_pending++; + return 0; + } + + uv__tty_queue_read(loop, handle); + + return 0; +} + + +int uv__tty_read_stop(uv_tty_t* handle) { + INPUT_RECORD record; + DWORD written, err; + + handle->flags &= ~UV_HANDLE_READING; + DECREASE_ACTIVE_COUNT(handle->loop, handle); + + if (!(handle->flags & UV_HANDLE_READ_PENDING)) + return 0; + + if (handle->flags & UV_HANDLE_TTY_RAW) { + /* Cancel raw read. Write some bullshit event to force the console wait to + * return. */ + memset(&record, 0, sizeof record); + record.EventType = FOCUS_EVENT; + if (!WriteConsoleInputW(handle->handle, &record, 1, &written)) { + return GetLastError(); + } + } else if (!(handle->flags & UV_HANDLE_CANCELLATION_PENDING)) { + /* Cancel line-buffered read if not already pending */ + err = uv__cancel_read_console(handle); + if (err) + return err; + + handle->flags |= UV_HANDLE_CANCELLATION_PENDING; + } + + return 0; +} + +static int uv__cancel_read_console(uv_tty_t* handle) { + HANDLE active_screen_buffer = INVALID_HANDLE_VALUE; + INPUT_RECORD record; + DWORD written; + DWORD err = 0; + LONG status; + + assert(!(handle->flags & UV_HANDLE_CANCELLATION_PENDING)); + + /* Hold the output lock during the cancellation, to ensure that further + writes don't interfere with the screen state. It will be the ReadConsole + thread's responsibility to release the lock. */ + uv_sem_wait(&uv_tty_output_lock); + status = InterlockedExchange(&uv__read_console_status, TRAP_REQUESTED); + if (status != IN_PROGRESS) { + /* Either we have managed to set a trap for the other thread before + ReadConsole is called, or ReadConsole has returned because the user + has pressed ENTER. In either case, there is nothing else to do. */ + uv_sem_post(&uv_tty_output_lock); + return 0; + } + + /* Save screen state before sending the VK_RETURN event */ + active_screen_buffer = CreateFileA("conout$", + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if (active_screen_buffer != INVALID_HANDLE_VALUE && + GetConsoleScreenBufferInfo(active_screen_buffer, + &uv__saved_screen_state)) { + InterlockedOr(&uv__restore_screen_state, 1); + } + + /* Write enter key event to force the console wait to return. */ + record.EventType = KEY_EVENT; + record.Event.KeyEvent.bKeyDown = TRUE; + record.Event.KeyEvent.wRepeatCount = 1; + record.Event.KeyEvent.wVirtualKeyCode = VK_RETURN; + record.Event.KeyEvent.wVirtualScanCode = + MapVirtualKeyW(VK_RETURN, MAPVK_VK_TO_VSC); + record.Event.KeyEvent.uChar.UnicodeChar = L'\r'; + record.Event.KeyEvent.dwControlKeyState = 0; + if (!WriteConsoleInputW(handle->handle, &record, 1, &written)) + err = GetLastError(); + + if (active_screen_buffer != INVALID_HANDLE_VALUE) + CloseHandle(active_screen_buffer); + + return err; +} + + +static void uv__tty_update_virtual_window(CONSOLE_SCREEN_BUFFER_INFO* info) { + uv_tty_virtual_width = info->dwSize.X; + uv_tty_virtual_height = info->srWindow.Bottom - info->srWindow.Top + 1; + + /* Recompute virtual window offset row. */ + if (uv_tty_virtual_offset == -1) { + uv_tty_virtual_offset = info->dwCursorPosition.Y; + } else if (uv_tty_virtual_offset < info->dwCursorPosition.Y - + uv_tty_virtual_height + 1) { + /* If suddenly find the cursor outside of the virtual window, it must have + * somehow scrolled. Update the virtual window offset. */ + uv_tty_virtual_offset = info->dwCursorPosition.Y - + uv_tty_virtual_height + 1; + } + if (uv_tty_virtual_offset + uv_tty_virtual_height > info->dwSize.Y) { + uv_tty_virtual_offset = info->dwSize.Y - uv_tty_virtual_height; + } + if (uv_tty_virtual_offset < 0) { + uv_tty_virtual_offset = 0; + } +} + + +static COORD uv__tty_make_real_coord(uv_tty_t* handle, + CONSOLE_SCREEN_BUFFER_INFO* info, int x, unsigned char x_relative, int y, + unsigned char y_relative) { + COORD result; + + uv__tty_update_virtual_window(info); + + /* Adjust y position */ + if (y_relative) { + y = info->dwCursorPosition.Y + y; + } else { + y = uv_tty_virtual_offset + y; + } + /* Clip y to virtual client rectangle */ + if (y < uv_tty_virtual_offset) { + y = uv_tty_virtual_offset; + } else if (y >= uv_tty_virtual_offset + uv_tty_virtual_height) { + y = uv_tty_virtual_offset + uv_tty_virtual_height - 1; + } + + /* Adjust x */ + if (x_relative) { + x = info->dwCursorPosition.X + x; + } + /* Clip x */ + if (x < 0) { + x = 0; + } else if (x >= uv_tty_virtual_width) { + x = uv_tty_virtual_width - 1; + } + + result.X = (unsigned short) x; + result.Y = (unsigned short) y; + return result; +} + + +static int uv__tty_emit_text(uv_tty_t* handle, WCHAR buffer[], DWORD length, + DWORD* error) { + DWORD written; + + if (*error != ERROR_SUCCESS) { + return -1; + } + + if (!WriteConsoleW(handle->handle, + (void*) buffer, + length, + &written, + NULL)) { + *error = GetLastError(); + return -1; + } + + return 0; +} + + +static int uv__tty_move_caret(uv_tty_t* handle, int x, unsigned char x_relative, + int y, unsigned char y_relative, DWORD* error) { + CONSOLE_SCREEN_BUFFER_INFO info; + COORD pos; + + if (*error != ERROR_SUCCESS) { + return -1; + } + + retry: + if (!GetConsoleScreenBufferInfo(handle->handle, &info)) { + *error = GetLastError(); + } + + pos = uv__tty_make_real_coord(handle, &info, x, x_relative, y, y_relative); + + if (!SetConsoleCursorPosition(handle->handle, pos)) { + if (GetLastError() == ERROR_INVALID_PARAMETER) { + /* The console may be resized - retry */ + goto retry; + } else { + *error = GetLastError(); + return -1; + } + } + + return 0; +} + + +static int uv__tty_reset(uv_tty_t* handle, DWORD* error) { + const COORD origin = {0, 0}; + const WORD char_attrs = uv_tty_default_text_attributes; + CONSOLE_SCREEN_BUFFER_INFO screen_buffer_info; + DWORD count, written; + + if (*error != ERROR_SUCCESS) { + return -1; + } + + /* Reset original text attributes. */ + if (!SetConsoleTextAttribute(handle->handle, char_attrs)) { + *error = GetLastError(); + return -1; + } + + /* Move the cursor position to (0, 0). */ + if (!SetConsoleCursorPosition(handle->handle, origin)) { + *error = GetLastError(); + return -1; + } + + /* Clear the screen buffer. */ + retry: + if (!GetConsoleScreenBufferInfo(handle->handle, &screen_buffer_info)) { + *error = GetLastError(); + return -1; + } + + count = screen_buffer_info.dwSize.X * screen_buffer_info.dwSize.Y; + + if (!(FillConsoleOutputCharacterW(handle->handle, + L'\x20', + count, + origin, + &written) && + FillConsoleOutputAttribute(handle->handle, + char_attrs, + written, + origin, + &written))) { + if (GetLastError() == ERROR_INVALID_PARAMETER) { + /* The console may be resized - retry */ + goto retry; + } else { + *error = GetLastError(); + return -1; + } + } + + /* Move the virtual window up to the top. */ + uv_tty_virtual_offset = 0; + uv__tty_update_virtual_window(&screen_buffer_info); + + /* Reset the cursor size and the cursor state. */ + if (!SetConsoleCursorInfo(handle->handle, &uv_tty_default_cursor_info)) { + *error = GetLastError(); + return -1; + } + + return 0; +} + + +static int uv__tty_clear(uv_tty_t* handle, int dir, char entire_screen, + DWORD* error) { + CONSOLE_SCREEN_BUFFER_INFO info; + COORD start, end; + DWORD count, written; + + int x1, x2, y1, y2; + int x1r, x2r, y1r, y2r; + + if (*error != ERROR_SUCCESS) { + return -1; + } + + if (dir == 0) { + /* Clear from current position */ + x1 = 0; + x1r = 1; + } else { + /* Clear from column 0 */ + x1 = 0; + x1r = 0; + } + + if (dir == 1) { + /* Clear to current position */ + x2 = 0; + x2r = 1; + } else { + /* Clear to end of row. We pretend the console is 65536 characters wide, + * uv__tty_make_real_coord will clip it to the actual console width. */ + x2 = 0xffff; + x2r = 0; + } + + if (!entire_screen) { + /* Stay on our own row */ + y1 = y2 = 0; + y1r = y2r = 1; + } else { + /* Apply columns direction to row */ + y1 = x1; + y1r = x1r; + y2 = x2; + y2r = x2r; + } + + retry: + if (!GetConsoleScreenBufferInfo(handle->handle, &info)) { + *error = GetLastError(); + return -1; + } + + start = uv__tty_make_real_coord(handle, &info, x1, x1r, y1, y1r); + end = uv__tty_make_real_coord(handle, &info, x2, x2r, y2, y2r); + count = (end.Y * info.dwSize.X + end.X) - + (start.Y * info.dwSize.X + start.X) + 1; + + if (!(FillConsoleOutputCharacterW(handle->handle, + L'\x20', + count, + start, + &written) && + FillConsoleOutputAttribute(handle->handle, + info.wAttributes, + written, + start, + &written))) { + if (GetLastError() == ERROR_INVALID_PARAMETER) { + /* The console may be resized - retry */ + goto retry; + } else { + *error = GetLastError(); + return -1; + } + } + + return 0; +} + +#define FLIP_FGBG \ + do { \ + WORD fg = info.wAttributes & 0xF; \ + WORD bg = info.wAttributes & 0xF0; \ + info.wAttributes &= 0xFF00; \ + info.wAttributes |= fg << 4; \ + info.wAttributes |= bg >> 4; \ + } while (0) + +static int uv__tty_set_style(uv_tty_t* handle, DWORD* error) { + unsigned short argc = handle->tty.wr.ansi_csi_argc; + unsigned short* argv = handle->tty.wr.ansi_csi_argv; + int i; + CONSOLE_SCREEN_BUFFER_INFO info; + + char fg_color = -1, bg_color = -1; + char fg_bright = -1, bg_bright = -1; + char inverse = -1; + + if (argc == 0) { + /* Reset mode */ + fg_color = uv_tty_default_fg_color; + bg_color = uv_tty_default_bg_color; + fg_bright = uv_tty_default_fg_bright; + bg_bright = uv_tty_default_bg_bright; + inverse = uv_tty_default_inverse; + } + + for (i = 0; i < argc; i++) { + short arg = argv[i]; + + if (arg == 0) { + /* Reset mode */ + fg_color = uv_tty_default_fg_color; + bg_color = uv_tty_default_bg_color; + fg_bright = uv_tty_default_fg_bright; + bg_bright = uv_tty_default_bg_bright; + inverse = uv_tty_default_inverse; + + } else if (arg == 1) { + /* Foreground bright on */ + fg_bright = 1; + + } else if (arg == 2) { + /* Both bright off */ + fg_bright = 0; + bg_bright = 0; + + } else if (arg == 5) { + /* Background bright on */ + bg_bright = 1; + + } else if (arg == 7) { + /* Inverse: on */ + inverse = 1; + + } else if (arg == 21 || arg == 22) { + /* Foreground bright off */ + fg_bright = 0; + + } else if (arg == 25) { + /* Background bright off */ + bg_bright = 0; + + } else if (arg == 27) { + /* Inverse: off */ + inverse = 0; + + } else if (arg >= 30 && arg <= 37) { + /* Set foreground color */ + fg_color = arg - 30; + + } else if (arg == 39) { + /* Default text color */ + fg_color = uv_tty_default_fg_color; + fg_bright = uv_tty_default_fg_bright; + + } else if (arg >= 40 && arg <= 47) { + /* Set background color */ + bg_color = arg - 40; + + } else if (arg == 49) { + /* Default background color */ + bg_color = uv_tty_default_bg_color; + bg_bright = uv_tty_default_bg_bright; + + } else if (arg >= 90 && arg <= 97) { + /* Set bold foreground color */ + fg_bright = 1; + fg_color = arg - 90; + + } else if (arg >= 100 && arg <= 107) { + /* Set bold background color */ + bg_bright = 1; + bg_color = arg - 100; + + } + } + + if (fg_color == -1 && bg_color == -1 && fg_bright == -1 && + bg_bright == -1 && inverse == -1) { + /* Nothing changed */ + return 0; + } + + if (!GetConsoleScreenBufferInfo(handle->handle, &info)) { + *error = GetLastError(); + return -1; + } + + if ((info.wAttributes & COMMON_LVB_REVERSE_VIDEO) > 0) { + FLIP_FGBG; + } + + if (fg_color != -1) { + info.wAttributes &= ~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); + if (fg_color & 1) info.wAttributes |= FOREGROUND_RED; + if (fg_color & 2) info.wAttributes |= FOREGROUND_GREEN; + if (fg_color & 4) info.wAttributes |= FOREGROUND_BLUE; + } + + if (fg_bright != -1) { + if (fg_bright) { + info.wAttributes |= FOREGROUND_INTENSITY; + } else { + info.wAttributes &= ~FOREGROUND_INTENSITY; + } + } + + if (bg_color != -1) { + info.wAttributes &= ~(BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE); + if (bg_color & 1) info.wAttributes |= BACKGROUND_RED; + if (bg_color & 2) info.wAttributes |= BACKGROUND_GREEN; + if (bg_color & 4) info.wAttributes |= BACKGROUND_BLUE; + } + + if (bg_bright != -1) { + if (bg_bright) { + info.wAttributes |= BACKGROUND_INTENSITY; + } else { + info.wAttributes &= ~BACKGROUND_INTENSITY; + } + } + + if (inverse != -1) { + if (inverse) { + info.wAttributes |= COMMON_LVB_REVERSE_VIDEO; + } else { + info.wAttributes &= ~COMMON_LVB_REVERSE_VIDEO; + } + } + + if ((info.wAttributes & COMMON_LVB_REVERSE_VIDEO) > 0) { + FLIP_FGBG; + } + + if (!SetConsoleTextAttribute(handle->handle, info.wAttributes)) { + *error = GetLastError(); + return -1; + } + + return 0; +} + + +static int uv__tty_save_state(uv_tty_t* handle, unsigned char save_attributes, + DWORD* error) { + CONSOLE_SCREEN_BUFFER_INFO info; + + if (*error != ERROR_SUCCESS) { + return -1; + } + + if (!GetConsoleScreenBufferInfo(handle->handle, &info)) { + *error = GetLastError(); + return -1; + } + + uv__tty_update_virtual_window(&info); + + handle->tty.wr.saved_position.X = info.dwCursorPosition.X; + handle->tty.wr.saved_position.Y = info.dwCursorPosition.Y - + uv_tty_virtual_offset; + handle->flags |= UV_HANDLE_TTY_SAVED_POSITION; + + if (save_attributes) { + handle->tty.wr.saved_attributes = info.wAttributes & + (FOREGROUND_INTENSITY | BACKGROUND_INTENSITY); + handle->flags |= UV_HANDLE_TTY_SAVED_ATTRIBUTES; + } + + return 0; +} + + +static int uv__tty_restore_state(uv_tty_t* handle, + unsigned char restore_attributes, DWORD* error) { + CONSOLE_SCREEN_BUFFER_INFO info; + WORD new_attributes; + + if (*error != ERROR_SUCCESS) { + return -1; + } + + if (handle->flags & UV_HANDLE_TTY_SAVED_POSITION) { + if (uv__tty_move_caret(handle, + handle->tty.wr.saved_position.X, + 0, + handle->tty.wr.saved_position.Y, + 0, + error) != 0) { + return -1; + } + } + + if (restore_attributes && + (handle->flags & UV_HANDLE_TTY_SAVED_ATTRIBUTES)) { + if (!GetConsoleScreenBufferInfo(handle->handle, &info)) { + *error = GetLastError(); + return -1; + } + + new_attributes = info.wAttributes; + new_attributes &= ~(FOREGROUND_INTENSITY | BACKGROUND_INTENSITY); + new_attributes |= handle->tty.wr.saved_attributes; + + if (!SetConsoleTextAttribute(handle->handle, new_attributes)) { + *error = GetLastError(); + return -1; + } + } + + return 0; +} + +static int uv__tty_set_cursor_visibility(uv_tty_t* handle, + BOOL visible, + DWORD* error) { + CONSOLE_CURSOR_INFO cursor_info; + + if (!GetConsoleCursorInfo(handle->handle, &cursor_info)) { + *error = GetLastError(); + return -1; + } + + cursor_info.bVisible = visible; + + if (!SetConsoleCursorInfo(handle->handle, &cursor_info)) { + *error = GetLastError(); + return -1; + } + + return 0; +} + +static int uv__tty_set_cursor_shape(uv_tty_t* handle, int style, DWORD* error) { + CONSOLE_CURSOR_INFO cursor_info; + + if (!GetConsoleCursorInfo(handle->handle, &cursor_info)) { + *error = GetLastError(); + return -1; + } + + if (style == 0) { + cursor_info.dwSize = uv_tty_default_cursor_info.dwSize; + } else if (style <= 2) { + cursor_info.dwSize = CURSOR_SIZE_LARGE; + } else { + cursor_info.dwSize = CURSOR_SIZE_SMALL; + } + + if (!SetConsoleCursorInfo(handle->handle, &cursor_info)) { + *error = GetLastError(); + return -1; + } + + return 0; +} + + +static int uv__tty_write_bufs(uv_tty_t* handle, + const uv_buf_t bufs[], + unsigned int nbufs, + DWORD* error) { + /* We can only write 8k characters at a time. Windows can't handle much more + * characters in a single console write anyway. */ + WCHAR utf16_buf[MAX_CONSOLE_CHAR]; + DWORD utf16_buf_used = 0; + unsigned int i; + +#define FLUSH_TEXT() \ + do { \ + if (utf16_buf_used > 0) { \ + uv__tty_emit_text(handle, utf16_buf, utf16_buf_used, error); \ + utf16_buf_used = 0; \ + } \ + } while (0) + +#define ENSURE_BUFFER_SPACE(wchars_needed) \ + if (wchars_needed > ARRAY_SIZE(utf16_buf) - utf16_buf_used) { \ + FLUSH_TEXT(); \ + } + + /* Cache for fast access */ + unsigned char utf8_bytes_left = handle->tty.wr.utf8_bytes_left; + unsigned int utf8_codepoint = handle->tty.wr.utf8_codepoint; + unsigned char previous_eol = handle->tty.wr.previous_eol; + unsigned short ansi_parser_state = handle->tty.wr.ansi_parser_state; + + /* Store the error here. If we encounter an error, stop trying to do i/o but + * keep parsing the buffer so we leave the parser in a consistent state. */ + *error = ERROR_SUCCESS; + + uv_sem_wait(&uv_tty_output_lock); + + for (i = 0; i < nbufs; i++) { + uv_buf_t buf = bufs[i]; + unsigned int j; + + for (j = 0; j < buf.len; j++) { + unsigned char c = buf.base[j]; + + /* Run the character through the utf8 decoder We happily accept non + * shortest form encodings and invalid code points - there's no real harm + * that can be done. */ + if (utf8_bytes_left == 0) { + /* Read utf-8 start byte */ + DWORD first_zero_bit; + unsigned char not_c = ~c; +#ifdef _MSC_VER /* msvc */ + if (_BitScanReverse(&first_zero_bit, not_c)) { +#else /* assume gcc */ + if (c != 0) { + first_zero_bit = (sizeof(int) * 8) - 1 - __builtin_clz(not_c); +#endif + if (first_zero_bit == 7) { + /* Ascii - pass right through */ + utf8_codepoint = (unsigned int) c; + + } else if (first_zero_bit <= 5) { + /* Multibyte sequence */ + utf8_codepoint = (0xff >> (8 - first_zero_bit)) & c; + utf8_bytes_left = (char) (6 - first_zero_bit); + + } else { + /* Invalid continuation */ + utf8_codepoint = UNICODE_REPLACEMENT_CHARACTER; + } + + } else { + /* 0xff -- invalid */ + utf8_codepoint = UNICODE_REPLACEMENT_CHARACTER; + } + + } else if ((c & 0xc0) == 0x80) { + /* Valid continuation of utf-8 multibyte sequence */ + utf8_bytes_left--; + utf8_codepoint <<= 6; + utf8_codepoint |= ((unsigned int) c & 0x3f); + + } else { + /* Start byte where continuation was expected. */ + utf8_bytes_left = 0; + utf8_codepoint = UNICODE_REPLACEMENT_CHARACTER; + /* Patch buf offset so this character will be parsed again as a start + * byte. */ + j--; + } + + /* Maybe we need to parse more bytes to find a character. */ + if (utf8_bytes_left != 0) { + continue; + } + + /* Parse vt100/ansi escape codes */ + if (uv__vterm_state == UV_TTY_SUPPORTED) { + /* Pass through escape codes if conhost supports them. */ + } else if (ansi_parser_state == ANSI_NORMAL) { + switch (utf8_codepoint) { + case '\033': + ansi_parser_state = ANSI_ESCAPE_SEEN; + continue; + + case 0233: + ansi_parser_state = ANSI_CSI; + handle->tty.wr.ansi_csi_argc = 0; + continue; + } + + } else if (ansi_parser_state == ANSI_ESCAPE_SEEN) { + switch (utf8_codepoint) { + case '[': + ansi_parser_state = ANSI_CSI; + handle->tty.wr.ansi_csi_argc = 0; + continue; + + case '^': + case '_': + case 'P': + case ']': + /* Not supported, but we'll have to parse until we see a stop code, + * e. g. ESC \ or BEL. */ + ansi_parser_state = ANSI_ST_CONTROL; + continue; + + case '\033': + /* Ignore double escape. */ + continue; + + case 'c': + /* Full console reset. */ + FLUSH_TEXT(); + uv__tty_reset(handle, error); + ansi_parser_state = ANSI_NORMAL; + continue; + + case '7': + /* Save the cursor position and text attributes. */ + FLUSH_TEXT(); + uv__tty_save_state(handle, 1, error); + ansi_parser_state = ANSI_NORMAL; + continue; + + case '8': + /* Restore the cursor position and text attributes */ + FLUSH_TEXT(); + uv__tty_restore_state(handle, 1, error); + ansi_parser_state = ANSI_NORMAL; + continue; + + default: + if (utf8_codepoint >= '@' && utf8_codepoint <= '_') { + /* Single-char control. */ + ansi_parser_state = ANSI_NORMAL; + continue; + } else { + /* Invalid - proceed as normal, */ + ansi_parser_state = ANSI_NORMAL; + } + } + + } else if (ansi_parser_state == ANSI_IGNORE) { + /* We're ignoring this command. Stop only on command character. */ + if (utf8_codepoint >= '@' && utf8_codepoint <= '~') { + ansi_parser_state = ANSI_NORMAL; + } + continue; + + } else if (ansi_parser_state == ANSI_DECSCUSR) { + /* So far we've the sequence `ESC [ arg space`, and we're waiting for + * the final command byte. */ + if (utf8_codepoint >= '@' && utf8_codepoint <= '~') { + /* Command byte */ + if (utf8_codepoint == 'q') { + /* Change the cursor shape */ + int style = handle->tty.wr.ansi_csi_argc + ? handle->tty.wr.ansi_csi_argv[0] : 1; + if (style >= 0 && style <= 6) { + FLUSH_TEXT(); + uv__tty_set_cursor_shape(handle, style, error); + } + } + + /* Sequence ended - go back to normal state. */ + ansi_parser_state = ANSI_NORMAL; + continue; + } + /* Unexpected character, but sequence hasn't ended yet. Ignore the rest + * of the sequence. */ + ansi_parser_state = ANSI_IGNORE; + + } else if (ansi_parser_state & ANSI_CSI) { + /* So far we've seen `ESC [`, and we may or may not have already parsed + * some of the arguments that follow. */ + + if (utf8_codepoint >= '0' && utf8_codepoint <= '9') { + /* Parse a numerical argument. */ + if (!(ansi_parser_state & ANSI_IN_ARG)) { + /* We were not currently parsing a number, add a new one. */ + /* Check for that there are too many arguments. */ + if (handle->tty.wr.ansi_csi_argc >= + ARRAY_SIZE(handle->tty.wr.ansi_csi_argv)) { + ansi_parser_state = ANSI_IGNORE; + continue; + } + ansi_parser_state |= ANSI_IN_ARG; + handle->tty.wr.ansi_csi_argc++; + handle->tty.wr.ansi_csi_argv[handle->tty.wr.ansi_csi_argc - 1] = + (unsigned short) utf8_codepoint - '0'; + continue; + + } else { + /* We were already parsing a number. Parse next digit. */ + uint32_t value = 10 * + handle->tty.wr.ansi_csi_argv[handle->tty.wr.ansi_csi_argc - 1]; + + /* Check for overflow. */ + if (value > UINT16_MAX) { + ansi_parser_state = ANSI_IGNORE; + continue; + } + + handle->tty.wr.ansi_csi_argv[handle->tty.wr.ansi_csi_argc - 1] = + (unsigned short) value + (utf8_codepoint - '0'); + continue; + } + + } else if (utf8_codepoint == ';') { + /* Denotes the end of an argument. */ + if (ansi_parser_state & ANSI_IN_ARG) { + ansi_parser_state &= ~ANSI_IN_ARG; + continue; + + } else { + /* If ANSI_IN_ARG is not set, add another argument and default + * it to 0. */ + + /* Check for too many arguments */ + if (handle->tty.wr.ansi_csi_argc >= + + ARRAY_SIZE(handle->tty.wr.ansi_csi_argv)) { + ansi_parser_state = ANSI_IGNORE; + continue; + } + + handle->tty.wr.ansi_csi_argc++; + handle->tty.wr.ansi_csi_argv[handle->tty.wr.ansi_csi_argc - 1] = 0; + continue; + } + + } else if (utf8_codepoint == '?' && + !(ansi_parser_state & ANSI_IN_ARG) && + !(ansi_parser_state & ANSI_EXTENSION) && + handle->tty.wr.ansi_csi_argc == 0) { + /* Pass through '?' if it is the first character after CSI */ + /* This is an extension character from the VT100 codeset */ + /* that is supported and used by most ANSI terminals today. */ + ansi_parser_state |= ANSI_EXTENSION; + continue; + + } else if (utf8_codepoint == ' ' && + !(ansi_parser_state & ANSI_EXTENSION)) { + /* We expect a command byte to follow after this space. The only + * command that we current support is 'set cursor style'. */ + ansi_parser_state = ANSI_DECSCUSR; + continue; + + } else if (utf8_codepoint >= '@' && utf8_codepoint <= '~') { + /* Command byte */ + if (ansi_parser_state & ANSI_EXTENSION) { + /* Sequence is `ESC [ ? args command`. */ + switch (utf8_codepoint) { + case 'l': + /* Hide the cursor */ + if (handle->tty.wr.ansi_csi_argc == 1 && + handle->tty.wr.ansi_csi_argv[0] == 25) { + FLUSH_TEXT(); + uv__tty_set_cursor_visibility(handle, 0, error); + } + break; + + case 'h': + /* Show the cursor */ + if (handle->tty.wr.ansi_csi_argc == 1 && + handle->tty.wr.ansi_csi_argv[0] == 25) { + FLUSH_TEXT(); + uv__tty_set_cursor_visibility(handle, 1, error); + } + break; + } + + } else { + /* Sequence is `ESC [ args command`. */ + int x, y, d; + switch (utf8_codepoint) { + case 'A': + /* cursor up */ + FLUSH_TEXT(); + y = -(handle->tty.wr.ansi_csi_argc + ? handle->tty.wr.ansi_csi_argv[0] : 1); + uv__tty_move_caret(handle, 0, 1, y, 1, error); + break; + + case 'B': + /* cursor down */ + FLUSH_TEXT(); + y = handle->tty.wr.ansi_csi_argc + ? handle->tty.wr.ansi_csi_argv[0] : 1; + uv__tty_move_caret(handle, 0, 1, y, 1, error); + break; + + case 'C': + /* cursor forward */ + FLUSH_TEXT(); + x = handle->tty.wr.ansi_csi_argc + ? handle->tty.wr.ansi_csi_argv[0] : 1; + uv__tty_move_caret(handle, x, 1, 0, 1, error); + break; + + case 'D': + /* cursor back */ + FLUSH_TEXT(); + x = -(handle->tty.wr.ansi_csi_argc + ? handle->tty.wr.ansi_csi_argv[0] : 1); + uv__tty_move_caret(handle, x, 1, 0, 1, error); + break; + + case 'E': + /* cursor next line */ + FLUSH_TEXT(); + y = handle->tty.wr.ansi_csi_argc + ? handle->tty.wr.ansi_csi_argv[0] : 1; + uv__tty_move_caret(handle, 0, 0, y, 1, error); + break; + + case 'F': + /* cursor previous line */ + FLUSH_TEXT(); + y = -(handle->tty.wr.ansi_csi_argc + ? handle->tty.wr.ansi_csi_argv[0] : 1); + uv__tty_move_caret(handle, 0, 0, y, 1, error); + break; + + case 'G': + /* cursor horizontal move absolute */ + FLUSH_TEXT(); + x = (handle->tty.wr.ansi_csi_argc >= 1 && + handle->tty.wr.ansi_csi_argv[0]) + ? handle->tty.wr.ansi_csi_argv[0] - 1 : 0; + uv__tty_move_caret(handle, x, 0, 0, 1, error); + break; + + case 'H': + case 'f': + /* cursor move absolute */ + FLUSH_TEXT(); + y = (handle->tty.wr.ansi_csi_argc >= 1 && + handle->tty.wr.ansi_csi_argv[0]) + ? handle->tty.wr.ansi_csi_argv[0] - 1 : 0; + x = (handle->tty.wr.ansi_csi_argc >= 2 && + handle->tty.wr.ansi_csi_argv[1]) + ? handle->tty.wr.ansi_csi_argv[1] - 1 : 0; + uv__tty_move_caret(handle, x, 0, y, 0, error); + break; + + case 'J': + /* Erase screen */ + FLUSH_TEXT(); + d = handle->tty.wr.ansi_csi_argc + ? handle->tty.wr.ansi_csi_argv[0] : 0; + if (d >= 0 && d <= 2) { + uv__tty_clear(handle, d, 1, error); + } + break; + + case 'K': + /* Erase line */ + FLUSH_TEXT(); + d = handle->tty.wr.ansi_csi_argc + ? handle->tty.wr.ansi_csi_argv[0] : 0; + if (d >= 0 && d <= 2) { + uv__tty_clear(handle, d, 0, error); + } + break; + + case 'm': + /* Set style */ + FLUSH_TEXT(); + uv__tty_set_style(handle, error); + break; + + case 's': + /* Save the cursor position. */ + FLUSH_TEXT(); + uv__tty_save_state(handle, 0, error); + break; + + case 'u': + /* Restore the cursor position */ + FLUSH_TEXT(); + uv__tty_restore_state(handle, 0, error); + break; + } + } + + /* Sequence ended - go back to normal state. */ + ansi_parser_state = ANSI_NORMAL; + continue; + + } else { + /* We don't support commands that use private mode characters or + * intermediaries. Ignore the rest of the sequence. */ + ansi_parser_state = ANSI_IGNORE; + continue; + } + + } else if (ansi_parser_state & ANSI_ST_CONTROL) { + /* Unsupported control code. + * Ignore everything until we see `BEL` or `ESC \`. */ + if (ansi_parser_state & ANSI_IN_STRING) { + if (!(ansi_parser_state & ANSI_BACKSLASH_SEEN)) { + if (utf8_codepoint == '"') { + ansi_parser_state &= ~ANSI_IN_STRING; + } else if (utf8_codepoint == '\\') { + ansi_parser_state |= ANSI_BACKSLASH_SEEN; + } + } else { + ansi_parser_state &= ~ANSI_BACKSLASH_SEEN; + } + } else { + if (utf8_codepoint == '\007' || (utf8_codepoint == '\\' && + (ansi_parser_state & ANSI_ESCAPE_SEEN))) { + /* End of sequence */ + ansi_parser_state = ANSI_NORMAL; + } else if (utf8_codepoint == '\033') { + /* Escape character */ + ansi_parser_state |= ANSI_ESCAPE_SEEN; + } else if (utf8_codepoint == '"') { + /* String starting */ + ansi_parser_state |= ANSI_IN_STRING; + ansi_parser_state &= ~ANSI_ESCAPE_SEEN; + ansi_parser_state &= ~ANSI_BACKSLASH_SEEN; + } else { + ansi_parser_state &= ~ANSI_ESCAPE_SEEN; + } + } + continue; + } else { + /* Inconsistent state */ + abort(); + } + + if (utf8_codepoint == 0x0a || utf8_codepoint == 0x0d) { + /* EOL conversion - emit \r\n when we see \n. */ + + if (utf8_codepoint == 0x0a && previous_eol != 0x0d) { + /* \n was not preceded by \r; print \r\n. */ + ENSURE_BUFFER_SPACE(2); + utf16_buf[utf16_buf_used++] = L'\r'; + utf16_buf[utf16_buf_used++] = L'\n'; + } else if (utf8_codepoint == 0x0d && previous_eol == 0x0a) { + /* \n was followed by \r; do not print the \r, since the source was + * either \r\n\r (so the second \r is redundant) or was \n\r (so the + * \n was processed by the last case and an \r automatically + * inserted). */ + } else { + /* \r without \n; print \r as-is. */ + ENSURE_BUFFER_SPACE(1); + utf16_buf[utf16_buf_used++] = (WCHAR) utf8_codepoint; + } + + previous_eol = (char) utf8_codepoint; + + } else if (utf8_codepoint <= 0xffff) { + /* Encode character into utf-16 buffer. */ + ENSURE_BUFFER_SPACE(1); + utf16_buf[utf16_buf_used++] = (WCHAR) utf8_codepoint; + previous_eol = 0; + } else { + ENSURE_BUFFER_SPACE(2); + utf8_codepoint -= 0x10000; + utf16_buf[utf16_buf_used++] = (WCHAR) (utf8_codepoint / 0x400 + 0xD800); + utf16_buf[utf16_buf_used++] = (WCHAR) (utf8_codepoint % 0x400 + 0xDC00); + previous_eol = 0; + } + } + } + + /* Flush remaining characters */ + FLUSH_TEXT(); + + /* Copy cached values back to struct. */ + handle->tty.wr.utf8_bytes_left = utf8_bytes_left; + handle->tty.wr.utf8_codepoint = utf8_codepoint; + handle->tty.wr.previous_eol = previous_eol; + handle->tty.wr.ansi_parser_state = ansi_parser_state; + + uv_sem_post(&uv_tty_output_lock); + + if (*error == STATUS_SUCCESS) { + return 0; + } else { + return -1; + } + +#undef FLUSH_TEXT +} + + +int uv__tty_write(uv_loop_t* loop, + uv_write_t* req, + uv_tty_t* handle, + const uv_buf_t bufs[], + unsigned int nbufs, + uv_write_cb cb) { + DWORD error; + + UV_REQ_INIT(req, UV_WRITE); + req->handle = (uv_stream_t*) handle; + req->cb = cb; + + handle->reqs_pending++; + handle->stream.conn.write_reqs_pending++; + REGISTER_HANDLE_REQ(loop, handle); + + req->u.io.queued_bytes = 0; + + if (!uv__tty_write_bufs(handle, bufs, nbufs, &error)) { + SET_REQ_SUCCESS(req); + } else { + SET_REQ_ERROR(req, error); + } + + uv__insert_pending_req(loop, (uv_req_t*) req); + + return 0; +} + + +int uv__tty_try_write(uv_tty_t* handle, + const uv_buf_t bufs[], + unsigned int nbufs) { + DWORD error; + + if (handle->stream.conn.write_reqs_pending > 0) + return UV_EAGAIN; + + if (uv__tty_write_bufs(handle, bufs, nbufs, &error)) + return uv_translate_sys_error(error); + + return uv__count_bufs(bufs, nbufs); +} + + +void uv__process_tty_write_req(uv_loop_t* loop, uv_tty_t* handle, + uv_write_t* req) { + int err; + + handle->write_queue_size -= req->u.io.queued_bytes; + UNREGISTER_HANDLE_REQ(loop, handle); + + if (req->cb) { + err = GET_REQ_ERROR(req); + req->cb(req, uv_translate_sys_error(err)); + } + + + handle->stream.conn.write_reqs_pending--; + if (handle->stream.conn.write_reqs_pending == 0 && + uv__is_stream_shutting(handle)) + uv__process_tty_shutdown_req(loop, + handle, + handle->stream.conn.shutdown_req); + + DECREASE_PENDING_REQ_COUNT(handle); +} + + +void uv__tty_close(uv_tty_t* handle) { + assert(handle->u.fd == -1 || handle->u.fd > 2); + if (handle->flags & UV_HANDLE_READING) + uv__tty_read_stop(handle); + + if (handle->u.fd == -1) + CloseHandle(handle->handle); + else + _close(handle->u.fd); + + handle->u.fd = -1; + handle->handle = INVALID_HANDLE_VALUE; + handle->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE); + uv__handle_closing(handle); + + if (handle->reqs_pending == 0) + uv__want_endgame(handle->loop, (uv_handle_t*) handle); +} + + +void uv__process_tty_shutdown_req(uv_loop_t* loop, uv_tty_t* stream, uv_shutdown_t* req) { + assert(stream->stream.conn.write_reqs_pending == 0); + assert(req); + + stream->stream.conn.shutdown_req = NULL; + UNREGISTER_HANDLE_REQ(loop, stream); + + /* TTY shutdown is really just a no-op */ + if (req->cb) { + if (stream->flags & UV_HANDLE_CLOSING) { + req->cb(req, UV_ECANCELED); + } else { + req->cb(req, 0); + } + } + + DECREASE_PENDING_REQ_COUNT(stream); +} + + +void uv__tty_endgame(uv_loop_t* loop, uv_tty_t* handle) { + assert(handle->flags & UV_HANDLE_CLOSING); + assert(handle->reqs_pending == 0); + + /* The wait handle used for raw reading should be unregistered when the + * wait callback runs. */ + assert(!(handle->flags & UV_HANDLE_TTY_READABLE) || + handle->tty.rd.read_raw_wait == NULL); + + assert(!(handle->flags & UV_HANDLE_CLOSED)); + uv__handle_close(handle); +} + + +int uv_tty_reset_mode(void) { + /* Not necessary to do anything. */ + return 0; +} + +/* Determine whether or not this version of windows supports + * proper ANSI color codes. Should be supported as of windows + * 10 version 1511, build number 10.0.10586. + */ +static void uv__determine_vterm_state(HANDLE handle) { + DWORD dwMode = 0; + + uv__need_check_vterm_state = FALSE; + if (!GetConsoleMode(handle, &dwMode)) { + return; + } + + dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + if (!SetConsoleMode(handle, dwMode)) { + return; + } + + uv__vterm_state = UV_TTY_SUPPORTED; +} + +static DWORD WINAPI uv__tty_console_resize_message_loop_thread(void* param) { + NTSTATUS status; + ULONG_PTR conhost_pid; + MSG msg; + + if (pSetWinEventHook == NULL || pNtQueryInformationProcess == NULL) + return 0; + + status = pNtQueryInformationProcess(GetCurrentProcess(), + ProcessConsoleHostProcess, + &conhost_pid, + sizeof(conhost_pid), + NULL); + + if (!NT_SUCCESS(status)) { + /* We couldn't retrieve our console host process, probably because this + * is a 32-bit process running on 64-bit Windows. Fall back to receiving + * console events from the input stream only. */ + return 0; + } + + /* Ensure the PID is a multiple of 4, which is required by SetWinEventHook */ + conhost_pid &= ~(ULONG_PTR)0x3; + + uv__tty_console_resized = CreateEvent(NULL, TRUE, FALSE, NULL); + if (uv__tty_console_resized == NULL) + return 0; + if (QueueUserWorkItem(uv__tty_console_resize_watcher_thread, + NULL, + WT_EXECUTELONGFUNCTION) == 0) + return 0; + + if (!pSetWinEventHook(EVENT_CONSOLE_LAYOUT, + EVENT_CONSOLE_LAYOUT, + NULL, + uv__tty_console_resize_event, + (DWORD)conhost_pid, + 0, + WINEVENT_OUTOFCONTEXT)) + return 0; + + while (GetMessage(&msg, NULL, 0, 0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + return 0; +} + +static void CALLBACK uv__tty_console_resize_event(HWINEVENTHOOK hWinEventHook, + DWORD event, + HWND hwnd, + LONG idObject, + LONG idChild, + DWORD dwEventThread, + DWORD dwmsEventTime) { + SetEvent(uv__tty_console_resized); +} + +static DWORD WINAPI uv__tty_console_resize_watcher_thread(void* param) { + for (;;) { + /* Make sure to not overwhelm the system with resize events */ + Sleep(33); + WaitForSingleObject(uv__tty_console_resized, INFINITE); + ResetEvent(uv__tty_console_resized); + uv__tty_console_signal_resize(); + } + return 0; +} + +static void uv__tty_console_signal_resize(void) { + CONSOLE_SCREEN_BUFFER_INFO sb_info; + int width, height; + + if (!GetConsoleScreenBufferInfo(uv__tty_console_handle, &sb_info)) + return; + + width = sb_info.dwSize.X; + height = sb_info.srWindow.Bottom - sb_info.srWindow.Top + 1; + + uv_mutex_lock(&uv__tty_console_resize_mutex); + if (width != uv__tty_console_width || height != uv__tty_console_height) { + uv__tty_console_width = width; + uv__tty_console_height = height; + uv_mutex_unlock(&uv__tty_console_resize_mutex); + uv__signal_dispatch(SIGWINCH); + } else { + uv_mutex_unlock(&uv__tty_console_resize_mutex); + } +} + +void uv_tty_set_vterm_state(uv_tty_vtermstate_t state) { + uv_sem_wait(&uv_tty_output_lock); + uv__need_check_vterm_state = FALSE; + uv__vterm_state = state; + uv_sem_post(&uv_tty_output_lock); +} + +int uv_tty_get_vterm_state(uv_tty_vtermstate_t* state) { + uv_sem_wait(&uv_tty_output_lock); + *state = uv__vterm_state; + uv_sem_post(&uv_tty_output_lock); + return 0; +} diff --git a/pkg/c3/defs.h b/pkg/c3/defs.h index 5adc05d599..86d4374dd5 100644 --- a/pkg/c3/defs.h +++ b/pkg/c3/defs.h @@ -280,4 +280,7 @@ c3_align_p(void const * p, size_t al, align_dir hilo) { return (void*)x; } +#define c3_likely(x) ( __builtin_expect(!!(x), 1) ) +#define c3_unlikely(x) ( __builtin_expect(!!(x), 0) ) + #endif /* ifndef C3_DEFS_H */ diff --git a/pkg/c3/motes.h b/pkg/c3/motes.h index 21dd9dac01..423634f81d 100644 --- a/pkg/c3/motes.h +++ b/pkg/c3/motes.h @@ -632,6 +632,7 @@ # define c3__jato c3_s4('j','a','t','o') # define c3__jet c3_s3('j','e','t') # define c3__jetd c3_s4('j','e','t','d') +# define c3__jinx c3_s4('j','i','n','x') # define c3__just c3_s4('j','u','s','t') # define c3__k c3_s1('k') # define c3__khan c3_s4('k','h','a','n') diff --git a/pkg/c3/platform/windows/wsetjmp.h b/pkg/c3/platform/windows/wsetjmp.h deleted file mode 100644 index 08e1957efa..0000000000 --- a/pkg/c3/platform/windows/wsetjmp.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef _MINGW_SETJMP_H -#define _MINGW_SETJMP_H -#include "setjmp.h" - -#define _longjmp longjmp - -#endif//_MINGW_SETJMP_H diff --git a/pkg/c3/portable.h b/pkg/c3/portable.h index 29172f270d..e07cdf4bcb 100644 --- a/pkg/c3/portable.h +++ b/pkg/c3/portable.h @@ -87,7 +87,7 @@ # include # include # include -# include +# include # include # include # include @@ -234,12 +234,12 @@ /* Stat. */ # if defined(U3_OS_linux) || defined(U3_OS_windows) -# define c3_stat_mtime(dp) (u3_time_t_in_ts((dp)->st_mtime)) +# define c3_stat_mtime(dp) (u3m_time_t_in_ts((dp)->st_mtime)) # elif defined(U3_OS_osx) -# define c3_stat_mtime(dp) (u3_time_in_ts(&((dp)->st_mtimespec))) +# define c3_stat_mtime(dp) (u3m_time_in_ts(&((dp)->st_mtimespec))) # define lseek64 lseek # elif defined(U3_OS_bsd) -# define c3_stat_mtime(dp) (u3_time_in_ts(&((dp)->st_mtim))) +# define c3_stat_mtime(dp) (u3m_time_in_ts(&((dp)->st_mtim))) # define lseek64 lseek # else # error "port: timeconvert" diff --git a/pkg/noun/allocate.c b/pkg/noun/allocate.c index eb127bfe96..68e23f3a19 100644 --- a/pkg/noun/allocate.c +++ b/pkg/noun/allocate.c @@ -1660,7 +1660,7 @@ u3a_print_quac(FILE* fil_u, c3_w den_w, u3m_quac* mas_u) u3m_quac* u3a_mark_road() { - u3m_quac** qua_u = c3_malloc(sizeof(*qua_u) * 14); + u3m_quac** qua_u = c3_malloc(sizeof(*qua_u) * 16); qua_u[0] = c3_calloc(sizeof(*qua_u[0])); qua_u[0]->nam_c = strdup("namespace"); @@ -1750,10 +1750,18 @@ u3a_mark_road() } qua_u[12] = c3_calloc(sizeof(*qua_u[12])); - qua_u[12]->nam_c = strdup("ford memoization cache"); - qua_u[12]->siz_w = u3h_mark(u3R->cax.for_p) * 4; - - qua_u[13] = NULL; + qua_u[12]->nam_c = strdup("loop hint set"); + qua_u[12]->siz_w = u3h_mark(u3R->lop_p) * 4; + + qua_u[13] = c3_calloc(sizeof(*qua_u[13])); + qua_u[13]->nam_c = strdup("timer stack"); + qua_u[13]->siz_w = u3a_mark_noun(u3R->tim) * 4; + + qua_u[14] = c3_calloc(sizeof(*qua_u[14])); + qua_u[14]->nam_c = strdup("ford memoization cache"); + qua_u[14]->siz_w = u3h_mark(u3R->cax.for_p) * 4; + + qua_u[15] = NULL; c3_w sum_w = 0; for (c3_w i_w = 0; qua_u[i_w]; i_w++) { @@ -1791,8 +1799,10 @@ u3a_rewrite_compact(void) u3a_relocate_noun(&(u3R->pro.don)); u3a_relocate_noun(&(u3R->pro.day)); u3a_relocate_noun(&(u3R->pro.trace)); + u3a_relocate_noun(&(u3R->tim)); u3h_relocate(&(u3R->cax.har_p)); u3h_relocate(&(u3R->cax.per_p)); + u3h_relocate(&(u3R->lop_p)); u3h_relocate(&(u3R->cax.for_p)); } @@ -1803,7 +1813,7 @@ u3a_idle(u3a_road* rod_u) { // XX ignores argument c3_w pag_w = _idle_pages(); - if ( pag_w ) { + if ( (u3C.wag_w & u3o_verbose) && pag_w ) { fprintf(stderr, "loom: idle %u complete pages\r\n", pag_w); } return (pag_w << u3a_page) + _idle_words(); @@ -1814,6 +1824,9 @@ u3a_ream(void) { _poison_pages(); _poison_words(); + + // XX enable behind flag + // _sane_dell(); } void diff --git a/pkg/noun/allocate.h b/pkg/noun/allocate.h index 0013db6f98..b1401c2a23 100644 --- a/pkg/noun/allocate.h +++ b/pkg/noun/allocate.h @@ -3,6 +3,7 @@ #include "error.h" #include "manage.h" +#include "rsignal.h" /** Constants. **/ @@ -144,8 +145,10 @@ STATIC_ASSERT( u3a_vits <= u3a_min_log, c3_w off_w; // spin stack offset c3_w fow_w; // spin stack overflow count + u3p(u3h_root) lop_p; // %loop hint set + u3_noun tim; // list of absolute deadlines - c3_w fut_w[30]; // futureproof buffer + c3_w fut_w[28]; // futureproof buffer struct { // escape buffer union { @@ -216,7 +219,8 @@ STATIC_ASSERT( u3a_vits <= u3a_min_log, /* u3a_flag: flags for how.fag_w. All arena related. */ enum u3a_flag { - u3a_flag_sand = 0x1, // bump allocation (XX not impl) + u3a_flag_sand = 1 << 1, // bump allocation (XX not impl) + u3a_flag_cash = 1 << 2, // memo cache harvesting }; /* u3a_pile: stack control, abstracted over road direction. diff --git a/pkg/noun/build.zig b/pkg/noun/build.zig index 2a3550fa8c..8ba440b469 100644 --- a/pkg/noun/build.zig +++ b/pkg/noun/build.zig @@ -197,7 +197,7 @@ pub fn build(b: *std.Build) !void { .linux => "platform/linux/rsignal.h", .windows => "platform/windows/rsignal.h", else => "", - }), "platform/rsignal.h"); + }), "rsignal.h"); b.installArtifact(pkg_noun); } diff --git a/pkg/noun/hashtable.c b/pkg/noun/hashtable.c index 6b1fb39f87..a2e05bddcc 100644 --- a/pkg/noun/hashtable.c +++ b/pkg/noun/hashtable.c @@ -421,6 +421,7 @@ _ch_some_del(u3h_slot* sot_w, u3_noun key, c3_w lef_w, c3_w rem_w) } /* u3h_del(); delete from hashtable. +** `key` is RETAINED */ void u3h_del(u3p(u3h_root) har_p, u3_noun key) diff --git a/pkg/noun/imprison.c b/pkg/noun/imprison.c index c182d95eb0..11765e5c88 100644 --- a/pkg/noun/imprison.c +++ b/pkg/noun/imprison.c @@ -9,6 +9,22 @@ #include "trace.h" #include "xtract.h" +#if defined(__x86_64__) +#include +#endif + +#ifdef __IMMINTRIN_H +#define _addcarry_w _addcarry_u32 +#else +static inline c3_b +_addcarry_w(c3_b car_b, c3_w a_w, c3_w b_w, c3_w* restrict c_w) +{ + c3_d sum_d = (c3_d)car_b + (c3_d)a_w + (c3_d)b_w; + *c_w = (c3_w)sum_d; + return (c3_b)(sum_d >> 32); +} +#endif + /* _ci_slab_size(): calculate slab bloq-size, checking for overflow. */ static c3_w @@ -475,20 +491,35 @@ u3i_vint(u3_noun a) { u3_assert(u3_none != a); - if ( _(u3a_is_cat(a)) ) { - return ( a == 0x7fffffff ) ? u3i_word(a + 1) : (a + 1); + if ( c3_likely(_(u3a_is_cat(a))) ) { + return ( c3_unlikely(a == 0x7fffffff) ) ? u3i_word(a + 1) : (a + 1); } - else if ( _(u3a_is_cell(a)) ) { + else if ( c3_unlikely(_(u3a_is_cell(a))) ) { return u3m_bail(c3__exit); } else { - mpz_t a_mp; + u3i_slab sab_u; + u3i_slab_init(&sab_u, 0, u3r_met(0, a) + 1); + + u3a_atom* pug_u = u3a_to_ptr(a); - u3r_mp(a_mp, a); - u3z(a); + c3_w i_w = 0; + c3_b car_b = 1; + c3_w *a_buf_w = pug_u->buf_w; + c3_w *b_buf_w = sab_u.buf_w; - mpz_add_ui(a_mp, a_mp, 1); - return u3i_mp(a_mp); + for (; i_w < pug_u->len_w && car_b; i_w++) { + car_b = _addcarry_w(car_b, a_buf_w[i_w], 0, &b_buf_w[i_w]); + } + + if (car_b) { + b_buf_w[pug_u->len_w] = 1; + } + else { + memcpy(&b_buf_w[i_w], &a_buf_w[i_w], (pug_u->len_w - i_w) << 2); + } + + return u3i_slab_mint(&sab_u); } } @@ -586,14 +617,18 @@ u3i_tape(const c3_c* txt_c) u3_noun u3i_list(u3_weak som, ...) { - u3_noun lit = u3_nul; + u3_noun lit = u3_nul; + u3_noun* let = &lit; + u3_noun *hed, *tel; va_list ap; if ( u3_none == som ) { return lit; } else { - lit = u3nc(som, lit); + *let = u3i_defcons(&hed, &tel); + *hed = som; + let = tel; } { @@ -605,13 +640,16 @@ u3i_list(u3_weak som, ...) break; } else { - lit = u3nc(tem, lit); + *let = u3i_defcons(&hed, &tel); + *hed = tem; + let = tel; } } va_end(ap); } - return u3kb_flop(lit); + *let = u3_nul; + return lit; } /* u3i_edit(): diff --git a/pkg/noun/jets/a/add.c b/pkg/noun/jets/a/add.c index 308d14380a..4920898405 100644 --- a/pkg/noun/jets/a/add.c +++ b/pkg/noun/jets/a/add.c @@ -6,6 +6,52 @@ #include "noun.h" +#if defined(__x86_64__) +#include +#endif + +#ifdef __IMMINTRIN_H +#define _addcarry_w _addcarry_u32 +#else +static inline c3_b +_addcarry_w(c3_b car_b, c3_w a_w, c3_w b_w, c3_w* restrict c_w) +{ + c3_d sum_d = (c3_d)car_b + (c3_d)a_w + (c3_d)b_w; + *c_w = (c3_w)sum_d; + return (c3_b)(sum_d >> 32); +} +#endif + +static void +_add_words(c3_w* a_buf_w, + c3_w a_len_w, + c3_w* b_buf_w, + c3_w b_len_w, + c3_w* restrict c_buf_w) +{ + c3_w min_w = c3_min(a_len_w, b_len_w); + c3_w max_w = c3_max(a_len_w, b_len_w); + c3_b car_b = 0; + + for (c3_w i_w = 0; i_w < min_w; i_w++) { + car_b = _addcarry_w(car_b, a_buf_w[i_w], b_buf_w[i_w], &c_buf_w[i_w]); + } + + c3_w* rest_w = ( a_len_w < b_len_w ) ? b_buf_w : a_buf_w; + + c3_w i_w = min_w; + for (; i_w < max_w && car_b; i_w++) { + car_b = _addcarry_w(car_b, rest_w[i_w], 0, &c_buf_w[i_w]); + } + + if ( car_b ) { + c_buf_w[max_w] = 1; + } + else { + memcpy(&c_buf_w[i_w], &rest_w[i_w], (max_w - i_w) << 2); + } +} + u3_noun u3qa_add(u3_atom a, u3_atom b) @@ -22,15 +68,24 @@ u3qa_add(u3_atom a, return u3k(a); } else { - mpz_t a_mp, b_mp; - - u3r_mp(a_mp, a); - u3r_mp(b_mp, b); + u3i_slab sab_u; + c3_w *a_buf_w, *b_buf_w, *c_buf_w; + c3_w a_len_w, b_len_w; - mpz_add(a_mp, a_mp, b_mp); - mpz_clear(b_mp); + a_buf_w = u3r_word_buffer(&a, &a_len_w); + b_buf_w = u3r_word_buffer(&b, &b_len_w); + // u3i_slab_init(&sab_u, 5, c3_max(a_len_w, b_len_w) + 1); + // we have to do more measuring to avoid growing atom buffers on each + // addition as u3a_wtrim noops as of 3e8473d + // + c3_w a_met0_w = u3r_met(0, a), + b_met0_w = u3r_met(0, b); + u3i_slab_init(&sab_u, 0, c3_max(a_met0_w, b_met0_w) + 1); + + c_buf_w = sab_u.buf_w; - return u3i_mp(a_mp); + _add_words(a_buf_w, a_len_w, b_buf_w, b_len_w, c_buf_w); + return u3i_slab_mint(&sab_u); } } diff --git a/pkg/noun/jets/a/dec.c b/pkg/noun/jets/a/dec.c index 02a8ba1df8..072e529ce0 100644 --- a/pkg/noun/jets/a/dec.c +++ b/pkg/noun/jets/a/dec.c @@ -19,17 +19,7 @@ u3qa_dec(u3_atom a) return u3m_error("decrement-underflow"); } else { - if ( _(u3a_is_cat(a)) ) { - return a - 1; - } - else { - mpz_t a_mp; - - u3r_mp(a_mp, a); - mpz_sub_ui(a_mp, a_mp, 1); - - return u3i_mp(a_mp); - } + return u3qa_sub(a, 1); } } diff --git a/pkg/noun/jets/a/gte.c b/pkg/noun/jets/a/gte.c index 371727d434..cbb2a25c79 100644 --- a/pkg/noun/jets/a/gte.c +++ b/pkg/noun/jets/a/gte.c @@ -8,30 +8,7 @@ u3_noun u3qa_gte(u3_atom a, u3_atom b) { - if (c3y == u3a_is_cat(a) || c3y == u3a_is_cat(b)) - { - return __( a >= b ); - } - - if (a == b) return c3y; - - u3a_atom* a_u = u3a_to_ptr(a); - u3a_atom* b_u = u3a_to_ptr(b); - - if (a_u->len_w != b_u->len_w) - { - return __( a_u->len_w > b_u->len_w ); - } - - c3_w* a_w = a_u->buf_w; - c3_w* b_w = b_u->buf_w; - for (c3_w i_w = a_u->len_w; i_w--;) - { - if (a_w[i_w] > b_w[i_w]) return c3y; - if (a_w[i_w] < b_w[i_w]) return c3n; - } - - return c3y; + return __( -1 != u3r_comp(a, b) ); } u3_noun diff --git a/pkg/noun/jets/a/gth.c b/pkg/noun/jets/a/gth.c index 3863b94b78..6e20ef21e9 100644 --- a/pkg/noun/jets/a/gth.c +++ b/pkg/noun/jets/a/gth.c @@ -9,30 +9,7 @@ u3_noun u3qa_gth(u3_atom a, u3_atom b) { - if (c3y == u3a_is_cat(a) || c3y == u3a_is_cat(b)) - { - return __( a > b ); - } - - if (a == b) return c3n; - - u3a_atom* a_u = u3a_to_ptr(a); - u3a_atom* b_u = u3a_to_ptr(b); - - if (a_u->len_w != b_u->len_w) - { - return __( a_u->len_w > b_u->len_w ); - } - - c3_w* a_w = a_u->buf_w; - c3_w* b_w = b_u->buf_w; - for (c3_w i_w = a_u->len_w; i_w--;) - { - if (a_w[i_w] > b_w[i_w]) return c3y; - if (a_w[i_w] < b_w[i_w]) return c3n; - } - - return c3n; + return __( 1 == u3r_comp(a, b) ); } u3_noun diff --git a/pkg/noun/jets/a/lte.c b/pkg/noun/jets/a/lte.c index 0565212695..ba26f55a41 100644 --- a/pkg/noun/jets/a/lte.c +++ b/pkg/noun/jets/a/lte.c @@ -9,30 +9,7 @@ u3_noun u3qa_lte(u3_atom a, u3_atom b) { - if (c3y == u3a_is_cat(a) || c3y == u3a_is_cat(b)) - { - return __( a <= b ); - } - - if (a == b) return c3y; - - u3a_atom* a_u = u3a_to_ptr(a); - u3a_atom* b_u = u3a_to_ptr(b); - - if (a_u->len_w != b_u->len_w) - { - return __( a_u->len_w < b_u->len_w ); - } - - c3_w* a_w = a_u->buf_w; - c3_w* b_w = b_u->buf_w; - for (c3_w i_w = a_u->len_w; i_w--;) - { - if (a_w[i_w] < b_w[i_w]) return c3y; - if (a_w[i_w] > b_w[i_w]) return c3n; - } - - return c3y; + return __( 1 != u3r_comp(a, b) ); } u3_noun diff --git a/pkg/noun/jets/a/lth.c b/pkg/noun/jets/a/lth.c index edf95b36f4..a4ffba55c2 100644 --- a/pkg/noun/jets/a/lth.c +++ b/pkg/noun/jets/a/lth.c @@ -8,30 +8,7 @@ u3_noun u3qa_lth(u3_atom a, u3_atom b) { - if (c3y == u3a_is_cat(a) || c3y == u3a_is_cat(b)) - { - return __( a < b ); - } - - if (a == b) return c3n; - - u3a_atom* a_u = u3a_to_ptr(a); - u3a_atom* b_u = u3a_to_ptr(b); - - if (a_u->len_w != b_u->len_w) - { - return __( a_u->len_w < b_u->len_w ); - } - - c3_w* a_w = a_u->buf_w; - c3_w* b_w = b_u->buf_w; - for (c3_w i_w = a_u->len_w; i_w--;) - { - if (a_w[i_w] < b_w[i_w]) return c3y; - if (a_w[i_w] > b_w[i_w]) return c3n; - } - - return c3n; + return __( -1 == u3r_comp(a, b) ); } u3_noun diff --git a/pkg/noun/jets/a/sub.c b/pkg/noun/jets/a/sub.c index 321ba778c4..8ae61789d6 100644 --- a/pkg/noun/jets/a/sub.c +++ b/pkg/noun/jets/a/sub.c @@ -6,6 +6,44 @@ #include "noun.h" +#if defined(__x86_64__) +#include +#endif + +#ifdef __IMMINTRIN_H +#define _subborrow_w _subborrow_u32 +#else +static inline c3_b +_subborrow_w(c3_b bor_b, c3_w a_w, c3_w b_w, c3_w* restrict c_w) +{ + c3_d dif_d = (c3_d)a_w - (c3_d)b_w - (c3_d)bor_b; + *c_w = (c3_w)dif_d; + return (c3_b)(dif_d >> 63); +} +#endif + +static void +_sub_words(c3_w* a_buf_w, + c3_w a_len_w, + c3_w* b_buf_w, + c3_w b_len_w, + c3_w* restrict c_buf_w) +{ + c3_b bor_b = 0; + + for (c3_w i_w = 0; i_w < b_len_w; i_w++) { + bor_b = _subborrow_w(bor_b, a_buf_w[i_w], b_buf_w[i_w], &c_buf_w[i_w]); + } + + c3_w i_w = b_len_w; + for (; i_w < a_len_w && bor_b; i_w++) { + bor_b = _subborrow_w(bor_b, a_buf_w[i_w], 0, &c_buf_w[i_w]); + } + + u3_assert( 0 == bor_b ); + memcpy(&c_buf_w[i_w], &a_buf_w[i_w], (a_len_w - i_w) << 2); +} + u3_noun u3qa_sub(u3_atom a, u3_atom b) @@ -22,21 +60,24 @@ u3qa_sub(u3_atom a, return u3k(a); } else { - mpz_t a_mp, b_mp; - - u3r_mp(a_mp, a); - u3r_mp(b_mp, b); - - if ( mpz_cmp(a_mp, b_mp) < 0 ) { - mpz_clear(a_mp); - mpz_clear(b_mp); - + c3_ys cmp_ys = u3r_comp(a, b); + if ( 0 == cmp_ys ) { + return 0; + } + if ( -1 == cmp_ys ) { return u3m_error("subtract-underflow"); } - mpz_sub(a_mp, a_mp, b_mp); - mpz_clear(b_mp); + u3i_slab sab_u; + c3_w *a_buf_w, *b_buf_w, *c_buf_w; + c3_w a_len_w, b_len_w; + + a_buf_w = u3r_word_buffer(&a, &a_len_w); + b_buf_w = u3r_word_buffer(&b, &b_len_w); + u3i_slab_init(&sab_u, 5, a_len_w); + c_buf_w = sab_u.buf_w; - return u3i_mp(a_mp); + _sub_words(a_buf_w, a_len_w, b_buf_w, b_len_w, c_buf_w); + return u3i_slab_mint(&sab_u); } } diff --git a/pkg/noun/jets/e/blake.c b/pkg/noun/jets/e/blake.c index cac4c481ca..d7b99b774e 100644 --- a/pkg/noun/jets/e/blake.c +++ b/pkg/noun/jets/e/blake.c @@ -112,6 +112,7 @@ c3_y flags_y = u3r_byte(0, flags); u3r_bytes(0, 32, cv_y, cv); urcrypt_blake3_chunk_output(wid_w, dat_y, cv_y, block_y, &block_len, &counter_d, &flags_y); + u3a_free(dat_y); return u3i_cell(u3i_bytes(32, cv_y), u3i_qual(u3k(counter), u3i_bytes(64, block_y), block_len, flags_y)); } } diff --git a/pkg/noun/jets/e/json_en.c b/pkg/noun/jets/e/json_en.c index f605ab42be..00004038d4 100644 --- a/pkg/noun/jets/e/json_en.c +++ b/pkg/noun/jets/e/json_en.c @@ -69,7 +69,7 @@ static c3_w _measure(u3_noun a); static void -_serialize(json_buffer*, u3_noun); +serialize(json_buffer*, u3_noun); /* ** core jet logic @@ -253,7 +253,7 @@ _serialize_array(json_buffer *buf_u, u3_noun a) while ( u3_nul != t ) { u3x_cell(t, &i, &t); - _serialize(buf_u, i); + serialize(buf_u, i); _append_char(buf_u, ','); } @@ -302,7 +302,7 @@ _serialize_object_helper(json_buffer *buf_u, u3_noun a) _serialize_string(buf_u, pn_a); _append_char(buf_u, ':'); - _serialize(buf_u, qn_a); + serialize(buf_u, qn_a); _append_char(buf_u, ','); _serialize_object_helper(buf_u, l_a); @@ -359,7 +359,7 @@ _measure(u3_noun a) } static void -_serialize(json_buffer *buf_u, u3_noun a) +serialize(json_buffer *buf_u, u3_noun a) { if ( u3_nul == a ) { _append_text(buf_u, _JSON_NULL, sizeof(_JSON_NULL) - 1); @@ -396,7 +396,7 @@ u3qe_json_en(u3_noun a) buf_u->len_w = 0; // note that it's structurally integral to call measure before serialize - _serialize(buf_u, a); + serialize(buf_u, a); return u3i_slab_mint_bytes(&sab_u); } diff --git a/pkg/noun/manage.c b/pkg/noun/manage.c index 0dcdba31f4..5132554a67 100644 --- a/pkg/noun/manage.c +++ b/pkg/noun/manage.c @@ -30,7 +30,6 @@ #include "nock.h" #include "openssl/crypto.h" #include "options.h" -#include "rsignal.h" #include "retrieve.h" #include "trace.h" #include "urcrypt.h" @@ -48,7 +47,7 @@ c3_o u3m_trap(void); #else -# define u3m_trap() (u3_noun)(setjmp(u3R->esc.buf)) +# define u3m_trap() (u3_noun)(_setjmp(u3R->esc.buf)) #endif /* u3m_signal(): treat a nock-level exception as a signal interrupt. @@ -108,7 +107,7 @@ // do not manipulate signals, do not modify shared state, and always either // return or longjmp. // -static jmp_buf u3_Signal; +static rsignal_jmpbuf u3_Signal; #ifndef U3_OS_windows #include "sigsegv.h" @@ -373,7 +372,7 @@ _cm_signal_recover(c3_l sig_l, u3_noun arg) /* _cm_signal_deep(): start deep processing; set timer for [mil_w] or 0. */ static void -_cm_signal_deep(c3_w mil_w) +_cm_signal_deep(void) { // disable outer system signal handling // @@ -393,6 +392,7 @@ _cm_signal_deep(c3_w mil_w) #endif rsignal_install_handler(SIGINT, _cm_signal_handle_intr); rsignal_install_handler(SIGTERM, _cm_signal_handle_term); + rsignal_install_handler(SIGVTALRM, _cm_signal_handle_alrm); // Provide a little emergency memory, for use in case things // go utterly haywire. @@ -403,21 +403,6 @@ _cm_signal_deep(c3_w mil_w) ); } - if ( mil_w ) { - struct itimerval itm_u; - - timerclear(&itm_u.it_interval); - itm_u.it_value.tv_sec = (mil_w / 1000); - itm_u.it_value.tv_usec = 1000 * (mil_w % 1000); - - if ( rsignal_setitimer(ITIMER_VIRTUAL, &itm_u, 0) ) { - u3l_log("loom: set timer failed %s", strerror(errno)); - } - else { - rsignal_install_handler(SIGVTALRM, _cm_signal_handle_alrm); - } - } - u3t_boot(); } @@ -531,6 +516,9 @@ _pave_parts(void) u3R->jed.han_p = u3h_new(); u3R->jed.bas_p = u3h_new(); u3R->byc.har_p = u3h_new(); + u3R->lop_p = u3h_new(); + u3R->tim = u3_nul; + u3R->how.fag_w = 0; } static c3_d @@ -665,12 +653,11 @@ _find_home(void) u3H->pam_d = _pave_params(); } - // if for_p is zero then it is an old pier pre ford cache, initialize the - // cache + // properly initialize things from zero-initialize future proof buffer + // XX cax.for_p // - if ( !u3R->cax.for_p ) { - u3R->cax.for_p = u3h_new_cache(u3C.per_w); - } + if ( !u3R->lop_p ) u3R->lop_p = u3h_new(); + if ( !u3R->cax.for_p ) u3R->cax.for_p = u3h_new_cache(u3C.per_w); } /* u3m_pave(): instantiate or activate image. @@ -1010,8 +997,6 @@ u3m_bail(u3_noun how) stk_u->fow_w = u3R->fow_w; } - /* Longjmp, with an underscore. - */ _longjmp(u3R->esc.buf, how); } @@ -1231,6 +1216,115 @@ u3m_hate(c3_w pad_w) ); } +// RETAINS `now`. +// +static void +_m_renew_timer(u3_atom now) +{ + u3_atom min = u3_nul; + u3a_road* rod_u = u3R; + c3_t no_timers_t = true; + while ( 1 ) { + for (u3_noun l = rod_u->tim; l; l = u3t(l)) { + no_timers_t = false; + u3_atom fut = u3h(l); + if ( _(u3qa_gth(fut, now)) ) { + min = ( u3_nul == min ) ? u3k(fut) : u3ka_min(min, u3k(fut)); + } + else { + // we are waiting for the signal to come, do nothing + // + u3z(min); + return; + } + } + if ( !rod_u->par_p ) break; + rod_u = u3to(u3_road, rod_u->par_p); + } + + if ( no_timers_t ) { + // no timers: `min` is still u3_nul. + // disarm the timer + // + struct itimerval itm_u; + timerclear(&itm_u.it_interval); + timerclear(&itm_u.it_value); + if ( rsignal_setitimer(ITIMER_VIRTUAL, &itm_u, 0) ) { + u3l_log("loom: clear timer failed %s", strerror(errno)); + } + return; + } + + if ( u3_nul == min ) { + // strange case: `now` is later or equal to all our deadlines. do nothing + // + return; + } + + u3_atom gap = u3ka_sub(min, u3k(now)); + + struct itimerval itm_u; + timerclear(&itm_u.it_interval); + c3_t is_set_t = u3m_time_out_it(&itm_u, gap); + if ( !is_set_t ) { + // the gap is too small to resolve in itimerval, emulate firing SIGALRM + // immediately + // + u3m_signal(c3__alrm); + } + if ( rsignal_setitimer(ITIMER_VIRTUAL, &itm_u, 0) ) { + u3l_log("loom: set timer failed %s", strerror(errno)); + } +} + +static void +_m_renew_now(void) +{ + struct timeval tim_u; + gettimeofday(&tim_u, 0); + u3_atom now = u3m_time_in_tv(&tim_u); + _m_renew_timer(now); + u3z(now); +} + +/* u3m_timer_set(): push a new timer to the timer stack. +** gap is @dr, gap != 0 +*/ +void +u3m_timer_set(u3_atom gap) +{ + if ( !u3R->par_p ) { + // noop on the home road since we have no jump buffer + // + u3z(gap); + return; + } + struct timeval tim_u; + gettimeofday(&tim_u, 0); + u3_atom now = u3m_time_in_tv(&tim_u); + u3_atom fut = u3ka_add(u3k(now), gap); + u3R->tim = u3nc(fut, u3R->tim); + _m_renew_timer(now); + u3z(now); +} + +/* u3m_timer_pop(): pop a timer off the timer stack. +** timer stack must be non-empty +*/ +void +u3m_timer_pop(void) +{ + if ( !u3R->par_p ) { + // noop on the home road since we have no jump buffer + // + return; + } + c3_dessert( c3y == u3du(u3R->tim) ); + u3_noun t = u3k(u3t(u3R->tim)); + u3z(u3R->tim), u3R->tim = t; + _m_renew_now(); +} + /* u3m_love(): return product from leap. */ u3_noun @@ -1243,10 +1337,16 @@ u3m_love(u3_noun pro) u3p(u3h_root) per_p = u3R->cax.per_p; u3p(u3h_root) for_p = u3R->cax.for_p; + // are there any timers on the road? + // + c3_o tim_o = u3du(u3R->tim); + // fallback to parent road (child heap on parent's stack) // u3m_fall(); + if ( _(tim_o) ) _m_renew_now(); + // copy product and caches off our stack // pro = u3a_take(pro); @@ -1350,7 +1450,7 @@ u3m_soft_top(c3_w mil_w, // timer ms /* Enter internal signal regime. */ - _cm_signal_deep(mil_w); + _cm_signal_deep(); if ( 0 != (sig_l = rsignal_setjmp(u3_Signal)) ) { // reinitialize trace state @@ -1370,9 +1470,13 @@ u3m_soft_top(c3_w mil_w, // timer ms */ u3m_hate(pad_w); + if ( mil_w ) { + u3m_timer_set(u3m_time_gap_in_mil(mil_w)); + } + /* Trap for ordinary nock exceptions. */ - if ( 0 == (why = (u3_noun)setjmp(u3R->esc.buf)) ) { + if ( 0 == (why = (u3_noun)_setjmp(u3R->esc.buf)) ) { pro = fun_f(arg); /* Make sure the inner routine did not create garbage. @@ -1466,15 +1570,12 @@ u3m_soft_cax(u3_funq fun_f, u3_noun why = 0, pro; u3_noun cax = u3_nul; - /* Save and set memo cache harvesting flag. - */ - c3_w wag_w = u3C.wag_w; - u3C.wag_w |= u3o_cash; - /* Record the cap, and leap. */ u3m_hate(1 << 18); + u3R->how.fag_w |= u3a_flag_cash; + /* Configure the new road. */ { @@ -1487,10 +1588,9 @@ u3m_soft_cax(u3_funq fun_f, /* Trap for exceptions. */ - if ( 0 == (why = (u3_noun)setjmp(u3R->esc.buf)) ) { + if ( 0 == (why = (u3_noun)_setjmp(u3R->esc.buf)) ) { u3t_off(coy_o); pro = fun_f(aga, agb); - u3C.wag_w = wag_w; #ifdef U3_CPU_DEBUG if ( u3R->all.max_w > 1000000 ) { @@ -1515,7 +1615,6 @@ u3m_soft_cax(u3_funq fun_f, } else { u3t_init(); - u3C.wag_w = wag_w; /* Produce - or fall again. */ @@ -1565,6 +1664,8 @@ u3m_soft_run(u3_noun gul, { u3_noun why = 0, pro; + c3_t cash_t = !!(u3R->how.fag_w & u3a_flag_cash); + /* Record the cap, and leap. */ u3m_hate(1 << 18); @@ -1573,8 +1674,7 @@ u3m_soft_run(u3_noun gul, */ { - // XX review - if ( (u3_nul == gul) || (u3C.wag_w & u3o_cash) ) { + if ( (u3_nul == gul) || cash_t ) { u3R->ski.gul = u3_nul; } else { @@ -1583,12 +1683,13 @@ u3m_soft_run(u3_noun gul, u3R->pro.don = u3to(u3_road, u3R->par_p)->pro.don; u3R->pro.trace = u3to(u3_road, u3R->par_p)->pro.trace; u3R->bug.tax = 0; + u3R->how.fag_w |= ( cash_t ) ? u3a_flag_cash : 0; } u3t_on(coy_o); /* Trap for exceptions. */ - if ( 0 == (why = (u3_noun)setjmp(u3R->esc.buf)) ) { + if ( 0 == (why = (u3_noun)_setjmp(u3R->esc.buf)) ) { u3t_off(coy_o); pro = fun_f(aga, agb); @@ -1690,7 +1791,7 @@ u3m_soft_esc(u3_noun ref, u3_noun sam) /* Trap for exceptions. */ - if ( 0 == (why = (u3_noun)setjmp(u3R->esc.buf)) ) { + if ( 0 == (why = (u3_noun)_setjmp(u3R->esc.buf)) ) { pro = u3n_slam_on(gul, u3nc(ref, sam)); /* Fall back to the old road, leaving temporary memory intact. @@ -1974,6 +2075,19 @@ u3m_pretty(u3_noun som) return pre_c; } +/* u3m_pretty_road(): dumb prettyprint to string. Road allocation +*/ +c3_c* +u3m_pretty_road(u3_noun som) +{ + c3_w len_w = _cm_in_pretty(som, c3y, 0); + c3_c* pre_c = u3a_malloc(len_w + 1); + + _cm_in_pretty(som, c3y, pre_c); + pre_c[len_w] = 0; + return pre_c; +} + /* _cm_in_pretty_path: measure/cut prettyprint. * * Modeled after _cm_in_pretty(), the backend to u3m_p(), but with the @@ -2626,3 +2740,199 @@ u3m_pack(void) return (u3a_open(u3R) - pre_w); } + +/* Time functions */ + +/* u3m_time_sec_in(): urbit seconds from unix time. +** +** Adjust for future leap secs! +*/ +c3_d +u3m_time_sec_in(c3_w unx_w) +{ + return 0x8000000cce9e0d80ULL + (c3_d)unx_w; +} + +/* u3m_time_sec_out(): unix time from urbit seconds. +** +** Adjust for future leap secs! +*/ +c3_w +u3m_time_sec_out(c3_d urs_d) +{ + c3_d adj_d = (urs_d - 0x8000000cce9e0d80ULL); + + if ( adj_d > 0xffffffffULL ) { + fprintf(stderr, "Agh! It's 2106! And no one's fixed this shite!\n"); + exit(1); + } + return (c3_w)adj_d; +} + +/* u3m_time_fsc_in(): urbit fracto-seconds from unix microseconds. +*/ +c3_d +u3m_time_fsc_in(c3_w usc_w) +{ + c3_d usc_d = usc_w; + + return ((usc_d * 65536ULL) / 1000000ULL) << 48ULL; +} + +/* u3m_time_fsc_out: unix microseconds from urbit fracto-seconds. +*/ +c3_w +u3m_time_fsc_out(c3_d ufc_d) +{ + return (c3_w) (((ufc_d >> 48ULL) * 1000000ULL) / 65536ULL); +} + +/* u3m_time_msc_out: unix microseconds from urbit fracto-seconds. +*/ +c3_w +u3m_time_msc_out(c3_d ufc_d) +{ + return (c3_w) (((ufc_d >> 48ULL) * 1000ULL) / 65536ULL); +} + +/* u3m_time_in_tv(): urbit time from struct timeval. +*/ +u3_atom +u3m_time_in_tv(struct timeval* tim_tv) +{ + c3_w unx_w = tim_tv->tv_sec; + c3_w usc_w = tim_tv->tv_usec; + c3_d cub_d[2]; + + cub_d[0] = u3m_time_fsc_in(usc_w); + cub_d[1] = u3m_time_sec_in(unx_w); + + return u3i_chubs(2, cub_d); +} + +/* u3m_time_out_tv(): struct timeval from urbit time. +*/ +void +u3m_time_out_tv(struct timeval* tim_tv, u3_noun now) +{ + c3_d ufc_d = u3r_chub(0, now); + c3_d urs_d = u3r_chub(1, now); + + tim_tv->tv_sec = u3m_time_sec_out(urs_d); + tim_tv->tv_usec = u3m_time_fsc_out(ufc_d); + + u3z(now); +} + +/* u3m_time_in_ts(): urbit time from struct timespec. +*/ +u3_atom +u3m_time_in_ts(struct timespec* tim_ts) +{ + struct timeval tim_tv; + + tim_tv.tv_sec = tim_ts->tv_sec; + tim_tv.tv_usec = (tim_ts->tv_nsec / 1000); + + return u3m_time_in_tv(&tim_tv); +} + +#if defined(U3_OS_linux) || defined(U3_OS_windows) +/* u3m_time_t_in_ts(): urbit time from time_t. +*/ +u3_atom +u3m_time_t_in_ts(time_t tim) +{ + struct timeval tim_tv; + + tim_tv.tv_sec = tim; + tim_tv.tv_usec = 0; + + return u3m_time_in_tv(&tim_tv); +} +#endif /* defined(U3_OS_linux) */ + +/* u3m_time_out_ts(): struct timespec from urbit time. +*/ +void +u3m_time_out_ts(struct timespec* tim_ts, u3_noun now) +{ + struct timeval tim_tv; + + u3m_time_out_tv(&tim_tv, now); + + tim_ts->tv_sec = tim_tv.tv_sec; + tim_ts->tv_nsec = (tim_tv.tv_usec * 1000); +} + +/* u3m_time_out_it(): struct itimerval from urbit time gap. +** returns true if it_value is set to non-zero values, false otherwise +*/ +c3_t +u3m_time_out_it(struct itimerval* tim_it, u3_noun gap) +{ + struct timeval tim_tv; + c3_d ufc_d = u3r_chub(0, gap); + c3_d urs_d = u3r_chub(1, gap); + tim_it->it_value.tv_sec = urs_d; + tim_it->it_value.tv_usec = u3m_time_fsc_out(ufc_d); + u3z(gap); + return tim_it->it_value.tv_sec || tim_it->it_value.tv_usec; +} + +/* u3m_time_gap_ms(): (wen - now) in ms. +*/ +c3_d +u3m_time_gap_ms(u3_noun now, u3_noun wen) +{ + if ( c3n == u3ka_gth(u3k(wen), u3k(now)) ) { + u3z(wen); u3z(now); + return 0ULL; + } + else { + u3_noun dif = u3ka_sub(wen, now); + c3_d fsc_d = u3r_chub(0, dif); + c3_d sec_d = u3r_chub(1, dif); + + u3z(dif); + return (sec_d * 1000ULL) + u3m_time_msc_out(fsc_d); + } +} + +/* u3m_time_gap_double(): (wen - now) in libev resolution. +*/ +double +u3m_time_gap_double(u3_noun now, u3_noun wen) +{ + mpz_t now_mp, wen_mp, dif_mp; + double sec_g = (((double)(1ULL << 32ULL)) * ((double)(1ULL << 32ULL))); + double gap_g, dif_g; + + u3r_mp(now_mp, now); + u3r_mp(wen_mp, wen); + mpz_init(dif_mp); + mpz_sub(dif_mp, wen_mp, now_mp); + + u3z(now); + u3z(wen); + + dif_g = mpz_get_d(dif_mp) / sec_g; + gap_g = (dif_g > 0.0) ? dif_g : 0.0; + mpz_clear(dif_mp); mpz_clear(wen_mp); mpz_clear(now_mp); + + return gap_g; +} + +/* u3m_time_gap_in_mil(): urbit time gap from milliseconds +*/ +u3_atom +u3m_time_gap_in_mil(c3_w mil_w) +{ + c3_d sec_d = mil_w / 1000; + c3_d usc_d = 1000 * (mil_w % 1000); + c3_d cub_d[2]; + + cub_d[0] = u3m_time_fsc_in(usc_d); + cub_d[1] = sec_d; + return u3i_chubs(2, cub_d); +} diff --git a/pkg/noun/manage.h b/pkg/noun/manage.h index f0779e9141..7a515e0ac8 100644 --- a/pkg/noun/manage.h +++ b/pkg/noun/manage.h @@ -9,6 +9,7 @@ #include "c3/c3.h" #include "types.h" #include "version.h" +#include "rsignal.h" /** System management. **/ @@ -182,6 +183,11 @@ c3_c* u3m_pretty(u3_noun som); + /* u3m_pretty_road(): dumb prettyprint to string. Road allocation + */ + c3_c* + u3m_pretty_road(u3_noun som); + /* u3m_pretty_path(): prettyprint a path to string. RETAIN. */ c3_c* @@ -212,4 +218,97 @@ c3_w u3m_pack(void); + /* Urbit time: 128 bits, leap-free. + ** + ** High 64 bits: 0x8000.000c.cea3.5380 + Unix time at leap 25 (Jul 2012) + ** Low 64 bits: 1/2^64 of a second. + ** + ** Seconds per Gregorian 400-block: 12.622.780.800 + ** 400-blocks from 0 to 0AD: 730.692.561 + ** Years from 0 to 0AD: 292.277.024.400 + ** Seconds from 0 to 0AD: 9.223.372.029.693.628.800 + ** Seconds between 0A and Unix epoch: 62.167.219.200 + ** Seconds before Unix epoch: 9.223.372.091.860.848.000 + ** The same, in C hex notation: 0x8000000cce9e0d80ULL + ** + ** XX: needs to be adjusted to implement Google leap-smear time. + */ + /* u3m_time_sec_in(): urbit seconds from unix time. + ** + ** Adjust (externally) for future leap secs! + */ + c3_d + u3m_time_sec_in(c3_w unx_w); + + /* u3m_time_sec_out(): unix time from urbit seconds. + ** + ** Adjust (externally) for future leap secs! + */ + c3_w + u3m_time_sec_out(c3_d urs_d); + + /* u3m_time_fsc_in(): urbit fracto-seconds from unix microseconds. + */ + c3_d + u3m_time_fsc_in(c3_w usc_w); + + /* u3m_time_fsc_out: unix microseconds from urbit fracto-seconds. + */ + c3_w + u3m_time_fsc_out(c3_d ufc_d); + + /* u3m_time_in_tv(): urbit time from struct timeval. + */ + u3_atom + u3m_time_in_tv(struct timeval* tim_tv); + + /* u3m_time_out_tv(): struct timeval from urbit time. + */ + void + u3m_time_out_tv(struct timeval* tim_tv, u3_noun now); + + /* u3m_time_in_ts(): urbit time from struct timespec. + */ + u3_atom + u3m_time_in_ts(struct timespec* tim_ts); + #if defined(U3_OS_linux) || defined(U3_OS_windows) + /* u3m_time_t_in_ts(): urbit time from time_t. + */ + u3_atom + u3m_time_t_in_ts(time_t tim); + #endif + + /* u3m_time_out_ts(): struct timespec from urbit time. + */ + void + u3m_time_out_ts(struct timespec* tim_ts, u3_noun now); + + /* u3m_time_out_it(): struct itimerval from urbit time gap. + ** returns true if it_value is set to non-zero values, false otherwise + */ + c3_t + u3m_time_out_it(struct itimerval* tim_it, u3_noun gap); + + /* u3m_time_gap_ms(): (wen - now) in ms. + */ + c3_d + u3m_time_gap_ms(u3_noun now, u3_noun wen); + + /* u3m_timer_set(): push a new timer to the timer stack. + ** gap is @dr, gap != 0 + */ + void + u3m_timer_set(u3_atom gap); + + /* u3m_timer_pop(): pop a timer off the timer stack. + ** timer stack must be non-empty + */ + void + u3m_timer_pop(void); + + /* u3m_time_gap_in_mil(): urbit time gap from milliseconds + */ + u3_atom + u3m_time_gap_in_mil(c3_w mil_w); + #endif /* ifndef U3_MANAGE_H */ diff --git a/pkg/noun/nock.c b/pkg/noun/nock.c index 777bb40209..8e73322b87 100644 --- a/pkg/noun/nock.c +++ b/pkg/noun/nock.c @@ -1031,6 +1031,7 @@ _n_bint(u3_noun* ops, u3_noun hif, u3_noun nef, c3_o los_o, c3_o tel_o) case c3__meme: case c3__nara: case c3__hela: + case c3__loop: case c3__bout: { u3_noun fen = u3_nul; c3_w nef_w = _n_comp(&fen, nef, los_o, c3n); @@ -1070,6 +1071,8 @@ _n_bint(u3_noun* ops, u3_noun hif, u3_noun nef, c3_o los_o, c3_o tel_o) case c3__nara: case c3__hela: case c3__spin: + case c3__loop: + case c3__jinx: case c3__bout: { u3_noun fen = u3_nul; c3_w nef_w = _n_comp(&fen, nef, los_o, c3n); @@ -1917,6 +1920,15 @@ _n_hilt_fore(u3_noun hin, u3_noun bus, u3_noun* out) *out = u3i_cell(tag, u3i_cell(har, per)); } break; + case c3__loop: { + u3_noun key = u3nc(u3k(bus), u3k(fol)); + if ( u3_none != u3h_git(u3R->lop_p, key) ) { + u3m_bail(c3__fail); + } + u3h_put(u3R->lop_p, key, u3_nul); + *out = u3nc(tag, key); + } break; + case c3__bout: { u3_atom now = u3i_chub(u3t_trace_time()); *out = u3i_cell(tag, now); @@ -1959,7 +1971,10 @@ static void _n_hilt_hind(u3_noun tok, u3_noun pro) { u3_noun p_tok, q_tok, r_tok; - if ( (c3y == u3r_cell(tok, &p_tok, &q_tok)) && (c3__bout == p_tok) ) { + if ( (c3y == u3r_cell(tok, &p_tok, &q_tok)) && (c3__loop == p_tok) ) { + u3h_del(u3R->lop_p, q_tok); + } + else if ( (c3y == u3r_cell(tok, &p_tok, &q_tok)) && (c3__bout == p_tok) ) { u3_atom delta = u3ka_sub(u3i_chub(u3t_trace_time()), u3k(q_tok)); c3_c str_c[64]; u3a_print_time(str_c, "took", u3r_chub(0, delta)); @@ -2010,11 +2025,32 @@ _n_hint_fore(u3_cell hin, u3_noun bus, u3_noun* clu) u3x_cell(hin, &tag, &fol); switch ( tag ) { + case c3__jinx: { + if ( c3n == u3ud(*clu) || u3_nul == *clu ) { + u3z(*clu); + *clu = u3_nul; + } + else { + u3m_timer_set(*clu); + *clu = c3__jinx; + } + } break; + case c3__bout: { u3_atom now = u3i_chub(u3t_trace_time()); *clu = u3nt(u3k(tag), *clu, now); } break; + case c3__loop: { + u3_noun key = u3nc(u3k(bus), u3k(fol)); + if ( u3_none != u3h_git(u3R->lop_p, key) ) { + u3t_mean(*clu); + u3m_bail(c3__fail); + } + u3h_put(u3R->lop_p, key, u3_nul); + u3z(*clu); + *clu = u3nc(tag, key); + } break; case c3__spin: { u3t_sstack_push(*clu); *clu = c3__spin; @@ -2081,9 +2117,15 @@ static void _n_hint_hind(u3_noun tok, u3_noun pro) { u3_noun p_tok, q_tok, r_tok; - if ( c3__spin == tok ) { + if ( c3__jinx == tok ) { + u3m_timer_pop(); + } + else if ( c3__spin == tok ) { u3t_sstack_pop(); } + else if ( (c3y == u3r_cell(tok, &p_tok, &q_tok)) && (c3__loop == p_tok) ) { + u3h_del(u3R->lop_p, q_tok); + } else if ( (c3y == u3r_trel(tok, &p_tok, &q_tok, &r_tok)) && (c3__bout == p_tok) ) { // get the microseconds elapsed u3_atom delta = u3ka_sub(u3i_chub(u3t_trace_time()), u3k(r_tok)); diff --git a/pkg/noun/options.h b/pkg/noun/options.h index 615b765bd2..6f617d5806 100644 --- a/pkg/noun/options.h +++ b/pkg/noun/options.h @@ -45,7 +45,7 @@ u3o_soft_mugs = 1 << 11, // continue replay on mismatch u3o_swap = 1 << 12, // enables ephemeral file u3o_toss = 1 << 13, // reclaim often - u3o_cash = 1 << 14, // memo cache harvesting + // u3o_cash = 1 << 14, // deprecated u3o_yolo = 1 << 15 // no brakes! }; diff --git a/pkg/noun/palloc.c b/pkg/noun/palloc.c index 33c9ab984e..e793b05145 100644 --- a/pkg/noun/palloc.c +++ b/pkg/noun/palloc.c @@ -5,6 +5,7 @@ #include "vortex.h" #undef SANITY +#undef PACK_CHECK #ifdef ASAN_ENABLED // XX build problems importing @@ -1043,6 +1044,73 @@ _post_status(u3_post som_p) } } +static void +_sane_dell(void) +{ + u3p(u3a_dell) pre_p, nex_p, fre_p = HEAP.fre_p; + u3a_dell* fre_u; + + if ( !HEAP.fre_p != !HEAP.erf_p ) { + fprintf(stderr, "dell: insane: head %u tail %u\r\n", !!HEAP.fre_p, !!HEAP.erf_p); + } + + while ( fre_p ) { + fre_u = u3to(u3a_dell, fre_p); + pre_p = fre_u->pre_p; + nex_p = fre_u->nex_p; + + if ( pre_p ) { + if ( u3to(u3a_dell, pre_p)->nex_p != fre_p ) { + fprintf(stderr, "dell: insane: prev next not us\r\n"); + } + } + else if ( fre_p != HEAP.fre_p ) { + fprintf(stderr, "dell: insane: missing previous\r\n"); + } + + if ( nex_p ) { + if ( u3to(u3a_dell, nex_p)->pre_p != fre_p ) { + fprintf(stderr, "dell: insane: next prev not us\r\n"); + } + } + else if ( fre_p != HEAP.erf_p ) { + fprintf(stderr, "dell: insane: missing next\r\n"); + } + + fre_p = nex_p; + } + + { + u3p(u3a_crag) *dir_u = u3to(u3p(u3a_crag), HEAP.pag_p); + c3_w fre_w, nex_w, pag_w = 0; + + fre_p = HEAP.fre_p; + + while ( pag_w < HEAP.len_w ) { + fre_u = u3tn(u3a_dell, fre_p); + fre_w = fre_u ? fre_u->pag_w : HEAP.len_w; // XX wrong in south + + for ( ; pag_w < fre_w; pag_w++ ) { + if ( !dir_u[pag_w] ) { + fprintf(stderr, "dell: insane page %u free in directory, not in list\r\n", pag_w); + } + } + + if ( fre_u ) { + nex_w = fre_u->pag_w + fre_u->siz_w; // XX wrong in south + + for ( ; pag_w < nex_w; pag_w++ ) { + if ( dir_u[pag_w] ) { + fprintf(stderr, "dell: insane page %u free in list, not in directory\r\n", pag_w); + } + } + + fre_p = fre_u->nex_p; + } + } + } +} + static c3_w _idle_pages(void) { @@ -1733,6 +1801,61 @@ typedef struct { c3_w mar_w[0]; } _ca_frag; +#ifdef PACK_CHECK +static c3_d +_pack_hash(u3_post foo) +{ + return foo * 11400714819323198485ULL; +} + +static c3_i +_pack_cmp(u3_noun a, u3_noun b) +{ + return a == b; +} + +#define NAME _pack_posts +#define KEY_TY u3_post +#define VAL_TY u3_post +#define HASH_FN _pack_hash +#define CMPR_FN _pack_cmp +#include "verstable.h" + +_pack_posts pos_u; +#endif + +static void +_pack_check_move(c3_w *dst_w, c3_w *src_w) +{ +#ifdef PACK_CHECK + u3_post src_p = u3a_outa(src_w); + u3_post dst_p = u3a_outa(dst_w); + _pack_posts_itr pit_u = vt_get(&pos_u, src_p); + u3_assert( !vt_is_end(pit_u) ); + u3_assert( pit_u.data->val == dst_p ); +#endif +} + +static void +_pack_check_full(c3_w *dst_w, c3_w *src_w, _ca_frag* fag_u) +{ +#ifdef PACK_CHECK + c3_g bit_g = fag_u->log_s - u3a_min_log; + const u3a_hunk_dose *hun_u = &(u3a_Hunk[bit_g]); + + _pack_check_move(dst_w, src_w); + + dst_w += hun_u->len_s * hun_u->hun_s; + src_w += hun_u->len_s * hun_u->hun_s; + + for ( c3_s ful_s = 0; ful_s < hun_u->ful_s; ful_s++ ) { + _pack_check_move(dst_w, src_w); + dst_w += hun_u->len_s; + src_w += hun_u->len_s; + } +#endif +} + // adapted from https://stackoverflow.com/a/27663998 and // https://gist.github.com/ideasman42/5921b0edfc6aa41a9ce0 // @@ -1934,6 +2057,10 @@ _pack_seek(void) c3_w blk_w, bit_w, fre_w = 0; u3_post dir_p; +#ifdef PACK_CHECK + vt_init(&pos_u); +#endif + { u3_post fre_p; u3a_dell *fre_u; @@ -1941,6 +2068,9 @@ _pack_seek(void) while ( (fre_p = HEAP.fre_p) ) { fre_u = u3to(u3a_dell, fre_p); HEAP.fre_p = fre_u->nex_p; + if ( HEAP.fre_p ) { + u3to(u3a_dell, HEAP.fre_p)->pre_p = 0; + } _ifree(fre_p); } } @@ -2113,6 +2243,19 @@ _pack_relocate_mark(u3_post som_p, c3_t *fir_t) } *fir_t = out_t; + +#ifdef PACK_CHECK + // XX also consider fir_t? + // + c3_z siz_z = vt_size(&pos_u); + _pack_posts_itr pit_u = vt_get_or_insert(&pos_u, som_p, out_p); + u3_assert( !vt_is_end(pit_u) ); // OOM + + if ( vt_size(&pos_u) == siz_z ) { + u3_assert( pit_u.data->val == out_p ); + } +#endif + return out_p; } @@ -2150,6 +2293,16 @@ _pack_relocate(u3_post som_p) out_p = _pack_relocate_hunk(rag_u, pag_w, pos_w); } +#ifdef PACK_CHECK + c3_z siz_z = vt_size(&pos_u); + _pack_posts_itr pit_u = vt_get_or_insert(&pos_u, som_p, out_p); + u3_assert( !vt_is_end(pit_u) ); // OOM + + if ( vt_size(&pos_u) == siz_z ) { + u3_assert( pit_u.data->val == out_p ); + } +#endif + return out_p; } @@ -2158,7 +2311,6 @@ _pack_relocate_heap(void) { // this is possible if freeing unused u3a_crag's // caused an entire hunk page to be free'd - // NB: this corrupts pre_p // if ( HEAP.fre_p ) { u3a_dell *fre_u; @@ -2173,6 +2325,12 @@ _pack_relocate_heap(void) while ( *ref_p ) { fre_u = u3to(u3a_dell, *ref_p); + // NB: this corrupts pre_p + // + // temporarily singly-linked, + // also avoids issues when freeing post pack + // + fre_u->pre_p = 0; *ref_p = _pack_relocate(*ref_p); ref_p = &(fre_u->nex_p); } @@ -2232,6 +2390,7 @@ _pack_move_chunks(c3_w pag_w, c3_w dir_w) while ( (pos_s < max_s) && fre_s ) { if ( hap_w[pos_s >> 5] & (1U << (pos_s & 31)) ) { ASAN_UNPOISON_MEMORY_REGION(dst_w, len_i); + _pack_check_move(dst_w, src_w); memcpy(dst_w, src_w, len_i); fre_s--; dst_w += off_w; @@ -2271,6 +2430,7 @@ _pack_move_chunks(c3_w pag_w, c3_w dir_w) while ( (pos_s < max_s) && (hap_w[pos_s >> 5] & (1U << (pos_s & 31))) ) { + _pack_check_move(src_w, src_w); pos_s++; src_w += off_w; } @@ -2286,6 +2446,7 @@ _pack_move_chunks(c3_w pag_w, c3_w dir_w) c3_w* doc_w = u3to(c3_w, page_to_post(new_w)); ASAN_UNPOISON_MEMORY_REGION(doc_w, len_i * hun_u->hun_s); + _pack_check_move(doc_w, soc_w); memcpy(doc_w, soc_w, len_i * hun_u->hun_s); // XX bump pos_s/src_w if !pos_s ? @@ -2300,6 +2461,7 @@ _pack_move_chunks(c3_w pag_w, c3_w dir_w) while ( pos_s < max_s ) { if ( hap_w[pos_s >> 5] & (1U << (pos_s & 31)) ) { ASAN_UNPOISON_MEMORY_REGION(dst_w, len_i); + _pack_check_move(dst_w, src_w); memcpy(dst_w, src_w, len_i); dst_w += off_w; } @@ -2357,6 +2519,14 @@ _pack_move(void) if ( u3a_free_pg != dir_w ) { if ( (u3a_rest_pg >= dir_w) || !(dir_w >> 31) ) { ASAN_UNPOISON_MEMORY_REGION(dst_w, len_i); + + if ( u3a_head_pg == dir_w ) { + _pack_check_move(dst_w, src_w); + } + else if ( u3a_rest_pg < dir_w ) { + _pack_check_full(dst_w, src_w, (void*)&(u3a_Gack.buf_w[dir_w])); + } + memcpy(dst_w, src_w, len_i); u3a_Gack.buf_w[new_w] = (u3a_rest_pg >= dir_w) ? dir_w @@ -2397,4 +2567,10 @@ _pack_move(void) _ifree(fre_p); } } + + HEAP.erf_p = 0; + +#ifdef PACK_CHECK + vt_cleanup(&pos_u); +#endif } diff --git a/pkg/noun/platform/windows/rsignal.h b/pkg/noun/platform/windows/rsignal.h index 998652cf3f..4465f29002 100644 --- a/pkg/noun/platform/windows/rsignal.h +++ b/pkg/noun/platform/windows/rsignal.h @@ -3,8 +3,13 @@ #ifndef _RSIGNAL_H #define _RSIGNAL_H -#define rsignal_setjmp setjmp -#define rsignal_longjmp longjmp +typedef struct { + jmp_buf jb; + unsigned long tid; +} rsignal_jmpbuf; + +#define rsignal_setjmp(buf) (buf.tid = GetCurrentThreadId(), setjmp(buf.jb)) +#define rsignal_longjmp(buf, val) if (buf.tid != GetCurrentThreadId()) {buf.jb.retval = (val); rsignal_post_longjmp(buf.tid, buf.jb.buffer);} else longjmp(buf.jb, val) void rsignal_raise(int sig); void rsignal_install_handler(int sig, __p_sig_fn_t fn); diff --git a/pkg/noun/platform/windows/setjmp.h b/pkg/noun/platform/windows/setjmp.h new file mode 100644 index 0000000000..b1cc0ca7d1 --- /dev/null +++ b/pkg/noun/platform/windows/setjmp.h @@ -0,0 +1,15 @@ +#ifndef _SETJMP_H +#define _SETJMP_H + +// msvcrt setjmp/longjmp are broken on 64-bit systems, use gcc builtins +typedef struct jmp_buf { + intptr_t buffer[5]; + int retval; +} jmp_buf; + +#define _setjmp setjmp +#define _longjmp longjmp +#define longjmp(buf, val) {(buf).retval = (val); __builtin_longjmp((void**)((buf).buffer), 1);} +#define setjmp(buf) (__builtin_setjmp((void**)(buf.buffer)) ? (buf.retval) : 0) + +#endif// _SETJMP_H diff --git a/pkg/noun/retrieve.c b/pkg/noun/retrieve.c index 3d2e182c47..9bf554cb99 100644 --- a/pkg/noun/retrieve.c +++ b/pkg/noun/retrieve.c @@ -1941,3 +1941,55 @@ u3r_safe(u3_noun fol, u3_weak* out) } } } + +/* u3r_word_buffer(): returns word buffer pointer of atom `*a` +** and the length of the buffer +*/ +c3_w* +u3r_word_buffer(u3_atom* a, c3_w* len_w) +{ + if ( _(u3a_is_cat(*a)) ) { + *len_w = 1; + return a; + } + u3a_atom* pug_u = u3a_to_ptr(*a); + *len_w = pug_u->len_w; + return pug_u->buf_w; +} + +static inline c3_ys +_comp_words(c3_w a_w, c3_w b_w) +{ + return (c3_ys)(a_w > b_w) - (c3_ys)(a_w < b_w); +} + +/* u3r_comp(): compares two atoms: +** returns 1 if a > b, -1 if a < b, 0 if they are equal +*/ +c3_ys +u3r_comp(u3_atom a, u3_atom b) +{ + if (c3y == u3a_is_cat(a) || c3y == u3a_is_cat(b)) { + return _comp_words(a, b); + } + + if ( a == b ) return 0; + + u3a_atom* a_u = u3a_to_ptr(a); + u3a_atom* b_u = u3a_to_ptr(b); + + if (a_u->len_w != b_u->len_w) { + return _comp_words(a_u->len_w, b_u->len_w); + } + + c3_w* a_w = a_u->buf_w; + c3_w* b_w = b_u->buf_w; + + for (c3_w i_w = a_u->len_w; i_w--;) { + if ( a_w[i_w] != b_w[i_w] ) { + return _comp_words(a_w[i_w], b_w[i_w]); + } + } + + return 0; +} diff --git a/pkg/noun/retrieve.h b/pkg/noun/retrieve.h index c02c008c97..9fbffdf5bf 100644 --- a/pkg/noun/retrieve.h +++ b/pkg/noun/retrieve.h @@ -544,4 +544,16 @@ c3_o u3r_safe(u3_noun fol, u3_weak* out); + /* u3r_word_buffer(): returns word buffer pointer of atom `*a` + ** and the length of the buffer + */ + c3_w* + u3r_word_buffer(u3_atom* a, c3_w* len_w); + + /* u3r_comp(): compares two atoms: + ** returns 1 if a > b, -1 if a < b, 0 if they are equal + */ + c3_ys + u3r_comp(u3_atom a, u3_atom b); + #endif /* ifndef U3_RETRIEVE_H */ diff --git a/pkg/noun/zave.c b/pkg/noun/zave.c index 40587ad23f..ed463cac5b 100644 --- a/pkg/noun/zave.c +++ b/pkg/noun/zave.c @@ -57,7 +57,7 @@ _har(u3a_road* rod_u, u3z_cid cid) u3_weak u3z_find(u3z_cid cid, u3_noun key) { - if ( (u3z_memo_toss == cid) || (u3C.wag_w & u3o_cash) ) { + if ( (u3z_memo_toss == cid) || (u3R->how.fag_w & u3a_flag_cash) ) { // XX under cash lookup in parent roads, // copying cache hits into the current road return u3h_get(_har(u3R, cid), key); diff --git a/pkg/vere/benchmarks.c b/pkg/vere/benchmarks.c index 9898217c5e..1e69b2e46d 100644 --- a/pkg/vere/benchmarks.c +++ b/pkg/vere/benchmarks.c @@ -35,7 +35,7 @@ _ames_writ_ex(void) { struct timeval tim_u; gettimeofday(&tim_u, 0); - wen = u3_time_in_tv(&tim_u); + wen = u3m_time_in_tv(&tim_u); } return u3nt(c3__work, 0, u3nc(wen, ovo)); diff --git a/pkg/vere/build.zig b/pkg/vere/build.zig index 6832a320f3..836fb15221 100644 --- a/pkg/vere/build.zig +++ b/pkg/vere/build.zig @@ -245,7 +245,6 @@ const c_source_files = [_][]const u8{ "melt.c", "newt.c", "pier.c", - "time.c", "ward.c", }; diff --git a/pkg/vere/io/ames.c b/pkg/vere/io/ames.c index 39f04b5ebf..e84edcb0c0 100644 --- a/pkg/vere/io/ames.c +++ b/pkg/vere/io/ames.c @@ -831,7 +831,7 @@ static void _ames_lane_into_cache(u3p(u3h_root) lax_p, u3_noun who, u3_noun las) { struct timeval tim_tv; gettimeofday(&tim_tv, 0); - u3_noun now = u3_time_in_tv(&tim_tv); + u3_noun now = u3m_time_in_tv(&tim_tv); u3_noun val = u3nc(las, now); u3h_put(lax_p, who, val); u3z(who); @@ -855,12 +855,12 @@ _ames_lane_from_cache(u3p(u3h_root) lax_p, u3_noun who, c3_o nal_o) { else { struct timeval tim_tv; gettimeofday(&tim_tv, 0); - u3_noun now = u3_time_in_tv(&tim_tv); + u3_noun now = u3m_time_in_tv(&tim_tv); u3_noun den = u3t(lac); // consider entries older than 2 minutes stale, ignore them // - if ( 120000 > u3_time_gap_ms(u3k(den), now) ) { + if ( 120000 > u3m_time_gap_ms(u3k(den), now) ) { lac = u3k(u3h(lac)); } else { lac = u3_none; @@ -1164,9 +1164,9 @@ _stun_time_gap(struct timeval sar_tv) { struct timeval tim_tv; gettimeofday(&tim_tv, 0); - u3_noun now = u3_time_in_tv(&tim_tv); - u3_noun den = u3_time_in_tv(&sar_tv); - return u3_time_gap_ms(den, now); + u3_noun now = u3m_time_in_tv(&tim_tv); + u3_noun den = u3m_time_in_tv(&sar_tv); + return u3m_time_gap_ms(den, now); } /* _stun_timer_cb(): advance stun state machine. @@ -2972,7 +2972,7 @@ u3_ames_io_init(u3_pier* pir_u) struct timeval tim_u; gettimeofday(&tim_u, 0); - now = u3_time_in_tv(&tim_u); + now = u3m_time_in_tv(&tim_u); sam_u->sev_l = u3r_mug(now); u3z(now); } diff --git a/pkg/vere/io/behn.c b/pkg/vere/io/behn.c index d2db3cebb9..e2475082c3 100644 --- a/pkg/vere/io/behn.c +++ b/pkg/vere/io/behn.c @@ -113,8 +113,8 @@ _behn_ef_doze(u3_behn* teh_u, u3_noun wen) struct timeval tim_tv; gettimeofday(&tim_tv, 0); - u3_noun now = u3_time_in_tv(&tim_tv); - c3_d gap_d = u3_time_gap_ms(now, u3k(u3t(wen))); + u3_noun now = u3m_time_in_tv(&tim_tv); + c3_d gap_d = u3m_time_gap_ms(now, u3k(u3t(wen))); teh_u->alm_o = c3y; uv_timer_start(&teh_u->tim_u, _behn_time_cb, gap_d, 0); @@ -248,7 +248,7 @@ u3_behn_io_init(u3_pier* pir_u) struct timeval tim_u; gettimeofday(&tim_u, 0); - now = u3_time_in_tv(&tim_u); + now = u3m_time_in_tv(&tim_u); teh_u->sev_l = u3r_mug(now); u3z(now); } diff --git a/pkg/vere/io/conn.c b/pkg/vere/io/conn.c index 8e0c3bfbe6..87efd3cf9d 100644 --- a/pkg/vere/io/conn.c +++ b/pkg/vere/io/conn.c @@ -962,7 +962,7 @@ u3_conn_io_init(u3_pier* pir_u) car_u->io.exit_f = _conn_io_exit; gettimeofday(&tim_u, 0); - now = u3_time_in_tv(&tim_u); + now = u3m_time_in_tv(&tim_u); con_u->sev_l = u3r_mug(now); u3z(now); return car_u; diff --git a/pkg/vere/io/cttp.c b/pkg/vere/io/cttp.c index fa6bb5bf42..af0e88e9ee 100644 --- a/pkg/vere/io/cttp.c +++ b/pkg/vere/io/cttp.c @@ -1161,7 +1161,7 @@ u3_cttp_io_init(u3_pier* pir_u) struct timeval tim_u; gettimeofday(&tim_u, 0); - now = u3_time_in_tv(&tim_u); + now = u3m_time_in_tv(&tim_u); ctp_u->sev_l = u3r_mug(now); u3z(now); } diff --git a/pkg/vere/io/http.c b/pkg/vere/io/http.c index c58fbde303..f096a9becc 100644 --- a/pkg/vere/io/http.c +++ b/pkg/vere/io/http.c @@ -857,7 +857,7 @@ _free_beam(beam* bem) static beam _get_beam(u3_hreq* req_u, c3_c* txt_c, c3_w len_w) { - beam bem; + beam bem = {u3_none, u3_none, u3_none, u3_none}; // get beak // @@ -938,7 +938,7 @@ _get_beam(u3_hreq* req_u, c3_c* txt_c, c3_w len_w) return bem; } -/* _http_req_dispatch(): dispatch http request +/* _http_req_dispatch(): dispatch http request. RETAINS `req` */ static void _http_req_dispatch(u3_hreq* req_u, u3_noun req) @@ -963,7 +963,7 @@ _http_req_dispatch(u3_hreq* req_u, u3_noun req) u3_noun adr = u3nc(c3__ipv4, u3i_words(1, &req_u->hon_u->ipf_w)); // XX loopback automatically secure too? // - u3_noun dat = u3nt(htp_u->sec, adr, req); + u3_noun dat = u3nt(htp_u->sec, adr, u3k(req)); cad = ( c3y == req_u->hon_u->htp_u->lop ) ? u3nc(u3i_string("request-local"), dat) @@ -972,9 +972,6 @@ _http_req_dispatch(u3_hreq* req_u, u3_noun req) } else { // '/_~_/' found - // `req` is unused, free it - // - u3z(req); bas_c = bas_c + 4; // retain '/' after /_~_ len_w = len_w - 4; @@ -1100,6 +1097,9 @@ _http_cache_respond(u3_hreq* req_u, u3_noun nun) u3_hreq* req_u = _http_req_prepare(rec_u, _http_req_new); _http_req_dispatch(req_u, req); } + // can be u3_none + // + u3z(req); } else if ( u3_none == u3r_at(7, nun) ) { h2o_send_error_500(rec_u, "Internal Server Error", "scry failed", 0); @@ -1672,6 +1672,10 @@ _http_rec_accept(h2o_handler_t* han_u, h2o_req_t* rec_u) } } + // can be u3_none + // + u3z(req); + return 0; } @@ -2186,7 +2190,7 @@ _http_serv_init_h2o(SSL_CTX* tls_u, c3_o log, c3_o red) { struct timeval tim_u; gettimeofday(&tim_u, 0); - now = u3_time_in_tv(&tim_u); + now = u3m_time_in_tv(&tim_u); } c3_c* pax_c = u3_Host.dir_c; u3_noun now = u3dc("scot", c3__da, now); @@ -3169,7 +3173,7 @@ u3_http_io_init(u3_pier* pir_u) struct timeval tim_u; gettimeofday(&tim_u, 0); - now = u3_time_in_tv(&tim_u); + now = u3m_time_in_tv(&tim_u); htd_u->sev_l = u3r_mug(now); u3z(now); } diff --git a/pkg/vere/io/mesa.c b/pkg/vere/io/mesa.c index 2d71418945..4de99d203e 100644 --- a/pkg/vere/io/mesa.c +++ b/pkg/vere/io/mesa.c @@ -3056,7 +3056,7 @@ u3_mesa_io_init(u3_pier* pir_u) struct timeval tim_u; gettimeofday(&tim_u, 0); - now = u3_time_in_tv(&tim_u); + now = u3m_time_in_tv(&tim_u); //sam_u->sev_l = u3r_mug(now); u3z(now); }*/ diff --git a/pkg/vere/io/term.c b/pkg/vere/io/term.c index 9bb6d32185..ca36408854 100644 --- a/pkg/vere/io/term.c +++ b/pkg/vere/io/term.c @@ -79,11 +79,9 @@ _term_alloc(uv_handle_t* had_u, ) { // this read can range from a single byte to a paste buffer - // 123 bytes has been chosen because its not a power of 2 - // this is probably still broken // - void* ptr_v = c3_malloc(123); - *buf = uv_buf_init(ptr_v, 123); + void* ptr_v = c3_malloc(128); + *buf = uv_buf_init(ptr_v, 128); } /* u3_term_log_init(): initialize terminal for logging @@ -798,16 +796,7 @@ _term_suck(u3_utty* uty_u, const c3_y* buf, ssize_t siz_i) { { if ( siz_i == UV_EOF ) { - // We hear EOF (on the third read callback) if - // 2x the _term_alloc() buffer size is pasted. - // The process hangs if we do nothing (and ctrl-z - // then corrupts the event log), so we force shutdown. - // - u3l_log("term: hangup (EOF)"); - - // XX revise - // - u3_pier_bail(u3_king_stub()); + return; } else if ( siz_i < 0 ) { u3l_log("term %d: read: %s", uty_u->tid_l, uv_strerror(siz_i)); diff --git a/pkg/vere/io/unix.c b/pkg/vere/io/unix.c index 47164614ed..9879e7db4f 100644 --- a/pkg/vere/io/unix.c +++ b/pkg/vere/io/unix.c @@ -1613,7 +1613,7 @@ u3_unix_io_init(u3_pier* pir_u) struct timeval tim_u; gettimeofday(&tim_u, 0); - now = u3_time_in_tv(&tim_u); + now = u3m_time_in_tv(&tim_u); unx_u->sev_l = u3r_mug(now); u3z(now); } diff --git a/pkg/vere/king.c b/pkg/vere/king.c index 33e730dcc7..c7c63597e8 100644 --- a/pkg/vere/king.c +++ b/pkg/vere/king.c @@ -1752,7 +1752,7 @@ u3_king_grab(void* vod_p) { struct timeval tim_u; gettimeofday(&tim_u, 0); - now = u3_time_in_tv(&tim_u); + now = u3m_time_in_tv(&tim_u); } { diff --git a/pkg/vere/main.c b/pkg/vere/main.c index a547e23167..a41fb7defc 100644 --- a/pkg/vere/main.c +++ b/pkg/vere/main.c @@ -6,7 +6,7 @@ #include "events.h" // XX remove, see full replay in _cw_play() #include "ivory.h" #include "ur/ur.h" -#include "platform/rsignal.h" +#include "rsignal.h" #include "vere.h" #ifndef U3_OS_windows diff --git a/pkg/vere/mars.c b/pkg/vere/mars.c index d5ac8f4f8c..62cc961fde 100644 --- a/pkg/vere/mars.c +++ b/pkg/vere/mars.c @@ -132,7 +132,7 @@ _mars_grab(u3_noun sac, c3_o pri_o) { struct timeval tim_u; gettimeofday(&tim_u, 0); - now = u3_time_in_tv(&tim_u); + now = u3m_time_in_tv(&tim_u); } { @@ -598,7 +598,7 @@ _mars_work(u3_mars* mar_u, u3_noun jar) struct timeval tim_u; gettimeofday(&tim_u, 0); - now = u3_time_in_tv(&tim_u); + now = u3m_time_in_tv(&tim_u); job = u3nc(now, u3k(job)); } u3z(jar); @@ -793,7 +793,7 @@ _mars_damp_file(void) { struct timeval tim_u; gettimeofday(&tim_u, 0); - now = u3_time_in_tv(&tim_u); + now = u3m_time_in_tv(&tim_u); } { @@ -1356,7 +1356,7 @@ u3_mars_play(u3_mars* mar_u, c3_d eve_d, c3_d sap_d) struct timeval tim_u; gettimeofday(&tim_u, 0); - now = _mars_show_time(u3_time_in_tv(&tim_u)); + now = _mars_show_time(u3m_time_in_tv(&tim_u)); now_c = u3r_string(now); u3z(now); } @@ -1821,7 +1821,7 @@ _mars_boot_make(u3_boot_opts* inp_u, // timestamp events, cons list // { - u3_noun now = u3_time_in_tv(&inp_u->tim_u); + u3_noun now = u3m_time_in_tv(&inp_u->tim_u); u3_noun bit = u3qc_bex(48); // 1/2^16 seconds u3_noun eve = u3kb_flop(bot); @@ -1926,7 +1926,7 @@ u3_mars_boot(u3_mars* mar_u, c3_d len_d, c3_y* hun_y) c3_rand(inp_u.eny_w); { - u3_noun now = u3_time_in_tv(&inp_u.tim_u); + u3_noun now = u3m_time_in_tv(&inp_u.tim_u); inp_u.sev_l = u3r_mug(now); u3z(now); } diff --git a/pkg/vere/pier.c b/pkg/vere/pier.c index 80c16624c3..ddc957b18a 100644 --- a/pkg/vere/pier.c +++ b/pkg/vere/pier.c @@ -821,7 +821,7 @@ _pier_wyrd_card(u3_pier* pir_u) struct timeval tim_u; gettimeofday(&tim_u, 0); - now = u3_time_in_tv(&tim_u); + now = u3m_time_in_tv(&tim_u); sev_l = u3r_mug(now); sen = u3dc("scot", c3__uv, sev_l); diff --git a/pkg/vere/platform/darwin/ptty.c b/pkg/vere/platform/darwin/ptty.c index a1ec334507..cf3c5327fd 100644 --- a/pkg/vere/platform/darwin/ptty.c +++ b/pkg/vere/platform/darwin/ptty.c @@ -178,7 +178,7 @@ u3_ptty_init(uv_loop_t* lup_u, const c3_c** err_c) pty_u->raw_u.c_cflag &= ~(CSIZE | PARENB); pty_u->raw_u.c_cflag |= CS8; pty_u->raw_u.c_oflag &= ~(OPOST); - pty_u->raw_u.c_cc[VMIN] = 0; + pty_u->raw_u.c_cc[VMIN] = 1; pty_u->raw_u.c_cc[VTIME] = 0; } diff --git a/pkg/vere/platform/linux/ptty.c b/pkg/vere/platform/linux/ptty.c index a1ec334507..cf3c5327fd 100644 --- a/pkg/vere/platform/linux/ptty.c +++ b/pkg/vere/platform/linux/ptty.c @@ -178,7 +178,7 @@ u3_ptty_init(uv_loop_t* lup_u, const c3_c** err_c) pty_u->raw_u.c_cflag &= ~(CSIZE | PARENB); pty_u->raw_u.c_cflag |= CS8; pty_u->raw_u.c_oflag &= ~(OPOST); - pty_u->raw_u.c_cc[VMIN] = 0; + pty_u->raw_u.c_cc[VMIN] = 1; pty_u->raw_u.c_cc[VTIME] = 0; } diff --git a/pkg/vere/time.c b/pkg/vere/time.c deleted file mode 100644 index 31465bbd61..0000000000 --- a/pkg/vere/time.c +++ /dev/null @@ -1,169 +0,0 @@ -/// @file - -#include "noun.h" -#include "vere.h" - -/* u3_time_sec_in(): urbit seconds from unix time. -** -** Adjust for future leap secs! -*/ -c3_d -u3_time_sec_in(c3_w unx_w) -{ - return 0x8000000cce9e0d80ULL + (c3_d)unx_w; -} - -/* u3_time_sec_out(): unix time from urbit seconds. -** -** Adjust for future leap secs! -*/ -c3_w -u3_time_sec_out(c3_d urs_d) -{ - c3_d adj_d = (urs_d - 0x8000000cce9e0d80ULL); - - if ( adj_d > 0xffffffffULL ) { - fprintf(stderr, "Agh! It's 2106! And no one's fixed this shite!\n"); - exit(1); - } - return (c3_w)adj_d; -} - -/* u3_time_fsc_in(): urbit fracto-seconds from unix microseconds. -*/ -c3_d -u3_time_fsc_in(c3_w usc_w) -{ - c3_d usc_d = usc_w; - - return ((usc_d * 65536ULL) / 1000000ULL) << 48ULL; -} - -/* u3_time_fsc_out: unix microseconds from urbit fracto-seconds. -*/ -c3_w -u3_time_fsc_out(c3_d ufc_d) -{ - return (c3_w) (((ufc_d >> 48ULL) * 1000000ULL) / 65536ULL); -} - -/* u3_time_msc_out: unix microseconds from urbit fracto-seconds. -*/ -c3_w -u3_time_msc_out(c3_d ufc_d) -{ - return (c3_w) (((ufc_d >> 48ULL) * 1000ULL) / 65536ULL); -} - -/* u3_time_in_tv(): urbit time from struct timeval. -*/ -u3_atom -u3_time_in_tv(struct timeval* tim_tv) -{ - c3_w unx_w = tim_tv->tv_sec; - c3_w usc_w = tim_tv->tv_usec; - c3_d cub_d[2]; - - cub_d[0] = u3_time_fsc_in(usc_w); - cub_d[1] = u3_time_sec_in(unx_w); - - return u3i_chubs(2, cub_d); -} - -/* u3_time_out_tv(): struct timeval from urbit time. -*/ -void -u3_time_out_tv(struct timeval* tim_tv, u3_noun now) -{ - c3_d ufc_d = u3r_chub(0, now); - c3_d urs_d = u3r_chub(1, now); - - tim_tv->tv_sec = u3_time_sec_out(urs_d); - tim_tv->tv_usec = u3_time_fsc_out(ufc_d); - - u3z(now); -} - -/* u3_time_in_ts(): urbit time from struct timespec. -*/ -u3_atom -u3_time_in_ts(struct timespec* tim_ts) -{ - struct timeval tim_tv; - - tim_tv.tv_sec = tim_ts->tv_sec; - tim_tv.tv_usec = (tim_ts->tv_nsec / 1000); - - return u3_time_in_tv(&tim_tv); -} - -#if defined(U3_OS_linux) || defined(U3_OS_windows) -/* u3_time_t_in_ts(): urbit time from time_t. -*/ -u3_atom -u3_time_t_in_ts(time_t tim) -{ - struct timeval tim_tv; - - tim_tv.tv_sec = tim; - tim_tv.tv_usec = 0; - - return u3_time_in_tv(&tim_tv); -} -#endif /* defined(U3_OS_linux) */ - -/* u3_time_out_ts(): struct timespec from urbit time. -*/ -void -u3_time_out_ts(struct timespec* tim_ts, u3_noun now) -{ - struct timeval tim_tv; - - u3_time_out_tv(&tim_tv, now); - - tim_ts->tv_sec = tim_tv.tv_sec; - tim_ts->tv_nsec = (tim_tv.tv_usec * 1000); -} - -/* u3_time_gap_ms(): (wen - now) in ms. -*/ -c3_d -u3_time_gap_ms(u3_noun now, u3_noun wen) -{ - if ( c3n == u3ka_gth(u3k(wen), u3k(now)) ) { - u3z(wen); u3z(now); - return 0ULL; - } - else { - u3_noun dif = u3ka_sub(wen, now); - c3_d fsc_d = u3r_chub(0, dif); - c3_d sec_d = u3r_chub(1, dif); - - u3z(dif); - return (sec_d * 1000ULL) + u3_time_msc_out(fsc_d); - } -} - -/* u3_time_gap_double(): (wen - now) in libev resolution. -*/ -double -u3_time_gap_double(u3_noun now, u3_noun wen) -{ - mpz_t now_mp, wen_mp, dif_mp; - double sec_g = (((double)(1ULL << 32ULL)) * ((double)(1ULL << 32ULL))); - double gap_g, dif_g; - - u3r_mp(now_mp, now); - u3r_mp(wen_mp, wen); - mpz_init(dif_mp); - mpz_sub(dif_mp, wen_mp, now_mp); - - u3z(now); - u3z(wen); - - dif_g = mpz_get_d(dif_mp) / sec_g; - gap_g = (dif_g > 0.0) ? dif_g : 0.0; - mpz_clear(dif_mp); mpz_clear(wen_mp); mpz_clear(now_mp); - - return gap_g; -} diff --git a/pkg/vere/vere.h b/pkg/vere/vere.h index 07bd3049d6..4044fdbade 100644 --- a/pkg/vere/vere.h +++ b/pkg/vere/vere.h @@ -673,76 +673,6 @@ /** Functions. **/ - /* Urbit time: 128 bits, leap-free. - ** - ** High 64 bits: 0x8000.000c.cea3.5380 + Unix time at leap 25 (Jul 2012) - ** Low 64 bits: 1/2^64 of a second. - ** - ** Seconds per Gregorian 400-block: 12.622.780.800 - ** 400-blocks from 0 to 0AD: 730.692.561 - ** Years from 0 to 0AD: 292.277.024.400 - ** Seconds from 0 to 0AD: 9.223.372.029.693.628.800 - ** Seconds between 0A and Unix epoch: 62.167.219.200 - ** Seconds before Unix epoch: 9.223.372.091.860.848.000 - ** The same, in C hex notation: 0x8000000cce9e0d80ULL - ** - ** XX: needs to be adjusted to implement Google leap-smear time. - */ - /* u3_time_sec_in(): urbit seconds from unix time. - ** - ** Adjust (externally) for future leap secs! - */ - c3_d - u3_time_sec_in(c3_w unx_w); - - /* u3_time_sec_out(): unix time from urbit seconds. - ** - ** Adjust (externally) for future leap secs! - */ - c3_w - u3_time_sec_out(c3_d urs_d); - - /* u3_time_fsc_in(): urbit fracto-seconds from unix microseconds. - */ - c3_d - u3_time_fsc_in(c3_w usc_w); - - /* u3_time_fsc_out: unix microseconds from urbit fracto-seconds. - */ - c3_w - u3_time_fsc_out(c3_d ufc_d); - - /* u3_time_in_tv(): urbit time from struct timeval. - */ - u3_atom - u3_time_in_tv(struct timeval* tim_tv); - - /* u3_time_out_tv(): struct timeval from urbit time. - */ - void - u3_time_out_tv(struct timeval* tim_tv, u3_noun now); - - /* u3_time_in_ts(): urbit time from struct timespec. - */ - u3_atom - u3_time_in_ts(struct timespec* tim_ts); -#if defined(U3_OS_linux) || defined(U3_OS_windows) - /* u3_time_t_in_ts(): urbit time from time_t. - */ - u3_atom - u3_time_t_in_ts(time_t tim); -#endif - - /* u3_time_out_ts(): struct timespec from urbit time. - */ - void - u3_time_out_ts(struct timespec* tim_ts, u3_noun now); - - /* u3_time_gap_ms(): (wen - now) in ms. - */ - c3_d - u3_time_gap_ms(u3_noun now, u3_noun wen); - /** Common structure lifecycle. **/ /* u3_dent_init(): initialize file record. From ff8a2df1fa064fe958ad8e7098d6377e1f423b76 Mon Sep 17 00:00:00 2001 From: Quodss Date: Tue, 25 Nov 2025 17:38:36 +0100 Subject: [PATCH 10/15] %drop hint to free ford cache --- pkg/noun/allocate.h | 1 + pkg/noun/manage.c | 21 +++++++++++++++++++-- pkg/noun/nock.c | 8 ++++++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/pkg/noun/allocate.h b/pkg/noun/allocate.h index b1401c2a23..fd7d6126ce 100644 --- a/pkg/noun/allocate.h +++ b/pkg/noun/allocate.h @@ -221,6 +221,7 @@ STATIC_ASSERT( u3a_vits <= u3a_min_log, enum u3a_flag { u3a_flag_sand = 1 << 1, // bump allocation (XX not impl) u3a_flag_cash = 1 << 2, // memo cache harvesting + u3a_flag_drop = 1 << 3, // drop ford cache bucket }; /* u3a_pile: stack control, abstracted over road direction. diff --git a/pkg/noun/manage.c b/pkg/noun/manage.c index 5132554a67..832693bdc5 100644 --- a/pkg/noun/manage.c +++ b/pkg/noun/manage.c @@ -1337,6 +1337,8 @@ u3m_love(u3_noun pro) u3p(u3h_root) per_p = u3R->cax.per_p; u3p(u3h_root) for_p = u3R->cax.for_p; + c3_w fag_w = u3R->how.fag_w; + // are there any timers on the road? // c3_o tim_o = u3du(u3R->tim); @@ -1353,7 +1355,9 @@ u3m_love(u3_noun pro) jed_u = u3j_take(jed_u); byc_p = u3n_take(byc_p); per_p = u3h_take(per_p); - for_p = u3h_take(for_p); + if ( !(fag_w & u3a_flag_drop) ) { + for_p = u3h_take(for_p); + } // pop the stack // @@ -1366,7 +1370,16 @@ u3m_love(u3_noun pro) u3j_reap(jed_u); u3n_reap(byc_p); u3z_reap(u3z_memo_keep, per_p); - u3z_reap(u3z_memo_ford, for_p); + if ( !(fag_w & u3a_flag_drop) ) { + u3z_reap(u3z_memo_ford, for_p); + } + + if ( (fag_w & u3a_flag_drop) ) { + u3h_free(u3R->cax.for_p); + u3R->cax.for_p = u3h_new_cache(u3C.per_w); + } + + u3R->how.fag_w |= (fag_w & u3a_flag_drop); return pro; } @@ -1512,6 +1525,10 @@ u3m_soft_top(c3_w mil_w, // timer ms */ u3z(arg); + /* Clean up the flags + */ + u3R->how.fag_w = 0; + /* Return the product. */ return pro; diff --git a/pkg/noun/nock.c b/pkg/noun/nock.c index 8e73322b87..9c251de6c9 100644 --- a/pkg/noun/nock.c +++ b/pkg/noun/nock.c @@ -1032,6 +1032,7 @@ _n_bint(u3_noun* ops, u3_noun hif, u3_noun nef, c3_o los_o, c3_o tel_o) case c3__nara: case c3__hela: case c3__loop: + case c3__drop: case c3__bout: { u3_noun fen = u3_nul; c3_w nef_w = _n_comp(&fen, nef, los_o, c3n); @@ -1929,6 +1930,13 @@ _n_hilt_fore(u3_noun hin, u3_noun bus, u3_noun* out) *out = u3nc(tag, key); } break; + case c3__drop: { + u3h_free(u3R->cax.for_p); + u3R->cax.for_p = u3h_new_cache(u3C.per_w); + u3R->how.fag_w |= u3a_flag_drop; + *out = u3_nul; + } break; + case c3__bout: { u3_atom now = u3i_chub(u3t_trace_time()); *out = u3i_cell(tag, now); From 5a239e3205c5f1a20381601c35834695845c40d2 Mon Sep 17 00:00:00 2001 From: Quodss Date: Wed, 26 Nov 2025 13:34:59 +0100 Subject: [PATCH 11/15] u3m_hate/love symmetry --- pkg/noun/manage.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/pkg/noun/manage.c b/pkg/noun/manage.c index 832693bdc5..465b731720 100644 --- a/pkg/noun/manage.c +++ b/pkg/noun/manage.c @@ -1209,8 +1209,14 @@ u3m_hate(c3_w pad_w) u3_assert(0 == u3R->ear_p); u3R->ear_p = u3R->cap_p; + + c3_w fag_w = u3R->how.fag_w; u3m_leap(pad_w); + // inherit forward-flowing flags + // + u3R->how.fag_w |= (fag_w & u3a_flag_cash); + u3R->bug.mer = u3i_string( "emergency buffer with sufficient space to cons the trace and bail" ); @@ -1349,13 +1355,17 @@ u3m_love(u3_noun pro) if ( _(tim_o) ) _m_renew_now(); + // inherit backward-flowing flags + // + u3R->how.fag_w |= (fag_w & u3a_flag_drop); + // copy product and caches off our stack // pro = u3a_take(pro); jed_u = u3j_take(jed_u); byc_p = u3n_take(byc_p); per_p = u3h_take(per_p); - if ( !(fag_w & u3a_flag_drop) ) { + if ( !(u3R->how.fag_w & u3a_flag_drop) ) { for_p = u3h_take(for_p); } @@ -1370,17 +1380,15 @@ u3m_love(u3_noun pro) u3j_reap(jed_u); u3n_reap(byc_p); u3z_reap(u3z_memo_keep, per_p); - if ( !(fag_w & u3a_flag_drop) ) { + if ( !(u3R->how.fag_w & u3a_flag_drop) ) { u3z_reap(u3z_memo_ford, for_p); } - if ( (fag_w & u3a_flag_drop) ) { + if ( (u3R->how.fag_w & u3a_flag_drop) ) { u3h_free(u3R->cax.for_p); u3R->cax.for_p = u3h_new_cache(u3C.per_w); } - u3R->how.fag_w |= (fag_w & u3a_flag_drop); - return pro; } @@ -1681,8 +1689,6 @@ u3m_soft_run(u3_noun gul, { u3_noun why = 0, pro; - c3_t cash_t = !!(u3R->how.fag_w & u3a_flag_cash); - /* Record the cap, and leap. */ u3m_hate(1 << 18); @@ -1691,7 +1697,7 @@ u3m_soft_run(u3_noun gul, */ { - if ( (u3_nul == gul) || cash_t ) { + if ( (u3_nul == gul) || (u3R->how.fag_w & u3a_flag_cash) ) { u3R->ski.gul = u3_nul; } else { @@ -1700,7 +1706,6 @@ u3m_soft_run(u3_noun gul, u3R->pro.don = u3to(u3_road, u3R->par_p)->pro.don; u3R->pro.trace = u3to(u3_road, u3R->par_p)->pro.trace; u3R->bug.tax = 0; - u3R->how.fag_w |= ( cash_t ) ? u3a_flag_cash : 0; } u3t_on(coy_o); From 910141b2d929ef17c267f26a30c7a89e8a518429 Mon Sep 17 00:00:00 2001 From: Quodss Date: Wed, 26 Nov 2025 13:37:51 +0100 Subject: [PATCH 12/15] comment --- pkg/noun/allocate.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/noun/allocate.h b/pkg/noun/allocate.h index fd7d6126ce..588bc234ce 100644 --- a/pkg/noun/allocate.h +++ b/pkg/noun/allocate.h @@ -220,8 +220,8 @@ STATIC_ASSERT( u3a_vits <= u3a_min_log, */ enum u3a_flag { u3a_flag_sand = 1 << 1, // bump allocation (XX not impl) - u3a_flag_cash = 1 << 2, // memo cache harvesting - u3a_flag_drop = 1 << 3, // drop ford cache bucket + u3a_flag_cash = 1 << 2, // memo cache harvesting, flows forward + u3a_flag_drop = 1 << 3, // drop ford cache bucket, flows backward }; /* u3a_pile: stack control, abstracted over road direction. From 7a7dcb2e510742a4dcad1f5bd74d6fd85b234313 Mon Sep 17 00:00:00 2001 From: midden-fabler Date: Mon, 1 Dec 2025 14:07:53 -0500 Subject: [PATCH 13/15] allocate, manage: back out drop hint changes --- pkg/noun/allocate.h | 1 - pkg/noun/manage.c | 23 ++--------------------- pkg/noun/nock.c | 8 -------- 3 files changed, 2 insertions(+), 30 deletions(-) diff --git a/pkg/noun/allocate.h b/pkg/noun/allocate.h index 588bc234ce..15e8883660 100644 --- a/pkg/noun/allocate.h +++ b/pkg/noun/allocate.h @@ -221,7 +221,6 @@ STATIC_ASSERT( u3a_vits <= u3a_min_log, enum u3a_flag { u3a_flag_sand = 1 << 1, // bump allocation (XX not impl) u3a_flag_cash = 1 << 2, // memo cache harvesting, flows forward - u3a_flag_drop = 1 << 3, // drop ford cache bucket, flows backward }; /* u3a_pile: stack control, abstracted over road direction. diff --git a/pkg/noun/manage.c b/pkg/noun/manage.c index 465b731720..e25ea76132 100644 --- a/pkg/noun/manage.c +++ b/pkg/noun/manage.c @@ -1343,8 +1343,6 @@ u3m_love(u3_noun pro) u3p(u3h_root) per_p = u3R->cax.per_p; u3p(u3h_root) for_p = u3R->cax.for_p; - c3_w fag_w = u3R->how.fag_w; - // are there any timers on the road? // c3_o tim_o = u3du(u3R->tim); @@ -1355,19 +1353,13 @@ u3m_love(u3_noun pro) if ( _(tim_o) ) _m_renew_now(); - // inherit backward-flowing flags - // - u3R->how.fag_w |= (fag_w & u3a_flag_drop); - // copy product and caches off our stack // pro = u3a_take(pro); jed_u = u3j_take(jed_u); byc_p = u3n_take(byc_p); per_p = u3h_take(per_p); - if ( !(u3R->how.fag_w & u3a_flag_drop) ) { - for_p = u3h_take(for_p); - } + for_p = u3h_take(for_p); // pop the stack // @@ -1380,14 +1372,7 @@ u3m_love(u3_noun pro) u3j_reap(jed_u); u3n_reap(byc_p); u3z_reap(u3z_memo_keep, per_p); - if ( !(u3R->how.fag_w & u3a_flag_drop) ) { - u3z_reap(u3z_memo_ford, for_p); - } - - if ( (u3R->how.fag_w & u3a_flag_drop) ) { - u3h_free(u3R->cax.for_p); - u3R->cax.for_p = u3h_new_cache(u3C.per_w); - } + u3z_reap(u3z_memo_ford, for_p); return pro; } @@ -1533,10 +1518,6 @@ u3m_soft_top(c3_w mil_w, // timer ms */ u3z(arg); - /* Clean up the flags - */ - u3R->how.fag_w = 0; - /* Return the product. */ return pro; diff --git a/pkg/noun/nock.c b/pkg/noun/nock.c index 9c251de6c9..8e73322b87 100644 --- a/pkg/noun/nock.c +++ b/pkg/noun/nock.c @@ -1032,7 +1032,6 @@ _n_bint(u3_noun* ops, u3_noun hif, u3_noun nef, c3_o los_o, c3_o tel_o) case c3__nara: case c3__hela: case c3__loop: - case c3__drop: case c3__bout: { u3_noun fen = u3_nul; c3_w nef_w = _n_comp(&fen, nef, los_o, c3n); @@ -1930,13 +1929,6 @@ _n_hilt_fore(u3_noun hin, u3_noun bus, u3_noun* out) *out = u3nc(tag, key); } break; - case c3__drop: { - u3h_free(u3R->cax.for_p); - u3R->cax.for_p = u3h_new_cache(u3C.per_w); - u3R->how.fag_w |= u3a_flag_drop; - *out = u3_nul; - } break; - case c3__bout: { u3_atom now = u3i_chub(u3t_trace_time()); *out = u3i_cell(tag, now); From 3291aff9d2edb44406d3b7f60ea86b05ecefb0e3 Mon Sep 17 00:00:00 2001 From: midden-fabler Date: Mon, 9 Mar 2026 11:49:50 -0400 Subject: [PATCH 14/15] misc: merge conflicts part 2 --- pkg/noun/manage.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pkg/noun/manage.c b/pkg/noun/manage.c index 683ebb39ce..a78cbb74e9 100644 --- a/pkg/noun/manage.c +++ b/pkg/noun/manage.c @@ -1002,15 +1002,12 @@ u3m_bail(u3_noun how) } } -<<<<<<< HEAD // Reset the spin stack pointer if ( NULL != stk_u ) { stk_u->off_w = u3R->off_w; stk_u->fow_w = u3R->fow_w; } -======= ->>>>>>> next/kelvin/408 _longjmp(u3R->esc.buf, how); } @@ -1371,15 +1368,12 @@ u3m_love(u3_noun pro) if ( _(tim_o) ) _m_renew_now(); -<<<<<<< HEAD -======= // restore slow stack pointer if ( NULL != u3t_Spin ) { u3t_Spin->off_w = u3R->off_w; u3t_Spin->fow_w = u3R->fow_w; } ->>>>>>> next/kelvin/408 // copy product and caches off our stack // pro = u3a_take(pro); From 14ac011f1d882dc867f44de2b7dc5b8581a63f42 Mon Sep 17 00:00:00 2001 From: midden-fabler Date: Mon, 9 Mar 2026 12:07:00 -0400 Subject: [PATCH 15/15] misc: merge conflicts part 3 --- pkg/noun/manage.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/pkg/noun/manage.c b/pkg/noun/manage.c index a78cbb74e9..21823b7bb7 100644 --- a/pkg/noun/manage.c +++ b/pkg/noun/manage.c @@ -1003,9 +1003,9 @@ u3m_bail(u3_noun how) } // Reset the spin stack pointer - if ( NULL != stk_u ) { - stk_u->off_w = u3R->off_w; - stk_u->fow_w = u3R->fow_w; + if ( NULL != u3t_Spin ) { + u3t_Spin->off_w = u3R->off_w; + u3t_Spin->fow_w = u3R->fow_w; } _longjmp(u3R->esc.buf, how); @@ -1358,10 +1358,6 @@ u3m_love(u3_noun pro) // c3_o tim_o = u3du(u3R->tim); - // are there any timers on the road? - // - c3_o tim_o = u3du(u3R->tim); - // fallback to parent road (child heap on parent's stack) // u3m_fall();