From 1f05927558d6caa129aa5b2261a43e6aba18dbca Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Mon, 9 Dec 2019 18:18:07 -0800 Subject: [PATCH] vere: adds tests for newt ipc framing, fixes infinite loop --- pkg/urbit/Makefile | 8 +- pkg/urbit/include/vere/vere.h | 12 +- pkg/urbit/tests/newt_tests.c | 338 ++++++++++++++++++++++++++++++++++ pkg/urbit/vere/newt.c | 163 +++++++--------- 4 files changed, 424 insertions(+), 97 deletions(-) create mode 100644 pkg/urbit/tests/newt_tests.c diff --git a/pkg/urbit/Makefile b/pkg/urbit/Makefile index a265d566af..a7c6ed3a17 100644 --- a/pkg/urbit/Makefile +++ b/pkg/urbit/Makefile @@ -31,11 +31,12 @@ CFLAGS := $(CFLAGS) all: $(all_exes) -test: build/ames_tests build/hashtable_tests build/jam_tests build/mug_tests build/noun_tests +test: build/ames_tests build/hashtable_tests build/jam_tests build/mug_tests build/newt_tests build/noun_tests ./build/ames_tests ./build/hashtable_tests ./build/jam_tests ./build/mug_tests + ./build/newt_tests ./build/noun_tests clean: @@ -66,6 +67,11 @@ build/mug_tests: $(common_objs) tests/mug_tests.o @mkdir -p ./build @$(CC) $^ $(LDFLAGS) -o $@ +build/newt_tests: $(common_objs) tests/newt_tests.o + @echo CC -o $@ + @mkdir -p ./build + @$(CC) $^ $(LDFLAGS) -o $@ + build/noun_tests: $(common_objs) tests/noun_tests.o @echo CC -o $@ @mkdir -p ./build diff --git a/pkg/urbit/include/vere/vere.h b/pkg/urbit/include/vere/vere.h index d8a3dbe03f..85399f63f9 100644 --- a/pkg/urbit/include/vere/vere.h +++ b/pkg/urbit/include/vere/vere.h @@ -257,7 +257,7 @@ /* u3_poke: poke callback function. */ - typedef void (*u3_poke)(void*, u3_noun); + typedef void (*u3_poke)(void*, u3_atom); /* u3_bail: bailout callback function. */ @@ -1172,6 +1172,16 @@ /** Stream messages. **/ + /* u3_newt_encode(): encode an atom to a length-prefixed byte buffer + */ + c3_y* + u3_newt_encode(u3_atom mat, c3_w* len_w); + + /* u3_newt_decode(): decode a (partial) length-prefixed byte buffer + */ + void + u3_newt_decode(u3_moat* mot_u, c3_y* buf_y, c3_w len_w); + /* u3_newt_write(): write atom to stream; free atom. */ void diff --git a/pkg/urbit/tests/newt_tests.c b/pkg/urbit/tests/newt_tests.c new file mode 100644 index 0000000000..41f20eb401 --- /dev/null +++ b/pkg/urbit/tests/newt_tests.c @@ -0,0 +1,338 @@ +#include "all.h" +#include "vere/vere.h" + +/* _setup(): prepare for tests. +*/ +static void +_setup(void) +{ + u3m_init(); + u3m_pave(c3y, c3n); +} + +static c3_w pok_w; +static c3_w bal_w; + +static void +_moat_poke_cb(void* vod_p, u3_atom a) +{ + pok_w++; + u3z(a); +} + +static void +_moat_bail_cb(void* vod_p, const c3_c* err_c) +{ + bal_w++; +} + +/* _test_newt_smol(): various scenarios with small messages +*/ +static void +_test_newt_smol(void) +{ + // =(2 (jam 0)) + // + u3_atom a = u3ke_jam(0); + u3_moat mot_u; + c3_w len_w; + c3_y* buf_y; + + memset(&mot_u, 0, sizeof(u3_moat)); + mot_u.pok_f = _moat_poke_cb; + mot_u.bal_f = _moat_bail_cb; + + // one message one buffer + // + { + pok_w = 0; + bal_w = 0; + + buf_y = u3_newt_encode(u3k(a), &len_w); + u3_newt_decode(&mot_u, buf_y, len_w); + + if ( 1 != pok_w ) { + fprintf(stderr, "newt smol fail (a)\n"); + exit(1); + } + } + + // two messages one buffer + // + { + pok_w = 0; + bal_w = 0; + + buf_y = u3_newt_encode(u3k(a), &len_w); + + buf_y = c3_realloc(buf_y, 2 * len_w); + memcpy(buf_y + len_w, buf_y, len_w); + len_w = 2 * len_w; + + u3_newt_decode(&mot_u, buf_y, len_w); + + if ( 2 != pok_w ) { + fprintf(stderr, "newt smol fail (b)\n"); + exit(1); + } + } + + // one message two buffers + // + { + c3_y* end_y; + pok_w = 0; + bal_w = 0; + + buf_y = u3_newt_encode(u3k(a), &len_w); + + end_y = c3_malloc(1); + end_y[0] = buf_y[len_w - 1]; + + u3_newt_decode(&mot_u, buf_y, len_w - 1); + + if ( 0 != pok_w ) { + fprintf(stderr, "newt smol fail (c)\n"); + exit(1); + } + + u3_newt_decode(&mot_u, end_y, 1); + + if ( 1 != pok_w ) { + fprintf(stderr, "newt smol fail (d)\n"); + exit(1); + } + } + + // two messages two buffers (overlapping length) + // + { + c3_y* haf_y; + c3_w haf_w, dub_w; + + pok_w = 0; + bal_w = 0; + + buf_y = u3_newt_encode(u3k(a), &len_w); + + dub_w = 2 * len_w; + haf_w = len_w / 2; + + // buf_y is all of message one, half of message two (not a full length) + // + buf_y = c3_realloc(buf_y, dub_w - haf_w); + memcpy(buf_y + len_w, buf_y, len_w - haf_w); + + // haf_y is the second half of message two + // + haf_y = c3_malloc(haf_w); + memcpy(haf_y, buf_y + (len_w - haf_w), haf_w); + + u3_newt_decode(&mot_u, buf_y, dub_w - haf_w); + + if ( 1 != pok_w ) { + fprintf(stderr, "newt smol fail (e)\n"); + exit(1); + } + + u3_newt_decode(&mot_u, haf_y, haf_w); + + if ( 2 != pok_w ) { + fprintf(stderr, "newt smol fail (f)\n"); + exit(1); + } + } + + u3z(a); +} + +/* _test_newt_vast(): various scenarios with larger messages +*/ +static void +_test_newt_vast(void) +{ + // =(53 (met 3 (jam "abcdefghijklmnopqrstuvwxyz"))) + // + u3_atom a = u3ke_jam(u3i_tape("abcdefghijklmnopqrstuvwxyz")); + u3_moat mot_u; + c3_w len_w; + c3_y* buf_y; + + memset(&mot_u, 0, sizeof(u3_moat)); + mot_u.pok_f = _moat_poke_cb; + mot_u.bal_f = _moat_bail_cb; + + // one message one buffer + // + { + pok_w = 0; + bal_w = 0; + + buf_y = u3_newt_encode(u3k(a), &len_w); + u3_newt_decode(&mot_u, buf_y, len_w); + + if ( 1 != pok_w ) { + fprintf(stderr, "newt vast fail (a)\n"); + exit(1); + } + } + + // two messages one buffer + // + { + pok_w = 0; + bal_w = 0; + + buf_y = u3_newt_encode(u3k(a), &len_w); + + buf_y = c3_realloc(buf_y, 2 * len_w); + memcpy(buf_y + len_w, buf_y, len_w); + len_w = 2 * len_w; + + u3_newt_decode(&mot_u, buf_y, len_w); + + if ( 2 != pok_w ) { + fprintf(stderr, "newt vast fail (b)\n"); + exit(1); + } + } + + // one message many buffers + // + { + pok_w = 0; + bal_w = 0; + + buf_y = u3_newt_encode(u3k(a), &len_w); + + { + c3_y* cop_y = c3_malloc(len_w); + c3_w haf_w = len_w / 2; + memcpy(cop_y, buf_y, len_w); + + u3_newt_decode(&mot_u, buf_y, haf_w); + + while ( haf_w < len_w ) { + c3_y* end_y = c3_malloc(1); + end_y[0] = cop_y[haf_w]; + + if ( 0 != pok_w ) { + fprintf(stderr, "newt vast fail (c) %u\n", haf_w); + exit(1); + } + + u3_newt_decode(&mot_u, end_y, 1); + haf_w++; + } + + c3_free(cop_y); + } + + if ( 1 != pok_w ) { + fprintf(stderr, "newt vast fail (d)\n"); + exit(1); + } + } + + // two messages two buffers + // + { + c3_y* haf_y; + c3_w haf_w, dub_w; + + pok_w = 0; + bal_w = 0; + + buf_y = u3_newt_encode(u3k(a), &len_w); + + dub_w = 2 * len_w; + haf_w = len_w / 2; + + // buf_y is all of message one, half of message two + // + buf_y = c3_realloc(buf_y, dub_w - haf_w); + memcpy(buf_y + len_w, buf_y, len_w - haf_w); + + // haf_y is the second half of message two + // + haf_y = c3_malloc(haf_w); + memcpy(haf_y, buf_y + (len_w - haf_w), haf_w); + + u3_newt_decode(&mot_u, buf_y, dub_w - haf_w); + + if ( 1 != pok_w ) { + fprintf(stderr, "newt vast fail (e)\n"); + exit(1); + } + + u3_newt_decode(&mot_u, haf_y, haf_w); + + if ( 2 != pok_w ) { + fprintf(stderr, "newt vast fail (f)\n"); + exit(1); + } + } + + // two messages many buffers + // + { + c3_w dub_w; + + pok_w = 0; + bal_w = 0; + + buf_y = u3_newt_encode(u3k(a), &len_w); + + dub_w = 2 * len_w; + + // buf_y is two copies of message + // + buf_y = c3_realloc(buf_y, dub_w); + memcpy(buf_y + len_w, buf_y, len_w); + + { + c3_y* cop_y = c3_malloc(dub_w); + c3_w haf_w = len_w + 1; + memcpy(cop_y, buf_y, dub_w); + + u3_newt_decode(&mot_u, buf_y, haf_w); + + while ( haf_w < dub_w ) { + c3_y* end_y = c3_malloc(1); + end_y[0] = cop_y[haf_w]; + + if ( 1 != pok_w ) { + fprintf(stderr, "newt vast fail (g) %u\n", haf_w); + exit(1); + } + + u3_newt_decode(&mot_u, end_y, 1); + haf_w++; + } + + c3_free(cop_y); + } + + if ( 2 != pok_w ) { + fprintf(stderr, "newt vast fail (h)\n"); + exit(1); + } + } + + u3z(a); +} + +/* main(): run all test cases. +*/ +int +main(int argc, char* argv[]) +{ + _setup(); + + _test_newt_smol(); + _test_newt_vast(); + + fprintf(stderr, "test_newt: ok\n"); + + return 0; +} diff --git a/pkg/urbit/vere/newt.c b/pkg/urbit/vere/newt.c index 05afb418b9..f5a8e418b1 100644 --- a/pkg/urbit/vere/newt.c +++ b/pkg/urbit/vere/newt.c @@ -31,8 +31,6 @@ #include "all.h" #include "vere/vere.h" -#undef NEWT_VERBOSE - /* _newt_gain_meat(): add a block to an existing message */ static void @@ -47,17 +45,6 @@ _newt_gain_meat(u3_moat* mot_u) met_u->len_d = mot_u->len_d; memcpy(met_u->hun_y, mot_u->rag_y, mot_u->len_d); -#ifdef NEWT_VERBOSE - u3l_log("newt: %d: create: msg %p, new block %p, len %" - PRIu64 ", has %" PRIu64 ", needs %" PRIu64 "\r\n", - getpid(), - mot_u->mes_u, - met_u, - met_u->len_d, - mot_u->mes_u->has_d, - mot_u->mes_u->len_d); -#endif - // enqueue block // if ( !mot_u->mes_u->meq_u ) { @@ -105,12 +92,6 @@ _newt_gain_mess(u3_moat* mot_u) nel_d); } -#ifdef NEWT_VERBOSE - u3l_log("newt: %d: parsed length %" PRIu64 "\r\n", - getpid(), - nel_d); -#endif - mot_u->len_d -= 8ULL; mot_u->mes_u = c3_malloc(sizeof(u3_mess)); @@ -202,11 +183,29 @@ _newt_poke_mess(u3_moat* mot_u) } } -/* _newt_consume(): advance buffer processing. +/* u3_newt_decode(): decode a (partial) length-prefixed byte buffer */ -static void -_newt_consume(u3_moat* mot_u) +void +u3_newt_decode(u3_moat* mot_u, c3_y* buf_y, c3_w len_w) { + // grow read buffer by `len_d` bytes + // + if ( mot_u->rag_y ) { + // XX check SIZE_MAX? + // + c3_d nel_d = mot_u->len_d + len_w; + + mot_u->rag_y = c3_realloc(mot_u->rag_y, nel_d); + memcpy(mot_u->rag_y + mot_u->len_d, buf_y, len_w); + + mot_u->len_d = nel_d; + c3_free(buf_y); + } + else { + mot_u->rag_y = buf_y; + mot_u->len_d = (c3_d)len_w; + } + // process stray bytes, trying to create a new message // or add a block to an existing one. // @@ -219,6 +218,9 @@ _newt_consume(u3_moat* mot_u) if ( 8ULL <= mot_u->len_d ) { _newt_gain_mess(mot_u); } + else { + break; + } } else { // there is a live message, add a block to the queue. @@ -256,6 +258,7 @@ _newt_read_cb(uv_stream_t* str_u, u3_moat* mot_u = (void *)str_u; if ( 0 > len_i ) { + c3_free(buf_u->base); uv_read_stop(str_u); mot_u->bal_f(mot_u->vod_p, uv_strerror(len_i)); } @@ -265,37 +268,7 @@ _newt_read_cb(uv_stream_t* str_u, c3_free(buf_u->base); } else { - c3_d len_d = (c3_d)len_i; - -#ifdef NEWT_VERBOSE - u3l_log("newt: %d: read %ld\r\n", getpid(), len_i); - - u3l_log("newt: %d: ", getpid()); - for ( int i = 0; i < len_i; i++) { - if (0 == (i % 16)) u3l_log("\r\n"); - u3l_log(" %02x", (unsigned) buf_u->base[i]); - } - - u3l_log("\r\nnewt: %d: \r\n", getpid()); -#endif - - // grow read buffer by `len_d` bytes - // - if ( mot_u->rag_y ) { - c3_d nel_d = mot_u->len_d + len_d; - - mot_u->rag_y = c3_realloc(mot_u->rag_y, nel_d); - memcpy(mot_u->rag_y + mot_u->len_d, buf_u->base, len_d); - - mot_u->len_d = nel_d; - c3_free(buf_u->base); - } - else { - mot_u->rag_y = (c3_y *)buf_u->base; - mot_u->len_d = len_d; - } - - _newt_consume(mot_u); + u3_newt_decode(mot_u, (c3_y*)buf_u->base, (c3_w)len_i); } } @@ -319,23 +292,23 @@ u3_newt_read(u3_moat* mot_u) } } -/* write request for newt +/* u3_write_t: write request for newt */ - struct _u3_write_t { - uv_write_t wri_u; - u3_mojo* moj_u; - void* vod_p; - c3_y* buf_y; - }; +typedef struct _u3_write_t { + uv_write_t wri_u; + u3_mojo* moj_u; + void* vod_p; + c3_y* buf_y; +} u3_write_t; /* _newt_write_cb(): generic write callback. */ static void _newt_write_cb(uv_write_t* wri_u, c3_i sas_i) { - struct _u3_write_t* req_u = (struct _u3_write_t*)wri_u; - void* vod_p = req_u->vod_p; - u3_mojo* moj_u = req_u->moj_u; + u3_write_t* req_u = (struct _u3_write_t*)wri_u; + void* vod_p = req_u->vod_p; + u3_mojo* moj_u = req_u->moj_u; free(req_u->buf_y); free(req_u); @@ -346,6 +319,31 @@ _newt_write_cb(uv_write_t* wri_u, c3_i sas_i) } } +/* u3_newt_encode(): encode an atom to a length-prefixed byte buffer +*/ +c3_y* +u3_newt_encode(u3_atom mat, c3_w* len_w) +{ + c3_w met_w = u3r_met(3, mat); + c3_y* buf_y; + + *len_w = 8 + met_w; + buf_y = c3_malloc(*len_w); + + // write header; c3_d is futureproofing + // + buf_y[0] = ((met_w >> 0) & 0xff); + buf_y[1] = ((met_w >> 8) & 0xff); + buf_y[2] = ((met_w >> 16) & 0xff); + buf_y[3] = ((met_w >> 24) & 0xff); + buf_y[4] = buf_y[5] = buf_y[6] = buf_y[7] = 0; + + u3r_bytes(0, met_w, buf_y + 8, mat); + u3z(mat); + + return buf_y; +} + /* u3_newt_write(): write atom to stream; free atom. */ void @@ -353,44 +351,19 @@ u3_newt_write(u3_mojo* moj_u, u3_atom mat, void* vod_p) { - c3_w len_w = u3r_met(3, mat); - c3_y* buf_y = c3_malloc(len_w + 8); - struct _u3_write_t* req_u = c3_malloc(sizeof(*req_u)); - uv_buf_t buf_u; - c3_i err_i; - - /* write header; c3_d is futureproofing - */ - buf_y[0] = ((len_w >> 0) & 0xff); - buf_y[1] = ((len_w >> 8) & 0xff); - buf_y[2] = ((len_w >> 16) & 0xff); - buf_y[3] = ((len_w >> 24) & 0xff); - buf_y[4] = buf_y[5] = buf_y[6] = buf_y[7] = 0; - u3r_bytes(0, len_w, buf_y + 8, mat); - u3z(mat); + u3_write_t* req_u = c3_malloc(sizeof(*req_u)); + c3_w len_w; + c3_y* buf_y = u3_newt_encode(mat, &len_w); + uv_buf_t buf_u; + c3_i err_i; req_u->moj_u = moj_u; req_u->buf_y = buf_y; - buf_u.base = (c3_c*) buf_y; - buf_u.len = len_w + 8; - -#ifdef NEWT_VERBOSE - u3l_log("newt: %d: write %d\n", getpid(), len_w + 8); -#endif - -#ifdef NEWT_VERBOSE - u3l_log("newt: %d: ", getpid()); - for ( int i = 0; i < len_w+8; i++) { - if (0 == (i % 16)) u3l_log("\r\n"); - u3l_log(" %02x", (unsigned) buf_u.base[i]); - } - u3l_log("\r\nnewt: %d: \r\n", getpid()); -#endif + buf_u = uv_buf_init((c3_c*)buf_y, len_w); if ( 0 != (err_i = uv_write((uv_write_t*)req_u, (uv_stream_t*)&moj_u->pyp_u, - &buf_u, - 1, + &buf_u, 1, _newt_write_cb)) ) { moj_u->bal_f(moj_u, uv_strerror(err_i));