diff --git a/.github/workflows/docker-shared.yml b/.github/workflows/docker-shared.yml index 6232b23ab2..7e73ae0729 100644 --- a/.github/workflows/docker-shared.yml +++ b/.github/workflows/docker-shared.yml @@ -38,7 +38,11 @@ jobs: - name: Install toolchains if: steps.build-cache.outputs.cache-hit != 'true' - run: bazel run //bazel/toolchain:x86_64-linux-musl-gcc + run: | + bazel run //bazel/toolchain:x86_64-linux-musl-gcc + + - name: Install dependencies + run: sudo apt-get install -y autoconf-archive - name: Set pace run: echo "${{ inputs.pace }}" > ./PACE diff --git a/.github/workflows/next.yml b/.github/workflows/next.yml index e0dd8feeec..07250449e3 100644 --- a/.github/workflows/next.yml +++ b/.github/workflows/next.yml @@ -20,7 +20,3 @@ jobs: upload: true next: ${{ github.ref_name }} secrets: inherit - - docker: - uses: ./.github/workflows/docker-shared.yml - secrets: inherit diff --git a/.github/workflows/shared.yml b/.github/workflows/shared.yml index eec7f3fec9..aee47884a5 100644 --- a/.github/workflows/shared.yml +++ b/.github/workflows/shared.yml @@ -43,7 +43,7 @@ jobs: # runner with BuildJet instead. - { target: linux-aarch64, runner: buildjet-2vcpu-ubuntu-2204-arm } - { target: linux-x86_64, runner: ubuntu-22.04 } - # GitHub doesn't provide macOS machines with Apple Silicon, so we + # GitHub doesn't provide macOS machines with Apple Silicon, so we # self-host a runner with MacStadium instead. - { target: macos-aarch64, runner: [self-hosted, macos, ARM64] } - { target: macos-x86_64, runner: macos-12 } @@ -72,7 +72,7 @@ jobs: /private/var/tmp/_bazel_$(whoami) # Cache musl libc toolchains. /usr/local/*-musl - + - name: chown /usr/local/*-musl if: ${{ matrix.target == 'linux-x86_64' || matrix.target == 'linux-aarch64'}} run: | @@ -113,17 +113,20 @@ jobs: run: | case "${{ matrix.target }}" in "linux-aarch64") + sudo apt-get -y install autoconf-archive bazel run //bazel/toolchain:aarch64-linux-musl-gcc ;; "linux-x86_64") + sudo apt-get -y install autoconf-archive bazel run //bazel/toolchain:x86_64-linux-musl-gcc ;; "macos-aarch64") + brew install pkg-config autoconf-archive ;; "macos-x86_64") # Switch Xcode path to match the path specified in our bazel toolchain. sudo xcode-select --switch /Library/Developer/CommandLineTools - brew install automake libtool llvm@15 + brew install automake autoconf-archive libtool llvm@15 ;; *) echo "Unsupported target: ${{ matrix.target }}" diff --git a/.gitignore b/.gitignore index 251a0d7f0f..da5bf8e6c8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ +# IDEs. +/.vscode +/.idea + # Bazel. /.user.bazelrc /bazel-* diff --git a/INSTALL.md b/INSTALL.md index e30f14f88e..5f3d2774f8 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -50,7 +50,7 @@ This will take a few minutes. ### macOS -After installing `automake` and `libtool`, you're ready to build Vere. +After installing `automake`, `autoconf-archive`, `pkg-config`, and `libtool`, you're ready to build Vere. ## Build Commands diff --git a/README.md b/README.md index 9e68fafdad..679d829fac 100644 --- a/README.md +++ b/README.md @@ -19,8 +19,6 @@ defined in its own package: - [`pkg/c3`](pkg/c3): A set of basic utilities for writing Urbit's style of C. - [`pkg/ent`](pkg/ent): A cross-platform wrapper for `getentropy(2)`. -- [`pkg/urcrypt`](pkg/urcrypt): A standardized interface for calling various - cryptographic functions used in the Urbit runtime. - [`pkg/ur`](pkg/ur): An implementation of [jam][jam] and [cue][cue], Urbit's bitwise noun serialization and deserialization algorithms, respectively. - [`pkg/noun`](pkg/noun): The Nock virtual machine and snapshotting system. diff --git a/VERSION b/VERSION index 3e162f02ea..9f55b2ccb5 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.12 +3.0 diff --git a/WORKSPACE.bazel b/WORKSPACE.bazel index 2cad948ace..d3c4848fd1 100644 --- a/WORKSPACE.bazel +++ b/WORKSPACE.bazel @@ -122,12 +122,12 @@ versioned_http_archive( ) versioned_http_archive( - name = "argon2", - build_file = "//bazel/third_party/argon2:argon2.BUILD", - sha256 = "40850e6e6324be10f14228d17b617ad2074bb926eeddd6fe40ad5df833833949", - strip_prefix = "argon2-{version}", - url = "https://github.com/urbit/argon2/archive/{version}.tar.gz", - version = "a4c1e3f7138c2e577376beb99f964cf71e1c8b1b", + name = "avahi", + build_file = "//bazel/third_party/avahi:avahi.BUILD", + sha256 = "060309d7a333d38d951bc27598c677af1796934dbd98e1024e7ad8de798fedda", + strip_prefix = "avahi-{version}", + url = "https://github.com/lathiat/avahi/releases/download/v{version}/avahi-{version}.tar.gz", + version = "0.8", ) versioned_http_archive( @@ -155,12 +155,22 @@ versioned_http_archive( ) versioned_http_archive( - name = "ed25519", - build_file = "//bazel/third_party/ed25519:ed25519.BUILD", - sha256 = "373923c85f61276e3cad2c0ae7a5d5cd4809ffe46c5abc1dc8276683a55782a0", - strip_prefix = "ed25519-{version}", - url = "https://github.com/orlp/ed25519/archive/{version}.tar.gz", - version = "7fa6712ef5d581a6981ec2b08ee623314cd1d1c4", + name = "dbus", + build_file = "//bazel/third_party/dbus:dbus.BUILD", + sha256 = "a6bd5bac5cf19f0c3c594bdae2565a095696980a683a0ef37cb6212e093bde35", + strip_prefix = "dbus-{version}", + url = "https://src.fedoraproject.org/repo/pkgs/dbus/dbus-{version}.tar.xz/sha512/f3dfc73da28cbe20449d15bbe4166c3574f0e551dfd15fca7cce2b8c71e778360ed2dd391ee5c414a7a47ff4b958727b26ef4cabfee70564f8d0a34bf5ad2386/dbus-1.14.8.tar.xz", + version = "1.14.8", +) + +versioned_http_archive( + name = "expat", + build_file = "//bazel/third_party/expat:expat.BUILD", + strip_prefix = "expat-{version}", + sha256 = "ef2420f0232c087801abf705e89ae65f6257df6b7931d37846a193ef2e8cdcbe", + # TODO: fix the R_2_5_0 nonsense + url = "https://github.com/libexpat/libexpat/releases/download/R_2_5_0/expat-{version}.tar.xz", + version = "2.5.0", ) versioned_http_archive( @@ -195,9 +205,9 @@ versioned_http_archive( versioned_http_archive( name = "io_bazel_rules_go", - sha256 = "f2dcd210c7095febe54b804bb1cd3a58fe8435a909db2ec04e31542631cf715c", + sha256 = "d6ab6b57e48c09523e93050f13698f708428cfd5e619252e369d377af6597707", url = "https://github.com/bazelbuild/rules_go/releases/download/v{version}/rules_go-v{version}.zip", - version = "0.31.0", + version = "0.43.0", ) versioned_http_file( @@ -214,17 +224,6 @@ versioned_http_file( version = "255fb1ca8206072f1d09425f0db61ecfe7ff5b17", ) -versioned_http_archive( - name = "keccak_tiny", - build_file = "//bazel/third_party/keccak_tiny:keccak_tiny.BUILD", - patch_args = ["-p1"], - patches = ["//bazel/third_party/keccak_tiny:{version}.patch"], - sha256 = "6d4717f96b84805886c74bad89e911076664d992f197634fd7cdfca2ac0f62ef", - strip_prefix = "keccak-tiny-{version}", - url = "https://github.com/coruus/keccak-tiny/archive/{version}.tar.gz", - version = "64b6647514212b76ae7bca0dea9b7b197d1d8186", -) - versioned_http_archive( name = "lmdb", build_file = "//bazel/third_party/lmdb:lmdb.BUILD", @@ -272,18 +271,6 @@ versioned_http_archive( version = "67108d883061043e55d0fb13961ac1b6fc8a485c", ) -versioned_http_archive( - name = "scrypt", - build_file = "//bazel/third_party/scrypt:scrypt.BUILD", - sha256 = "df681fb19b653b1a12970ebb6091bb2b58411b9e7baf01143870f6be3f099541", - strip_prefix = "libscrypt-{version}", - url = "https://github.com/technion/libscrypt/archive/{version}.tar.gz", - # When bumping the version, compare `Makefile` in the `scrypt` repo to - # {build_file} and confirm that {build_file} remains an accurate description - # of the scrypt build process. - version = "60e585cdd752262b22ed4113eca41c0461a61608", -) - versioned_http_archive( name = "secp256k1", build_file = "//bazel/third_party/secp256k1:secp256k1.BUILD", @@ -337,6 +324,15 @@ versioned_http_file( version = "ea8fee3aa0434d4bdf1bf785e5ec346c7ecba7fd", ) +versioned_http_archive( + name = "urcrypt", + build_file = "//bazel/third_party/urcrypt:urcrypt.BUILD", + sha256 = "afc1182e10eeebaeb2a111c2bd889747792d255e26aba7fdcd6751d0d3c2bb35", + strip_prefix = "urcrypt-{version}", + url = "https://github.com/urbit/urcrypt/archive/{version}.tar.gz", + version = "43479c3262a11e20da5f6218f3b0b3d63931ceea", +) + versioned_http_archive( name = "uv", build_file = "//bazel/third_party/uv:uv.BUILD", diff --git a/bazel/BUILD.bazel b/bazel/BUILD.bazel index bb93f5d26c..5ac74d0b96 100644 --- a/bazel/BUILD.bazel +++ b/bazel/BUILD.bazel @@ -11,7 +11,6 @@ refresh_compile_commands( "//pkg/ent", "//pkg/noun", "//pkg/ur", - "//pkg/urcrypt", "//pkg/vere:urbit", ], # No need to add flags already in .bazelrc. They're automatically picked up. @@ -19,4 +18,4 @@ refresh_compile_commands( # Wildcard patterns, like //... for everything, *are* allowed here, just like a build. # As are additional targets (+) and subtractions (-), like in bazel query https://docs.bazel.build/versions/main/query.html#expressions # And if you're working on a header-only library, specify a test or binary target that compiles it. -) \ No newline at end of file +) diff --git a/bazel/third_party/avahi/BUILD.bazel b/bazel/third_party/avahi/BUILD.bazel new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bazel/third_party/avahi/avahi.BUILD b/bazel/third_party/avahi/avahi.BUILD new file mode 100644 index 0000000000..84d27171c2 --- /dev/null +++ b/bazel/third_party/avahi/avahi.BUILD @@ -0,0 +1,30 @@ +load("@rules_foreign_cc//foreign_cc:defs.bzl", "configure_make") + +filegroup( + name = "all", + srcs = glob(["**"]), +) + +cc_library( + name = "dns-sd", + hdrs = ["dns_sd.h"], + visibility = ["//visibility:public"], +) + +configure_make( + name = "avahi", + args = select({ + "@platforms//os:macos": ["--jobs=`sysctl -n hw.logicalcpu`"], + "//conditions:default": ["--jobs=`nproc`"], + }), + configure_options = ["--with-dbus-system-address='unix:path=/var/run/dbus/system_bus_socket' --with-xml=none --disable-libevent --disable-glib --disable-gobject --disable-gdbm --disable-qt3 --disable-qt4 --disable-qt5 --disable-gtk --disable-gtk3 --disable-mono --disable-monodoc --disable-python --disable-libdaemon --enable-compat-libdns_sd --disable-rpath"], + lib_source = ":all", + # out_include_dir = "avahi-compat-libdns_sd", + deps = ["@dbus"], + configure_in_place = True, + autogen = True, + autoconf = True, + autogen_command = "bootstrap.sh", + out_static_libs = ["libdns_sd.a", "libavahi-client.a", "libavahi-common.a"], + visibility = ["//visibility:public"], +) diff --git a/bazel/third_party/dbus/BUILD.bazel b/bazel/third_party/dbus/BUILD.bazel new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bazel/third_party/dbus/dbus.BUILD b/bazel/third_party/dbus/dbus.BUILD new file mode 100644 index 0000000000..0642883163 --- /dev/null +++ b/bazel/third_party/dbus/dbus.BUILD @@ -0,0 +1,21 @@ +load("@rules_foreign_cc//foreign_cc:defs.bzl", "configure_make") + +filegroup( + name = "all", + srcs = glob(["**"]), +) + +configure_make( + name = "dbus", + lib_name = "libdbus-1", + args = select({ + "@platforms//os:macos": ["--jobs=`sysctl -n hw.logicalcpu`"], + "//conditions:default": ["--jobs=`nproc`"], + }), + copts = ["-O3"], + configure_options = ["--disable-selinux --without-x --disable-tests"], + lib_source = ":all", + configure_in_place = True, + deps = ["@expat"], + visibility = ["//visibility:public"], +) diff --git a/bazel/third_party/expat/BUILD.bazel b/bazel/third_party/expat/BUILD.bazel new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bazel/third_party/expat/expat.BUILD b/bazel/third_party/expat/expat.BUILD new file mode 100644 index 0000000000..030f81ed78 --- /dev/null +++ b/bazel/third_party/expat/expat.BUILD @@ -0,0 +1,18 @@ +load("@rules_foreign_cc//foreign_cc:defs.bzl", "configure_make") + +filegroup( + name = "all", + srcs = glob(["**"]), +) + +configure_make( + name = "expat", + args = select({ + "@platforms//os:macos": ["--jobs=`sysctl -n hw.logicalcpu`"], + "//conditions:default": ["--jobs=`nproc`"], + }), + copts = ["-O3"], + lib_source = ":all", + out_static_libs = ["libexpat.a"], + visibility = ["//visibility:public"], +) diff --git a/bazel/third_party/urcrypt/BUILD.bazel b/bazel/third_party/urcrypt/BUILD.bazel new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bazel/third_party/urcrypt/urcrypt.BUILD b/bazel/third_party/urcrypt/urcrypt.BUILD new file mode 100644 index 0000000000..973830447d --- /dev/null +++ b/bazel/third_party/urcrypt/urcrypt.BUILD @@ -0,0 +1,28 @@ +load("@rules_foreign_cc//foreign_cc:defs.bzl", "configure_make") + +filegroup( + name = "all", + srcs = glob(["**"]), +) + +configure_make( + name = "urcrypt", + autogen = True, + configure_in_place = True, + configure_options = [ + "--disable-shared", + ], + copts = [ + "-Wall", + "-g", + "-O3", + ], + deps = [ + "@aes_siv", + "@openssl", + "@secp256k1" + ], + lib_source = ":all", + out_static_libs = ["liburcrypt.a"], + visibility = ["//visibility:public"], +) diff --git a/pkg/c3/defs.h b/pkg/c3/defs.h index 38b39263a9..bbf8d91ef7 100644 --- a/pkg/c3/defs.h +++ b/pkg/c3/defs.h @@ -149,6 +149,8 @@ mkdir(a, b);}) # define c3_rmdir(a) ({ \ rmdir(a);}) +# define c3_link(a, b) ({ \ + link(a, b);}) # define c3_unlink(a) ({ \ unlink(a);}) # define c3_fopen(a, b) ({ \ diff --git a/pkg/c3/motes.h b/pkg/c3/motes.h index 0000d5c593..beaa33d63b 100644 --- a/pkg/c3/motes.h +++ b/pkg/c3/motes.h @@ -308,6 +308,7 @@ # define c3__deep c3_s4('d','e','e','p') # define c3__defn c3_s4('d','e','f','n') # define c3__del c3_s3('d','e','l') +# define c3__dear c3_s4('d','e','a','r') # define c3__delc c3_s4('d','e','l','c') # define c3__delt c3_s4('d','e','l','t') # define c3__dept c3_s4('d','e','p','t') @@ -998,6 +999,7 @@ # define c3__sard c3_s4('s','a','r','d') # define c3__sav c3_s3('s','a','v') # define c3__save c3_s4('s','a','v','e') +# define c3__saxo c3_s4('s','a','x','o') # define c3__scam c3_s4('s','c','a','m') # define c3__scan c3_s4('s','c','a','n') # define c3__scry c3_s4('s','c','r','y') @@ -1111,6 +1113,7 @@ # define c3__ston c3_s4('s','t','o','n') # define c3__stop c3_s4('s','t','o','p') # define c3__stub c3_s4('s','t','u','b') +# define c3__stun c3_s4('s','t','u','n') # define c3__stur c3_s4('s','t','u','r') # define c3__sub c3_s3('s','u','b') # define c3__sunt c3_s4('s','u','n','t') diff --git a/pkg/noun/BUILD.bazel b/pkg/noun/BUILD.bazel index 964e4bf60f..a6b8de6d7a 100644 --- a/pkg/noun/BUILD.bazel +++ b/pkg/noun/BUILD.bazel @@ -8,9 +8,8 @@ vere_library( name = "noun", srcs = glob( [ - "*.c", - "*.h", - "jets/tree.c", + "**/*.c", + "**/*.h", "jets/*.h", "jets/**/*.c", ], @@ -34,13 +33,13 @@ vere_library( "//pkg/c3", "//pkg/ent", "//pkg/ur", - "//pkg/urcrypt", "@gmp", "@murmur3", "@openssl", "@pdjson", "@sigsegv", "@softfloat", + "@urcrypt", ] + select({ "@platforms//os:macos": ["//pkg/noun/platform/darwin"], "@platforms//os:linux": ["//pkg/noun/platform/linux"], diff --git a/pkg/noun/allocate.c b/pkg/noun/allocate.c index ace4473a4a..8ca5085397 100644 --- a/pkg/noun/allocate.c +++ b/pkg/noun/allocate.c @@ -16,15 +16,6 @@ u3_road* u3a_Road; c3_w u3_Code; #endif -// declarations of inline functions -// - -void u3a_config_loom(c3_w ver_w); -void *u3a_into(c3_w x); -c3_w u3a_outa(void *p); -c3_w u3a_to_off(c3_w som); -void *u3a_to_ptr(c3_w som); -c3_w *u3a_to_wtr(c3_w som); c3_w u3a_to_pug(c3_w off); c3_w u3a_to_pom(c3_w off); @@ -67,37 +58,38 @@ _box_count(c3_ws siz_ws) { } _box_count, (others?) should have perhaps its own header and certainly its own prefix. having to remind yourself that _box_count doesn't actually do anything unless U3_CPU_DEBUG is defined is annoying. */ -#define _box_vaal(box_u) \ - do { \ - c3_dessert(((uintptr_t)u3a_boxto(box_u) \ - & u3C.balign_d-1) == 0); \ - c3_dessert((((u3a_box*)(box_u))->siz_w \ - & u3C.walign_w-1) == 0); \ +#define _box_vaal(box_u) \ + do { \ + c3_dessert(((uintptr_t)u3a_boxto(box_u) \ + & u3a_balign-1) == 0); \ + c3_dessert((((u3a_box*)(box_u))->siz_w \ + & u3a_walign-1) == 0); \ } while(0) /* _box_slot(): select the right free list to search for a block. - TODO: do we really need a loop to do this? - - so our free list logic looks like this: - siz_w < 6 words then [0] - siz_w < 16 then [1] - siz_w < 32 then [2] - siz_w < 64 then [3] - ... - siz_w > 4G then [26] +** +** siz_w == 6 words then [0] +** siz_w < 16 then [1] +** siz_w < 32 then [2] +** siz_w < 64 then [3] +** ... +** siz_w >= 2GB then [26] */ static c3_w _box_slot(c3_w siz_w) { - if ( siz_w < u3a_minimum ) { + if ( u3a_minimum == siz_w ) { return 0; } - - for (c3_w i_w = 1; i_w < u3a_fbox_no; i_w++) { - if ( siz_w < 16 ) return i_w; - siz_w = (siz_w + 1) >> 1; + else if ( !(siz_w >> 4) ) { + c3_dessert( u3a_minimum < siz_w ); + return 1; + } + else { + c3_w bit_w = c3_bits_word(siz_w) - 3; + c3_w max_w = u3a_fbox_no - 1; + return c3_min(bit_w, max_w); } - return u3a_fbox_no - 1; } /* _box_make(): construct a box. @@ -302,7 +294,7 @@ _ca_box_make_hat(c3_w len_w, c3_w ald_w, c3_w off_w, c3_w use_w) all_p += c3_wiseof(u3a_box) + off_w; pad_w = c3_align(all_p, ald_w, C3_ALGHI) - all_p; - siz_w = c3_align(len_w + pad_w, u3C.walign_w, C3_ALGHI); + siz_w = c3_align(len_w + pad_w, u3a_walign, C3_ALGHI); // hand-inlined: siz_w >= u3a_open(u3R) // @@ -316,7 +308,7 @@ _ca_box_make_hat(c3_w len_w, c3_w ald_w, c3_w off_w, c3_w use_w) all_p += c3_wiseof(u3a_box) + off_w; pad_w = all_p - c3_align(all_p, ald_w, C3_ALGLO); - siz_w = c3_align(len_w + pad_w, u3C.walign_w, C3_ALGHI); + siz_w = c3_align(len_w + pad_w, u3a_walign, C3_ALGHI); // hand-inlined: siz_w >= u3a_open(u3R) // @@ -508,7 +500,7 @@ _ca_willoc(c3_w len_w, c3_w ald_w, c3_w off_w) box_p = all_p = *pfr_p; all_p += c3_wiseof(u3a_box) + off_w; c3_w pad_w = c3_align(all_p, ald_w, C3_ALGHI) - all_p; - c3_w des_w = c3_align(siz_w + pad_w, u3C.walign_w, C3_ALGHI); + c3_w des_w = c3_align(siz_w + pad_w, u3a_walign, C3_ALGHI); /* calls maximally requesting DWORD alignment of returned pointer shouldn't require padding. */ @@ -711,17 +703,17 @@ u3a_wtrim(void* tox_v, c3_w old_w, c3_w len_w) c3_w* box_w = (void*)u3a_botox(nov_w); c3_w* end_w = c3_align(nov_w + len_w + 1, /* +1 for trailing allocation size */ - u3C.balign_d, + u3a_balign, C3_ALGHI); c3_w asz_w = (end_w - box_w); /* total size in words of new allocation */ if (box_u->siz_w <= asz_w) return; c3_w bsz_w = box_u->siz_w - asz_w; /* size diff in words between old and new */ - c3_dessert(asz_w && ((asz_w & u3C.walign_w-1) == 0)); /* new allocation size must be non-zero and DWORD multiple */ + c3_dessert(asz_w && ((asz_w & u3a_walign-1) == 0)); /* new allocation size must be non-zero and DWORD multiple */ c3_dessert(end_w < (box_w + box_u->siz_w)); /* desired alloc end must not exceed existing boundaries */ - c3_dessert(((uintptr_t)end_w & u3C.balign_d-1) == 0); /* address of box getting freed must be DWORD aligned */ - c3_dessert((bsz_w & u3C.walign_w-1) == 0); /* size of box getting freed must be DWORD multiple */ + c3_dessert(((uintptr_t)end_w & u3a_balign-1) == 0); /* address of box getting freed must be DWORD aligned */ + c3_dessert((bsz_w & u3a_walign-1) == 0); /* size of box getting freed must be DWORD multiple */ _box_attach(_box_make(end_w, bsz_w, 0)); /* free the unneeded space */ @@ -1122,9 +1114,7 @@ _ca_take_atom(u3a_atom* old_u) static inline u3_cell _ca_take_cell(u3a_cell* old_u, u3_noun hed, u3_noun tel) { - // XX use u3a_celloc? - // - c3_w* new_w = u3a_walloc(c3_wiseof(u3a_cell)); + c3_w* new_w = u3a_celloc(); u3a_cell* new_u = (u3a_cell*)(void *)new_w; u3_cell new = u3a_to_pom(u3a_outa(new_u)); @@ -1747,24 +1737,12 @@ u3a_rewritten_noun(u3_noun som) return som; } u3_post som_p = u3a_rewritten(u3a_to_off(som)); - - /* If this is being called during a migration, one-bit pointer compression - needs to be temporarily enabled so the rewritten reference is compressed */ - if (u3C.migration_state == MIG_REWRITE_COMPRESSED) - u3C.vits_w = 1; - if ( c3y == u3a_is_pug(som) ) { - som_p = u3a_to_pug(som_p); + return u3a_to_pug(som_p); } else { - som_p = u3a_to_pom(som_p); + return u3a_to_pom(som_p); } - - /* likewise, pointer compression is disabled until migration is complete */ - if (u3C.migration_state == MIG_REWRITE_COMPRESSED) - u3C.vits_w = 0; - - return som_p; } /* u3a_mark_mptr(): mark a malloc-allocated ptr for gc. @@ -2152,7 +2130,8 @@ u3a_mark_road(FILE* fil_u) tot_w += u3a_maid(fil_u, " profile batteries", u3a_mark_noun(u3R->pro.don)); tot_w += u3a_maid(fil_u, " profile doss", u3a_mark_noun(u3R->pro.day)); tot_w += u3a_maid(fil_u, " new profile trace", u3a_mark_noun(u3R->pro.trace)); - tot_w += u3a_maid(fil_u, " memoization cache", u3h_mark(u3R->cax.har_p)); + tot_w += u3a_maid(fil_u, " transient memoization cache", u3h_mark(u3R->cax.har_p)); + tot_w += u3a_maid(fil_u, " persistent memoization cache", u3h_mark(u3R->cax.per_p)); return u3a_maid(fil_u, "total road stuff", tot_w); } @@ -2179,6 +2158,7 @@ u3a_rewrite_compact(void) u3a_rewrite_noun(u3R->pro.day); u3a_rewrite_noun(u3R->pro.trace); u3h_rewrite(u3R->cax.har_p); + u3h_rewrite(u3R->cax.per_p); u3R->ski.gul = u3a_rewritten_noun(u3R->ski.gul); u3R->bug.tax = u3a_rewritten_noun(u3R->bug.tax); @@ -2187,6 +2167,7 @@ u3a_rewrite_compact(void) u3R->pro.day = u3a_rewritten_noun(u3R->pro.day); 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); } /* _ca_print_box(): heuristically print the contents of an allocation box. @@ -2317,6 +2298,69 @@ u3a_idle(u3a_road* rod_u) return fre_w; } +/* u3a_ream(): ream free-lists. +*/ +void +u3a_ream(void) +{ + u3p(u3a_fbox) lit_p; + u3a_fbox* fox_u; + c3_w sel_w, i_w; + + for ( i_w = 0; i_w < u3a_fbox_no; i_w++ ) { + lit_p = u3R->all.fre_p[i_w]; + + while ( lit_p ) { + fox_u = u3to(u3a_fbox, lit_p); + lit_p = fox_u->nex_p; + sel_w = _box_slot(fox_u->box_u.siz_w); + + if ( sel_w != i_w ) { + // inlined _box_detach() + // + { + u3p(u3a_fbox) fre_p = u3of(u3a_fbox, &(fox_u->box_u)); + u3p(u3a_fbox) pre_p = u3to(u3a_fbox, fre_p)->pre_p; + u3p(u3a_fbox) nex_p = u3to(u3a_fbox, fre_p)->nex_p; + + if ( nex_p ) { + if ( u3to(u3a_fbox, nex_p)->pre_p != fre_p ) { + u3_assert(!"loom: corrupt"); + } + u3to(u3a_fbox, nex_p)->pre_p = pre_p; + } + if ( pre_p ) { + if( u3to(u3a_fbox, pre_p)->nex_p != fre_p ) { + u3_assert(!"loom: corrupt"); + } + u3to(u3a_fbox, pre_p)->nex_p = nex_p; + } + else { + if ( fre_p != u3R->all.fre_p[i_w] ) { + u3_assert(!"loom: corrupt"); + } + u3R->all.fre_p[i_w] = nex_p; + } + } + + // inlined _box_attach() + { + u3p(u3a_fbox) fre_p = u3of(u3a_fbox, &(fox_u->box_u)); + u3p(u3a_fbox)* pfr_p = &u3R->all.fre_p[sel_w]; + u3p(u3a_fbox) nex_p = *pfr_p; + + u3to(u3a_fbox, fre_p)->pre_p = 0; + u3to(u3a_fbox, fre_p)->nex_p = nex_p; + if ( nex_p ) { + u3to(u3a_fbox, nex_p)->pre_p = fre_p; + } + (*pfr_p) = fre_p; + } + } + } + } +} + /* u3a_sweep(): sweep a fully marked road. */ c3_w diff --git a/pkg/noun/allocate.h b/pkg/noun/allocate.h index 7355954abd..b851b64c98 100644 --- a/pkg/noun/allocate.h +++ b/pkg/noun/allocate.h @@ -3,7 +3,6 @@ #include "error.h" #include "manage.h" -#include "options.h" /** Constants. **/ @@ -11,14 +10,21 @@ */ # define u3a_bits U3_OS_LoomBits /* 30 */ - /* u3a_vits_max: number of virtual bits in a reference gained via pointer - compression + /* u3a_vits: number of virtual bits in a noun reference gained via shifting */ -# define u3a_vits_max 1 +# define u3a_vits 1 + + /* u3a_walign: references into the loom are guaranteed to be word-aligned to: + */ +# define u3a_walign (1 << u3a_vits) + + /* u3a_balign: u3a_walign in bytes + */ +# define u3a_balign (sizeof(c3_w)*u3a_walign) /* u3a_bits_max: max loom bex */ -# define u3a_bits_max (8 * sizeof(c3_w) + u3a_vits_max) +# define u3a_bits_max (8 * sizeof(c3_w) + u3a_vits) /* u3a_page: number of bits in word-addressed page. 12 == 16K page */ @@ -26,11 +32,11 @@ /* u3a_pages: maximum number of pages in memory. */ -# define u3a_pages (1ULL << (u3a_bits + u3a_vits_max - u3a_page) ) +# define u3a_pages (1ULL << (u3a_bits + u3a_vits - u3a_page) ) /* u3a_words: maximum number of words in memory. */ -# define u3a_words ( 1ULL << (u3a_bits + u3a_vits_max )) +# define u3a_words ( 1ULL << (u3a_bits + u3a_vits)) /* u3a_bytes: maximum number of bytes in memory. */ @@ -148,11 +154,11 @@ u3a_jets jed; // jet dashboard - struct { // bytecode state - u3p(u3h_root) har_p; // formula->post of bytecode + struct { // bytecode state + u3p(u3h_root) har_p; // formula->post of bytecode } byc; - struct { // namespace + struct { // scry namespace u3_noun gul; // (list $+(* (unit (unit)))) now } ski; @@ -169,8 +175,9 @@ u3_noun day; // doss, only in u3H (moveme) } pro; - struct { // memoization - u3p(u3h_root) har_p; // (map (pair term noun) noun) + struct { // memoization caches + u3p(u3h_root) har_p; // transient + u3p(u3h_root) per_p; // persistent } cax; } u3a_road; typedef u3a_road u3_road; @@ -226,7 +233,7 @@ /* u3a_is_atom(): yes if noun [som] is direct atom or indirect atom. */ # define u3a_is_atom(som) c3o(u3a_is_cat(som), \ - u3a_is_pug(som)) + u3a_is_pug(som)) # define u3ud(som) u3a_is_atom(som) /* u3a_is_cell: yes if noun [som] is cell. @@ -355,7 +362,7 @@ # define _rod_vaal(rod_u) \ do { \ c3_dessert(((uintptr_t)((u3a_road*)(rod_u))->hat_p \ - & u3C.walign_w-1) == 0); \ + & u3a_walign-1) == 0); \ } while(0) @@ -374,68 +381,40 @@ # define u3_Loom ((c3_w *)(void *)U3_OS_LoomBase) - /** inline functions. - **/ - /* u3a_config_loom(): configure loom information by u3v version - */ - inline void u3a_config_loom(c3_w ver_w) { - switch (ver_w) { - case U3V_VER1: - u3C.vits_w = 0; - break; - case U3V_VER2: - u3C.vits_w = 1; - break; - default: - u3_assert(0); - } - - u3C.walign_w = 1 << u3C.vits_w; - u3C.balign_d = sizeof(c3_w) * u3C.walign_w; -} - /* u3a_into(): convert loom offset [x] into generic pointer. */ - inline void *u3a_into(c3_w x) { - return u3_Loom + x; - } +# define u3a_into(x) ((void *)(u3_Loom + (x))) /* u3a_outa(): convert pointer [p] into word offset into loom. */ - inline c3_w u3a_outa(void *p) { - return ((c3_w *)p) - u3_Loom; - } +# define u3a_outa(p) ((c3_w *)(void *)(p) - u3_Loom) /* u3a_to_off(): mask off bits 30 and 31 from noun [som]. */ - inline c3_w u3a_to_off(c3_w som) { - return (som & 0x3fffffff) << u3C.vits_w; - } +# define u3a_to_off(som) (((som) & 0x3fffffff) << u3a_vits) /* u3a_to_ptr(): convert noun [som] into generic pointer into loom. */ - inline void *u3a_to_ptr(c3_w som) { - return u3a_into(u3a_to_off(som)); - } +# define u3a_to_ptr(som) (u3a_into(u3a_to_off(som))) /* u3a_to_wtr(): convert noun [som] into word pointer into loom. */ - inline c3_w *u3a_to_wtr(c3_w som) { - return (c3_w *)u3a_to_ptr(som); - } +# define u3a_to_wtr(som) ((c3_w *)u3a_to_ptr(som)) + /** Inline functions. + **/ /* u3a_to_pug(): set bit 31 of [off]. */ inline c3_w u3a_to_pug(c3_w off) { - c3_dessert((off & u3C.walign_w-1) == 0); - return (off >> u3C.vits_w) | 0x80000000; + c3_dessert((off & u3a_walign-1) == 0); + return (off >> u3a_vits) | 0x80000000; } /* u3a_to_pom(): set bits 30 and 31 of [off]. */ inline c3_w u3a_to_pom(c3_w off) { - c3_dessert((off & u3C.walign_w-1) == 0); - return (off >> u3C.vits_w) | 0xc0000000; + c3_dessert((off & u3a_walign-1) == 0); + return (off >> u3a_vits) | 0xc0000000; } /** road stack. @@ -683,6 +662,11 @@ c3_w u3a_idle(u3a_road* rod_u); + /* u3a_ream(): ream free-lists. + */ + void + u3a_ream(void); + /* u3a_sweep(): sweep a fully marked road. */ c3_w diff --git a/pkg/noun/events.c b/pkg/noun/events.c index dc1ba965d0..d4f6c92ed0 100644 --- a/pkg/noun/events.c +++ b/pkg/noun/events.c @@ -398,23 +398,14 @@ _ce_ephemeral_open(c3_i* eph_i) /* _ce_image_open(): open or create image. */ static c3_o -_ce_image_open(u3e_image* img_u) +_ce_image_open(u3e_image* img_u, c3_c* ful_c) { c3_i mod_i = O_RDWR | O_CREAT; - c3_c ful_c[8193]; - - snprintf(ful_c, 8192, "%s", u3P.dir_c); - c3_mkdir(ful_c, 0700); - - snprintf(ful_c, 8192, "%s/.urb", u3P.dir_c); - c3_mkdir(ful_c, 0700); - - snprintf(ful_c, 8192, "%s/.urb/chk", u3P.dir_c); - c3_mkdir(ful_c, 0700); - snprintf(ful_c, 8192, "%s/.urb/chk/%s.bin", u3P.dir_c, img_u->nam_c); - if ( -1 == (img_u->fid_i = c3_open(ful_c, mod_i, 0666)) ) { - fprintf(stderr, "loom: c3_open %s: %s\r\n", ful_c, strerror(errno)); + c3_c pax_c[8192]; + snprintf(pax_c, 8192, "%s/%s.bin", ful_c, img_u->nam_c); + if ( -1 == (img_u->fid_i = c3_open(pax_c, mod_i, 0666)) ) { + fprintf(stderr, "loom: c3_open %s: %s\r\n", pax_c, strerror(errno)); return c3n; } else if ( c3n == _ce_image_stat(img_u, &img_u->pgs_w) ) { @@ -787,14 +778,16 @@ _ce_patch_sync(u3_ce_patch* pat_u) /* _ce_image_sync(): make sure image is synced to disk. */ -static void +static c3_o _ce_image_sync(u3e_image* img_u) { if ( -1 == c3_sync(img_u->fid_i) ) { fprintf(stderr, "loom: image (%s) sync failed: %s\r\n", img_u->nam_c, strerror(errno)); - u3_assert(!"loom: image sync"); + return c3n; } + + return c3y; } /* _ce_image_resize(): resize image, truncating if it shrunk. @@ -1345,54 +1338,77 @@ _ce_image_copy(u3e_image* fom_u, u3e_image* tou_u) return c3y; } -/* u3e_backup(): copy snapshot to .urb/bhk (if it doesn't exist yet). +/* u3e_backup(): copy snapshot from [pux_c] to [pax_c], + * overwriting optionally. note that image files must + * be named "north" and "south". */ c3_o -u3e_backup(c3_o ovw_o) +u3e_backup(c3_c* pux_c, c3_c* pax_c, c3_o ovw_o) { - u3e_image nop_u = { .nam_c = "north", .pgs_w = 0 }; - u3e_image sop_u = { .nam_c = "south", .pgs_w = 0 }; - c3_i mod_i = O_RDWR | O_CREAT; // XX O_TRUNC ? - c3_c ful_c[8193]; + // source image files from [pux_c] + u3e_image nux_u = { .nam_c = "north", .pgs_w = 0 }; + u3e_image sux_u = { .nam_c = "south", .pgs_w = 0 }; + + // destination image files to [pax_c] + u3e_image nax_u = { .nam_c = "north", .pgs_w = 0 }; + u3e_image sax_u = { .nam_c = "south", .pgs_w = 0 }; - snprintf(ful_c, 8192, "%s/.urb/bhk", u3P.dir_c); + c3_i mod_i = O_RDWR | O_CREAT; + + if ( !pux_c || !pax_c ) { + fprintf(stderr, "loom: image backup: bad path\r\n"); + return c3n; + } - if ( (c3n == ovw_o) && c3_mkdir(ful_c, 0700) ) { + if ( (c3n == ovw_o) && c3_mkdir(pax_c, 0700) ) { if ( EEXIST != errno ) { fprintf(stderr, "loom: image backup: %s\r\n", strerror(errno)); } return c3n; } - snprintf(ful_c, 8192, "%s/.urb/bhk/%s.bin", u3P.dir_c, nop_u.nam_c); - - if ( -1 == (nop_u.fid_i = c3_open(ful_c, mod_i, 0666)) ) { - fprintf(stderr, "loom: c3_open %s: %s\r\n", ful_c, strerror(errno)); + // open source image files if they exist + // + c3_c nux_c[8193]; + snprintf(nux_c, 8192, "%s/%s.bin", pux_c, nux_u.nam_c); + if ( (0 != access(nux_c, F_OK)) || (c3n == _ce_image_open(&nux_u, pux_c)) ) { + fprintf(stderr, "loom: couldn't open north image at %s\r\n", pux_c); + return c3n; + } + c3_c sux_c[8193]; + snprintf(sux_c, 8192, "%s/%s.bin", pux_c, sux_u.nam_c); + if ( (0 != access(sux_c, F_OK)) || (c3n == _ce_image_open(&sux_u, pux_c)) ) { + fprintf(stderr, "loom: couldn't open south image at %s\r\n", pux_c); return c3n; } - snprintf(ful_c, 8192, "%s/.urb/bhk/%s.bin", u3P.dir_c, sop_u.nam_c); - - if ( -1 == (sop_u.fid_i = c3_open(ful_c, mod_i, 0666)) ) { - fprintf(stderr, "loom: c3_open %s: %s\r\n", ful_c, strerror(errno)); + // open destination image files + c3_c nax_c[8193]; + snprintf(nax_c, 8192, "%s/%s.bin", pax_c, nax_u.nam_c); + if ( -1 == (nax_u.fid_i = c3_open(nax_c, mod_i, 0666)) ) { + fprintf(stderr, "loom: c3_open %s: %s\r\n", nax_c, strerror(errno)); + return c3n; + } + c3_c sax_c[8193]; + snprintf(sax_c, 8192, "%s/%s.bin", pax_c, sax_u.nam_c); + if ( -1 == (sax_u.fid_i = c3_open(sax_c, mod_i, 0666)) ) { + fprintf(stderr, "loom: c3_open %s: %s\r\n", sax_c, strerror(errno)); return c3n; } - if ( (c3n == _ce_image_copy(&u3P.nor_u, &nop_u)) - || (c3n == _ce_image_copy(&u3P.sou_u, &sop_u)) ) + if ( (c3n == _ce_image_copy(&nux_u, &nax_u)) + || (c3n == _ce_image_copy(&sux_u, &sax_u)) + || (c3n == _ce_image_sync(&nax_u)) + || (c3n == _ce_image_sync(&sax_u)) ) { - - c3_unlink(ful_c); - snprintf(ful_c, 8192, "%s/.urb/bhk/%s.bin", u3P.dir_c, nop_u.nam_c); - c3_unlink(ful_c); - snprintf(ful_c, 8192, "%s/.urb/bhk", u3P.dir_c); - c3_rmdir(ful_c); + c3_unlink(nax_c); + c3_unlink(sax_c); fprintf(stderr, "loom: image backup failed\r\n"); return c3n; } - close(nop_u.fid_i); - close(sop_u.fid_i); + close(nax_u.fid_i); + close(sax_u.fid_i); fprintf(stderr, "loom: image backup complete\r\n"); return c3y; } @@ -1462,8 +1478,9 @@ u3e_save(u3_post low_p, u3_post hig_p) _ce_patch_apply(pat_u); - _ce_image_sync(&u3P.nor_u); - _ce_image_sync(&u3P.sou_u); + u3_assert( c3y == _ce_image_sync(&u3P.nor_u) ); + u3_assert( c3y == _ce_image_sync(&u3P.sou_u) ); + _ce_patch_free(pat_u); _ce_patch_delete(); @@ -1502,8 +1519,6 @@ u3e_save(u3_post low_p, u3_post hig_p) } u3e_toss(low_p, hig_p); - - u3e_backup(c3n); } /* _ce_toss_pages(): discard ephemeral pages. @@ -1573,8 +1588,10 @@ u3e_live(c3_o nuu_o, c3_c* dir_c) // Open image files. // - if ( (c3n == _ce_image_open(&u3P.nor_u)) || - (c3n == _ce_image_open(&u3P.sou_u)) ) + c3_c chk_c[8193]; + snprintf(chk_c, 8193, "%s/.urb/chk", u3P.dir_c); + if ( (c3n == _ce_image_open(&u3P.nor_u, chk_c)) || + (c3n == _ce_image_open(&u3P.sou_u, chk_c)) ) { fprintf(stderr, "boot: image failed\r\n"); exit(1); @@ -1587,8 +1604,8 @@ u3e_live(c3_o nuu_o, c3_c* dir_c) */ if ( 0 != (pat_u = _ce_patch_open()) ) { _ce_patch_apply(pat_u); - _ce_image_sync(&u3P.nor_u); - _ce_image_sync(&u3P.sou_u); + u3_assert( c3y == _ce_image_sync(&u3P.nor_u) ); + u3_assert( c3y == _ce_image_sync(&u3P.sou_u) ); _ce_patch_free(pat_u); _ce_patch_delete(); } @@ -1659,6 +1676,9 @@ u3e_stop(void) close(u3P.eph_i); unlink(u3C.eph_c); } + + close(u3P.sou_u.fid_i); + close(u3P.sou_u.fid_i); } /* u3e_yolo(): disable dirty page tracking, read/write whole loom. @@ -1698,6 +1718,8 @@ u3e_init(void) { u3P.pag_w = u3C.wor_i >> u3a_page; + u3P.nor_u.fid_i = u3P.sou_u.fid_i = -1; + u3e_foul(); #ifdef U3_GUARD_PAGE diff --git a/pkg/noun/events.h b/pkg/noun/events.h index b17dcba65d..7442c9df94 100644 --- a/pkg/noun/events.h +++ b/pkg/noun/events.h @@ -74,10 +74,11 @@ /** Functions. **/ - /* u3e_backup(): copy the snapshot from chk to bhk. + /* u3e_backup(): copy the snapshot from [pux_c] to [pax_c], + * overwriting optional. */ - c3_o - u3e_backup(c3_o ovw_o); + c3_o + u3e_backup(c3_c* pux_c, c3_c* pax_c, c3_o ovw_o); /* u3e_fault(): handle a memory fault. */ diff --git a/pkg/noun/hashtable.c b/pkg/noun/hashtable.c index 5915de5944..a21352bf82 100644 --- a/pkg/noun/hashtable.c +++ b/pkg/noun/hashtable.c @@ -898,6 +898,31 @@ u3h_take(u3p(u3h_root) har_p) return u3h_take_with(har_p, u3a_take); } +/* _ch_take_uni_cb(): take a key/value pair, put into [dst_p]. +*/ +static void +_ch_take_uni_cb(u3_cell kev, void* wit) +{ + u3a_cell* kev_u = u3a_to_ptr(kev); + u3_noun key = u3a_take(kev_u->hed); + u3_noun val = u3a_take(kev_u->tel); + + { + u3p(u3h_root) dst_p = *(u3p(u3h_root)*)wit; + u3h_put(dst_p, key, val); + } + + u3z(key); +} + +/* u3h_take_uni(): take entries from [src_p], put into [dst_p]. +*/ +void +u3h_take_uni(u3p(u3h_root) dst_p, u3p(u3h_root) src_p) +{ + u3h_walk_with(src_p, _ch_take_uni_cb, &dst_p); +} + /* _ch_mark_buck(): mark bucket for gc. */ c3_w @@ -1017,15 +1042,8 @@ _ch_rewrite_node(u3h_node* han_u, c3_w lef_w) else { void* hav_v = u3h_slot_to_node(sot_w); u3h_node* nod_u = u3to(u3h_node,u3a_rewritten(u3of(u3h_node,hav_v))); - - if (u3C.migration_state == MIG_REWRITE_COMPRESSED) - u3C.vits_w = 1; - han_u->sot_w[i_w] = u3h_node_to_slot(nod_u); - if (u3C.migration_state == MIG_REWRITE_COMPRESSED) - u3C.vits_w = 0; - if ( 0 == lef_w ) { _ch_rewrite_buck(hav_v); } else { @@ -1057,15 +1075,8 @@ u3h_rewrite(u3p(u3h_root) har_p) else if ( _(u3h_slot_is_node(sot_w)) ) { u3h_node* han_u = u3h_slot_to_node(sot_w); u3h_node* nod_u = u3to(u3h_node,u3a_rewritten(u3of(u3h_node,han_u))); - - if (u3C.migration_state == MIG_REWRITE_COMPRESSED) - u3C.vits_w = 1; - har_u->sot_w[i_w] = u3h_node_to_slot(nod_u); - if (u3C.migration_state == MIG_REWRITE_COMPRESSED) - u3C.vits_w = 0; - _ch_rewrite_node(han_u, 25); } } diff --git a/pkg/noun/hashtable.h b/pkg/noun/hashtable.h index be1d3f93be..69ba962dd9 100644 --- a/pkg/noun/hashtable.h +++ b/pkg/noun/hashtable.h @@ -16,9 +16,8 @@ *** corresponding to the 32-slot nodes for everything under *** the root node. *** - *** We store an extra "freshly warm" bit for a simple - *** clock-algorithm reclamation policy, not yet implemented. - *** Search "clock algorithm" to figure it out. + *** We store an extra "freshly warm" bit and use it for a simple + *** clock-algorithm reclamation policy. **/ /* u3h_slot: map slot. ** @@ -79,8 +78,8 @@ # define u3h_slot_is_node(sot) ((1 == ((sot) >> 30)) ? c3y : c3n) # define u3h_slot_is_noun(sot) ((1 == ((sot) >> 31)) ? c3y : c3n) # define u3h_slot_is_warm(sot) (((sot) & 0x40000000) ? c3y : c3n) -# define u3h_slot_to_node(sot) (u3a_into(((sot) & 0x3fffffff) << u3C.vits_w)) -# define u3h_node_to_slot(ptr) ((u3a_outa((ptr)) >> u3C.vits_w) | 0x40000000) +# define u3h_slot_to_node(sot) (u3a_into(((sot) & 0x3fffffff) << u3a_vits)) +# define u3h_node_to_slot(ptr) ((u3a_outa((ptr)) >> u3a_vits) | 0x40000000) # define u3h_noun_be_warm(sot) ((sot) | 0x40000000) # define u3h_noun_be_cold(sot) ((sot) & ~0x40000000) # define u3h_slot_to_noun(sot) (0x40000000 | (sot)) @@ -180,6 +179,11 @@ u3p(u3h_root) u3h_take(u3p(u3h_root) har_p); + /* u3h_take_uni(): take entries from [src_p], put into [dst_p]. + */ + void + u3h_take_uni(u3p(u3h_root) dst_p, u3p(u3h_root) src_p); + /* u3h_wyt(): number of entries */ c3_w diff --git a/pkg/noun/imprison.c b/pkg/noun/imprison.c index 1ff8200790..cc945cb27a 100644 --- a/pkg/noun/imprison.c +++ b/pkg/noun/imprison.c @@ -149,6 +149,21 @@ u3i_slab_from(u3i_slab* sab_u, u3_atom a, c3_g met_g, c3_d len_d) // copies [a], zero-initializes any additional space // u3r_words(0, sab_u->len_w, sab_u->buf_w, a); + + // if necessary, mask off extra most-significant bits + // from most-significant word + // + if ( (5 > met_g) && (u3r_met(5, a) >= sab_u->len_w) ) { + // NB: overflow already checked in _ci_slab_size() + // + c3_d bit_d = len_d << met_g; + c3_w wor_w = bit_d >> 5; + c3_w bit_w = bit_d & 0x1f; + + if ( bit_w ) { + sab_u->buf_w[wor_w] &= ((c3_w)1 << bit_w) - 1; + } + } } /* u3i_slab_grow(): resize slab, zero-initializing new space. @@ -595,133 +610,6 @@ u3i_list(u3_weak som, ...) return u3kb_flop(lit); } -static u3_noun -_edit_cat(u3_noun big, c3_l axe_l, u3_noun som) -{ - if ( c3n == u3du(big) ) { - return u3m_bail(c3__exit); - } - else { - u3_noun pro; - switch ( axe_l ) { - case 2: - pro = u3nc(som, u3k(u3t(big))); - break; - case 3: - pro = u3nc(u3k(u3h(big)), som); - break; - default: { - c3_l mor_l = u3x_mas(axe_l); - pro = ( 2 == u3x_cap(axe_l) ) - ? u3nc(_edit_cat(u3k(u3h(big)), mor_l, som), u3k(u3t(big))) - : u3nc(u3k(u3h(big)), _edit_cat(u3k(u3t(big)), mor_l, som)); - break; - } - } - u3z(big); - return pro; - } -} - -static u3_noun -_edit(u3_noun big, u3_noun axe, u3_noun som) -{ - if ( c3y == u3a_is_cat(axe) ) { - return _edit_cat(big, (c3_l) axe, som); - } - else if ( c3n == u3du(big) ) { - return u3m_bail(c3__exit); - } - else { - u3_noun mor = u3qc_mas(axe), - pro = ( 2 == u3qc_cap(axe) ) - ? u3nc(_edit(u3k(u3h(big)), mor, som), u3k(u3t(big))) - : u3nc(u3k(u3h(big)), _edit(u3k(u3t(big)), mor, som)); - u3z(mor); - u3z(big); - return pro; - } -} - -static u3_noun _edit_or_mutate_cat(u3_noun, c3_l, u3_noun); -static u3_noun _edit_or_mutate(u3_noun, u3_noun, u3_noun); - -static void -_mutate_cat(u3_noun big, c3_l axe_l, u3_noun som) -{ - if ( c3n == u3du(big) ) { - u3m_bail(c3__exit); - } - else { - u3a_cell* cel_u = (void*) u3a_to_ptr(big); - switch ( axe_l ) { - case 2: - u3z(cel_u->hed); - cel_u->hed = som; - break; - case 3: - u3z(cel_u->tel); - cel_u->tel = som; - break; - default: { - u3_noun* tar = ( 2 == u3x_cap(axe_l) ) - ? &(cel_u->hed) - : &(cel_u->tel); - *tar = _edit_or_mutate_cat(*tar, u3x_mas(axe_l), som); - } - } - cel_u->mug_w = 0; - } -} - -static void -_mutate(u3_noun big, u3_noun axe, u3_noun som) -{ - if ( c3y == u3a_is_cat(axe) ) { - _mutate_cat(big, (c3_l) axe, som); - } - else if ( c3n == u3du(big) ) { - u3m_bail(c3__exit); - } - else { - u3a_cell* cel_u = (void*) u3a_to_ptr(big); - u3_noun mor = u3qc_mas(axe); - u3_noun* tar = ( 2 == u3qc_cap(axe) ) - ? &(cel_u->hed) - : &(cel_u->tel); - *tar = _edit_or_mutate(*tar, mor, som); - cel_u->mug_w = 0; - u3z(mor); - } -} - -static u3_noun -_edit_or_mutate_cat(u3_noun big, c3_l axe_l, u3_noun som) -{ - if ( c3y == u3a_is_mutable(u3R, big) ) { - _mutate_cat(big, axe_l, som); - return big; - } - else { - return _edit_cat(big, axe_l, som); - } -} - -static u3_noun -_edit_or_mutate(u3_noun big, u3_noun axe, u3_noun som) -{ - if ( c3y == u3a_is_cat(axe) ) { - return _edit_or_mutate_cat(big, (c3_l) axe, som); - } - else if ( c3y == u3a_is_mutable(u3R, big) ) { - _mutate(big, axe, som); - return big; - } - else { - return _edit(big, axe, som); - } -} - /* u3i_edit(): ** ** Mutate `big` at axis `axe` with new value `som`. @@ -730,15 +618,52 @@ _edit_or_mutate(u3_noun big, u3_noun axe, u3_noun som) u3_noun u3i_edit(u3_noun big, u3_noun axe, u3_noun som) { + u3_noun pro; + u3_noun* out = &pro; + switch ( axe ) { - case 0: - return u3m_bail(c3__exit); - case 1: - u3z(big); - return som; - default: - return _edit_or_mutate(big, axe, som); + case 0: return u3m_bail(c3__exit); + case 1: break; + + default: { + c3_w dep_w = u3r_met(0, u3x_atom(axe)) - 2; + const c3_w* axe_w = ( c3y == u3a_is_cat(axe) ) + ? &axe + : ((u3a_atom*)u3a_to_ptr(axe))->buf_w; + + do { + u3a_cell* big_u = u3a_to_ptr(big); + u3_noun* old = (u3_noun*)&(big_u->hed); + const c3_y bit_y = 1 & (axe_w[dep_w >> 5] >> (dep_w & 31)); + + if ( c3n == u3a_is_cell(big) ) { + return u3m_bail(c3__exit); + } + else if ( c3y == u3a_is_mutable(u3R, big) ) { + *out = big; + out = &(old[bit_y]); + big = *out; + big_u->mug_w = 0; + } + else { + u3_noun luz = big; + u3_noun* new[2]; + + *out = u3i_defcons(&new[0], &new[1]); + out = new[bit_y]; + big = u3k(old[bit_y]); + *(new[!bit_y]) = u3k(old[!bit_y]); + + u3z(luz); + } + } + while ( dep_w-- ); + } } + + u3z(big); + *out = som; + return pro; } /* u3i_molt(): diff --git a/pkg/noun/jets.c b/pkg/noun/jets.c index 00edefec40..d252a5bfb1 100644 --- a/pkg/noun/jets.c +++ b/pkg/noun/jets.c @@ -14,19 +14,10 @@ #include "retrieve.h" #include "serial.h" #include "trace.h" -#include "urcrypt/urcrypt.h" +#include "urcrypt.h" #include "vortex.h" #include "xtract.h" -/** Data structures. -**/ - -/* _cj_hank: cached hook information. - */ -typedef struct { - u3_weak hax; // axis of hooked inner core - u3j_site sit_u; // call-site data -} _cj_hank; /** Functions. **/ @@ -1069,7 +1060,7 @@ _cj_prog(u3_weak loc, u3_noun fol) /* cj_hank_find(): find cached hook information, keyed by arbitrary * prefix and term cords. RETAIN. */ -static _cj_hank* +static u3j_hank* _cj_hank_find(u3_noun pre, u3_noun tam) { u3_noun key = u3nc(u3k(pre), u3k(tam)); @@ -1077,10 +1068,10 @@ _cj_hank_find(u3_noun pre, u3_noun tam) if ( u3_none != got ) { u3z(key); - return u3to(_cj_hank, got); + return u3to(u3j_hank, got); } else { - _cj_hank* new_u = u3a_walloc(c3_wiseof(_cj_hank)); + u3j_hank* new_u = u3a_walloc(c3_wiseof(u3j_hank)); u3a_road* rod_u = u3R; while ( rod_u->par_p && u3_none == got ) { @@ -1092,7 +1083,7 @@ _cj_hank_find(u3_noun pre, u3_noun tam) new_u->hax = u3_none; } else { - _cj_hank* old_u = u3to(_cj_hank, got); + u3j_hank* old_u = u3to(u3j_hank, got); if ( u3_none != (new_u->hax = old_u->hax) ) { // it's unusual but safe to "take" here, because // u3a_take will no-op on senior nouns (just as u3k would) @@ -1101,7 +1092,7 @@ _cj_hank_find(u3_noun pre, u3_noun tam) } } - u3h_put(u3R->jed.han_p, key, u3of(_cj_hank, new_u)); + u3h_put(u3R->jed.han_p, key, u3of(u3j_hank, new_u)); u3z(key); return new_u; } @@ -1112,7 +1103,7 @@ _cj_hank_find(u3_noun pre, u3_noun tam) * core on return if valid. RETAIN. */ static c3_o -_cj_hank_fine(_cj_hank* han_u, u3_noun cor, u3_noun *inn) +_cj_hank_fine(u3j_hank* han_u, u3_noun cor, u3_noun *inn) { u3_noun hax = han_u->hax; if ( u3_none == hax ) { @@ -1134,7 +1125,7 @@ _cj_hank_fine(_cj_hank* han_u, u3_noun cor, u3_noun *inn) /* _cj_hank_lose(): release memory maintained in a hook cache. */ static void -_cj_hank_lose(_cj_hank* han_u) +_cj_hank_lose(u3j_hank* han_u) { if ( u3_none != han_u->hax ) { u3z(han_u->hax); @@ -1145,7 +1136,7 @@ _cj_hank_lose(_cj_hank* han_u) /* _cj_hank_fill(): slow path, populate han_u. */ static u3_noun -_cj_hank_fill(_cj_hank* han_u, u3_noun tam, u3_noun cor) +_cj_hank_fill(u3j_hank* han_u, u3_noun tam, u3_noun cor) { u3_weak loc, col; u3_noun got, pat, nam, huc; @@ -1642,7 +1633,7 @@ u3j_cook(const c3_c* key_c, const c3_c* tam_c) { u3_noun pro, key, tam, inn; - _cj_hank* han_u; + u3j_hank* han_u; u3t_on(glu_o); key = u3i_string(key_c); @@ -1990,94 +1981,65 @@ u3j_rite_mine(u3j_rite* rit_u, u3_noun clu, u3_noun cor) u3t_off(glu_o); } -/* _cj_take_hank_cb(): u3h_take_with cb for taking hanks +/* _cj_reap_hank(): promote call site. */ -static u3p(_cj_hank) -_cj_take_hank_cb(u3p(_cj_hank) nah_p) +static void +_cj_reap_hank(u3_cell kev) { - _cj_hank* nah_u = u3to(_cj_hank, nah_p); - _cj_hank* han_u = u3a_walloc(c3_wiseof(_cj_hank)); + u3a_cell* kev_u = u3a_to_ptr(kev); + u3j_hank* nah_u = u3to(u3j_hank, kev_u->tel); + u3j_hank* han_u; + u3_weak got; + u3_noun key; if ( u3_none == nah_u->hax ) { - han_u->hax = u3_none; - // han_u->sit_u left uninitialized, will be ignored - } - else { - han_u->hax = u3a_take(nah_u->hax); - u3j_site_take(&(han_u->sit_u), &(nah_u->sit_u)); + return; } - return u3of(_cj_hank, han_u); -} - -/* u3j_take(): copy junior jet state. -*/ -u3a_jets -u3j_take(u3a_jets jed_u) -{ - jed_u.war_p = u3h_take(jed_u.war_p); - jed_u.cod_p = u3h_take(jed_u.cod_p); - jed_u.han_p = u3h_take_with(jed_u.han_p, _cj_take_hank_cb); - jed_u.bas_p = u3h_take(jed_u.bas_p); - return jed_u; -} - -/* _cj_merge_hank_cb(): u3h_uni_with cb for integrating taken hanks -** NB "transfers" or frees hanks in jed_u.han_p -*/ -static void -_cj_merge_hank_cb(u3_noun kev, void* wit) -{ - u3p(u3h_root) han_p = *(u3p(u3h_root)*)wit; - _cj_hank* nah_u; - u3_noun key; - u3p(_cj_hank) nah_p; - u3x_cell(kev, &key, &nah_p); - - nah_u = u3to(_cj_hank, nah_p); + // you have to keep what you take + // + key = u3a_take(kev_u->hed); + got = u3h_git(u3R->jed.han_p, key); - if ( u3_none == nah_u->hax ) { - u3a_wfree(nah_u); + // promote + // + if ( u3_none == got ) { + han_u = u3a_walloc(c3_wiseof(u3j_hank)); + han_u->hax = u3a_take(nah_u->hax); + u3j_site_take(&(han_u->sit_u), &(nah_u->sit_u)); } + // integrate + // else { - _cj_hank* han_u; - u3_weak got = u3h_git(u3R->jed.han_p, key); + u3_weak old; - if ( u3_none == got ) { - han_u = nah_u; - } - else { - han_u = u3to(_cj_hank, got); - - if ( u3_none != han_u->hax ) { - u3z(han_u->hax); - } - han_u->hax = nah_u->hax; + han_u = u3to(u3j_hank, got); + old = han_u->hax; + han_u->hax = u3a_take(nah_u->hax); + u3j_site_take(&(nah_u->sit_u), &(nah_u->sit_u)); + u3j_site_merge(&(han_u->sit_u), &(nah_u->sit_u)); - u3j_site_merge(&(han_u->sit_u), &(nah_u->sit_u)); - u3a_wfree(nah_u); + if ( u3_none != old ) { + u3z(old); } - - u3h_put(han_p, key, u3of(_cj_hank, han_u)); } + + u3h_put(u3R->jed.han_p, key, u3of(u3j_hank, han_u)); + u3z(key); } /* u3j_reap(): promote jet state. */ void -u3j_reap(u3a_jets jed_u) +u3j_reap(u3a_jets* jed_u) { - u3h_uni(u3R->jed.war_p, jed_u.war_p); - u3h_free(jed_u.war_p); - - u3h_uni(u3R->jed.cod_p, jed_u.cod_p); - u3h_free(jed_u.cod_p); - - u3h_walk_with(jed_u.han_p, _cj_merge_hank_cb, &u3R->jed.han_p); - u3h_free(jed_u.han_p); - - u3h_uni(u3R->jed.bas_p, jed_u.bas_p); - u3h_free(jed_u.bas_p); + u3h_take_uni(u3R->jed.cod_p, jed_u->cod_p); + // call sites must be reaped before the warm dashboard; + // they may contain references to labels on this road + // + u3h_walk(jed_u->han_p, _cj_reap_hank); + u3h_take_uni(u3R->jed.war_p, jed_u->war_p); + u3h_take_uni(u3R->jed.bas_p, jed_u->bas_p); } /* _cj_ream(): ream list of battery [bash registry] pairs. RETAIN. @@ -2170,7 +2132,7 @@ _cj_warm_tap(u3_noun kev, void* wit) static void _cj_ream_hank(u3_noun kev) { - u3j_site_ream(&(u3to(_cj_hank, u3t(kev))->sit_u)); + u3j_site_ream(&(u3to(u3j_hank, u3t(kev))->sit_u)); } /* u3j_ream(): rebuild warm state @@ -2304,7 +2266,7 @@ static void _cj_mark_hank(u3_noun kev, void* dat) { c3_w* tot_w = (c3_w*) dat; - _cj_hank* han_u = u3to(_cj_hank, u3t(kev)); + u3j_hank* han_u = u3to(u3j_hank, u3t(kev)); *tot_w += u3a_mark_ptr(han_u); if ( u3_none != han_u->hax ) { *tot_w += u3a_mark_noun(han_u->hax); @@ -2337,12 +2299,12 @@ u3j_mark(FILE* fil_u) return u3a_maid(fil_u, "total jet stuff", tot_w); } -/* _cj_free_hank(): free an entry from the hank cache. +/* u3j_free_hank(): free an entry from the hank cache. */ -static void -_cj_free_hank(u3_noun kev) +void +u3j_free_hank(u3_noun kev) { - _cj_hank* han_u = u3to(_cj_hank, u3t(kev)); + u3j_hank* han_u = u3to(u3j_hank, u3t(kev)); if ( u3_none != han_u->hax ) { u3z(han_u->hax); u3j_site_lose(&(han_u->sit_u)); @@ -2355,7 +2317,7 @@ _cj_free_hank(u3_noun kev) void u3j_free(void) { - u3h_walk(u3R->jed.han_p, _cj_free_hank); + u3h_walk(u3R->jed.han_p, u3j_free_hank); u3h_free(u3R->jed.war_p); u3h_free(u3R->jed.cod_p); u3h_free(u3R->jed.han_p); @@ -2377,10 +2339,9 @@ u3j_reclaim(void) // if ( &(u3H->rod_u) == u3R ) { // u3j_ream(); // } - // clear the jet hank cache // - u3h_walk(u3R->jed.han_p, _cj_free_hank); + u3h_walk(u3R->jed.han_p, u3j_free_hank); u3h_free(u3R->jed.han_p); u3R->jed.han_p = u3h_new(); } diff --git a/pkg/noun/jets.h b/pkg/noun/jets.h index 9c5241c5db..9723a0d5f7 100644 --- a/pkg/noun/jets.h +++ b/pkg/noun/jets.h @@ -126,6 +126,13 @@ u3p(u3j_fink) fin_p; // fine check } u3j_site; + /* u3j_hank: cached hook information. + */ + typedef struct { + u3_weak hax; // axis of hooked inner core + u3j_site sit_u; // call-site data + } u3j_hank; + /** Globals. **/ /* u3_Dash: jet dashboard. @@ -208,12 +215,7 @@ /* u3j_reap(): promote jet state. */ void - u3j_reap(u3a_jets jed_u); - - /* u3j_take(): copy junior jet state. - */ - u3a_jets - u3j_take(u3a_jets jed_u); + u3j_reap(u3a_jets* jed_u); /* u3j_rite_mine(): mine cor with clu, using u3j_rite for caching */ @@ -297,6 +299,11 @@ void u3j_free(void); + /* u3j_free_hank(): free an entry from the hank cache. + */ + void + u3j_free_hank(u3_noun kev); + /* u3j_reclaim(): clear ad-hoc persistent caches to reclaim memory. */ void diff --git a/pkg/noun/jets/b/mate.c b/pkg/noun/jets/b/mate.c new file mode 100644 index 0000000000..f8dbd31654 --- /dev/null +++ b/pkg/noun/jets/b/mate.c @@ -0,0 +1,30 @@ +/// @file + +#include "jets/q.h" +#include "jets/w.h" + +#include "noun.h" + + + u3_noun + u3qb_mate(u3_noun a, + u3_noun b) + { + if ( u3_nul == b ) { + return u3k(a); + } else if ( u3_nul == a ) { + return u3k(b); + } else if ( c3y == u3r_sing(u3t(a), u3t(b)) ) { + return u3k(a); + } else { + return u3m_error("mate"); + } + } + u3_noun + u3wb_mate(u3_noun cor) + { + u3_noun a, b; + u3x_mean(cor, u3x_sam_2, &a, u3x_sam_3, &b, 0); + return u3qb_mate(a, b); + } + diff --git a/pkg/noun/jets/c/cap.c b/pkg/noun/jets/c/cap.c index c7edfbeb2e..95bef1fe95 100644 --- a/pkg/noun/jets/c/cap.c +++ b/pkg/noun/jets/c/cap.c @@ -5,32 +5,21 @@ #include "noun.h" +u3_noun +u3qc_cap(u3_atom a) +{ + c3_w met_w = u3r_met(0, a); - u3_noun - u3qc_cap(u3_atom a) - { - c3_w met_w = u3r_met(0, a); - - if ( met_w < 2 ) { - return u3m_bail(c3__exit); - } - else if ( (1 == u3r_bit((met_w - 2), a)) ) { - return 3; - } else { - return 2; - } + if ( 2 > met_w ) { + return u3m_bail(c3__exit); } - u3_noun - u3wc_cap(u3_noun cor) - { - u3_noun a; - - if ( (u3_none == (a = u3r_at(u3x_sam, cor))) || - (c3n == u3ud(a)) ) - { - return u3m_bail(c3__exit); - } else { - return u3qc_cap(a); - } + else { + return 2 + u3r_bit((met_w - 2), a); } +} +u3_noun +u3wc_cap(u3_noun cor) +{ + return u3qc_cap(u3x_atom(u3x_at(u3x_sam, cor))); +} diff --git a/pkg/noun/jets/c/dor.c b/pkg/noun/jets/c/dor.c index dc463f7ae6..0c1e220afc 100644 --- a/pkg/noun/jets/c/dor.c +++ b/pkg/noun/jets/c/dor.c @@ -7,8 +7,8 @@ u3_noun - u3qc_dor(u3_atom a, - u3_atom b) + u3qc_dor(u3_noun a, + u3_noun b) { if ( c3y == u3r_sing(a, b) ) { return c3y; diff --git a/pkg/noun/jets/c/mas.c b/pkg/noun/jets/c/mas.c index 7b2658d738..b0be6e1ae1 100644 --- a/pkg/noun/jets/c/mas.c +++ b/pkg/noun/jets/c/mas.c @@ -1,45 +1,48 @@ -/// @file - #include "jets/q.h" #include "jets/w.h" #include "noun.h" +u3_noun +u3qc_mas(u3_atom a) +{ + c3_w b_w; - u3_noun - u3qc_mas(u3_atom a) - { - c3_w b_w; - u3_atom c, d, e, f; + if ( c3y == u3a_is_cat(a) ) { + b_w = c3_bits_word(a); - b_w = u3r_met(0, a); - if ( b_w < 2 ) { + if ( 2 > b_w ) { return u3m_bail(c3__exit); } else { - c = u3qc_bex((b_w - 1)); - d = u3qc_bex((b_w - 2)); - e = u3qa_sub(a, c); - f = u3qc_con(e, d); - - u3z(c); - u3z(d); - u3z(e); - - return f; + a &= ~((c3_w)1 << (b_w - 1)); + a |= ((c3_w)1 << (b_w - 2)); + return a; } } - u3_noun - u3wc_mas(u3_noun cor) - { - u3_noun a; - - if ( (u3_none == (a = u3r_at(u3x_sam, cor))) || - (c3n == u3ud(a)) ) - { - return u3m_bail(c3__exit); - } else { - return u3qc_mas(a); + else { + b_w = u3r_met(0, a); + + if ( 64 > b_w ) { + c3_d a_d = u3r_chub(0, a); + a_d &= ~((c3_d)1 << (b_w - 1)); + a_d |= ((c3_d)1 << (b_w - 2)); + return u3i_chub(a_d); + } + else { + u3i_slab sab_u; + u3i_slab_from(&sab_u, a, 0, b_w - 1); + + b_w -= 2; + sab_u.buf_w[(b_w >> 5)] |= ((c3_w)1 << (b_w & 31)); + + return u3i_slab_mint(&sab_u); } } +} +u3_noun +u3wc_mas(u3_noun cor) +{ + return u3qc_mas(u3x_atom(u3x_at(u3x_sam, cor))); +} diff --git a/pkg/noun/jets/c/mor.c b/pkg/noun/jets/c/mor.c index 6967a8a1b6..97ba1410bd 100644 --- a/pkg/noun/jets/c/mor.c +++ b/pkg/noun/jets/c/mor.c @@ -7,8 +7,8 @@ u3_noun - u3qc_mor(u3_atom a, - u3_atom b) + u3qc_mor(u3_noun a, + u3_noun b) { c3_w c_w = u3r_mug(u3r_mug(a)); c3_w d_w = u3r_mug(u3r_mug(b)); diff --git a/pkg/noun/jets/c/peg.c b/pkg/noun/jets/c/peg.c index 28a8f3b2bd..a700752e28 100644 --- a/pkg/noun/jets/c/peg.c +++ b/pkg/noun/jets/c/peg.c @@ -1,50 +1,66 @@ -/// @file - #include "jets/q.h" #include "jets/w.h" #include "noun.h" +u3_noun +u3qc_peg(u3_atom a, u3_atom b) +{ + if ( (0 == a) || (0 == b) ) { + return u3m_bail(c3__exit); + } + else if ( 1 == b ) { + return u3k(a); + } - u3_noun - u3qc_peg(u3_atom a, - u3_atom b) - { - if ( 1 == b ) { - return u3k(a); - } + c3_d a_d, b_d; + c3_w c_w; - u3_atom c, d, e, f, g, h; + if ( (c3y == u3a_is_cat(a)) && (c3y == u3a_is_cat(b)) ) { + c_w = c3_bits_word(b) - 1; + a_d = a; + b_d = b; + } + else { + c3_w d_w = u3r_met(0, a); + c3_d e_d; - c = u3r_met(0, b); - d = u3qa_dec(c); - e = u3qc_lsh(0, d, 1); - f = u3qa_sub(b, e); - g = u3qc_lsh(0, d, a); - h = u3qa_add(f, g); + c_w = u3r_met(0, b) - 1; + e_d = (c3_d)c_w + d_w; - u3z(c); - u3z(d); - u3z(e); - u3z(f); - u3z(g); + if ( 64 <= e_d ) { + u3i_slab sab_u; + u3i_slab_init(&sab_u, 0, e_d); - return h; - } - u3_noun - u3wc_peg(u3_noun cor) - { - u3_noun a, b; - - if ( (c3n == u3r_mean(cor, u3x_sam_2, &a, u3x_sam_3, &b, 0)) || - (0 == a) || - (0 == b) || - (c3n == u3ud(b)) || - (c3n == u3ud(a) && b != 1) ) - { - return u3m_bail(c3__exit); - } else { - return u3qc_peg(a, b); + u3r_chop(0, 0, c_w, 0, sab_u.buf_w, b); + u3r_chop(0, 0, d_w, c_w, sab_u.buf_w, a); + + return u3i_slab_moot(&sab_u); } + + a_d = u3r_chub(0, a); + b_d = u3r_chub(0, b); } + b_d &= ((c3_d)1 << c_w) - 1; + a_d <<= c_w; + a_d ^= b_d; + + return u3i_chub(a_d); +} + +u3_noun +u3wc_peg(u3_noun cor) +{ + u3_noun a, b; + + if ( (c3n == u3r_mean(cor, u3x_sam_2, &a, u3x_sam_3, &b, 0)) || + (c3n == u3ud(b)) || + (c3n == u3ud(a) && b != 1) ) + { + return u3m_bail(c3__exit); + } + else { + return u3qc_peg(a, b); + } +} diff --git a/pkg/noun/jets/d/by_bif.c b/pkg/noun/jets/d/by_bif.c index d9e27d1756..0a2f23a666 100644 --- a/pkg/noun/jets/d/by_bif.c +++ b/pkg/noun/jets/d/by_bif.c @@ -17,19 +17,17 @@ _b_bif_putroot(u3_noun a, else { u3_noun n_a, l_a, r_a; u3_noun p_n_a, q_n_a; - u3_noun p_b, q_b; u3x_trel(a, &n_a, &l_a, &r_a); - u3x_cell(b, &p_b, &q_b); u3x_cell(n_a, &p_n_a, &q_n_a); - if ( c3y == u3r_sing(p_b, p_n_a) ) { + if ( c3y == u3r_sing(b, p_n_a) ) { return u3nt(u3k(b), u3k(l_a), u3k(r_a)); } else { u3_noun c, n_c, l_c, r_c; u3_noun d; - if ( c3y == u3qc_gor(p_b, p_n_a) ) { + if ( c3y == u3qc_gor(b, p_n_a) ) { c = _b_bif_putroot(l_a, b); u3r_trel(c, &n_c, &l_c, &r_c); d = u3nt(u3k(n_c), diff --git a/pkg/noun/jets/d/by_dif.c b/pkg/noun/jets/d/by_dif.c index a3f35f3cde..44dcd1ad1b 100644 --- a/pkg/noun/jets/d/by_dif.c +++ b/pkg/noun/jets/d/by_dif.c @@ -61,11 +61,12 @@ u3qdb_dif(u3_noun a, return u3k(a); } else { - u3_noun n_b, l_b, r_b; + u3_noun n_b, p_n_b, q_n_b, l_b, r_b; u3_noun c, l_c, r_c; u3x_trel(b, &n_b, &l_b, &r_b); + u3x_cell(n_b, &p_n_b, &q_n_b); - c = u3qdb_bif(a, n_b); + c = u3qdb_bif(a, p_n_b); u3x_cell(c, &l_c, &r_c); u3_noun d = u3qdb_dif(l_c, l_b); diff --git a/pkg/noun/jets/f/ap.c b/pkg/noun/jets/f/ap.c deleted file mode 100644 index c1c872c797..0000000000 --- a/pkg/noun/jets/f/ap.c +++ /dev/null @@ -1,1081 +0,0 @@ -/// @file - -#include "jets/q.h" -#include "jets/w.h" - -#include "noun.h" - - -/** forward declares -**/ - u3_noun u3wfp_rake(u3_noun); - u3_noun u3wfp_open(u3_noun); - u3_noun u3wfp_hack(u3_noun); - - static u3_noun - _ap_open_l(u3_noun, u3_noun, u3_noun); - - // make sure these match the array below! - // -# define _ap_jet_open 0 -# define _ap_jet_rake 1 -# define _ap_jet_hack 2 - -#if 0 - static u3_noun - _open_in(u3_noun ter, u3_noun gen); - /* ~(. al gen) - */ - static u3_noun - _al_bore(u3_noun ter, - u3_noun gen) - { - u3_noun gat = u3j_hook(u3k(ter), "al"); - - return u3i_molt(gat, u3x_sam, u3nc(c3__herb, u3k(gen)), 0); - } - /* ~(. al gen) - */ - static u3_noun - _al_core(u3_noun ter, - u3_noun gen) - { - u3_noun gat = u3j_hook(u3k(ter), "al"); - - return u3i_molt(gat, u3x_sam, u3k(gen), 0); - } - - /* van is transferred, gen is retained - */ - static u3_noun - _ap_bunt(u3_noun van, - u3_noun gen) - { - u3_noun pro = u3qfl_bunt(van, gen); - - u3z(van); - return pro; - } - -/** open cases -**/ - -#define _open_do_p(stem) \ - static u3_noun _open_in_##stem \ - ( u3_noun ter, u3_noun p_gen) - -#define _open_do_pq(stem) \ - static u3_noun _open_in_##stem \ - ( u3_noun ter, u3_noun p_gen, u3_noun q_gen) - -#define _open_do_pqr(stem) \ - static u3_noun _open_in_##stem \ - ( u3_noun ter, u3_noun p_gen, u3_noun q_gen, u3_noun r_gen) - -#define _open_do_pqrs(stem) \ - static u3_noun _open_in_##stem \ - ( u3_noun ter, u3_noun p_gen, u3_noun q_gen, u3_noun r_gen, \ - u3_noun s_gen) - -/*** -**** -***/ - _open_do_pq(tsbr) // =: - { - return u3nt(c3__tsls, - _ap_bunt(_al_core(ter, p_gen), p_gen), - u3k(q_gen)); - } - _open_do_pq(tscl) // =: - { - return u3nt(c3__tsgr, - u3nt(c3__cncb, - u3nc(u3nc(u3_nul, 1), - u3_nul), - u3k(p_gen)), - u3k(q_gen)); - } - _open_do_pqr(tsdt) // =. - { - return u3nt(c3__tsgr, - u3nt(c3__cncb, - u3nc(u3nc(u3_nul, 1), - u3_nul), - u3nc(u3nc(u3k(p_gen), - u3k(q_gen)), - u3_nul)), - u3k(r_gen)); - } - _open_do_pq(tsgl) // =< - { - return u3nt(c3__tsgr, - u3k(q_gen), - u3k(p_gen)); - } - _open_do_pq(tshp) // =- - { - return u3nt(c3__tsls, - u3k(q_gen), - u3k(p_gen)); - } - _open_do_pq(tsls) // =+ - { - return u3nt(c3__tsgr, - u3nc(u3k(p_gen), - u3nc(u3_nul, 1)), - u3k(q_gen)); - } - _open_do_p(tssg) // =~ - { - if ( !_(u3du(p_gen)) ) { - return u3nc(0, 1); - } else { - u3_noun tp_gen = u3t(p_gen); - u3_noun ip_gen = u3h(p_gen); - - if ( (u3_nul == p_gen) ) { - return u3nc(u3_blip, 1); - } - else if ( (u3_nul == tp_gen) ) { - return u3k(ip_gen); - } - else { - return u3nt(c3__tsgr, - u3k(ip_gen), - _open_in_tssg(ter, tp_gen)); - } - } - } -/*** -**** -***/ - _open_do_p(bccb) // $_ - { - return _ap_bunt(_al_core(ter, p_gen), p_gen); - } - _open_do_p(bctr) // $* - { - return - u3nc(c3__ktsg, - _ap_bunt(_al_core(ter, p_gen), - p_gen)); - } - _open_do_p(bczp) // $! - { - return u3nt(c3__bccb, - c3__axil, - u3k(p_gen)); - } -/*** -**** -***/ - _open_do_p(brhp) // |- - { - return u3nt(c3__tsgl, - u3nc(c3__cnzy, u3_blip), - u3nc(c3__brdt, u3k(p_gen))); - } - _open_do_p(brdt) // |. - { - return u3nc(c3__brcn, - u3nt(u3nt(u3_blip, c3__ash, u3k(p_gen)), - u3_nul, - u3_nul)); - } - -/*** -**** -***/ - _open_do_p(wtbr) // ?| - { - if ( (u3_nul == p_gen) ) { - return u3nt(c3__dtzz, 'f', c3n); - } - else { - u3_noun ip_gen = u3h(p_gen); - u3_noun tp_gen = u3t(p_gen); - - return u3nq(c3__wtcl, - u3k(ip_gen), - u3nt(c3__dtzz, 'f', c3y), - _open_in_wtbr(ter, tp_gen)); - } - } - _open_do_pqr(wtkt) // ?^ - { - return u3nq(c3__wtcl, - u3nt(c3__wtts, - u3nt(c3__axil, c3__atom, u3_blip), - u3k(p_gen)), - u3k(r_gen), - u3k(q_gen)); - } - _open_do_pq(wtgl) // ?< - { - return u3nq(c3__wtcl, - u3k(p_gen), - u3nc(c3__zpzp, u3_nul), - u3k(q_gen)); - } - _open_do_pqr(wtdt) // ?. - { - return u3nq(c3__wtcl, - u3k(p_gen), - u3k(r_gen), - u3k(q_gen)); - } - _open_do_pq(wtgr) // ?> - { - return u3nq(c3__wtcl, - u3k(p_gen), - u3k(q_gen), - u3nc(c3__zpzp, u3_nul)); - } - _open_do_pq(wthp) // ?- - { - if ( (u3_nul == q_gen) ) { - return u3nc(c3__zpfs, - u3nc(c3__cnzz, - u3k(p_gen))); - } - else { - u3_noun iq_gen = u3h(q_gen); - u3_noun tq_gen = u3t(q_gen); - u3_noun piq_gen = u3h(iq_gen); - u3_noun qiq_gen = u3t(iq_gen); - - return u3nq(c3__wtcl, - u3nt(c3__wtts, - u3k(piq_gen), - u3k(p_gen)), - u3k(qiq_gen), - _open_in_wthp(ter, p_gen, tq_gen)); - } - } - _open_do_p(wtpm) // ?& - { - if ( (u3_nul == p_gen) ) { - return u3nt(c3__dtzz, 'f', c3y); - } - else { - u3_noun ip_gen = u3h(p_gen); - u3_noun tp_gen = u3t(p_gen); - - return u3nq(c3__wtcl, - u3k(ip_gen), - _open_in_wtpm(ter, tp_gen), - u3nt(c3__dtzz, 'f', c3n)); - } - } - _open_do_pqr(wtls) // ?+ - { u3_noun tul = u3nc(u3nc(u3nc(c3__axil, c3__noun), - u3k(q_gen)), - u3_nul); - u3_noun zal = u3qb_weld(r_gen, tul); - u3_noun ret = u3nt(c3__wthp, u3k(p_gen), zal); - - u3z(tul); - return ret; - - } - _open_do_pqr(wtpt) // ?@ - { - return u3nq(c3__wtcl, - u3nt(c3__wtts, - u3nt(c3__axil, - c3__atom, - u3_blip), - u3k(p_gen)), - u3k(q_gen), - u3k(r_gen)); - } - _open_do_pqr(wtsg) // ?~ - { - return u3nq(c3__wtcl, - u3nt(c3__wtts, - u3nc(c3__axil, c3__null), - u3k(p_gen)), - u3k(q_gen), - u3k(r_gen)); - } - _open_do_p(wtzp) // ?! - { - return u3nq(c3__wtcl, - u3k(p_gen), - u3nt(c3__dtzz, 'f', c3n), - u3nt(c3__dtzz, 'f', c3y)); - } -/*** -**** -***/ - _open_do_pq(zpcb) // !_ - { - return u3k(q_gen); - } - _open_do_p(zpgr) // !> - { - return u3nq(c3__cnhp, - u3nc(c3__cnzy, c3__onan), - u3nt(c3__zpsm, - u3nc(c3__bctr, - u3nc(c3__herb, - u3nc(c3__cnzy, - c3__abel))), - u3k(p_gen)), - u3_nul); - } -/*** -**** -***/ - _open_do_pq(clhp) // :- - { - return u3nc(u3k(p_gen), - u3k(q_gen)); - } - _open_do_pq(clcb) // :_ - { - return u3nc(u3k(q_gen), - u3k(p_gen)); - } - _open_do_p(clcn) // :% - { - return u3nc(u3nc(c3__clsg, - u3k(p_gen)), - u3nc(c3__bczp, c3__null)); - } - _open_do_pqrs(clkt) // :^ - { - return u3nq(u3k(p_gen), - u3k(q_gen), - u3k(r_gen), - u3k(s_gen)); - } - _open_do_pqr(clls) // :+ - { - return u3nt(u3k(p_gen), - u3k(q_gen), - u3k(r_gen)); - } - _open_do_p(clsg) // :~ - { - if ( (u3_nul == p_gen) ) { - return u3nt(c3__dtzz, 'n', u3_nul); - } - else { - u3_noun ip_gen = u3h(p_gen); - u3_noun tp_gen = u3t(p_gen); - - return u3nc(u3k(ip_gen), - _open_in_clsg(ter, tp_gen)); - } - } - _open_do_p(cltr) // :* - { - if ( (u3_nul == p_gen) ) { - return u3nc(c3__zpzp, u3_nul); - } - else { - u3_noun ip_gen = u3h(p_gen); - u3_noun tp_gen = u3t(p_gen); - - if ( (u3_nul == tp_gen) ) { - return u3k(ip_gen); - } else { - return u3nc(u3k(ip_gen), - _open_in_cltr(ter, tp_gen)); - } - } - } -/*** -**** -***/ - _open_do_pq(cncb) // %_ - { - return u3nc(c3__ktls, - u3nq(u3nc(c3__cnzz, u3k(p_gen)), - c3__cnts, - u3k(p_gen), - u3k(q_gen))); - } -#if 0 - _open_do_pq(cncl) // %: - { - return u3nq - (c3__cnsg, - u3nc(u3_blip, u3_nul), - u3k(p_gen), - u3k(q_gen)); - } -#endif - _open_do_pq(cndt) // %. - { - return u3nt(c3__cnhp, - u3k(q_gen), - u3nc(u3k(p_gen), u3_nul)); - } - _open_do_pqrs(cnkt) // %^ - { - return u3nq(c3__cnhp, - u3k(p_gen), - u3k(q_gen), - u3nt(u3k(r_gen), - u3k(s_gen), - u3_nul)); - } - _open_do_pq(cnhp) // %- - { - if ( (u3_nul == q_gen) ) { - return u3nt(c3__tsgr, - u3k(p_gen), - u3nc(c3__cnzy, u3_blip)); - } else { - return u3nq(c3__cncl, - u3k(p_gen), - c3__cltr, - u3k(q_gen)); - } - } - _open_do_pqr(cnls) // %+ - { - return u3nc(c3__cnhp, - u3nq(u3k(p_gen), - u3k(q_gen), - u3k(r_gen), - u3_nul)); - } - _open_do_pqr(cnsg) // %~ - { - return u3nq(c3__cntr, - u3k(p_gen), - u3k(q_gen), - u3nc(u3nc(u3nc(u3nc(u3_nul, 6), 0), u3k(r_gen)), 0)); - } - _open_do_p(cnzy) // %cnzy - { - return u3nt(c3__cnts, - u3nc(u3k(p_gen), u3_nul), - u3_nul); - } - _open_do_p(cnzz) // %cnzz - { - return u3nt(c3__cnts, u3k(p_gen), u3_nul); - } -/*** -**** -***/ - _open_do_p(hxgl) // #< - { - return u3nq(c3__cnhp, - u3nc(c3__cnzy, c3__noah), - u3nc(c3__zpgr, - u3nc(c3__cltr, u3k(p_gen))), - u3_nul); - } - _open_do_p(hxgr) // #> - { - return u3nq(c3__cnhp, - u3nc(c3__cnzy, c3__cain), - u3nc(c3__zpgr, - u3nc(c3__cltr, u3k(p_gen))), - u3_nul); - } -/*** -**** -***/ - _open_do_pq(ktdt) // ^. - { - return u3nt(c3__ktls, - u3nq(c3__cnhp, u3k(p_gen), u3k(q_gen), u3_nul), - u3k(q_gen)); - } -#if 0 - _open_do_pq(kthp) // ^- - { - return u3nt(c3__ktls, - _ap_bunt(_al_bore(ter, p_gen), p_gen), - u3k(q_gen)); - } -#endif -/*** -**** -***/ - _open_do_pq(brcb) // |_ - { - return u3nt(c3__tsls, - u3nc(c3__bctr, u3k(p_gen)), - u3nc(c3__brcn, u3k(q_gen))); - } - _open_do_pq(brkt) // |^ - { - u3_noun diz = u3nc(c3__ash, u3k(p_gen)); - u3_noun ret = u3nt(c3__tsgr, - u3nc(c3__brcn, - u3qdb_put(q_gen, u3_blip, diz)), - u3nc(c3__cnzy, u3_blip)); - - u3_assert(0); - u3z(diz); - return ret; - } - _open_do_pq(brls) // |+ - { - return u3nc(c3__ktbr, - u3nt(c3__brts, - u3k(p_gen), - u3k(q_gen))); - } - _open_do_p(brwt) // |? - { - return u3nt(c3__ktwt, - c3__brdt, - u3k(p_gen)); - } -/*** -**** -***/ - _open_do_pq(sgts) // ~= - { - return u3nt(c3__sggr, - u3nc(c3__germ, u3k(p_gen)), - u3k(q_gen)); - } -#if 0 - _open_do_pq(sgbr) // ~| - { - return u3nt - (c3__sggr, - u3nc(c3__mean, u3k(p_gen)), - u3k(q_gen)); - } -#endif - _open_do_pq(sggl) // ~> - { - return u3nt(c3__tsgl, - u3nq(c3__sggr, u3k(p_gen), u3_nul, 1), - u3k(q_gen)); - } - _open_do_pq(sgbc) // ~$ - { - return u3nt(c3__sggr, - u3nq(c3__live, - c3__dtzz, - u3_blip, - u3k(p_gen)), - u3k(q_gen)); - } - _open_do_pq(sgcb) // ~_ - { - return u3nt(c3__sggr, - u3nc(c3__mean, - u3nc(c3__brdt, - u3k(p_gen))), - u3k(q_gen)); - } - static u3_noun - _sgcn_a(u3_noun r_gen, - u3_noun nob) - { - if ( c3n == u3du(r_gen) ) { - return u3k(nob); - } else { - u3_noun ir_gen = u3h(r_gen); - u3_noun tr_gen = u3t(r_gen); - u3_noun pir_gen, qir_gen; - - u3x_cell(ir_gen, &pir_gen, &qir_gen); - - return u3nc(u3nc(u3nt(c3__dtzz, u3_blip, u3k(pir_gen)), - u3nc(c3__zpts, u3k(qir_gen))), - _sgcn_a(tr_gen, nob)); - } - } - _open_do_pqrs(sgcn) // ~% - { - return u3nt(c3__sggl, - u3nq(c3__sgcn, - c3__clls, - u3nt(c3__dtzz, u3_blip, u3k(p_gen)), - u3nt(u3nc(c3__zpts, u3k(q_gen)), - c3__clsg, - _sgcn_a(r_gen, u3_nul))), - u3k(s_gen)); - } - _open_do_pq(sgfs) // ~/ - { - return u3nc(c3__sgcn, - u3nq(u3k(p_gen), - u3nc(u3_nul, 7), - u3_nul, - u3k(q_gen))); - } - _open_do_pq(sgls) // ~+ - { - return u3nt(c3__sggr, - u3nq(c3__sgls, c3__dtzz, u3_blip, u3k(p_gen)), - u3k(q_gen)); - } - _open_do_pqr(sgpm) // ~& - { - return u3nt(c3__sggr, - u3nt(c3__slog, - u3nt(c3__dtzy, u3_blip, u3k(p_gen)), - u3nq(c3__cnhp, u3nc(c3__cnzy, c3__cain), - u3nc(c3__zpgr, u3k(q_gen)), u3_nul)), - u3k(r_gen)); - } - _open_do_pqrs(sgwt) // ~? - { - return u3nt(c3__tsls, - u3nq(c3__wtdt, - u3k(q_gen), - u3nc(c3__bczp, c3__null), - u3nc(u3nc(c3__bczp, c3__null), u3k(r_gen))), - u3nq(c3__wtsg, - u3nc(u3nc(u3_nul, 2),u3_nul), - u3nt(c3__tsgr, - u3nc(u3_nul, 3), - u3k(s_gen)), - u3nq(c3__sgpm, - u3k(p_gen), - u3nc(u3_nul, 5), - u3nt(c3__tsgr, - u3nc(u3_nul, 3), - u3k(s_gen))))); - } -/*** -**** -***/ - static u3_noun - _smcl_in(u3_noun q_gen) - { - u3_noun hq_gen = u3h(q_gen); - u3_noun tq_gen = u3t(q_gen); - - if ( c3n == u3du(tq_gen) ) { - return u3nt(c3__tsgr, - u3nc(u3_nul, 3), - u3k(hq_gen)); - } else { - return u3nc(c3__cnhp, - u3nq(u3nc(u3_nul, 2), - u3nt(c3__tsgr, - u3nc(u3_nul, 3), - u3k(hq_gen)), - _smcl_in(tq_gen), - u3_nul)); - } - } - _open_do_pq(smcl) - { - if ( c3n == u3du(q_gen) ) { - return u3nc(c3__zpzp, u3_nul); - } - else if ( u3_nul == u3t(q_gen) ) { - return u3k(u3h(q_gen)); - } - else { - return u3nt(c3__tsls, - u3k(p_gen), - _smcl_in(q_gen)); - } - } -#if 0 - _open_do_pq(smsm) - { - return - u3nt(c3__tsgr, u3nq(c3__ktts, c3__v, u3_nul, 1), - u3nt(c3__tsls, - u3nt(c3__ktts, c3__a, - u3nt(c3__tsgr, u3nc(c3__cnzy, c3__v), - u3k(p_gen))), - u3nt(c3__tsls, - u3nt(c3__ktts, c3__b, - u3nt(c3__tsgr, - u3nc(c3__cnzy, c3__v), - u3k(q_gen))), - u3nt(c3__tsls, - u3nt(c3__ktts, c3__c, - u3nq(c3__cnhp, - u3nc(c3__cnzy, c3__a), - u3nc(c3__cnzy, c3__b), - u3_nul)), - u3nt(c3__wtgr, - u3nt(c3__dtts, - u3nc(c3__cnzy, c3__c), - u3nc(c3__cnzy, c3__b)), - u3nc(c3__cnzy, c3__c)))))); - } -#endif - - /** open - **/ - static u3_noun - _open_in(u3_noun ter, - u3_noun gen) - { - u3_noun p_gen, q_gen, r_gen, s_gen; - - return u3_none; - - if ( c3y == u3ud(gen) ) { - // printf("studly\n"); - // u3_err("stud m", gen); - return u3m_bail(c3__exit); - - return u3nt(c3__cnts, - u3nc(u3k(gen), u3_nul), - u3_nul); - } - else switch ( u3h(gen) ) { - default: return u3_none; - - case u3_nul: { - return u3nt(c3__cnts, - u3nc(u3k(gen), u3_nul), - u3_nul); - } - -# define _open_p(stem) \ - case c3__##stem: \ - return _open_in_##stem(ter, u3t(gen)); \ - -# define _open_pq(stem) \ - case c3__##stem: \ - if ( c3n == u3r_cell(u3t(gen), &p_gen, &q_gen) ) { \ - return u3m_bail(c3__fail); \ - } else return _open_in_##stem(ter, p_gen, q_gen); - -# define _open_pqr(stem) \ - case c3__##stem: \ - if ( c3n == u3r_trel(u3t(gen), &p_gen, &q_gen, &r_gen) ) { \ - return u3m_bail(c3__fail); \ - } else return _open_in_##stem(ter, p_gen, q_gen, r_gen); - -# define _open_pqrs(stem) \ - case c3__##stem: \ - if ( c3n == u3r_qual\ - (u3t(gen), &p_gen, &q_gen, &r_gen, &s_gen) )\ - { \ - return u3m_bail(c3__fail); \ - } else return _open_in_##stem(ter, p_gen, q_gen, r_gen, s_gen); - - _open_p (bccb); - _open_p (bctr); - _open_p (bczp); - - _open_p (brdt); - _open_pq (brcb); - _open_p (brhp); - _open_pq (brkt); - _open_pq (brls); - _open_p (brwt); - - _open_pq (clcb); - _open_p (clcn); - _open_pq (clhp); - _open_pqrs(clkt); - _open_pqr (clls); - _open_p (cltr); - _open_p (clsg); - _open_pq (cncb); - // _open_pq (cncl); - _open_pq (cndt); - _open_pqrs(cnkt); - _open_pq (cnhp); - _open_pqr (cnls); - _open_pqr (cnsg); - _open_p (cnzy); - _open_p (cnzz); - - _open_p (hxgl); - _open_p (hxgr); - - _open_pq (ktdt); -// _open_pq (kthp); - - _open_pq (sgts); -// _open_pq (sgbr); - _open_pq (sggl); - _open_pq (sgbc); - _open_pq (sgcb); - _open_pqrs(sgcn); - _open_pq (sgfs); - _open_pq (sgls); - _open_pqr (sgpm); - _open_pqrs(sgwt); - - _open_pq (smcl); - // _open_pq (smsm); - - _open_pq (tsbr); - _open_pq (tscl); - _open_pqr (tsdt); - _open_pq (tsgl); - _open_pq (tshp); - _open_pq (tsls); - _open_p (tssg); - - _open_pqr (wtdt); - _open_pq (wtgl); - _open_pqr (wtpt); - _open_pqr (wtsg); - _open_p (wtzp); - _open_p (wtbr); - _open_pq (wthp); - _open_pq (wtgr); - _open_pqr (wtls); - _open_pqr (wtkt); - _open_p (wtpm); - - _open_pq (zpcb); - _open_p (zpgr); - } - } - - /** rake - **/ - u3_noun - u3qfp_rake(u3_noun gen) - { - u3_noun p_gen, q_gen; - - if ( c3y == u3ud(gen) ) { - return u3nc(u3k(gen), u3_nul); - } - else switch ( u3h(gen) ) { - default: return u3m_error("rake-twig"); - - case u3_nul: return u3nc(u3k(gen), u3_nul); - - case c3__cnzy: { - return u3nc(u3k(u3t(gen)), u3_nul); - } - case c3__cnzz: { - return u3k(u3t(gen)); - } - case c3__cnts: { - if ( c3n == u3r_cell(u3t(gen), &p_gen, &q_gen) ) { - return u3m_bail(c3__fail); - } - else { - if ( u3_nul != q_gen ) { - return u3m_bail(c3__fail); - } - else { - return u3k(p_gen); - } - } - } - case c3__zpcb: { - if ( c3n == u3r_cell(u3t(gen), &p_gen, &q_gen) ) { - return u3m_bail(c3__fail); - } - else return u3qfp_rake(q_gen); - } - } - } - u3_noun - u3wfp_rake(u3_noun cor) - { - u3_noun gen; - - if ( u3_none == (gen = u3r_at(u3x_sam, cor)) ) { - return u3m_bail(c3__fail); - } else { - return u3qfp_rake(gen); - } - } - - /** hack - **/ - u3_noun - u3qfp_hack(u3_noun ter, - u3_noun gen) - { - u3_noun p_gen, q_gen; - u3_noun ret; - - if ( c3y == u3du(u3h(gen)) ) { - return u3nt(c3y, - u3k(u3h(gen)), - u3k(u3t(gen))); - } - else switch ( u3h(gen) ) { - case c3__tsgr: u3x_cell(u3t(gen), &p_gen, &q_gen); - { - if ( (c3n == u3du(p_gen)) || (u3_nul != u3h(p_gen)) ) { - return u3nc(c3n, u3k(gen)); - } - else { - u3_noun pyr = u3qfp_hack(ter, q_gen); - - if ( c3y == u3h(pyr) ) { - ret = u3nt(c3y, - u3nt(c3__tsgr, - u3k(p_gen), - u3k(u3h(u3t(pyr)))), - u3nt(c3__tsgr, - u3k(p_gen), - u3k(u3t(u3t(pyr))))); - } - else { - ret = u3nc(c3n, - u3nt(c3__tsgr, - u3k(p_gen), - u3k(u3t(pyr)))); - } - u3z(pyr); - return ret; - } - } - case c3__zpcb: u3x_cell(u3t(gen), &p_gen, &q_gen); - { - u3_noun pyr = u3qfp_hack(ter, q_gen); - - if ( c3y == u3h(pyr) ) { - ret = u3nt(c3y, - u3nt(c3__zpcb, - u3k(p_gen), - u3k(u3h(u3t(pyr)))), - u3nt(c3__zpcb, - u3k(p_gen), - u3k(u3t(u3t(pyr))))); - } - else { - ret = u3nc(c3n, - u3nt(c3__zpcb, - u3k(p_gen), - u3k(u3t(pyr)))); - } - u3z(pyr); - return ret; - } - default: break; - } - - { - u3_noun voq = _ap_open_l(ter, gen); - - if ( u3_none == voq ) { - return u3nc(c3n, u3k(gen)); - } - else if ( c3y == u3r_sing(voq, gen) ) { - return u3nc(c3n, voq); - } - else { - ret = u3qfp_hack(ter, voq); - - u3z(voq); - return ret; - } - } - } - - u3_noun - u3wfp_hack(u3_noun cor) - { - u3_noun gen; - - if ( u3_none == (gen = u3r_at(u3x_sam, cor)) ) { - return u3m_bail(c3__fail); - } else { - u3_noun ter = u3r_at(u3x_con, cor); - - return u3qfp_hack(ter, gen); - } - } -#endif - -/* boilerplate -*/ - static u3_noun - _ap_core(u3_noun ter, - u3_noun gen) - { - u3_noun gat = u3j_cook("_ap_core-ap", u3k(ter), "ap"); - - return u3i_molt(gat, u3x_sam, u3k(gen), 0); - } - - static u3_noun - _ar_core(u3_noun van, - u3_noun ref, - u3_noun syn) - { - u3_noun gat = u3j_hook(u3k(van), "ar"); - - return u3i_molt(gat, u3x_sam, u3nc(u3k(ref), u3k(syn)), 0); - } - -/* fish -*/ - u3_noun - u3qfr_fish(u3_noun van, - u3_noun ref, - u3_noun syn, - u3_noun axe) - { - u3_noun gat = u3j_soft(_ar_core(van, ref, syn), "fish"); - - return u3n_kick_on(u3i_molt(gat, - u3x_sam, - u3k(axe), - 0)); - } - -/* open -*/ - static u3_noun - _ap_open_n(u3_noun ter, - u3_noun fab, - u3_noun gen) - { - u3_noun cor = _ap_core(ter, gen); - -#if 1 - if ( c3n == fab ) { - cor = u3i_molt(cor, 14, c3n, 0); - } -#endif - return u3j_soft(cor, "open"); - } - static u3_noun - _ap_open_l(u3_noun ter, - u3_noun fab, - u3_noun gen) - { -#if 0 - u3_noun pro = _open_in(ter, gen); - - if ( u3_none != pro ) { - return pro; - } else { - return _ap_open_n(ter, gen); - } -#else - return _ap_open_n(ter, fab, gen); -#endif - } - - u3_noun - u3qfp_open(u3_noun ter, - u3_noun fab, - u3_noun gen) - { - return _ap_open_l(ter, fab, gen); - } - - u3_noun - u3wfp_open(u3_noun cor) - { - u3_noun gen; - - if ( u3_none == (gen = u3r_at(u3x_sam, cor)) ) { - return u3m_bail(c3__fail); - } else { - u3_noun ter = u3r_at(u3x_con, cor); - - return u3qfp_open(ter, c3y, gen); - } - } - diff --git a/pkg/noun/jets/f/fitz.c b/pkg/noun/jets/f/fitz.c index f8cff2b1c9..b1c2a5dfb3 100644 --- a/pkg/noun/jets/f/fitz.c +++ b/pkg/noun/jets/f/fitz.c @@ -5,63 +5,71 @@ #include "noun.h" +static u3_noun +_fitz_fiz(u3_noun yaz, + u3_noun wix) +{ + c3_w yaz_w = u3r_met(3, yaz); + c3_w wix_w = u3r_met(3, wix); + c3_y yaz_y, wix_y; - static u3_noun - _fitz_fiz(u3_noun yaz, - u3_noun wix) - { - c3_w yaz_w = u3r_met(3, yaz); - c3_w wix_w = u3r_met(3, wix); - c3_y yaz_y, wix_y; - - yaz_y = (0 == yaz_w) ? 0 : u3r_byte((yaz_w - 1), yaz); - if ( (yaz_y < 'A') || (yaz_y > 'Z') ) yaz_y = 0; + yaz_y = (0 == yaz_w) ? 0 : u3r_byte((yaz_w - 1), yaz); + if ( (yaz_y < 'A') || (yaz_y > 'Z') ) yaz_y = 0; - wix_y = (0 == wix_w) ? 0 : u3r_byte((wix_w - 1), wix); - if ( (wix_y < 'A') || (wix_y > 'Z') ) wix_y = 0; + wix_y = (0 == wix_w) ? 0 : u3r_byte((wix_w - 1), wix); + if ( (wix_y < 'A') || (wix_y > 'Z') ) wix_y = 0; - if ( yaz_y && wix_y ) { - if ( !wix_y || (wix_y > yaz_y) ) { - return c3n; - } + if ( yaz_y && wix_y ) { + if ( wix_y > yaz_y ) { + return c3n; } - return c3y; } - u3_noun - u3qf_fitz(u3_noun yaz, - u3_noun wix) - { - c3_w i_w, met_w = c3_min(u3r_met(3, yaz), u3r_met(3, wix)); + return c3y; +} - if ( c3n == _fitz_fiz(yaz, wix) ) { - return c3n; +u3_noun +u3qf_fitz(u3_noun yaz, + u3_noun wix) +{ + c3_w yet_w = u3r_met(3, yaz); + c3_w wet_w = u3r_met(3, wix); + + c3_w i_w, met_w = c3_min(yet_w, wet_w); + + if ( c3n == _fitz_fiz(yaz, wix) ) { + return c3n; + } + for ( i_w = 0; i_w < met_w; i_w++ ) { + c3_y yaz_y = u3r_byte(i_w, yaz); + c3_y wix_y = u3r_byte(i_w, wix); + + if ( (i_w == (yet_w - 1)) && (yaz_y >= 'A') && (yaz_y <= 'Z')) { + return c3y; } - for ( i_w = 0; i_w < met_w; i_w++ ) { - c3_y yaz_y = u3r_byte(i_w, yaz); - c3_y wix_y = u3r_byte(i_w, wix); - if ( (yaz_y >= 'A') && (yaz_y <= 'Z') ) yaz_y = 0; - if ( (wix_y >= 'A') && (wix_y <= 'Z') ) wix_y = 0; + if ( (i_w == (wet_w - 1)) && (wix_y >= 'A') && (wix_y <= 'Z')) { + return c3y; + } - if ( yaz_y && wix_y && (yaz_y != wix_y) ) { - return c3n; - } + if ( yaz_y != wix_y ) { + return c3n; } - return c3y; } + return c3y; +} - u3_noun - u3wf_fitz(u3_noun cor) - { - u3_noun yaz, wix; +u3_noun +u3wf_fitz(u3_noun cor) +{ + u3_noun yaz, wix; - if ( (c3n == u3r_mean(cor, u3x_sam_2, &yaz, u3x_sam_3, &wix, 0)) || - (c3n == u3ud(yaz)) || - (c3n == u3ud(wix)) ) - { - return u3m_bail(c3__fail); - } else { - return u3qf_fitz(yaz, wix); - } + if ( (c3n == u3r_mean(cor, u3x_sam_2, &yaz, u3x_sam_3, &wix, 0)) || + (c3n == u3ud(yaz)) || + (c3n == u3ud(wix)) ) + { + return u3m_bail(c3__fail); + } else { + return u3qf_fitz(yaz, wix); } +} diff --git a/pkg/noun/jets/f/flan.c b/pkg/noun/jets/f/flan.c index 93fa95ba1e..319e1f0547 100644 --- a/pkg/noun/jets/f/flan.c +++ b/pkg/noun/jets/f/flan.c @@ -35,7 +35,7 @@ } } u3_noun - u3wf_flan(u3_noun cor) + u3wf_flan_139(u3_noun cor) { u3_noun bos, nif; diff --git a/pkg/noun/jets/f/flip.c b/pkg/noun/jets/f/flip.c index d306c88e7a..9eb922e5e1 100644 --- a/pkg/noun/jets/f/flip.c +++ b/pkg/noun/jets/f/flip.c @@ -27,7 +27,7 @@ } } u3_noun - u3wf_flip(u3_noun cor) + u3wf_flip_139(u3_noun cor) { u3_noun hel; diff --git a/pkg/noun/jets/f/flor.c b/pkg/noun/jets/f/flor.c index f67997bba4..031168e953 100644 --- a/pkg/noun/jets/f/flor.c +++ b/pkg/noun/jets/f/flor.c @@ -35,7 +35,7 @@ } } u3_noun - u3wf_flor(u3_noun cor) + u3wf_flor_139(u3_noun cor) { u3_noun bos, nif; diff --git a/pkg/noun/jets/f/hike.c b/pkg/noun/jets/f/hike.c deleted file mode 100644 index 8183e32283..0000000000 --- a/pkg/noun/jets/f/hike.c +++ /dev/null @@ -1,137 +0,0 @@ -/// @file - -#include "jets/q.h" -#include "jets/w.h" - -#include "noun.h" - - -/* internal tools -*/ - /* _lily_hike_belt_root(): convert (pac) to a list of root tools. - */ - static u3_noun - _lily_hike_belt_root(u3_noun pac) - { - if ( (u3_nul == pac) ) { - return u3_nul; - } - else { - u3_atom axis = u3h(u3h(pac)); - u3_noun tool = u3t(u3h(pac)); - u3_noun list_tool = _lily_hike_belt_root(u3t(pac)); - - if ( c3y == u3r_sing(1, axis) ) { - return u3nc(u3k(tool), - list_tool); - } - else return list_tool; - } - } - - /* _lily_hike_belt_l(): factor (pac) left. - */ - static u3_noun - _lily_hike_belt_l(u3_noun pac) - { - if ( (u3_nul == pac) ) { - return u3_nul; - } - else { - u3_atom axis = u3h(u3h(pac)); - u3_noun tool = u3t(u3h(pac)); - u3_noun belt_l = _lily_hike_belt_l(u3t(pac)); - - { - if ( (1 != axis) && - (c3y == u3r_sing(2, u3qc_cap(axis))) ) - { - u3_atom axis_tap = u3qc_mas(axis); - - return u3nc(u3nc(u3k(axis_tap), - u3k(tool)), - belt_l); - } - else return belt_l; - } - } - } - - /* _lily_hike_belt_r(): factor (pac) right. - */ - static u3_noun - _lily_hike_belt_r(u3_noun pac) - { - if ( (u3_nul == pac) ) { - return u3_nul; - } - else { - u3_atom axis = u3h(u3h(pac)); - u3_noun tool = u3t(u3h(pac)); - u3_noun belt_r = _lily_hike_belt_r(u3t(pac)); - - { - if ( (1 != axis) && - (c3y == u3r_sing(3, u3qc_cap(axis))) ) - { - u3_atom axis_tap = u3qc_mas(axis); - - return u3nc(u3nc(u3k(axis_tap), - u3k(tool)), - belt_r); - } - else return belt_r; - } - } - } - - u3_noun - u3qf_hike(u3_noun axe, - u3_noun pac) - { - u3_assert(0); - if ( (u3_nul == pac) ) { - return u3nc(0, u3k(axe)); - } - else { - u3_noun zet = _lily_hike_belt_root(pac); - - if ( u3_nul != zet ) { - u3_noun fol = u3k(u3h(zet)); - - u3z(zet); - return fol; - } - else { - u3_noun tum = _lily_hike_belt_l(pac); - u3_noun gam = _lily_hike_belt_r(pac); - u3_noun hax = u3qc_peg(axe, 2); - u3_noun moz = u3qc_peg(axe, 3); - u3_noun zip = u3qf_hike(hax, tum); - u3_noun dof = u3qf_hike(moz, gam); - u3_noun fol = u3qf_cons(zip, dof); - - u3z(tum); - u3z(gam); - u3z(hax); - u3z(moz); - u3z(zip); - u3z(dof); - - return fol; - } - } - } - u3_noun - u3wf_hike(u3_noun cor) - { - u3_noun axe, pac; - - if ( (c3n == u3r_mean(cor, u3x_sam_2, &axe, u3x_sam_3, &pac, 0)) || - (c3n == u3ud(axe)) ) - { - return u3m_bail(c3__fail); - } else { - return u3qf_hike(axe, pac); - } - } diff --git a/pkg/noun/jets/f/ut_crop.c b/pkg/noun/jets/f/ut_crop.c index 1969a13a3d..189564d0c3 100644 --- a/pkg/noun/jets/f/ut_crop.c +++ b/pkg/noun/jets/f/ut_crop.c @@ -20,7 +20,7 @@ u3wfu_crop(u3_noun cor) u3_weak vet = u3r_at(u3qfu_van_vet, van); c3_m fun_m = 141 + c3__crop + ((!!vet) << 8); u3_noun key = u3z_key_3(fun_m, sut, ref, bat); - u3_weak pro = u3z_find(key); + u3_weak pro = u3z_find(u3z_memo_toss, key); if ( u3_none != pro ) { u3z(key); @@ -28,7 +28,7 @@ u3wfu_crop(u3_noun cor) } else { pro = u3n_nock_on(u3k(cor), u3k(u3x_at(u3x_bat, cor))); - return u3z_save(key, pro); + return u3z_save(u3z_memo_toss, key, pro); } } } diff --git a/pkg/noun/jets/f/ut_fish.c b/pkg/noun/jets/f/ut_fish.c index e7505b9345..e8acbd43cf 100644 --- a/pkg/noun/jets/f/ut_fish.c +++ b/pkg/noun/jets/f/ut_fish.c @@ -21,7 +21,7 @@ u3wfu_fish(u3_noun cor) u3_weak vet = u3r_at(u3qfu_van_vet, van); c3_m fun_m = 141 + c3__fish + ((!!vet) << 8); u3_noun key = u3z_key_3(fun_m, sut, axe, bat); - u3_weak pro = u3z_find(key); + u3_weak pro = u3z_find(u3z_memo_toss, key); if ( u3_none != pro ) { u3z(key); @@ -29,7 +29,7 @@ u3wfu_fish(u3_noun cor) } else { pro = u3n_nock_on(u3k(cor), u3k(u3x_at(u3x_bat, cor))); - return u3z_save(key, pro); + return u3z_save(u3z_memo_toss, key, pro); } } } diff --git a/pkg/noun/jets/f/ut_fuse.c b/pkg/noun/jets/f/ut_fuse.c index f9fac33a4d..8e4e1a6135 100644 --- a/pkg/noun/jets/f/ut_fuse.c +++ b/pkg/noun/jets/f/ut_fuse.c @@ -20,7 +20,7 @@ u3wfu_fuse(u3_noun cor) u3_weak vet = u3r_at(u3qfu_van_vet, van); c3_m fun_m = 141 + c3__fuse + ((!!vet) << 8); u3_noun key = u3z_key_3(fun_m, sut, ref, bat); - u3_weak pro = u3z_find(key); + u3_weak pro = u3z_find(u3z_memo_toss, key); if ( u3_none != pro ) { u3z(key); @@ -28,7 +28,7 @@ u3wfu_fuse(u3_noun cor) } else { pro = u3n_nock_on(u3k(cor), u3k(u3x_at(u3x_bat, cor))); - return u3z_save(key, pro); + return u3z_save(u3z_memo_toss, key, pro); } } } diff --git a/pkg/noun/jets/f/ut_mint.c b/pkg/noun/jets/f/ut_mint.c index fc8424854a..618e7291fd 100644 --- a/pkg/noun/jets/f/ut_mint.c +++ b/pkg/noun/jets/f/ut_mint.c @@ -22,7 +22,7 @@ u3wfu_mint(u3_noun cor) c3_m fun_m = 141 + c3__mint; u3_noun vet = u3r_at(u3qfu_van_vet, van); u3_noun key = u3z_key_5(fun_m, vet, sut, gol, gen, bat); - u3_weak pro = u3z_find(key); + u3_weak pro = u3z_find(u3z_memo_toss, key); if ( u3_none != pro ) { u3z(key); @@ -30,7 +30,7 @@ u3wfu_mint(u3_noun cor) } else { pro = u3n_nock_on(u3k(cor), u3k(u3x_at(u3x_bat, cor))); - return u3z_save(key, pro); + return u3z_save(u3z_memo_toss, key, pro); } } } diff --git a/pkg/noun/jets/f/ut_mull.c b/pkg/noun/jets/f/ut_mull.c index 98e62ca57e..767ab3c51a 100644 --- a/pkg/noun/jets/f/ut_mull.c +++ b/pkg/noun/jets/f/ut_mull.c @@ -23,7 +23,7 @@ u3wfu_mull(u3_noun cor) u3_weak vet = u3r_at(u3qfu_van_vet, van); c3_m fun_m = 141 + c3__mull + ((!!vet) << 8); u3_noun key = u3z_key_5(fun_m, sut, gol, dox, gen, bat); - u3_weak pro = u3z_find(key); + u3_weak pro = u3z_find(u3z_memo_toss, key); if ( u3_none != pro ) { u3z(key); @@ -31,7 +31,7 @@ u3wfu_mull(u3_noun cor) } else { pro = u3n_nock_on(u3k(cor), u3k(u3x_at(u3x_bat, cor))); - return u3z_save(key, pro); + return u3z_save(u3z_memo_toss, key, pro); } } } diff --git a/pkg/noun/jets/f/ut_nest.c b/pkg/noun/jets/f/ut_nest.c index 765081a5cb..9bc53bf0f9 100644 --- a/pkg/noun/jets/f/ut_nest.c +++ b/pkg/noun/jets/f/ut_nest.c @@ -27,7 +27,7 @@ u3wfu_nest_dext(u3_noun dext_core) u3_weak vet = u3r_at(u3qfu_van_vet, van); c3_m fun_m = 141 + c3__dext + ((!!vet) << 8); u3_noun key = u3z_key_3(fun_m, sut, ref, bat); - u3_weak pro = u3z_find(key); + u3_weak pro = u3z_find(u3z_memo_toss, key); if ( u3_none != pro ) { u3z(key); @@ -39,7 +39,7 @@ u3wfu_nest_dext(u3_noun dext_core) if ( ((c3y == pro) && (u3_nul == reg)) || ((c3n == pro) && (u3_nul == seg)) ) { - return u3z_save(key, pro); + return u3z_save(u3z_memo_toss, key, pro); } else { u3z(key); diff --git a/pkg/noun/jets/f/ut_rest.c b/pkg/noun/jets/f/ut_rest.c index 3fdbed23b0..87ff60fa68 100644 --- a/pkg/noun/jets/f/ut_rest.c +++ b/pkg/noun/jets/f/ut_rest.c @@ -20,7 +20,7 @@ u3wfu_rest(u3_noun cor) u3_weak vet = u3r_at(u3qfu_van_vet, van); c3_m fun_m = 141 + c3__rest + ((!!vet) << 8); u3_noun key = u3z_key_3(fun_m, sut, leg, bat); - u3_weak pro = u3z_find(key); + u3_weak pro = u3z_find(u3z_memo_toss, key); if ( u3_none != pro ) { u3z(key); @@ -28,7 +28,7 @@ u3wfu_rest(u3_noun cor) } else { pro = u3n_nock_on(u3k(cor), u3k(u3x_at(u3x_bat, cor))); - return u3z_save(key, pro); + return u3z_save(u3z_memo_toss, key, pro); } } } diff --git a/pkg/noun/jets/q.h b/pkg/noun/jets/q.h index 33c63ef42b..fd7c5981fb 100644 --- a/pkg/noun/jets/q.h +++ b/pkg/noun/jets/q.h @@ -32,6 +32,7 @@ u3_noun u3qb_lien(u3_noun, u3_noun); u3_noun u3qb_murn(u3_noun, u3_noun); u3_noun u3qb_need(u3_noun); + u3_noun u3qb_mate(u3_noun, u3_noun); u3_noun u3qb_reap(u3_atom, u3_noun); u3_noun u3qb_reel(u3_noun, u3_noun); u3_noun u3qb_roll(u3_noun, u3_noun); @@ -222,10 +223,7 @@ u3_noun u3qf_face(u3_noun, u3_noun); u3_noun u3qf_fine(u3_noun, u3_noun, u3_noun); u3_noun u3qf_fitz(u3_noun, u3_noun); - u3_noun u3qf_flan(u3_noun, u3_noun); u3_noun u3qf_flay(u3_noun); - u3_noun u3qf_flip(u3_noun); - u3_noun u3qf_flor(u3_noun, u3_noun); u3_noun u3qf_forq(u3_noun, u3_noun); u3_noun u3qf_fork(u3_noun); u3_noun u3qf_grof(u3_noun); @@ -254,3 +252,4 @@ void u3qf_test(const c3_c*, u3_noun); #endif /* ifndef U3_JETS_Q_H */ + diff --git a/pkg/noun/jets/tree.c b/pkg/noun/jets/tree.c index c15e29ba8e..9e920becda 100644 --- a/pkg/noun/jets/tree.c +++ b/pkg/noun/jets/tree.c @@ -546,17 +546,17 @@ static c3_c* _140_pen_fitz_ha[] = { "469abe976ec15eeff9a87bce385f2c87c9bd89814ce2858aa9fee094beea1e5d", 0 }; -static u3j_harm _140_pen_flan_a[] = {{".2", u3wf_flan}, {}}; +static u3j_harm _140_pen_flan_a[] = {{".2", u3wf_flan_139}, {}}; static c3_c* _140_pen_flan_ha[] = { "cc00cb9373b0274af4e17d7acd77f65d8a2fa886e422c949c12d9d9e7cb3525b", 0 }; -static u3j_harm _140_pen_flip_a[] = {{".2", u3wf_flip}, {}}; +static u3j_harm _140_pen_flip_a[] = {{".2", u3wf_flip_139}, {}}; static c3_c* _140_pen_flip_ha[] = { "6e97fab9d039e715a30af5da93ef97389babfdcae7ef87655d278e77a1af0f0c", 0 }; -static u3j_harm _140_pen_flor_a[] = {{".2", u3wf_flor}, {}}; +static u3j_harm _140_pen_flor_a[] = {{".2", u3wf_flor_139}, {}}; static c3_c* _140_pen_flor_ha[] = { "ab5360aacf0c9a325727e90e1caea9c42f5d94ccc248c9e1f253b0922b4c4e63", 0 @@ -567,10 +567,6 @@ static c3_c* _140_pen_fork_ha[] = { 0 }; -// hike disabled while implementing edit -// static u3j_harm _140_pen_hike_a[] = {{".2", u3wf_hike}, {}}; -// static c3_c* _140_pen_hike_ha[] = {0}; - static u3j_harm _140_pen_look_a[] = {{".2", u3wf_look}, {}}; static c3_c* _140_pen_look_ha[] = { "fdda2166a2b9e1a9bda6ab375dd6fb6c610e18f54636a5e89896b45fd0a7169b", @@ -702,17 +698,6 @@ static u3j_hood _140_pen__ut_ho[] = {}, }; -// XX unused, hook removed, delete source -// -#if 0 -static u3j_harm _140_pen__ap_a[] = - { {"open", u3wfp_open}, - {"rake", u3wfp_rake}, - {} - }; -static c3_c* _140_pen__ap_ha[] = {0}; -#endif - static u3j_core _140_pen_d[] = { { "hex", 7, 0, _140_hex_d, _140_hex_ha }, @@ -726,15 +711,8 @@ static u3j_core _140_pen_d[] = { "flip", 7, _140_pen_flip_a, 0, _140_pen_flip_ha }, { "flor", 7, _140_pen_flor_a, 0, _140_pen_flor_ha }, { "fork", 7, _140_pen_fork_a, 0, _140_pen_fork_ha }, - // XX implementation obsolete, update or remove - // - // { "hike", 7, _140_pen_hike_a, 0, _140_pen_hike_ha }, { "look", 7, _140_pen_look_a, 0, _140_pen_look_ha }, { "loot", 7, _140_pen_loot_a, 0, _140_pen_loot_ha }, - - // XX unused, hook removed, delete source - // - // { "ap", 7, _140_pen__ap_a, 0, _140_pen__ap_ha }, { "ut", 15, 0, _140_pen__ut_d, _140_pen__ut_ha, _140_pen__ut_ho }, {} }; @@ -1825,11 +1803,7 @@ static c3_c* _140_two__in_ha[] = { "1f0a6f8b945b243520b77069060589938d9e651e34b24924db9528d02a98014f", 0 }; - static u3j_harm _140_two__by_bif_a[] = {{".2", u3wdb_bif, c3y}, {}}; - static c3_c* _140_two__by_bif_ha[] = { - "d377a032a3866e76f6f5217c7c0ed0519b768d8b1c5107e35f7dbf18d8f60880", - 0 - }; + static u3j_harm _140_two__by_del_a[] = {{".2", u3wdb_del, c3y}, {}}; static c3_c* _140_two__by_del_ha[] = { "09f78d6235d3fce8303c7bc663988349b7d4592abdacfb09b833d2f43629b6b6", @@ -1909,7 +1883,9 @@ static u3j_core _140_two__by_d[] = { { "all", 7, _140_two__by_all_a, 0, _140_two__by_all_ha }, { "any", 7, _140_two__by_any_a, 0, _140_two__by_any_ha }, { "apt", 7, _140_two__by_apt_a, 0, _140_two__by_apt_ha }, - { "bif", 7, _140_two__by_bif_a, 0, _140_two__by_bif_ha }, + // disabled due to interface change in %138 + // + // { "bif", 7, _140_two__by_bif_a, 0, _140_two__by_bif_ha }, { "del", 7, _140_two__by_del_a, 0, _140_two__by_del_ha }, { "dif", 7, _140_two__by_dif_a, 0, _140_two__by_dif_ha }, { "gas", 7, _140_two__by_gas_a, 0, _140_two__by_gas_ha }, @@ -2181,15 +2157,8 @@ static u3j_core _139_pen_d[] = { "flip", 7, _140_pen_flip_a, 0, no_hashes }, { "flor", 7, _140_pen_flor_a, 0, no_hashes }, { "fork", 7, _140_pen_fork_a, 0, no_hashes }, - // XX implementation obsolete, update or remove - // - // { "hike", 7, _140_pen_hike_a, 0, no_hashes }, { "look", 7, _140_pen_look_a, 0, no_hashes }, { "loot", 7, _140_pen_loot_a, 0, no_hashes }, - - // XX unused, hook removed, delete source - // - // { "ap", 7, _140_pen__ap_a, 0, no_hashes }, { "ut", 15, 0, _140_pen__ut_d, no_hashes, _140_pen__ut_ho }, {} }; @@ -2224,11 +2193,8 @@ static u3j_core _139_qua_d[] = { "mole", 7, _140_qua_mole_a, 0, no_hashes }, { "mule", 7, _140_qua_mule_a, 0, no_hashes }, - // XX disabled, implicated in memory corruption - // write tests and re-enable - // - // { "scot", 7, _140_qua_scot_a, 0, no_hashes }, - // { "scow", 7, _140_qua_scow_a, 0, no_hashes }, + { "scot", 7, _140_qua_scot_a, 0, no_hashes }, + { "scow", 7, _140_qua_scow_a, 0, no_hashes }, { "slaw", 7, _140_qua_slaw_a, 0, no_hashes }, {} }; @@ -2359,6 +2325,195 @@ u3j_core _k139_d[] = {} }; +static u3j_core _138_pen_d[] = +{ { "hex", 7, 0, _139_hex_d, no_hashes }, + + { "cell", 7, _140_pen_cell_a, 0, no_hashes }, + { "comb", 7, _140_pen_comb_a, 0, no_hashes }, + { "cons", 7, _140_pen_cons_a, 0, no_hashes }, + { "core", 7, _140_pen_core_a, 0, no_hashes }, + { "face", 7, _140_pen_face_a, 0, no_hashes }, + { "fitz", 7, _140_pen_fitz_a, 0, no_hashes }, + // flan, flip, and flor removed + // + { "fork", 7, _140_pen_fork_a, 0, no_hashes }, + { "look", 7, _140_pen_look_a, 0, no_hashes }, + { "loot", 7, _140_pen_loot_a, 0, no_hashes }, + { "ut", 15, 0, _140_pen__ut_d, no_hashes, _140_pen__ut_ho }, + {} +}; + +static u3j_core _138_qua_d[] = +{ { "pen", 3, 0, _138_pen_d, no_hashes, _140_pen_ho }, + + { "po", 7, 0, _140_qua__po_d, no_hashes }, + + { "trip", 7, _140_qua_trip_a, 0, no_hashes }, + + { "bend", 7, 0, _140_qua__bend_d, no_hashes }, + { "cold", 7, 0, _140_qua__cold_d, no_hashes }, + { "comp", 7, 0, _140_qua__comp_d, no_hashes }, + { "cook", 7, 0, _140_qua__cook_d, no_hashes }, + { "easy", 7, 0, _140_qua__easy_d, no_hashes }, + { "glue", 7, 0, _140_qua__glue_d, no_hashes }, + { "here", 7, 0, _140_qua__here_d, no_hashes }, + { "just", 7, 0, _140_qua__just_d, no_hashes }, + { "mask", 7, 0, _140_qua__mask_d, no_hashes }, + { "shim", 7, 0, _140_qua__shim_d, no_hashes }, + { "stag", 7, 0, _140_qua__stag_d, no_hashes }, + { "stew", 7, 0, _140_qua__stew_d, no_hashes }, + { "stir", 7, 0, _140_qua__stir_d, no_hashes }, + + { "pfix", 7, _140_qua_pfix_a, 0, no_hashes }, + { "plug", 7, _140_qua_plug_a, 0, no_hashes }, + { "pose", 7, _140_qua_pose_a, 0, no_hashes }, + { "sfix", 7, _140_qua_sfix_a, 0, no_hashes }, + + { "mink", 7, _140_qua_mink_a, 0, no_hashes }, + { "mole", 7, _140_qua_mole_a, 0, no_hashes }, + { "mule", 7, _140_qua_mule_a, 0, no_hashes }, + + { "scot", 7, _140_qua_scot_a, 0, no_hashes }, + { "scow", 7, _140_qua_scow_a, 0, no_hashes }, + { "slaw", 7, _140_qua_slaw_a, 0, no_hashes }, + {} +}; + +static u3j_core _138_tri_d[] = +{ { "qua", 3, 0, _138_qua_d, no_hashes, _140_qua_ho }, + + { "cofl", 7, 0, _140_tri__cofl_d, no_hashes }, + { "rd", 7, 0, _140_tri__rd_d, no_hashes }, + { "rs", 7, 0, _140_tri__rs_d, no_hashes }, + { "rq", 7, 0, _140_tri__rq_d, no_hashes }, + { "rh", 7, 0, _140_tri__rh_d, no_hashes }, + { "og", 7, 0, _140_tri__og_d, no_hashes }, + + { "sha", 7, 0, _140_tri__sha_d, no_hashes }, + { "shax", 7, _140_tri_shax_a, 0, no_hashes }, + { "shay", 7, _140_tri_shay_a, 0, no_hashes }, + { "shas", 7, _140_tri_shas_a, 0, no_hashes }, + { "shal", 7, _140_tri_shal_a, 0, no_hashes }, + + { "ob", 3, 0, _140_ob_d, no_hashes, _140_ob_ho }, + {} +}; + +static u3j_harm _138_two__by_bif_a[] = {{".2", u3wdb_bif, c3y}, {}}; + +static u3j_core _138_two__by_d[] = + { { "all", 7, _140_two__by_all_a, 0, _140_two__by_all_ha }, + { "any", 7, _140_two__by_any_a, 0, _140_two__by_any_ha }, + { "apt", 7, _140_two__by_apt_a, 0, _140_two__by_apt_ha }, + { "bif", 7, _138_two__by_bif_a, 0, no_hashes }, + { "del", 7, _140_two__by_del_a, 0, _140_two__by_del_ha }, + { "dif", 7, _140_two__by_dif_a, 0, _140_two__by_dif_ha }, + { "gas", 7, _140_two__by_gas_a, 0, _140_two__by_gas_ha }, + { "get", 7, _140_two__by_get_a, 0, _140_two__by_get_ha }, + { "has", 7, _140_two__by_has_a, 0, _140_two__by_has_ha }, + { "int", 7, _140_two__by_int_a, 0, _140_two__by_int_ha }, + { "jab", 7, _140_two__by_jab_a, 0, _140_two__by_jab_ha }, + { "key", 7, _140_two__by_key_a, 0, _140_two__by_key_ha }, + { "put", 7, _140_two__by_put_a, 0, _140_two__by_put_ha }, + { "rep", 7, _140_two__by_rep_a, 0, _140_two__by_rep_ha }, + { "run", 7, _140_two__by_run_a, 0, _140_two__by_run_ha }, + { "tap", 7, _140_two__by_tap_a, 0, _140_two__by_tap_ha }, + { "uni", 7, _140_two__by_uni_a, 0, _140_two__by_uni_ha }, + { "urn", 7, _140_two__by_urn_a, 0, _140_two__by_urn_ha }, + { "wyt", 3, _140_two__by_wyt_a, 0, _140_two__by_wyt_ha }, + {} + }; + +static u3j_harm _138_two_mate_a[] = {{".2", u3wb_mate, c3y}, {}}; + +static u3j_core _138_two_d[] = +{ { "tri", 3, 0, _138_tri_d, no_hashes, _140_tri_ho }, + + { "find", 7, _140_two_find_a, 0, no_hashes }, + { "flop", 7, _140_two_flop_a, 0, no_hashes }, + { "lent", 7, _140_two_lent_a, 0, no_hashes }, + { "levy", 7, _140_two_levy_a, 0, no_hashes }, + { "lien", 7, _140_two_lien_a, 0, no_hashes }, + { "murn", 7, _140_two_murn_a, 0, no_hashes }, + { "need", 7, _140_two_need_a, 0, no_hashes }, + { "mate", 7, _138_two_mate_a, 0, no_hashes }, + { "reap", 7, _140_two_reap_a, 0, no_hashes }, + { "reel", 7, _140_two_reel_a, 0, no_hashes }, + { "roll", 7, _140_two_roll_a, 0, no_hashes }, + { "skid", 7, _140_two_skid_a, 0, no_hashes }, + { "skim", 7, _140_two_skim_a, 0, no_hashes }, + { "skip", 7, _140_two_skip_a, 0, no_hashes }, + { "scag", 7, _140_two_scag_a, 0, no_hashes }, + { "slag", 7, _140_two_slag_a, 0, no_hashes }, + { "snag", 7, _140_two_snag_a, 0, no_hashes }, + { "sort", 7, _140_two_sort_a, 0, no_hashes }, + { "turn", 7, _140_two_turn_a, 0, no_hashes }, + { "weld", 7, _140_two_weld_a, 0, no_hashes }, + { "welp", 7, _140_two_welp_a, 0, no_hashes }, + { "zing", 7, _140_two_zing_a, 0, no_hashes }, + + { "bex", 7, _140_two_bex_a, 0, no_hashes }, + { "cat", 7, _140_two_cat_a, 0, no_hashes }, + { "can", 7, _140_two_can_a, 0, no_hashes }, + { "con", 7, _140_two_con_a, 0, no_hashes }, + { "cue", 7, _140_two_cue_a, 0, no_hashes }, + { "cut", 7, _140_two_cut_a, 0, no_hashes }, + { "dis", 7, _140_two_dis_a, 0, no_hashes }, + { "dor", 7, _140_two_dor_a, 0, no_hashes }, + { "end", 7, _140_two_end_a, 0, no_hashes }, + { "gor", 7, _140_two_gor_a, 0, no_hashes }, + { "jam", 7, _140_two_jam_a, 0, no_hashes }, + { "lsh", 7, _140_two_lsh_a, 0, no_hashes }, + { "mat", 7, _140_two_mat_a, 0, no_hashes }, + { "met", 7, _140_two_met_a, 0, no_hashes }, + { "mix", 7, _140_two_mix_a, 0, no_hashes }, + { "mor", 7, _140_two_mor_a, 0, no_hashes }, + { "mug", 7, _140_two_mug_a, 0, no_hashes }, + { "muk", 59, _140_two_muk_a, 0, no_hashes }, + { "rap", 7, _140_two_rap_a, 0, no_hashes }, + { "rep", 7, _140_two_rep_a, 0, no_hashes }, + { "rev", 7, _140_two_rev_a, 0, no_hashes }, + { "rip", 7, _140_two_rip_a, 0, no_hashes }, + { "rsh", 7, _140_two_rsh_a, 0, no_hashes }, + { "swp", 7, _140_two_swp_a, 0, no_hashes }, + { "rub", 7, _140_two_rub_a, 0, no_hashes }, + { "pow", 7, _140_two_pow_a, 0, no_hashes }, + { "sqt", 7, _140_two_sqt_a, 0, no_hashes }, + { "xeb", 7, _140_two_xeb_a, 0, no_hashes }, + + { "by", 7, 0, _138_two__by_d, no_hashes }, + { "in", 7, 0, _139_two__in_d, no_hashes }, + {} +}; + +static u3j_core _138_one_d[] = +{ { "two", 3, 0, _138_two_d, no_hashes }, + + { "add", 7, _140_one_add_a, 0, no_hashes }, + { "dec", 7, _140_one_dec_a, 0, no_hashes }, + { "div", 7, _140_one_div_a, 0, no_hashes }, + { "dvr", 7, _140_one_dvr_a, 0, no_hashes }, + { "gte", 7, _140_one_gte_a, 0, no_hashes }, + { "gth", 7, _140_one_gth_a, 0, no_hashes }, + { "lte", 7, _140_one_lte_a, 0, no_hashes }, + { "lth", 7, _140_one_lth_a, 0, no_hashes }, + { "max", 7, _140_one_max_a, 0, no_hashes }, + { "min", 7, _140_one_min_a, 0, no_hashes }, + { "mod", 7, _140_one_mod_a, 0, no_hashes }, + { "mul", 7, _140_one_mul_a, 0, no_hashes }, + { "sub", 7, _140_one_sub_a, 0, no_hashes }, + + { "cap", 7, _140_one_cap_a, 0, no_hashes }, + { "mas", 7, _140_one_mas_a, 0, no_hashes }, + { "peg", 7, _140_one_peg_a, 0, no_hashes }, + {} +}; + +u3j_core _k138_d[] = +{ { "one", 3, 0, _138_one_d, no_hashes }, + {} +}; + // TODO: probably need different ha hashes @@ -2428,6 +2583,7 @@ u3j_core _a50_d[] = static u3j_core _d[] = { { "k140", 0, 0, _k140_d, _k140_ha, 0, (u3j_core*) 140, 0 }, { "k139", 0, 0, _k139_d, no_hashes, 0, (u3j_core*) 139, 0 }, + { "k138", 0, 0, _k138_d, no_hashes, 0, (u3j_core*) 138, 0 }, { "a50", 0, 0, _a50_d, _k140_ha, 0, (u3j_core*) c3__a50, 0 }, {} }; @@ -2438,3 +2594,4 @@ u3j_Dash = { 0, 0 }; + diff --git a/pkg/noun/jets/w.h b/pkg/noun/jets/w.h index d838416c03..51dbd4471f 100644 --- a/pkg/noun/jets/w.h +++ b/pkg/noun/jets/w.h @@ -32,6 +32,7 @@ u3_noun u3wb_lien(u3_noun); u3_noun u3wb_murn(u3_noun); u3_noun u3wb_need(u3_noun); + u3_noun u3wb_mate(u3_noun); u3_noun u3wb_reap(u3_noun); u3_noun u3wb_reel(u3_noun); u3_noun u3wb_roll(u3_noun); @@ -298,10 +299,10 @@ u3_noun u3wf_face(u3_noun); u3_noun u3wf_fine(u3_noun); u3_noun u3wf_fitz(u3_noun); - u3_noun u3wf_flan(u3_noun); + u3_noun u3wf_flan_139(u3_noun); u3_noun u3wf_flay(u3_noun); - u3_noun u3wf_flip(u3_noun); - u3_noun u3wf_flor(u3_noun); + u3_noun u3wf_flip_139(u3_noun); + u3_noun u3wf_flor_139(u3_noun); u3_noun u3wf_forq(u3_noun); u3_noun u3wf_fork(u3_noun); u3_noun u3wf_hint(u3_noun); @@ -332,3 +333,4 @@ u3_noun u3wfu_rest(u3_noun); #endif /* ifndef U3_JETS_W_H */ + diff --git a/pkg/noun/jets_tests.c b/pkg/noun/jets_tests.c index 257ec81e3b..222b31afbf 100644 --- a/pkg/noun/jets_tests.c +++ b/pkg/noun/jets_tests.c @@ -860,6 +860,71 @@ _test_ob(void) return ret_i; } +static c3_i +_test_mas(void) +{ + c3_i ret_i = 1; + u3_atom res; + + if ( 0x4000 != (res = u3qc_mas(0x8000)) ) { + fprintf(stderr, "test mas fail: (mas 0x8000) != 0x4000: 0x'%x'\r\n", res); + ret_i = 0; + } + + if ( 0x20000000 != (res = u3qc_mas(0x40000000)) ) { + fprintf(stderr, "test mas fail: (mas 0x4000.0000) != 0x2000.0000: 0x%x\r\n", res); + ret_i = 0; + } + + { + u3_atom sam, pro; + + sam = u3qc_bex(36); + pro = u3qc_bex(35); + res = u3qc_mas(sam); + + if ( c3n == u3r_sing(pro, res) ) { + c3_c* out_c; + u3s_etch_ux_c(res, &out_c); + fprintf(stderr, "test mas fail: (mas (bex 36)) != (bex 35): %s\r\n", out_c); + c3_free(out_c); + ret_i = 0; + } + + u3z(res); u3z(sam); u3z(pro); + + sam = u3qc_bex(64); + pro = u3qc_bex(63); + res = u3qc_mas(sam); + + if ( c3n == u3r_sing(pro, res) ) { + c3_c* out_c; + u3s_etch_ux_c(res, &out_c); + fprintf(stderr, "test mas fail: (mas (bex 64)) != (bex 63): %s\r\n", out_c); + c3_free(out_c); + ret_i = 0; + } + + u3z(res); u3z(sam); u3z(pro); + + sam = u3qc_bex(65); + pro = u3qc_bex(64); + res = u3qc_mas(sam); + + if ( c3n == u3r_sing(pro, res) ) { + c3_c* out_c; + u3s_etch_ux_c(res, &out_c); + fprintf(stderr, "test mas fail: (mas (bex 65)) != (bex 64): %s\r\n", out_c); + c3_free(out_c); + ret_i = 0; + } + + u3z(res); u3z(sam); u3z(pro); + } + + return ret_i; +} + static c3_i _test_jets(void) { @@ -900,6 +965,11 @@ _test_jets(void) ret_i = 0; } + if ( !_test_mas() ) { + fprintf(stderr, "test jets: mas: failed\r\n"); + ret_i = 0; + } + return ret_i; } diff --git a/pkg/noun/manage.c b/pkg/noun/manage.c index 5ff7d7b129..68a117fedf 100644 --- a/pkg/noun/manage.c +++ b/pkg/noun/manage.c @@ -1,6 +1,8 @@ /// @file -#include "manage.h" +#include "pkg/noun/manage.h" +#include "pkg/noun/v2/manage.h" +#include "pkg/noun/v3/manage.h" #include #include @@ -20,7 +22,7 @@ #include "platform/rsignal.h" #include "retrieve.h" #include "trace.h" -#include "urcrypt/urcrypt.h" +#include "urcrypt.h" #include "vortex.h" #include "xtract.h" @@ -473,9 +475,8 @@ u3m_mark(FILE* fil_u) static void _pave_parts(void) { - // TODO: pass `u3_Host.ops_u.hap_w` into `noun` library as an argument and use - // as size of memo cache. - u3R->cax.har_p = u3h_new_cache(50000); + u3R->cax.har_p = u3h_new_cache(u3C.hap_w); // transient + u3R->cax.per_p = u3h_new_cache(u3C.per_w); // persistent u3R->jed.war_p = u3h_new(); u3R->jed.cod_p = u3h_new(); u3R->jed.han_p = u3h_new(); @@ -488,7 +489,7 @@ _pave_parts(void) static u3_road* _pave_road(c3_w* rut_w, c3_w* mat_w, c3_w* cap_w, c3_w siz_w) { - c3_dessert(((uintptr_t)rut_w & u3C.balign_d-1) == 0); + c3_dessert(((uintptr_t)rut_w & u3a_balign-1) == 0); u3_road* rod_u = (void*) mat_w; // enable in case of corruption @@ -529,8 +530,8 @@ _pave_north(c3_w* mem_w, c3_w siz_w, c3_w len_w, c3_o kid_o) // 00~~~|R|---|H|######|C|+++|M|~~~FF // ^--u3R which _pave_road returns (u3H for home road) // - c3_w* mat_w = c3_align(mem_w + len_w - siz_w, u3C.balign_d, C3_ALGLO); - c3_w* rut_w = c3_align(mem_w, u3C.balign_d, C3_ALGHI); + c3_w* mat_w = c3_align(mem_w + len_w - siz_w, u3a_balign, C3_ALGLO); + c3_w* rut_w = c3_align(mem_w, u3a_balign, C3_ALGHI); c3_w* cap_w = mat_w; if ( c3y == kid_o ) { @@ -560,8 +561,8 @@ _pave_south(c3_w* mem_w, c3_w siz_w, c3_w len_w) // 00~~~|M|+++|C|######|H|---|R|~~~FFF // ^---u3R which _pave_road returns // - c3_w* mat_w = c3_align(mem_w, u3C.balign_d, C3_ALGHI); - c3_w* rut_w = c3_align(mem_w + len_w, u3C.balign_d, C3_ALGLO); + c3_w* mat_w = c3_align(mem_w, u3a_balign, C3_ALGHI); + c3_w* rut_w = c3_align(mem_w + len_w, u3a_balign, C3_ALGLO); c3_w* cap_w = mat_w + siz_w; u3e_ward(u3of(c3_w, cap_w) - 1, u3of(c3_w, rut_w)); @@ -574,12 +575,9 @@ _pave_south(c3_w* mem_w, c3_w siz_w, c3_w len_w) static void _pave_home(void) { - /* a pristine home road will always have compressed references */ - u3a_config_loom(U3V_VERLAT); - - c3_w* mem_w = u3_Loom + u3C.walign_w; + c3_w* mem_w = u3_Loom + u3a_walign; c3_w siz_w = c3_wiseof(u3v_home); - c3_w len_w = u3C.wor_i - u3C.walign_w; + c3_w len_w = u3C.wor_i - u3a_walign; u3H = (void *)_pave_north(mem_w, siz_w, len_w, c3n); u3H->ver_w = U3V_VERLAT; @@ -597,14 +595,29 @@ static void _find_home(void) { c3_w ver_w = *(u3_Loom + u3C.wor_i - 1); - u3a_config_loom(ver_w); + c3_o mig_o = c3y; // did we migrate? + + switch ( ver_w ) { + case U3V_VER1: u3m_v2_migrate(); + case U3V_VER2: u3m_v3_migrate(); + case U3V_VER3: { + mig_o = c3n; + break; + } + default: { + fprintf(stderr, "loom: checkpoint version mismatch: " + "have %u, need %u\r\n", + ver_w, U3V_VERLAT); + abort(); + } + } // NB: the home road is always north // - c3_w* mem_w = u3_Loom + u3C.walign_w; + c3_w* mem_w = u3_Loom + u3a_walign; c3_w siz_w = c3_wiseof(u3v_home); - c3_w len_w = u3C.wor_i - u3C.walign_w; - c3_w* mat_w = c3_align(mem_w + len_w - siz_w, u3C.balign_d, C3_ALGLO); + c3_w len_w = u3C.wor_i - u3a_walign; + c3_w* mat_w = c3_align(mem_w + len_w - siz_w, u3a_balign, C3_ALGLO); u3H = (void *)mat_w; u3R = &u3H->rod_u; @@ -616,7 +629,7 @@ _find_home(void) // check for obvious corruption // - { + if ( c3n == mig_o ) { c3_w nor_w, sou_w; u3_post low_p, hig_p; u3m_water(&low_p, &hig_p); @@ -637,23 +650,15 @@ _find_home(void) fprintf(stderr, "loom: strange size north (%u, %u)\r\n", nor_w, u3P.nor_u.pgs_w); } + + // XX move me + // + u3a_ream(); } /* As a further guard against any sneaky loom corruption */ u3a_loom_sane(); - if (U3V_VERLAT > ver_w) { - u3m_migrate(U3V_VERLAT); - u3a_config_loom(U3V_VERLAT); - } - else if ( U3V_VERLAT < ver_w ) { - fprintf(stderr, "loom: checkpoint version mismatch: " - "have %u, need %u\r\n", - ver_w, - U3V_VERLAT); - abort(); - } - _rod_vaal(u3R); } @@ -748,13 +753,6 @@ u3m_dump(void) c3_i u3m_bail(u3_noun how) { - if ( &(u3H->rod_u) == u3R ) { - // XX set exit code - // - fprintf(stderr, "home: bailing out\r\n"); - abort(); - } - // printf some metadata // switch ( how ) { @@ -779,6 +777,13 @@ u3m_bail(u3_noun how) } } + if ( &(u3H->rod_u) == u3R ) { + // XX set exit code + // + fprintf(stderr, "home: bailing out\r\n"); + abort(); + } + // intercept fatal errors // switch ( how ) { @@ -865,7 +870,7 @@ u3m_leap(c3_w pad_w) } pad_w += c3_wiseof(u3a_road); len_w = u3a_open(u3R) - pad_w; - c3_align(len_w, u3C.walign_w, C3_ALGHI); + c3_align(len_w, u3a_walign, C3_ALGHI); } /* Allocate a region on the cap. @@ -996,27 +1001,27 @@ 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; // fallback to parent road (child heap on parent's stack) // u3m_fall(); - // copy product and caches off our stack + // copy product off our stack // - pro = u3a_take(pro); - jed_u = u3j_take(jed_u); - byc_p = u3n_take(byc_p); + pro = u3a_take(pro); + + // integrate junior caches + // + u3j_reap(&jed_u); + u3n_reap(byc_p); + u3z_reap(per_p); // pop the stack // u3R->cap_p = u3R->ear_p; u3R->ear_p = 0; - // integrate junior caches - // - u3j_reap(jed_u); - u3n_reap(byc_p); - return pro; } @@ -1762,14 +1767,6 @@ _cm_limits(void) # endif } -/* u3m_backup(): copy snapshot to .urb/bhk (if it doesn't exist yet). -*/ -c3_o -u3m_backup(c3_o ovw_o) -{ - return u3e_backup(ovw_o); -} - /* u3m_fault(): handle a memory event with libsigsegv protocol. */ c3_i @@ -2090,6 +2087,42 @@ u3m_stop() u3je_secp_stop(); } +/* u3m_pier(): make a pier. +*/ +c3_c* +u3m_pier(c3_c* dir_c) +{ + c3_c ful_c[8193]; + + u3C.dir_c = dir_c; + + snprintf(ful_c, 8192, "%s", dir_c); + if ( c3_mkdir(ful_c, 0700) ) { + if ( EEXIST != errno ) { + fprintf(stderr, "loom: pier create: %s\r\n", strerror(errno)); + exit(1); + } + } + + snprintf(ful_c, 8192, "%s/.urb", dir_c); + if ( c3_mkdir(ful_c, 0700) ) { + if ( EEXIST != errno ) { + fprintf(stderr, "loom: .urb create: %s\r\n", strerror(errno)); + exit(1); + } + } + + snprintf(ful_c, 8192, "%s/.urb/chk", dir_c); + if ( c3_mkdir(ful_c, 0700) ) { + if ( EEXIST != errno ) { + fprintf(stderr, "loom: .urb/chk create: %s\r\n", strerror(errno)); + exit(1); + } + } + + return strdup(dir_c); +} + /* u3m_boot(): start the u3 system. return next event, starting from 1. */ c3_d @@ -2105,7 +2138,7 @@ u3m_boot(c3_c* dir_c, size_t len_i) /* Activate the storage system. */ - nuu_o = u3e_live(c3n, dir_c); + nuu_o = u3e_live(c3n, u3m_pier(dir_c)); /* Activate tracing. */ @@ -2174,7 +2207,7 @@ u3m_boot_lite(size_t len_i) return 0; } -/* u3m_reclaim: clear persistent caches to reclaim memory +/* u3m_reclaim: clear persistent caches to reclaim memory. */ void u3m_reclaim(void) @@ -2190,7 +2223,7 @@ u3m_reclaim(void) static void _cm_pack_rewrite(void) { - // XX fix u3a_rewrit* to support south roads + // XX fix u3a_rewrite* to support south roads // u3_assert( &(u3H->rod_u) == u3R ); @@ -2228,158 +2261,3 @@ u3m_pack(void) return (u3a_open(u3R) - pre_w); } - -static void -_migrate_reclaim() -{ - fprintf(stderr, "loom: migration reclaim\r\n"); - u3m_reclaim(); -} - -static void -_migrate_seek(const u3a_road *rod_u) -{ - /* - very much like u3a_pack_seek with the following changes: - - there is no need to account for free space as |pack is performed before - the migration - - odd sized boxes will be padded by one word to achieve an even size - - rut will be moved from one word ahead of u3_Loom to two words ahead - */ - c3_w * box_w = u3a_into(rod_u->rut_p); - c3_w * end_w = u3a_into(rod_u->hat_p); - u3_post new_p = (rod_u->rut_p + 1 + c3_wiseof(u3a_box)); - u3a_box * box_u = (void *)box_w; - - fprintf(stderr, "loom: migration seek\r\n"); - - for (; box_w < end_w - ; box_w += box_u->siz_w - , box_u = (void*)box_w) - { - if (!box_u->use_w) - continue; - u3_assert(box_u->siz_w); - u3_assert(box_u->use_w); - box_w[box_u->siz_w - 1] = new_p; - new_p = c3_align(new_p + box_u->siz_w, 2, C3_ALGHI); - } -} - -static void -_migrate_rewrite() -{ - fprintf(stderr, "loom: migration rewrite\r\n"); - - /* So that rewritten pointers are compressed, this flag is set */ - u3C.migration_state = MIG_REWRITE_COMPRESSED; - _cm_pack_rewrite(); - u3C.migration_state = MIG_NONE; -} - -static void -_migrate_move(u3a_road *rod_u) -{ - fprintf(stderr, "loom: migration move\r\n"); - - c3_z hiz_z = u3a_heap(rod_u) * sizeof(c3_w); - - /* calculate required shift distance to prevent write head overlapping read head */ - c3_w off_w = 1; /* at least 1 word because u3R->rut_p migrates from 1 to 2 */ - for (u3a_box *box_u = u3a_into(rod_u->rut_p) - ; (void *)box_u < u3a_into(rod_u->hat_p) - ; box_u = (void *)((c3_w *)box_u + box_u->siz_w)) - off_w += box_u->siz_w & 1; /* odd-sized boxes are padded by one word */ - - /* shift */ - memmove(u3a_into(u3H->rod_u.rut_p + off_w), - u3a_into(u3H->rod_u.rut_p), - hiz_z); - /* manually zero the former rut */ - *(c3_w *)u3a_into(rod_u->rut_p) = 0; - - /* relocate boxes to DWORD-aligned addresses stored in trailing size word */ - c3_w *box_w = u3a_into(rod_u->rut_p + off_w); - c3_w *end_w = u3a_into(rod_u->hat_p + off_w); - u3a_box *old_u = (void *)box_w; - c3_w siz_w = old_u->siz_w; - u3p(c3_w) new_p = rod_u->rut_p + 1 + c3_wiseof(u3a_box); - c3_w *new_w; - - for (; box_w < end_w - ; box_w += siz_w - , old_u = (void *)box_w - , siz_w = old_u->siz_w) { - old_u->use_w &= 0x7fffffff; - - if (!old_u->use_w) - continue; - - new_w = (void *)u3a_botox(u3a_into(new_p)); - u3_assert(box_w[siz_w - 1] == new_p); - u3_assert(new_w <= box_w); - - c3_w i_w; - for (i_w = 0; i_w < siz_w - 1; i_w++) - new_w[i_w] = box_w[i_w]; - - if (siz_w & 1) { - new_w[i_w++] = 0; /* pad odd sized boxes */ - new_w[i_w++] = siz_w + 1; /* restore trailing size word */ - new_w[0] = siz_w + 1; /* and the leading size word */ - } - else { - new_w[i_w++] = siz_w; - } - - new_p += i_w; - } - - /* restore proper heap state */ - rod_u->rut_p = 2; - rod_u->hat_p = new_p - c3_wiseof(u3a_box); - - /* like |pack, clear the free lists and cell allocator */ - for (c3_w i_w = 0; i_w < u3a_fbox_no; i_w++) - u3R->all.fre_p[i_w] = 0; - - u3R->all.fre_w = 0; - u3R->all.cel_p = 0; -} - - -/* u3m_migrate: perform loom migration if necessary. - ver_w - target version -*/ -void -u3m_migrate(u3v_version ver_w) -{ - if (u3H->ver_w == ver_w) - return; - - /* 1 -> 2 is all that is currently supported */ - c3_dessert(u3H->ver_w == U3V_VER1 && - ver_w == U3V_VER2); - - /* only home road migration is supported */ - c3_dessert((uintptr_t)u3H == (uintptr_t)u3R); - - fprintf(stderr, "loom: migration running. This may take several minutes to perform.\r\n"); - fprintf(stderr, "loom: have version: %"PRIc3_w" migrating to version: %"PRIc3_w"\r\n", - u3H->ver_w, ver_w); - - /* packing first simplifies migration logic and minimizes required buffer space */ - u3m_pack(); - - /* perform the migration in a pattern similar to |pack */ - _migrate_reclaim(); - _migrate_seek(&u3H->rod_u); - _migrate_rewrite(); - _migrate_move(&u3H->rod_u); - - /* finally update the version and commit to disk */ - u3H->ver_w = ver_w; - /* extra assurance we haven't corrupted the loom before writing to disk */ - u3a_loom_sane(); - u3m_save(); -} diff --git a/pkg/noun/manage.h b/pkg/noun/manage.h index 297585c486..14f9cc503d 100644 --- a/pkg/noun/manage.h +++ b/pkg/noun/manage.h @@ -3,6 +3,9 @@ #ifndef U3_MANAGE_H #define U3_MANAGE_H +#include "v1/manage.h" +#include "v2/manage.h" + #include "c3.h" #include "types.h" #include "version.h" @@ -14,6 +17,11 @@ c3_d u3m_boot(c3_c* dir_c, size_t len_i); + /* u3m_pier(): make a pier. + */ + c3_c* + u3m_pier(c3_c* dir_c); + /* u3m_boot_lite(): start without checkpointing. */ c3_d @@ -180,7 +188,7 @@ void u3m_wall(u3_noun wol); - /* u3m_reclaim: clear persistent caches to reclaim memory + /* u3m_reclaim: clear persistent caches to reclaim memory. */ void u3m_reclaim(void); @@ -190,10 +198,4 @@ c3_w u3m_pack(void); - /* u3m_migrate: perform loom migration if necessary. - ver_w - target version - */ - void - u3m_migrate(u3v_version ver_w); - #endif /* ifndef U3_MANAGE_H */ diff --git a/pkg/noun/nock.c b/pkg/noun/nock.c index 6ac08779ef..daeefdea61 100644 --- a/pkg/noun/nock.c +++ b/pkg/noun/nock.c @@ -619,7 +619,7 @@ _n_melt(u3_noun ops, c3_w* byc_w, c3_w* cal_w, case SKIB: case SLIB: { c3_l tot_l = 0, - sip_l = u3h(u3t(op)); + sip_l = u3h(u3t(u3t(op))); c3_w j_w, k_w = i_w; for ( j_w = 0; j_w < sip_l; ++j_w ) { tot_l += siz_y[++k_w]; @@ -830,7 +830,9 @@ _n_prog_asm(u3_noun ops, u3n_prog* pog_u, u3_noun sip) _n_prog_asm_inx(buf_y, &i_w, mem_s, cod); mem_u = &(pog_u->mem_u.sot_u[mem_s++]); mem_u->sip_l = sip_l; - mem_u->key = u3k(u3t(u3t(op))); + // [op_y, cid, mem_w, nef] + mem_u->key = u3k(u3t(u3t(u3t(op)))); + mem_u->cid = u3h(u3t(op)); break; } @@ -1117,16 +1119,21 @@ _n_bint(u3_noun* ops, u3_noun hif, u3_noun nef, c3_o los_o, c3_o tel_o) c3_w mem_w = 0; c3_y op_y; - // we just throw away the hint (why is this not a static hint?) tot_w += _n_comp(ops, hod, c3n, c3n); ++tot_w; _n_emit(ops, TOSS); - // memoizing code always loses TOS because SAVE needs [pro key] mem_w += _n_comp(&mem, nef, c3y, c3n); ++mem_w; _n_emit(&mem, SAVE); op_y = (c3y == los_o) ? SLIB : SKIB; // overflows to SLIS / SKIS - ++tot_w; _n_emit(ops, u3nt(op_y, mem_w, u3k(nef))); + u3z_cid cid = u3z_memo_toss; + { + u3_weak con = u3r_skip(hod); + if ( (u3_none != con) && (c3y == u3du(con)) ) { + cid = u3z_memo_keep; + } + } + ++tot_w; _n_emit(ops, u3nq(op_y, cid, mem_w, u3k(nef))); tot_w += mem_w; _n_apen(ops, mem); break; } @@ -2610,9 +2617,9 @@ _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(144 + c3__nock, x); + o = u3z_find_m(mem_u->cid, 144 + c3__nock, x); if ( u3_none == o ) { - _n_push(mov, off, x); + _n_push(mov, off, u3nc(mem_u->cid, x)); _n_push(mov, off, u3k(u3h(x))); } else { @@ -2623,11 +2630,16 @@ _n_burn(u3n_prog* pog_u, u3_noun bus, c3_ys mov, c3_ys off) BURN(); do_save: - x = _n_pep(mov, off); + x = _n_pep(mov, off); // product top = _n_peek(off); o = *top; - if ( &(u3H->rod_u) != u3R ) { - u3z_save_m(144 + c3__nock, o, x); + 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); + } + else if ( u3z_memo_keep == u3h(o) ) { + fprintf(stderr, "\r\nnock: userspace can't save to persistent cache\r\n"); } *top = x; u3z(o); @@ -2853,6 +2865,7 @@ _cn_take_prog_dat(u3n_prog* dst_u, u3n_prog* src_u) u3n_memo* ome_u = &(dst_u->mem_u.sot_u[i_w]); ome_u->sip_l = emo_u->sip_l; ome_u->key = u3a_take(emo_u->key); + ome_u->cid = emo_u->cid; } for ( i_w = 0; i_w < src_u->cal_u.len_w; ++i_w ) { @@ -2866,12 +2879,11 @@ _cn_take_prog_dat(u3n_prog* dst_u, u3n_prog* src_u) } } -/* _cn_take_prog_cb(): u3h_take_with cb for taking junior u3n_prog's. +/* _cn_take_prog(): take junior u3n_prog. */ -static u3p(u3n_prog) -_cn_take_prog_cb(u3p(u3n_prog) pog_p) +static u3n_prog* +_cn_take_prog(u3n_prog* pog_u) { - u3n_prog* pog_u = u3to(u3n_prog, pog_p); u3n_prog* gop_u; if ( c3y == pog_u->byc_u.own_o ) { @@ -2887,17 +2899,8 @@ _cn_take_prog_cb(u3p(u3n_prog) pog_p) } _cn_take_prog_dat(gop_u, pog_u); - // _n_prog_take_dat(gop_u, pog_u, c3n); - - return u3of(u3n_prog, gop_u); -} -/* u3n_take(): copy junior bytecode state. -*/ -u3p(u3h_root) -u3n_take(u3p(u3h_root) har_p) -{ - return u3h_take_with(har_p, _cn_take_prog_cb); + return gop_u; } /* _cn_merge_prog_dat(): copy references from src_u u3n_prog to dst_u. @@ -2918,6 +2921,7 @@ _cn_merge_prog_dat(u3n_prog* dst_u, u3n_prog* src_u) u3z(emo_u->key); emo_u->sip_l = ome_u->sip_l; emo_u->key = ome_u->key; + emo_u->cid = ome_u->cid; } for ( i_w = 0; i_w < src_u->cal_u.len_w; ++i_w ) { @@ -2931,29 +2935,36 @@ _cn_merge_prog_dat(u3n_prog* dst_u, u3n_prog* src_u) } } -/* _cn_merge_prog_cb(): u3h_walk_with cb for integrating taken u3n_prog's. +/* _cn_reap_prog_cb(): promote junior bytecode entry. */ static void -_cn_merge_prog_cb(u3_noun kev, void* wit) +_cn_reap_prog_cb(u3_cell kev) { - u3p(u3h_root) har_p = *(u3p(u3h_root)*)wit; - u3n_prog* pog_u; - u3_weak got; - u3_noun key; - u3p(u3n_prog) pog_p; - u3x_cell(kev, &key, &pog_p); - - pog_u = u3to(u3n_prog, pog_p); - got = u3h_git(har_p, key); + u3a_cell* kev_u = u3a_to_ptr(kev); + u3_noun key = u3a_take(kev_u->hed); + u3n_prog* pog_u = u3to(u3n_prog, kev_u->tel); + u3_weak got = u3h_git(u3R->byc.har_p, key); - if ( u3_none != got ) { + // promote + // + if ( u3_none == got ) { + pog_u = _cn_take_prog(pog_u); + } + // integrate + // + else { u3n_prog* sep_u = u3to(u3n_prog, got); + _cn_take_prog_dat(pog_u, pog_u); _cn_merge_prog_dat(sep_u, pog_u); - u3a_free(pog_u); pog_u = sep_u; } - u3h_put(har_p, key, u3of(u3n_prog, pog_u)); + // we must always put, because we have taken. + // we must always keep what we have taken, + // or we can break relocation pointers. + // + u3h_put(u3R->byc.har_p, key, u3of(u3n_prog, pog_u)); + u3z(key); } /* u3n_reap(): promote bytecode state. @@ -2961,9 +2972,7 @@ _cn_merge_prog_cb(u3_noun kev, void* wit) void u3n_reap(u3p(u3h_root) har_p) { - u3h_walk_with(har_p, _cn_merge_prog_cb, &u3R->byc.har_p); - // NB *not* u3n_free, _cn_merge_prog_cb() transfers u3n_prog's - u3h_free(har_p); + u3h_walk(har_p, _cn_reap_prog_cb); } /* _n_ream(): ream program call sites diff --git a/pkg/noun/nock.h b/pkg/noun/nock.h index 7dded22058..e680327c74 100644 --- a/pkg/noun/nock.h +++ b/pkg/noun/nock.h @@ -8,6 +8,7 @@ #include "c3.h" #include "jets.h" #include "types.h" +#include "zave.h" /** Data structures. *** @@ -18,6 +19,7 @@ typedef struct { c3_l sip_l; u3_noun key; + u3z_cid cid; } u3n_memo; /* u3n_prog: program compiled from nock @@ -114,11 +116,6 @@ void u3n_reap(u3p(u3h_root) har_p); - /* u3n_take(): copy junior bytecode state. - */ - u3p(u3h_root) - u3n_take(u3p(u3h_root) har_p); - /* u3n_mark(): mark bytecode cache. */ c3_w diff --git a/pkg/noun/options.h b/pkg/noun/options.h index 1bd65320fc..29ca6ce9d9 100644 --- a/pkg/noun/options.h +++ b/pkg/noun/options.h @@ -15,16 +15,10 @@ c3_c* dir_c; // execution directory (pier) c3_c* eph_c; // ephemeral file c3_w wag_w; // flags (both ways) - c3_w vits_w; // number of virtual bits in reference - c3_w walign_w; // word alignment - c3_d balign_d; // byte alignment - enum { - MIG_NONE, - MIG_REWRITE_COMPRESSED, - } migration_state; - size_t wor_i; // loom word-length (<= u3a_words) c3_w tos_w; // loom toss skip-length + c3_w hap_w; // transient memoization cache size + c3_w per_w; // persistent memoization cache size void (*stderr_log_f)(c3_c*); // errors from c code void (*slog_f)(u3_noun); // function pointer for slog void (*sign_hold_f)(void); // suspend system signal regime diff --git a/pkg/noun/retrieve.c b/pkg/noun/retrieve.c index d1790c7320..8f9847ff92 100644 --- a/pkg/noun/retrieve.c +++ b/pkg/noun/retrieve.c @@ -9,6 +9,35 @@ #include "trace.h" #include "xtract.h" + +// declarations of inline functions +// +c3_o +u3r_cell(u3_noun a, u3_noun* b, u3_noun* c); +c3_o +u3r_trel(u3_noun a, u3_noun* b, u3_noun* c, u3_noun* d); +c3_o +u3r_qual(u3_noun a, + u3_noun* b, + u3_noun* c, + u3_noun* d, + u3_noun* e); +c3_o +u3r_quil(u3_noun a, + u3_noun* b, + u3_noun* c, + u3_noun* d, + u3_noun* e, + u3_noun* f); +c3_o +u3r_hext(u3_noun a, + u3_noun* b, + u3_noun* c, + u3_noun* d, + u3_noun* e, + u3_noun* f, + u3_noun* g); + /* _frag_word(): fast fragment/branch prediction for top word. */ static u3_weak @@ -833,27 +862,6 @@ u3r_bite(u3_noun bite, u3_atom* bloq, u3_atom *step) } } -/* u3r_cell(): -** -** Factor (a) as a cell (b c). -*/ -c3_o -u3r_cell(u3_noun a, - u3_noun* b, - u3_noun* c) -{ - u3_assert(u3_none != a); - - if ( _(u3a_is_atom(a)) ) { - return c3n; - } - else { - if ( b ) *b = u3a_h(a); - if ( c ) *c = u3a_t(a); - return c3y; - } -} - /* u3r_p(): ** ** & [0] if [a] is of the form [b *c]. @@ -937,90 +945,6 @@ u3r_pqrs(u3_noun a, else return c3n; } -/* u3r_trel(): -** -** Factor (a) as a trel (b c d). -*/ -c3_o -u3r_trel(u3_noun a, - u3_noun *b, - u3_noun *c, - u3_noun *d) -{ - u3_noun guf; - - if ( (c3y == u3r_cell(a, b, &guf)) && - (c3y == u3r_cell(guf, c, d)) ) { - return c3y; - } - else { - return c3n; - } -} - -/* u3r_qual(): -** -** Factor (a) as a qual (b c d e). -*/ -c3_o -u3r_qual(u3_noun a, - u3_noun* b, - u3_noun* c, - u3_noun* d, - u3_noun* e) -{ - u3_noun guf; - - if ( (c3y == u3r_cell(a, b, &guf)) && - (c3y == u3r_trel(guf, c, d, e)) ) { - return c3y; - } - else return c3n; -} - -/* u3r_quil(): -** -** Factor (a) as a quil (b c d e f). -*/ -c3_o -u3r_quil(u3_noun a, - u3_noun* b, - u3_noun* c, - u3_noun* d, - u3_noun* e, - u3_noun* f) -{ - u3_noun guf; - - if ( (c3y == u3r_cell(a, b, &guf)) && - (c3y == u3r_qual(guf, c, d, e, f)) ) { - return c3y; - } - else return c3n; -} - -/* u3r_hext(): -** -** Factor (a) as a hext (b c d e f g) -*/ -c3_o -u3r_hext(u3_noun a, - u3_noun* b, - u3_noun* c, - u3_noun* d, - u3_noun* e, - u3_noun* f, - u3_noun* g) -{ - u3_noun guf; - - if ( (c3y == u3r_cell(a, b, &guf)) && - (c3y == u3r_quil(guf, c, d, e, f, g)) ) { - return c3y; - } - else return c3n; -} - /* u3r_met(): ** ** Return the size of (b) in bits, rounded up to @@ -1903,3 +1827,29 @@ u3r_mug(u3_noun veb) return mug_l; } + +/* u3r_skip(): +** +** Extract a constant from a formula, ignoring +** safe/static hints, doing no computation. +*/ +u3_weak +u3r_skip(u3_noun fol) +{ + while ( c3y == u3du(fol) ) { + switch ( u3h(fol) ) { + default: return u3_none; + case 1: return u3t(fol); + case 11: { + u3_noun arg = u3t(fol), + hod = u3h(arg); + + if ( (c3y == u3du(hod)) && (u3_none == u3r_skip(u3t(hod))) ) { + return u3_none; + } + fol = u3t(arg); + } + } + } + return u3_none; +} diff --git a/pkg/noun/retrieve.h b/pkg/noun/retrieve.h index a98a5e4697..7423ad9f49 100644 --- a/pkg/noun/retrieve.h +++ b/pkg/noun/retrieve.h @@ -4,11 +4,107 @@ #define U3_RETRIEVE_H #include "c3.h" +#include "allocate.h" +#include "error.h" #include "gmp.h" #include "types.h" /** u3r_*: read without ever crashing. **/ + + /* u3r_cell(): factor (a) as a cell (b c). + */ + inline c3_o + u3r_cell(u3_noun a, u3_noun* b, u3_noun* c) + { + u3a_cell* cel_u; + + u3_assert(u3_none != a); + + if ( c3y == u3a_is_cell(a) ) { + cel_u = u3a_to_ptr(a); + if ( b ) *b = cel_u->hed; + if ( c ) *c = cel_u->tel; + return c3y; + } + else { + return c3n; + } + } + + /* u3r_trel(): factor (a) as a trel (b c d). + */ + inline c3_o + u3r_trel(u3_noun a, u3_noun *b, u3_noun *c, u3_noun *d) + { + u3_noun guf; + + if ( (c3y == u3r_cell(a, b, &guf)) && + (c3y == u3r_cell(guf, c, d)) ) { + return c3y; + } + else { + return c3n; + } + } + + /* u3r_qual(): factor (a) as a qual (b c d e). + */ + inline c3_o + u3r_qual(u3_noun a, + u3_noun* b, + u3_noun* c, + u3_noun* d, + u3_noun* e) + { + u3_noun guf; + + if ( (c3y == u3r_cell(a, b, &guf)) && + (c3y == u3r_trel(guf, c, d, e)) ) { + return c3y; + } + else return c3n; + } + + /* u3r_quil(): factor (a) as a quil (b c d e f). + */ + inline c3_o + u3r_quil(u3_noun a, + u3_noun* b, + u3_noun* c, + u3_noun* d, + u3_noun* e, + u3_noun* f) + { + u3_noun guf; + + if ( (c3y == u3r_cell(a, b, &guf)) && + (c3y == u3r_qual(guf, c, d, e, f)) ) { + return c3y; + } + else return c3n; + } + + /* u3r_hext(): factor (a) as a hext (b c d e f g) + */ + inline c3_o + u3r_hext(u3_noun a, + u3_noun* b, + u3_noun* c, + u3_noun* d, + u3_noun* e, + u3_noun* f, + u3_noun* g) + { + u3_noun guf; + + if ( (c3y == u3r_cell(a, b, &guf)) && + (c3y == u3r_quil(guf, c, d, e, f, g)) ) { + return c3y; + } + else return c3n; + } + /* u3r_at(): fragment `a` of `b`, or u3_none. */ u3_weak @@ -183,61 +279,6 @@ c3_o u3r_bite(u3_noun bite, u3_atom* bloq, u3_atom *step); - /* u3r_cell(): - ** - ** Divide `a` as a cell `[b c]`. - */ - c3_o - u3r_cell(u3_noun a, - u3_noun* b, - u3_noun* c); - - /* u3r_trel(): - ** - ** Divide `a` as a trel `[b c d]`. - */ - c3_o - u3r_trel(u3_noun a, - u3_noun* b, - u3_noun* c, - u3_noun* d); - - /* u3r_qual(): - ** - ** Divide (a) as a qual [b c d e]. - */ - c3_o - u3r_qual(u3_noun a, - u3_noun* b, - u3_noun* c, - u3_noun* d, - u3_noun* e); - - /* u3r_quil(): - ** - ** Divide (a) as a quil [b c d e f]. - */ - c3_o - u3r_quil(u3_noun a, - u3_noun* b, - u3_noun* c, - u3_noun* d, - u3_noun* e, - u3_noun* f); - - /* u3r_hext(): - ** - ** Divide (a) as a hext [b c d e f g]. - */ - c3_o - u3r_hext(u3_noun a, - u3_noun* b, - u3_noun* c, - u3_noun* d, - u3_noun* e, - u3_noun* f, - u3_noun* g); - /* u3r_p(): ** ** & [0] if [a] is of the form [b *c]. @@ -486,4 +527,12 @@ c3_y* u3r_tape(u3_noun a); + /* u3r_skip(): + ** + ** Extract a constant from a formula, ignoring + ** safe/static hints, doing no computation. + */ + u3_weak + u3r_skip(u3_noun fol); + #endif /* ifndef U3_RETRIEVE_H */ diff --git a/pkg/noun/serial.c b/pkg/noun/serial.c index 698a959d66..f994814fe1 100644 --- a/pkg/noun/serial.c +++ b/pkg/noun/serial.c @@ -36,7 +36,7 @@ struct _cs_jam_fib { /* _cs_jam_fib_grow(): reallocate buffer with fibonacci growth */ -static void +static inline void _cs_jam_fib_grow(struct _cs_jam_fib* fib_u, c3_w mor_w) { c3_w wan_w = fib_u->bit_w + mor_w; @@ -45,6 +45,7 @@ _cs_jam_fib_grow(struct _cs_jam_fib* fib_u, c3_w mor_w) // if ( wan_w < mor_w ) { u3m_bail(c3__fail); + return; } if ( wan_w > fib_u->a_w ) { @@ -64,7 +65,7 @@ _cs_jam_fib_grow(struct _cs_jam_fib* fib_u, c3_w mor_w) /* _cs_jam_fib_chop(): chop [met_w] bits of [a] into [fib_u] */ -static void +static inline void _cs_jam_fib_chop(struct _cs_jam_fib* fib_u, c3_w met_w, u3_noun a) { c3_w bit_w = fib_u->bit_w; @@ -86,12 +87,51 @@ _cs_jam_fib_mat(struct _cs_jam_fib* fib_u, u3_noun a) _cs_jam_fib_chop(fib_u, 1, 1); } else { - c3_w a_w = u3r_met(0, a); - c3_w b_w = c3_bits_word(a_w); + c3_w a_w = u3r_met(0, a); + c3_w b_w = c3_bits_word(a_w); + c3_w bit_w = fib_u->bit_w; + + // amortize overflow checks and reallocation + // + { + c3_w met_w = a_w + (2 * b_w); + + if ( a_w > (UINT32_MAX - 64) ) { + u3m_bail(c3__fail); + return; + } + + _cs_jam_fib_grow(fib_u, met_w); + fib_u->bit_w += met_w; + } - _cs_jam_fib_chop(fib_u, b_w+1, 1 << b_w); - _cs_jam_fib_chop(fib_u, b_w-1, a_w & ((1 << (b_w-1)) - 1)); - _cs_jam_fib_chop(fib_u, a_w, a); + { + c3_w src_w[2]; + c3_w* buf_w = fib_u->sab_u->buf_w; + + // _cs_jam_fib_chop(fib_u, b_w+1, 1 << b_w); + // + { + c3_d dat_d = (c3_d)1 << b_w; + src_w[0] = (c3_w)dat_d; + src_w[1] = dat_d >> 32; + + u3r_chop_words(0, 0, b_w + 1, bit_w, buf_w, 2, src_w); + bit_w += b_w + 1; + } + + // _cs_jam_fib_chop(fib_u, b_w-1, a_w); + // + { + src_w[0] = a_w; + u3r_chop_words(0, 0, b_w - 1, bit_w, buf_w, 1, src_w); + bit_w += b_w - 1; + } + + // _cs_jam_fib_chop(fib_u, a_w, a); + // + u3r_chop(0, 0, a_w, bit_w, buf_w, a); + } } } diff --git a/pkg/noun/trace.c b/pkg/noun/trace.c index 8768531d62..fd33169e8d 100644 --- a/pkg/noun/trace.c +++ b/pkg/noun/trace.c @@ -299,7 +299,7 @@ u3t_flee(void) /* u3t_trace_open(): opens a trace file and writes the preamble. */ void -u3t_trace_open(c3_c* dir_c) +u3t_trace_open(const c3_c* dir_c) { c3_c fil_c[2048]; diff --git a/pkg/noun/trace.h b/pkg/noun/trace.h index 667cdfae4b..695a5fe133 100644 --- a/pkg/noun/trace.h +++ b/pkg/noun/trace.h @@ -103,7 +103,7 @@ /* u3t_trace_open(): opens the path for writing tracing information. */ void - u3t_trace_open(c3_c *dir_c); + u3t_trace_open(const c3_c *dir_c); /* u3t_trace_close(): closes the trace file. optional. */ diff --git a/pkg/noun/v1/allocate.c b/pkg/noun/v1/allocate.c new file mode 100644 index 0000000000..7ce4521ecc --- /dev/null +++ b/pkg/noun/v1/allocate.c @@ -0,0 +1,238 @@ +/// @file + +#include "pkg/noun/allocate.h" +#include "pkg/noun/v1/allocate.h" + +#include "pkg/noun/v1/hashtable.h" + +/* _box_v1_slot(): select the right free list to search for a block. +*/ +static c3_w +_box_v1_slot(c3_w siz_w) +{ + if ( siz_w < u3a_v1_minimum ) { + return 0; + } + else { + c3_w i_w = 1; + + while ( 1 ) { + if ( i_w == u3a_v1_fbox_no ) { + return (i_w - 1); + } + if ( siz_w < 16 ) { + return i_w; + } + siz_w = (siz_w + 1) >> 1; + i_w += 1; + } + } +} + +/* _box_v1_make(): construct a box. +*/ +static u3a_v1_box* +_box_v1_make(void* box_v, c3_w siz_w, c3_w use_w) +{ + u3a_v1_box* box_u = box_v; + c3_w* box_w = box_v; + + u3_assert(siz_w >= u3a_v1_minimum); + + box_w[0] = siz_w; + box_w[siz_w - 1] = siz_w; + box_u->use_w = use_w; + +# ifdef U3_MEMORY_DEBUG + box_u->cod_w = u3_Code; + box_u->eus_w = 0; +# endif + + return box_u; +} + +/* _box_v1_attach(): attach a box to the free list. +*/ +static void +_box_v1_attach(u3a_v1_box* box_u) +{ + u3_assert(box_u->siz_w >= (1 + c3_wiseof(u3a_v1_fbox))); + u3_assert(0 != u3of(u3a_v1_fbox, box_u)); + + { + c3_w sel_w = _box_v1_slot(box_u->siz_w); + u3p(u3a_v1_fbox) fre_p = u3of(u3a_v1_fbox, box_u); + u3p(u3a_v1_fbox)* pfr_p = &u3R_v1->all.fre_p[sel_w]; + u3p(u3a_v1_fbox) nex_p = *pfr_p; + + u3to(u3a_v1_fbox, fre_p)->pre_p = 0; + u3to(u3a_v1_fbox, fre_p)->nex_p = nex_p; + if ( u3to(u3a_v1_fbox, fre_p)->nex_p ) { + u3to(u3a_v1_fbox, u3to(u3a_v1_fbox, fre_p)->nex_p)->pre_p = fre_p; + } + (*pfr_p) = fre_p; + } +} + +/* _box_v1_detach(): detach a box from the free list. +*/ +static void +_box_v1_detach(u3a_v1_box* box_u) +{ + u3p(u3a_v1_fbox) fre_p = u3of(u3a_v1_fbox, box_u); + u3p(u3a_v1_fbox) pre_p = u3to(u3a_v1_fbox, fre_p)->pre_p; + u3p(u3a_v1_fbox) nex_p = u3to(u3a_v1_fbox, fre_p)->nex_p; + + + if ( nex_p ) { + if ( u3to(u3a_v1_fbox, nex_p)->pre_p != fre_p ) { + u3_assert(!"loom: corrupt"); + } + u3to(u3a_v1_fbox, nex_p)->pre_p = pre_p; + } + if ( pre_p ) { + if( u3to(u3a_v1_fbox, pre_p)->nex_p != fre_p ) { + u3_assert(!"loom: corrupt"); + } + u3to(u3a_v1_fbox, pre_p)->nex_p = nex_p; + } + else { + c3_w sel_w = _box_v1_slot(box_u->siz_w); + + if ( fre_p != u3R_v1->all.fre_p[sel_w] ) { + u3_assert(!"loom: corrupt"); + } + u3R_v1->all.fre_p[sel_w] = nex_p; + } +} + +/* _box_v1_free(): free and coalesce. +*/ +static void +_box_v1_free(u3a_v1_box* box_u) +{ + c3_w* box_w = (c3_w *)(void *)box_u; + + u3_assert(box_u->use_w != 0); + box_u->use_w -= 1; + if ( 0 != box_u->use_w ) { + return; + } + + // we're always migrating a north road, so no need to check for it + { + /* Try to coalesce with the block below. + */ + if ( box_w != u3a_v1_into(u3R_v1->rut_p) ) { + c3_w laz_w = *(box_w - 1); + u3a_v1_box* pox_u = (u3a_v1_box*)(void *)(box_w - laz_w); + + if ( 0 == pox_u->use_w ) { + _box_v1_detach(pox_u); + _box_v1_make(pox_u, (laz_w + box_u->siz_w), 0); + + box_u = pox_u; + box_w = (c3_w*)(void *)pox_u; + } + } + + /* Try to coalesce with the block above, or the wilderness. + */ + if ( (box_w + box_u->siz_w) == u3a_v1_into(u3R_v1->hat_p) ) { + u3R_v1->hat_p = u3a_v1_outa(box_w); + } + else { + u3a_v1_box* nox_u = (u3a_v1_box*)(void *)(box_w + box_u->siz_w); + + if ( 0 == nox_u->use_w ) { + _box_v1_detach(nox_u); + _box_v1_make(box_u, (box_u->siz_w + nox_u->siz_w), 0); + } + _box_v1_attach(box_u); + } + } +} + +/* u3a_v1_wfree(): free storage. +*/ +void +u3a_v1_wfree(void* tox_v) +{ + _box_v1_free(u3a_v1_botox(tox_v)); +} + +/* u3a_v1_free(): free for aligned malloc. +*/ +void +u3a_v1_free(void* tox_v) +{ + if (NULL == tox_v) + return; + + c3_w* tox_w = tox_v; + c3_w pad_w = tox_w[-1]; + c3_w* org_w = tox_w - (pad_w + 1); + + // u3l_log("free %p %p", org_w, tox_w); + u3a_v1_wfree(org_w); +} + +/* u3a_v1_reclaim(): clear ad-hoc persistent caches to reclaim memory. +*/ +void +u3a_v1_reclaim(void) +{ + // clear the memoization cache + // + u3h_v1_free_nodes(u3R_v1->cax.har_p); +} + +/* _me_v1_lose_north(): lose on a north road. +*/ +static void +_me_v1_lose_north(u3_noun dog) +{ +top: + { + c3_w* dog_w = u3a_v1_to_ptr(dog); + u3a_v1_box* box_u = u3a_v1_botox(dog_w); + + if ( box_u->use_w > 1 ) { + box_u->use_w -= 1; + } + else { + if ( 0 == box_u->use_w ) { + u3m_v1_bail(c3__foul); + } + else { + if ( _(u3a_v1_is_pom(dog)) ) { + u3a_v1_cell* dog_u = (void *)dog_w; + u3_noun h_dog = dog_u->hed; + u3_noun t_dog = dog_u->tel; + + if ( !_(u3a_v1_is_cat(h_dog)) ) { + _me_v1_lose_north(h_dog); + } + u3a_v1_wfree(dog_w); + if ( !_(u3a_v1_is_cat(t_dog)) ) { + dog = t_dog; + goto top; + } + } + else { + u3a_v1_wfree(dog_w); + } + } + } + } +} + +/* u3a_v1_lose(): lose a reference count. +*/ +void +u3a_v1_lose(u3_noun som) +{ + if ( !_(u3a_v1_is_cat(som)) ) { + _me_v1_lose_north(som); + } +} diff --git a/pkg/noun/v1/allocate.h b/pkg/noun/v1/allocate.h new file mode 100644 index 0000000000..4544417b78 --- /dev/null +++ b/pkg/noun/v1/allocate.h @@ -0,0 +1,78 @@ +#ifndef U3_ALLOCATE_V1_H +#define U3_ALLOCATE_V1_H + +#include "pkg/noun/allocate.h" +#include "pkg/noun/v2/allocate.h" + + /** Aliases. + **/ +# define u3R_v1 u3R_v2 +# define u3a_v1_botox u3a_v2_botox +# define u3a_v1_box u3a_v2_box +# define u3a_v1_cell u3a_v2_cell +# define u3a_v1_fbox u3a_v2_fbox +# define u3a_v1_fbox_no u3a_v2_fbox_no +# define u3a_v1_into u3a_v2_into +# define u3a_v1_is_cat u3a_v2_is_cat +# define u3a_v1_is_north u3a_v2_is_north +# define u3a_v1_is_pom u3a_v2_is_pom +# define u3a_v1_minimum u3a_v2_minimum +# define u3a_v1_outa u3a_v2_outa + + /** Structures. + **/ + /** Macros. Should be better commented. + **/ + /* Inside a noun. + */ + /* u3a_v1_to_off(): mask off bits 30 and 31 from noun [som]. + */ +# define u3a_v1_to_off(som) ((som) & 0x3fffffff) + + /* u3a_v1_to_ptr(): convert noun [som] into generic pointer into loom. + */ +# define u3a_v1_to_ptr(som) (u3a_v1_into(u3a_v1_to_off(som))) + + /* u3a_v1_to_wtr(): convert noun [som] into word pointer into loom. + */ +# define u3a_v1_to_wtr(som) ((c3_w *)u3a_v1_to_ptr(som)) + + /* u3a_v1_to_pug(): set bit 31 of [off]. + */ +# define u3a_v1_to_pug(off) (off | 0x80000000) + + /* u3a_v1_to_pom(): set bits 30 and 31 of [off]. + */ +# define u3a_v1_to_pom(off) (off | 0xc0000000) + + /** Functions. + **/ + /** Allocation. + **/ + /* Word-aligned allocation. + */ + /* u3a_v1_wfree(): free storage. + */ + void + u3a_v1_wfree(void* lag_v); + + /* C-style aligned allocation - *not* compatible with above. + */ + /* u3a_v1_free(): free for aligned malloc. + */ + void + u3a_v1_free(void* tox_v); + + /* Reference and arena control. + */ + /* u3a_v1_reclaim(): clear ad-hoc persistent caches to reclaim memory. + */ + void + u3a_v1_reclaim(void); + + /* u3a_v1_lose(): lose a reference count. + */ + void + u3a_v1_lose(u3_noun som); + +#endif /* ifndef U3_ALLOCATE_H */ diff --git a/pkg/noun/v1/hashtable.c b/pkg/noun/v1/hashtable.c new file mode 100644 index 0000000000..aa3a9b7aef --- /dev/null +++ b/pkg/noun/v1/hashtable.c @@ -0,0 +1,173 @@ +/// @file + +#include "pkg/noun/hashtable.h" +#include "pkg/noun/v1/hashtable.h" + +#include "pkg/noun/allocate.h" +#include "pkg/noun/v1/allocate.h" + + +/* _ch_v1_popcount(): number of bits set in word. A standard intrinsic. +** NB: copy of _ch_v1_popcount in pkg/noun/hashtable.c +*/ +static c3_w +_ch_v1_popcount(c3_w num_w) +{ + return __builtin_popcount(num_w); +} + +/* _ch_v1_free_buck(): free bucket +** NB: copy of _ch_v1_free_buck in pkg/noun/hashtable.c +*/ +static void +_ch_v1_free_buck(u3h_v1_buck* hab_u) +{ + c3_w i_w; + + for ( i_w = 0; i_w < hab_u->len_w; i_w++ ) { + u3a_v1_lose(u3h_v1_slot_to_noun(hab_u->sot_w[i_w])); + } + u3a_v1_wfree(hab_u); +} + +/* _ch_v1_free_node(): free node. +*/ +static void +_ch_v1_free_node(u3h_v1_node* han_u, c3_w lef_w) +{ + c3_w len_w = _ch_v1_popcount(han_u->map_w); + c3_w i_w; + + lef_w -= 5; + + for ( i_w = 0; i_w < len_w; i_w++ ) { + c3_w sot_w = han_u->sot_w[i_w]; + + if ( _(u3h_v1_slot_is_noun(sot_w)) ) { + u3a_v1_lose(u3h_v1_slot_to_noun(sot_w)); + } + else { + void* hav_v = u3h_v1_slot_to_node(sot_w); + + if ( 0 == lef_w ) { + _ch_v1_free_buck(hav_v); + } else { + _ch_v1_free_node(hav_v, lef_w); + } + } + } + u3a_v1_wfree(han_u); +} + +/* u3h_v1_free_nodes(): free hashtable nodes. +*/ +void +u3h_v1_free_nodes(u3p(u3h_v1_root) har_p) +{ + u3h_v1_root* har_u = u3to(u3h_v1_root, har_p); + c3_w i_w; + + for ( i_w = 0; i_w < 64; i_w++ ) { + c3_w sot_w = har_u->sot_w[i_w]; + + if ( _(u3h_v1_slot_is_noun(sot_w)) ) { + u3a_v1_lose(u3h_v1_slot_to_noun(sot_w)); + } + else if ( _(u3h_v1_slot_is_node(sot_w)) ) { + u3h_v1_node* han_u = (u3h_v1_node*) u3h_v1_slot_to_node(sot_w); + + _ch_v1_free_node(han_u, 25); + } + har_u->sot_w[i_w] = 0; + } + har_u->use_w = 0; + har_u->arm_u.mug_w = 0; + har_u->arm_u.inx_w = 0; +} + +/* _ch_v1_walk_buck(): walk bucket for gc. +** NB: copy of _ch_v1_walk_buck in pkg/noun/hashtable.c +*/ +static void +_ch_v1_walk_buck(u3h_v1_buck* hab_u, void (*fun_f)(u3_noun, void*), void* wit) +{ + c3_w i_w; + + for ( i_w = 0; i_w < hab_u->len_w; i_w++ ) { + fun_f(u3h_v1_slot_to_noun(hab_u->sot_w[i_w]), wit); + } +} + +/* _ch_v1_walk_node(): walk node for gc. +*/ +static void +_ch_v1_walk_node(u3h_v1_node* han_u, c3_w lef_w, void (*fun_f)(u3_noun, void*), void* wit) +{ + c3_w len_w = _ch_v1_popcount(han_u->map_w); + c3_w i_w; + + lef_w -= 5; + + for ( i_w = 0; i_w < len_w; i_w++ ) { + c3_w sot_w = han_u->sot_w[i_w]; + + if ( _(u3h_v1_slot_is_noun(sot_w)) ) { + u3_noun kev = u3h_v1_slot_to_noun(sot_w); + + fun_f(kev, wit); + } + else { + void* hav_v = u3h_v1_slot_to_node(sot_w); + + if ( 0 == lef_w ) { + _ch_v1_walk_buck(hav_v, fun_f, wit); + } else { + _ch_v1_walk_node(hav_v, lef_w, fun_f, wit); + } + } + } +} + +/* u3h_v1_walk_with(): traverse hashtable with key, value fn and data + * argument; RETAINS. +*/ +void +u3h_v1_walk_with(u3p(u3h_v1_root) har_p, + void (*fun_f)(u3_noun, void*), + void* wit) +{ + u3h_v1_root* har_u = u3to(u3h_v1_root, har_p); + c3_w i_w; + + for ( i_w = 0; i_w < 64; i_w++ ) { + c3_w sot_w = har_u->sot_w[i_w]; + + if ( _(u3h_v1_slot_is_noun(sot_w)) ) { + u3_noun kev = u3h_v1_slot_to_noun(sot_w); + + fun_f(kev, wit); + } + else if ( _(u3h_v1_slot_is_node(sot_w)) ) { + u3h_v1_node* han_u = (u3h_v1_node*) u3h_v1_slot_to_node(sot_w); + + _ch_v1_walk_node(han_u, 25, fun_f, wit); + } + } +} + +/* _ch_v1_walk_plain(): use plain u3_noun fun_f for each node + */ +static void +_ch_v1_walk_plain(u3_noun kev, void* wit) +{ + void (*fun_f)(u3_noun) = wit; + fun_f(kev); +} + +/* u3h_v1_walk(): u3h_v1_walk_with, but with no data argument +*/ +void +u3h_v1_walk(u3p(u3h_v1_root) har_p, void (*fun_f)(u3_noun)) +{ + u3h_v1_walk_with(har_p, _ch_v1_walk_plain, fun_f); +} diff --git a/pkg/noun/v1/hashtable.h b/pkg/noun/v1/hashtable.h new file mode 100644 index 0000000000..2d5fdea6f3 --- /dev/null +++ b/pkg/noun/v1/hashtable.h @@ -0,0 +1,51 @@ +#ifndef U3_HASHTABLE_V1_H +#define U3_HASHTABLE_V1_H + +#include "pkg/noun/hashtable.h" +#include "pkg/noun/v2/hashtable.h" + + /** Aliases. + **/ +# define u3h_v1_buck u3h_v2_buck +# define u3h_v1_node u3h_v2_node +# define u3h_v1_root u3h_v2_root +# define u3h_v1_slot_is_node u3h_v2_slot_is_node +# define u3h_v1_slot_is_noun u3h_v2_slot_is_noun +# define u3h_v1_slot_to_noun u3h_v2_slot_to_noun + + /** Data structures. + **/ + + /** HAMT macros. + *** + *** Coordinate with u3_noun definition! + **/ + /* u3h_v1_slot_to_node(): slot to node pointer + ** u3h_v1_node_to_slot(): node pointer to slot + */ +# define u3h_v1_slot_to_node(sot) (u3a_v1_into((sot) & 0x3fffffff)) +# define u3h_v1_node_to_slot(ptr) (u3a_v1_outa(ptr) | 0x40000000) + + /** Functions. + *** + *** Needs: delete and merge functions; clock reclamation function. + **/ + /* u3h_v1_free(): free hashtable. + */ + void + u3h_v1_free_nodes(u3p(u3h_root) har_p); + + /* u3h_v1_walk_with(): traverse hashtable with key, value fn and data + * argument; RETAINS. + */ + void + u3h_v1_walk_with(u3p(u3h_root) har_p, + void (*fun_f)(u3_noun, void*), + void* wit); + + /* u3h_v1_walk(): u3h_v1_walk_with, but with no data argument + */ + void + u3h_v1_walk(u3p(u3h_root) har_p, void (*fun_f)(u3_noun)); + +#endif /* ifndef U3_HASHTABLE_H */ diff --git a/pkg/noun/v1/jets.c b/pkg/noun/v1/jets.c new file mode 100644 index 0000000000..272930e11d --- /dev/null +++ b/pkg/noun/v1/jets.c @@ -0,0 +1,87 @@ +/// @file + +#include "pkg/noun/vortex.h" + +#include "pkg/noun/jets.h" +#include "pkg/noun/v1/jets.h" +#include "pkg/noun/v2/jets.h" + +#include "pkg/noun/v1/allocate.h" +#include "pkg/noun/v1/hashtable.h" + + +/* _cj_fink_free(): lose and free everything in a u3j_v1_fink. +*/ +static void +_cj_v1_fink_free(u3p(u3j_v1_fink) fin_p) +{ + c3_w i_w; + u3j_v1_fink* fin_u = u3to(u3j_v1_fink, fin_p); + u3a_v1_lose(fin_u->sat); + for ( i_w = 0; i_w < fin_u->len_w; ++i_w ) { + u3j_v1_fist* fis_u = &(fin_u->fis_u[i_w]); + u3a_v1_lose(fis_u->bat); + u3a_v1_lose(fis_u->pax); + } + u3a_v1_wfree(fin_u); +} + +/* u3j_v1_rite_lose(): lose references of u3j_v1_rite (but do not free). + */ +void +u3j_v1_rite_lose(u3j_v1_rite* rit_u) +{ + if ( (c3y == rit_u->own_o) && u3_none != rit_u->clu ) { + u3a_v1_lose(rit_u->clu); + _cj_v1_fink_free(rit_u->fin_p); + } +} + + +/* u3j_v1_site_lose(): lose references of u3j_v1_site (but do not free). + */ +void +u3j_v1_site_lose(u3j_v1_site* sit_u) +{ + u3a_v1_lose(sit_u->axe); + if ( u3_none != sit_u->bat ) { + u3a_v1_lose(sit_u->bat); + } + if ( u3_none != sit_u->bas ) { + u3a_v1_lose(sit_u->bas); + } + if ( u3_none != sit_u->loc ) { + u3a_v1_lose(sit_u->loc); + u3a_v1_lose(sit_u->lab); + if ( c3y == sit_u->fon_o ) { + if ( sit_u->fin_p ) { + _cj_v1_fink_free(sit_u->fin_p); + } + } + } +} + +/* _cj_v1_free_hank(): free an entry from the hank cache. +*/ +static void +_cj_v1_free_hank(u3_noun kev) +{ + u3a_v1_cell* cel_u = (u3a_v1_cell*) u3a_v1_to_ptr(kev); + u3j_v1_hank* han_u = u3to(u3j_v1_hank, cel_u->tel); + if ( u3_none != han_u->hax ) { + u3a_v1_lose(han_u->hax); + u3j_v1_site_lose(&(han_u->sit_u)); + } + u3a_v1_wfree(han_u); +} + +/* u3j_v1_reclaim(): clear ad-hoc persistent caches to reclaim memory. +*/ +void +u3j_v1_reclaim(void) +{ + // clear the jet hank cache + // + u3h_v1_walk(u3R_v1->jed.han_p, _cj_v1_free_hank); + u3h_v1_free_nodes(u3R_v1->jed.han_p); +} diff --git a/pkg/noun/v1/jets.h b/pkg/noun/v1/jets.h new file mode 100644 index 0000000000..bf3dfe6afe --- /dev/null +++ b/pkg/noun/v1/jets.h @@ -0,0 +1,34 @@ +/// @file + +#ifndef U3_JETS_V1_H +#define U3_JETS_V1_H + +#include "pkg/noun/jets.h" +#include "pkg/noun/v2/jets.h" + + /** Aliases. + **/ +# define u3j_v1_fink u3j_v2_fink +# define u3j_v1_fist u3j_v2_fist +# define u3j_v1_hank u3j_v2_hank +# define u3j_v1_rite u3j_v2_rite +# define u3j_v1_site u3j_v2_site + + /** Functions. + **/ + /* u3j_v1_reclaim(): clear ad-hoc persistent caches to reclaim memory. + */ + void + u3j_v1_reclaim(void); + + /* u3j_v1_rite_lose(): lose references of u3j_rite (but do not free). + */ + void + u3j_v1_rite_lose(u3j_v1_rite* rit_u); + + /* u3j_v1_site_lose(): lose references of u3j_site (but do not free). + */ + void + u3j_v1_site_lose(u3j_v1_site* sit_u); + +#endif /* ifndef U3_JETS_V1_H */ diff --git a/pkg/noun/v1/manage.c b/pkg/noun/v1/manage.c new file mode 100644 index 0000000000..0eac6a8d7b --- /dev/null +++ b/pkg/noun/v1/manage.c @@ -0,0 +1,20 @@ +/// @file + +#include "pkg/noun/v1/manage.h" + +#include "pkg/noun/v1/allocate.h" +#include "pkg/noun/v1/hashtable.h" +#include "pkg/noun/v1/jets.h" +#include "pkg/noun/v1/nock.h" +#include "pkg/noun/v1/vortex.h" + +/* u3m_v1_reclaim: clear persistent caches to reclaim memory +*/ +void +u3m_v1_reclaim(void) +{ + u3v_v1_reclaim(); + u3j_v1_reclaim(); + u3n_v1_reclaim(); + u3a_v1_reclaim(); +} diff --git a/pkg/noun/v1/manage.h b/pkg/noun/v1/manage.h new file mode 100644 index 0000000000..669d69ea36 --- /dev/null +++ b/pkg/noun/v1/manage.h @@ -0,0 +1,17 @@ +/// @file + +#ifndef U3_MANAGE_V1_H +#define U3_MANAGE_V1_H + + /** Aliases. + **/ +# define u3m_v1_bail u3m_bail + + /** System management. + **/ + /* u3m_v1_reclaim: clear persistent caches to reclaim memory + */ + void + u3m_v1_reclaim(void); + +#endif /* ifndef U3_MANAGE_V1_H */ diff --git a/pkg/noun/v1/nock.c b/pkg/noun/v1/nock.c new file mode 100644 index 0000000000..1ac2931a97 --- /dev/null +++ b/pkg/noun/v1/nock.c @@ -0,0 +1,69 @@ +/// @file + +#include "pkg/noun/nock.h" +#include "pkg/noun/v1/nock.h" + +#include "pkg/noun/v1/allocate.h" +#include "pkg/noun/v1/hashtable.h" +#include "pkg/noun/jets.h" +#include "pkg/noun/v1/jets.h" + +/* u3n_v1_reclaim(): clear ad-hoc persistent caches to reclaim memory. +*/ +void +u3n_v1_reclaim(void) +{ + // clear the bytecode cache + // + // We can't just u3h_v1_free() -- the value is a post to a u3n_v1_prog. + // Note that the hank cache *must* also be freed (in u3j_v1_reclaim()) + // + u3n_v1_free(); +} + +/* _cn_v1_prog_free(): free memory retained by program pog_u +*/ +static void +_cn_v1_prog_free(u3n_v1_prog* pog_u) +{ + // fix up pointers for loom portability + pog_u->byc_u.ops_y = (c3_y*) ((void*) pog_u) + sizeof(u3n_v1_prog); + pog_u->lit_u.non = (u3_noun*) (pog_u->byc_u.ops_y + pog_u->byc_u.len_w); + pog_u->mem_u.sot_u = (u3n_v1_memo*) (pog_u->lit_u.non + pog_u->lit_u.len_w); + pog_u->cal_u.sit_u = (u3j_v1_site*) (pog_u->mem_u.sot_u + pog_u->mem_u.len_w); + pog_u->reg_u.rit_u = (u3j_v1_rite*) (pog_u->cal_u.sit_u + pog_u->cal_u.len_w); + + c3_w dex_w; + for (dex_w = 0; dex_w < pog_u->lit_u.len_w; ++dex_w) { + u3a_v1_lose(pog_u->lit_u.non[dex_w]); + } + for (dex_w = 0; dex_w < pog_u->mem_u.len_w; ++dex_w) { + u3a_v1_lose(pog_u->mem_u.sot_u[dex_w].key); + } + for (dex_w = 0; dex_w < pog_u->cal_u.len_w; ++dex_w) { + u3j_v1_site_lose(&(pog_u->cal_u.sit_u[dex_w])); + } + for (dex_w = 0; dex_w < pog_u->reg_u.len_w; ++dex_w) { + u3j_v1_rite_lose(&(pog_u->reg_u.rit_u[dex_w])); + } + u3a_v1_free(pog_u); +} + +/* _n_v1_feb(): u3h_v1_walk helper for u3n_v1_free + */ +static void +_n_v1_feb(u3_noun kev) +{ + u3a_v1_cell *cel_u = (u3a_v1_cell*) u3a_v1_to_ptr(kev); + _cn_v1_prog_free(u3to(u3n_v1_prog, cel_u->tel)); +} + +/* u3n_v1_free(): free bytecode cache + */ +void +u3n_v1_free() +{ + u3p(u3h_v1_root) har_p = u3R_v1->byc.har_p; + u3h_v1_walk(har_p, _n_v1_feb); + u3h_v1_free_nodes(har_p); +} diff --git a/pkg/noun/v1/nock.h b/pkg/noun/v1/nock.h new file mode 100644 index 0000000000..5320307b6b --- /dev/null +++ b/pkg/noun/v1/nock.h @@ -0,0 +1,25 @@ +/// @file + +#ifndef U3_NOCK_V1_H +#define U3_NOCK_V1_H + +#include "pkg/noun/v2/nock.h" + + /** Aliases. + **/ +# define u3n_v1_memo u3n_v2_memo +# define u3n_v1_prog u3n_v2_prog + + /** Functions. + **/ + /* u3n_v1_reclaim(): clear ad-hoc persistent caches to reclaim memory. + */ + void + u3n_v1_reclaim(void); + + /* u3n_v1_free(): free bytecode cache. + */ + void + u3n_v1_free(void); + +#endif /* ifndef U3_NOCK_V1_H */ diff --git a/pkg/noun/v1/vortex.c b/pkg/noun/v1/vortex.c new file mode 100644 index 0000000000..e1ec21b22d --- /dev/null +++ b/pkg/noun/v1/vortex.c @@ -0,0 +1,17 @@ +/// @file + +#include "pkg/noun/vortex.h" +#include "pkg/noun/v1/vortex.h" + +#include "pkg/noun/v1/allocate.h" + +/* u3v_v1_reclaim(): clear ad-hoc persistent caches to reclaim memory. +*/ +void +u3v_v1_reclaim(void) +{ + // clear the u3v_wish cache + // + u3a_v1_lose(u3A_v1->yot); + u3A_v1->yot = u3_nul; +} diff --git a/pkg/noun/v1/vortex.h b/pkg/noun/v1/vortex.h new file mode 100644 index 0000000000..84cf6e405c --- /dev/null +++ b/pkg/noun/v1/vortex.h @@ -0,0 +1,22 @@ +/// @file + +#ifndef U3_VORTEX_V1_H +#define U3_VORTEX_V1_H + +#include "pkg/noun/allocate.h" +#include "pkg/noun/v2/vortex.h" + + /** Aliases. + **/ +# define u3H_v1 u3H_v2 +# define u3A_v1 u3A_v2 +# define u3v_v1_home u3v_v2_home + + /** Functions. + **/ + /* u3v_v1_reclaim(): clear ad-hoc persistent caches to reclaim memory. + */ + void + u3v_v1_reclaim(void); + +#endif /* ifndef U3_VORTEX_V1_H */ diff --git a/pkg/noun/v2/allocate.c b/pkg/noun/v2/allocate.c new file mode 100644 index 0000000000..25cf04a948 --- /dev/null +++ b/pkg/noun/v2/allocate.c @@ -0,0 +1,73 @@ +/// @file + +#include "pkg/noun/allocate.h" +#include "pkg/noun/v1/allocate.h" +#include "pkg/noun/v2/allocate.h" + +#include "pkg/noun/v2/hashtable.h" +#include "log.h" +#include "pkg/noun/v2/manage.h" +#include "options.h" +#include "retrieve.h" +#include "trace.h" +#include "vortex.h" + +u3a_v2_road* u3a_v2_Road; + +u3_noun +u3a_v2_rewritten_noun(u3_noun som) +{ + if ( c3y == u3a_v2_is_cat(som) ) { + return som; + } + u3_post som_p = u3a_v2_rewritten(u3a_v1_to_off(som)); + + if ( c3y == u3a_v2_is_pug(som) ) { + som_p = u3a_v2_to_pug(som_p); + } + else { + som_p = u3a_v2_to_pom(som_p); + } + + return som_p; +} + +/* u3a_v2_mig_rewrite_compact(): rewrite pointers in ad-hoc persistent road structures. +*/ +void +u3a_v2_mig_rewrite_compact(void) +{ + u3a_v2_rewrite_noun(u3R_v2->ski.gul); + u3a_v2_rewrite_noun(u3R_v2->bug.tax); + u3a_v2_rewrite_noun(u3R_v2->bug.mer); + u3a_v2_rewrite_noun(u3R_v2->pro.don); + u3a_v2_rewrite_noun(u3R_v2->pro.day); + u3a_v2_rewrite_noun(u3R_v2->pro.trace); + u3h_v2_rewrite(u3R_v2->cax.har_p); + + u3R_v2->ski.gul = u3a_v2_rewritten_noun(u3R_v2->ski.gul); + u3R_v2->bug.tax = u3a_v2_rewritten_noun(u3R_v2->bug.tax); + u3R_v2->bug.mer = u3a_v2_rewritten_noun(u3R_v2->bug.mer); + u3R_v2->pro.don = u3a_v2_rewritten_noun(u3R_v2->pro.don); + u3R_v2->pro.day = u3a_v2_rewritten_noun(u3R_v2->pro.day); + u3R_v2->pro.trace = u3a_v2_rewritten_noun(u3R_v2->pro.trace); + u3R_v2->cax.har_p = u3a_v2_rewritten(u3R_v2->cax.har_p); +} + +void +u3a_v2_rewrite_noun(u3_noun som) +{ + if ( c3n == u3a_v2_is_cell(som) ) { + return; + } + + if ( c3n == u3a_v2_rewrite_ptr(u3a_v1_to_ptr((som))) ) return; + + u3a_v2_cell* cel = (u3a_v2_cell*) u3a_v1_to_ptr(som); + + u3a_v2_rewrite_noun(cel->hed); + u3a_v2_rewrite_noun(cel->tel); + + cel->hed = u3a_v2_rewritten_noun(cel->hed); + cel->tel = u3a_v2_rewritten_noun(cel->tel); +} diff --git a/pkg/noun/v2/allocate.h b/pkg/noun/v2/allocate.h new file mode 100644 index 0000000000..1f191795fc --- /dev/null +++ b/pkg/noun/v2/allocate.h @@ -0,0 +1,134 @@ +#ifndef U3_ALLOCATE_V2_H +#define U3_ALLOCATE_V2_H + +#include "pkg/noun/allocate.h" + +#include "pkg/noun/v2/manage.h" +#include "options.h" + + /** Aliases. + **/ +# define u3a_v2_botox u3a_botox +# define u3a_v2_box u3a_box +# define u3a_v2_cell u3a_cell +# define u3a_v2_fbox u3a_fbox +# define u3a_v2_fbox_no u3a_fbox_no +# define u3a_v2_free u3a_free +# define u3a_v2_heap u3a_heap +# define u3a_v2_into u3a_into +# define u3a_v2_is_cat u3a_is_cat +# define u3a_v2_is_cell u3a_is_cell +# define u3a_v2_is_north u3a_is_north +# define u3a_v2_is_pom u3a_is_pom +# define u3a_v2_is_pug u3a_is_pug +# define u3a_v2_lose u3a_lose +# define u3a_v2_malloc u3a_malloc +# define u3a_v2_minimum u3a_minimum +# define u3a_v2_outa u3a_outa +# define u3a_v2_pack_seek u3a_pack_seek +# define u3a_v2_rewrite u3a_rewrite +# define u3a_v2_rewrite_ptr u3a_rewrite_ptr +# define u3a_v2_rewritten u3a_rewritten +# define u3a_v2_to_off u3a_to_off +# define u3a_v2_to_ptr u3a_to_ptr +# define u3a_v2_to_wtr u3a_to_wtr +# define u3a_v2_to_pug u3a_to_pug +# define u3a_v2_to_pom u3a_to_pom +# define u3a_v2_wfree u3a_wfree + + /** Data structures. + **/ + /* u3a_v2_road: contiguous allocation and execution context. + */ + typedef struct _u3a_v2_road { + u3p(struct _u3a_v2_road) par_p; // parent road + u3p(struct _u3a_v2_road) kid_p; // child road list + u3p(struct _u3a_v2_road) nex_p; // sibling road + + u3p(c3_w) cap_p; // top of transient region + u3p(c3_w) hat_p; // top of durable region + u3p(c3_w) mat_p; // bottom of transient region + u3p(c3_w) rut_p; // bottom of durable region + u3p(c3_w) ear_p; // original cap if kid is live + + c3_w fut_w[32]; // futureproof buffer + + struct { // escape buffer + union { + jmp_buf buf; + c3_w buf_w[256]; // futureproofing + }; + } esc; + + struct { // miscellaneous config + c3_w fag_w; // flag bits + } how; // + + struct { // allocation pools + u3p(u3a_v2_fbox) fre_p[u3a_v2_fbox_no]; // heap by node size log + u3p(u3a_fbox) cel_p; // custom cell allocator + c3_w fre_w; // number of free words + c3_w max_w; // maximum allocated + } all; + + u3a_jets jed; // jet dashboard + + struct { // bytecode state + u3p(u3h_root) har_p; // formula->post of bytecode + } byc; + + struct { // namespace + u3_noun gul; // (list $+(* (unit (unit)))) now + } ski; + + struct { // trace stack + u3_noun tax; // (list ,*) + u3_noun mer; // emergency buffer to release + } bug; + + struct { // profile stack + c3_d nox_d; // nock steps + c3_d cel_d; // cell allocations + u3_noun don; // (list batt) + u3_noun trace; // (list trace) + u3_noun day; // doss, only in u3H (moveme) + } pro; + + struct { // memoization + u3p(u3h_root) har_p; // (map (pair term noun) noun) + } cax; + } u3a_v2_road; + + /** Globals. + **/ + /// Current road (thread-local). + extern u3a_v2_road* u3a_v2_Road; +# define u3R_v2 u3a_v2_Road + + /** Functions. + **/ + /** Allocation. + **/ + /* Reference and arena control. + */ + /* u3a_v2_mig_rewrite_compact(): rewrite pointers in ad-hoc persistent road structures. + */ + void + u3a_v2_mig_rewrite_compact(void); + + /* u3a_v2_rewrite_noun(): rewrite a noun for compaction. + */ + void + u3a_v2_rewrite_noun(u3_noun som); + + /* u3a_v2_rewritten(): rewrite a pointer for compaction. + */ + u3_post + u3a_v2_rewritten(u3_post som_p); + + /* u3a_v2_rewritten(): rewritten noun pointer for compaction. + */ + u3_noun + u3a_v2_rewritten_noun(u3_noun som); + +#endif /* ifndef U3_ALLOCATE_V2_H */ diff --git a/pkg/noun/v2/hashtable.c b/pkg/noun/v2/hashtable.c new file mode 100644 index 0000000000..6963d07fde --- /dev/null +++ b/pkg/noun/v2/hashtable.c @@ -0,0 +1,143 @@ +/// @file + +#include "pkg/noun/hashtable.h" +#include "pkg/noun/v1/hashtable.h" +#include "pkg/noun/v2/hashtable.h" + +#include "pkg/noun/allocate.h" +#include "pkg/noun/v1/allocate.h" +#include "pkg/noun/v2/allocate.h" + +/* _ch_v2_popcount(): number of bits set in word. A standard intrinsic. +** NB: copy of _ch_v2_popcount in pkg/noun/hashtable.c +*/ +static c3_w +_ch_v2_popcount(c3_w num_w) +{ + return __builtin_popcount(num_w); +} + +/* _ch_v2_free_buck(): free bucket +** NB: copy of _ch_v2_free_buck in pkg/noun/hashtable.c +*/ +static void +_ch_v2_free_buck(u3h_v2_buck* hab_u) +{ + c3_w i_w; + + for ( i_w = 0; i_w < hab_u->len_w; i_w++ ) { + u3z(u3h_v2_slot_to_noun(hab_u->sot_w[i_w])); + } + u3a_v2_wfree(hab_u); +} + +/* _ch_v2_free_node(): free node. +*/ +static void +_ch_v2_free_node(u3h_v2_node* han_u, c3_w lef_w) +{ + c3_w len_w = _ch_v2_popcount(han_u->map_w); + c3_w i_w; + + lef_w -= 5; + + for ( i_w = 0; i_w < len_w; i_w++ ) { + c3_w sot_w = han_u->sot_w[i_w]; + + if ( _(u3h_v2_slot_is_noun(sot_w)) ) { + u3z(u3h_v2_slot_to_noun(sot_w)); + } + else { + // NB: u3h_v2_slot_to_node() + void* hav_v = u3h_v2_slot_to_node(sot_w); + + if ( 0 == lef_w ) { + _ch_v2_free_buck(hav_v); + } else { + _ch_v2_free_node(hav_v, lef_w); + } + } + } + u3a_v2_wfree(han_u); +} + +/* _ch_v2_rewrite_buck(): rewrite buck for compaction. +*/ +void +_ch_v2_rewrite_buck(u3h_v2_buck* hab_u) +{ + if ( c3n == u3a_v2_rewrite_ptr(hab_u) ) return; + c3_w i_w; + + for ( i_w = 0; i_w < hab_u->len_w; i_w++ ) { + u3_noun som = u3h_v2_slot_to_noun(hab_u->sot_w[i_w]); + hab_u->sot_w[i_w] = u3h_v2_noun_to_slot(u3a_v2_rewritten_noun(som)); + u3a_v2_rewrite_noun(som); + } +} + +/* _ch_v2_rewrite_node(): rewrite node for compaction. +*/ +void +_ch_v2_rewrite_node(u3h_v2_node* han_u, c3_w lef_w) +{ + if ( c3n == u3a_v2_rewrite_ptr(han_u) ) return; + + c3_w len_w = _ch_v2_popcount(han_u->map_w); + c3_w i_w; + + lef_w -= 5; + + for ( i_w = 0; i_w < len_w; i_w++ ) { + c3_w sot_w = han_u->sot_w[i_w]; + + if ( _(u3h_v2_slot_is_noun(sot_w)) ) { + u3_noun kev = u3h_v2_slot_to_noun(sot_w); + han_u->sot_w[i_w] = u3h_v2_noun_to_slot(u3a_v2_rewritten_noun(kev)); + + u3a_v2_rewrite_noun(kev); + } + else { + void* hav_v = u3h_v1_slot_to_node(sot_w); + u3h_v2_node* nod_u = u3to(u3h_v2_node, u3a_v2_rewritten(u3of(u3h_v2_node,hav_v))); + + han_u->sot_w[i_w] = u3h_v2_node_to_slot(nod_u); + + if ( 0 == lef_w ) { + _ch_v2_rewrite_buck(hav_v); + } else { + _ch_v2_rewrite_node(hav_v, lef_w); + } + } + } +} + +/* u3h_v2_rewrite(): rewrite pointers during compaction. +*/ +void +u3h_v2_rewrite(u3p(u3h_v2_root) har_p) +{ + u3h_v2_root* har_u = u3to(u3h_v2_root, har_p); + c3_w i_w; + + if ( c3n == u3a_v2_rewrite_ptr(har_u) ) return; + + for ( i_w = 0; i_w < 64; i_w++ ) { + c3_w sot_w = har_u->sot_w[i_w]; + + if ( _(u3h_v2_slot_is_noun(sot_w)) ) { + u3_noun kev = u3h_v2_slot_to_noun(sot_w); + har_u->sot_w[i_w] = u3h_v2_noun_to_slot(u3a_v2_rewritten_noun(kev)); + + u3a_v2_rewrite_noun(kev); + } + else if ( _(u3h_v2_slot_is_node(sot_w)) ) { + u3h_v2_node* han_u = (u3h_v2_node*) u3h_v1_slot_to_node(sot_w); + u3h_v2_node* nod_u = u3to(u3h_v2_node, u3a_v2_rewritten(u3of(u3h_v2_node,han_u))); + + har_u->sot_w[i_w] = u3h_v2_node_to_slot(nod_u); + + _ch_v2_rewrite_node(han_u, 25); + } + } +} diff --git a/pkg/noun/v2/hashtable.h b/pkg/noun/v2/hashtable.h new file mode 100644 index 0000000000..09025fc19f --- /dev/null +++ b/pkg/noun/v2/hashtable.h @@ -0,0 +1,42 @@ +#ifndef U3_HASHTABLE_V2_H +#define U3_HASHTABLE_V2_H + +#define u3h_v2_buck u3h_buck +#define u3h_v2_free u3h_free +#define u3h_v2_new u3h_new +#define u3h_v2_node u3h_node +#define u3h_v2_noun_to_slot u3h_noun_to_slot +#define u3h_v2_root u3h_root +#define u3h_v2_slot_is_node u3h_slot_is_node +#define u3h_v2_slot_is_noun u3h_slot_is_noun +#define u3h_v2_slot_to_noun u3h_slot_to_noun +#define u3h_v2_walk u3h_walk +#define u3h_v2_walk_with u3h_walk_with + +#include "pkg/noun/hashtable.h" + +#include "c3.h" +#include "types.h" + + /** Data structures. + **/ + + /** HAMT macros. + *** + *** Coordinate with u3_noun definition! + **/ + /* u3h_v2_slot_to_node(): slot to node pointer + ** u3h_v2_node_to_slot(): node pointer to slot + */ +# define u3h_v2_slot_to_node(sot) (u3a_v2_into(((sot) & 0x3fffffff) << u3a_vits)) +# define u3h_v2_node_to_slot(ptr) ((u3a_v2_outa((ptr)) >> u3a_vits) | 0x40000000) + + /** Functions. + *** + **/ + /* u3h_v2_rewrite(): rewrite hashtable for compaction. + */ + void + u3h_v2_rewrite(u3p(u3h_root) har_p); + +#endif /* U3_HASHTABLE_V2_H */ diff --git a/pkg/noun/v2/jets.c b/pkg/noun/v2/jets.c new file mode 100644 index 0000000000..c22ea97f3b --- /dev/null +++ b/pkg/noun/v2/jets.c @@ -0,0 +1,54 @@ +/// @file + +#include "pkg/noun/vortex.h" + +#include "pkg/noun/jets.h" +#include "pkg/noun/v2/jets.h" + +#include "pkg/noun/v2/allocate.h" +#include "pkg/noun/v2/hashtable.h" +#include "pkg/noun/v2/vortex.h" + +#include "pkg/noun/v3/hashtable.h" +#include "pkg/noun/v3/jets.h" + +/* u3j_v2_reclaim(): clear ad-hoc persistent caches to reclaim memory. +*/ +void +u3j_v2_reclaim(void) +{ + // set globals (required for aliased functions) + u3H = (u3v_home*) u3H_v2; + u3R = (u3a_road*) u3R_v2; + + // clear the jet hank cache + // + u3h_v3_walk(u3R->jed.han_p, u3j_v3_free_hank); + u3h_v3_free(u3R->jed.han_p); + u3R->jed.han_p = u3h_v3_new(); +} + +/* u3j_v2_mig_rewrite_compact(): rewrite jet state for compaction. + * + * NB: u3R_v2->jed.han_p *must* be cleared (currently via u3j_v2_reclaim above) + * since it contains hanks which are not nouns but have loom pointers. + * Alternately, rewrite the entries with u3h_v2_walk, using u3j_v2_mark as a + * template for how to walk. There's an untested attempt at this in git + * history at e8a307a. +*/ +void +u3j_v2_mig_rewrite_compact() +{ + u3h_v2_rewrite(u3R_v2->jed.war_p); + u3h_v2_rewrite(u3R_v2->jed.cod_p); + u3h_v2_rewrite(u3R_v2->jed.han_p); + u3h_v2_rewrite(u3R_v2->jed.bas_p); + + u3h_v2_rewrite(u3R_v2->jed.hot_p); + u3R_v2->jed.hot_p = u3a_v2_rewritten(u3R_v2->jed.hot_p); + + u3R_v2->jed.war_p = u3a_v2_rewritten(u3R_v2->jed.war_p); + u3R_v2->jed.cod_p = u3a_v2_rewritten(u3R_v2->jed.cod_p); + u3R_v2->jed.han_p = u3a_v2_rewritten(u3R_v2->jed.han_p); + u3R_v2->jed.bas_p = u3a_v2_rewritten(u3R_v2->jed.bas_p); +} diff --git a/pkg/noun/v2/jets.h b/pkg/noun/v2/jets.h new file mode 100644 index 0000000000..526046905d --- /dev/null +++ b/pkg/noun/v2/jets.h @@ -0,0 +1,35 @@ +/// @file + +#ifndef U3_JETS_V2_H +#define U3_JETS_V2_H + +#include "pkg/noun/allocate.h" +#include "pkg/noun/jets.h" + + + /** Aliases. + **/ +# define u3j_v2_core u3j_core +# define u3j_v2_fink u3j_fink +# define u3j_v2_fist u3j_fist +# define u3j_v2_hank u3j_hank +# define u3j_v2_free_hank u3j_free_hank +# define u3j_v2_harm u3j_harm +# define u3j_v2_rite u3j_rite +# define u3j_v2_site u3j_site +# define u3j_v2_rite_lose u3j_rite_lose +# define u3j_v2_site_lose u3j_site_lose + + /** Functions. + **/ + /* u3j_v2_reclaim(): clear ad-hoc persistent caches to reclaim memory. + */ + void + u3j_v2_reclaim(void); + + /* u3j_v2_mig_rewrite_compact(): rewrite jet state for compaction. + */ + void + u3j_v2_mig_rewrite_compact(); + +#endif /* ifndef U3_JETS_V2_H */ diff --git a/pkg/noun/v2/manage.c b/pkg/noun/v2/manage.c new file mode 100644 index 0000000000..113ecc2b6b --- /dev/null +++ b/pkg/noun/v2/manage.c @@ -0,0 +1,174 @@ +/// @file + +#include "pkg/noun/v2/manage.h" + +#include "pkg/noun/v1/allocate.h" +#include "pkg/noun/v2/allocate.h" +#include "pkg/noun/v2/hashtable.h" +#include "pkg/noun/v2/jets.h" +#include "pkg/noun/v2/nock.h" +#include "pkg/noun/v2/options.h" +#include "pkg/noun/vortex.h" +#include "pkg/noun/v1/vortex.h" +#include "pkg/noun/v2/vortex.h" + +/* _cm_pack_rewrite(): trace through arena, rewriting pointers. +*/ +static void +_cm_pack_rewrite(void) +{ + u3v_v2_mig_rewrite_compact(); + u3j_v2_mig_rewrite_compact(); + u3n_v2_mig_rewrite_compact(); + u3a_v2_mig_rewrite_compact(); +} + +static void +_migrate_reclaim() +{ + // XX update this and similar printfs + fprintf(stderr, "loom: migration reclaim\r\n"); + u3m_v1_reclaim(); +} + +static void +_migrate_seek(const u3a_v2_road *rod_u) +{ + /* + very much like u3a_v2_pack_seek with the following changes: + - there is no need to account for free space as |pack is performed before + the migration + - odd sized boxes will be padded by one word to achieve an even size + - rut will be moved from one word ahead of u3_Loom to two words ahead + */ + c3_w * box_w = u3a_v2_into(rod_u->rut_p); + c3_w * end_w = u3a_v2_into(rod_u->hat_p); + u3_post new_p = (rod_u->rut_p + 1 + c3_wiseof(u3a_v2_box)); + u3a_v2_box * box_u = (void *)box_w; + + fprintf(stderr, "loom: migration seek\r\n"); + + for (; box_w < end_w + ; box_w += box_u->siz_w + , box_u = (void*)box_w) + { + if (!box_u->use_w) + continue; + u3_assert(box_u->siz_w); + u3_assert(box_u->use_w); + box_w[box_u->siz_w - 1] = new_p; + new_p = c3_align(new_p + box_u->siz_w, 2, C3_ALGHI); + } +} + +static void +_migrate_rewrite() +{ + fprintf(stderr, "loom: migration rewrite\r\n"); + + _cm_pack_rewrite(); +} + +static void +_migrate_move(u3a_v2_road *rod_u) +{ + fprintf(stderr, "loom: migration move\r\n"); + + c3_z hiz_z = u3a_v2_heap(rod_u) * sizeof(c3_w); + + /* calculate required shift distance to prevent write head overlapping read head */ + c3_w off_w = 1; /* at least 1 word because u3R_v1->rut_p migrates from 1 to 2 */ + for (u3a_v2_box *box_u = u3a_v2_into(rod_u->rut_p) + ; (void *)box_u < u3a_v2_into(rod_u->hat_p) + ; box_u = (void *)((c3_w *)box_u + box_u->siz_w)) + off_w += box_u->siz_w & 1; /* odd-sized boxes are padded by one word */ + + /* shift */ + memmove(u3a_v2_into(u3H_v2->rod_u.rut_p + off_w), + u3a_v2_into(u3H_v2->rod_u.rut_p), + hiz_z); + /* manually zero the former rut */ + *(c3_w *)u3a_v2_into(rod_u->rut_p) = 0; + + /* relocate boxes to DWORD-aligned addresses stored in trailing size word */ + c3_w *box_w = u3a_v2_into(rod_u->rut_p + off_w); + c3_w *end_w = u3a_v2_into(rod_u->hat_p + off_w); + u3a_v2_box *old_u = (void *)box_w; + c3_w siz_w = old_u->siz_w; + u3p(c3_w) new_p = rod_u->rut_p + 1 + c3_wiseof(u3a_v2_box); + c3_w *new_w; + + for (; box_w < end_w + ; box_w += siz_w + , old_u = (void *)box_w + , siz_w = old_u->siz_w) { + old_u->use_w &= 0x7fffffff; + + if (!old_u->use_w) + continue; + + new_w = (void *)u3a_v2_botox(u3a_v2_into(new_p)); + u3_assert(box_w[siz_w - 1] == new_p); + u3_assert(new_w <= box_w); + + c3_w i_w; + for (i_w = 0; i_w < siz_w - 1; i_w++) + new_w[i_w] = box_w[i_w]; + + if (siz_w & 1) { + new_w[i_w++] = 0; /* pad odd sized boxes */ + new_w[i_w++] = siz_w + 1; /* restore trailing size word */ + new_w[0] = siz_w + 1; /* and the leading size word */ + } + else { + new_w[i_w++] = siz_w; + } + + new_p += i_w; + } + + /* restore proper heap state */ + rod_u->rut_p = 2; + rod_u->hat_p = new_p - c3_wiseof(u3a_v2_box); + + /* like |pack, clear the free lists and cell allocator */ + for (c3_w i_w = 0; i_w < u3a_v2_fbox_no; i_w++) + u3R_v1->all.fre_p[i_w] = 0; + + u3R_v1->all.fre_w = 0; + u3R_v1->all.cel_p = 0; +} + + +/* u3m_v2_migrate: perform loom migration if necessary. +*/ +void +u3m_v2_migrate() +{ + c3_w len_w = u3C_v2.wor_i - 1; + c3_w ver_w = *(u3_Loom + len_w); + + u3_assert( U3V_VER1 == ver_w ); + + c3_w* mem_w = u3_Loom + 1; + c3_w siz_w = c3_wiseof(u3v_v1_home); + c3_w* mat_w = (mem_w + len_w) - siz_w; + + u3H_v1 = (void *)mat_w; + u3R_v1 = &u3H_v1->rod_u; + + u3R_v1->cap_p = u3R_v1->mat_p = u3a_v1_outa(u3H_v1); + + fprintf(stderr, "loom: pointer compression migration running...\r\n"); + + /* perform the migration in a pattern similar to |pack */ + _migrate_reclaim(); + _migrate_seek(&u3H_v1->rod_u); + _migrate_rewrite(); + _migrate_move(&u3H_v1->rod_u); + + /* finally update the version and commit to disk */ + u3H_v1->ver_w = U3V_VER2; + + fprintf(stderr, "loom: pointer compression migration done\r\n"); +} diff --git a/pkg/noun/v2/manage.h b/pkg/noun/v2/manage.h new file mode 100644 index 0000000000..1d03b97809 --- /dev/null +++ b/pkg/noun/v2/manage.h @@ -0,0 +1,13 @@ +/// @file + +#ifndef U3_MANAGE_V2_H +#define U3_MANAGE_V2_H + + /** System management. + **/ + /* u3m_v2_migrate: perform pointer compression loom migration if necessary. + */ + void + u3m_v2_migrate(); + +#endif /* ifndef U3_MANAGE_V2_H */ diff --git a/pkg/noun/v2/nock.c b/pkg/noun/v2/nock.c new file mode 100644 index 0000000000..e6ff116ce1 --- /dev/null +++ b/pkg/noun/v2/nock.c @@ -0,0 +1,92 @@ +/// @file + +#include "pkg/noun/v2/nock.h" + +#include "pkg/noun/vortex.h" + +#include "pkg/noun/v2/allocate.h" +#include "pkg/noun/v2/hashtable.h" +#include "pkg/noun/v2/vortex.h" + +#include "pkg/noun/v3/hashtable.h" + +/* u3n_v2_reclaim(): clear ad-hoc persistent caches to reclaim memory. +*/ +void +u3n_v2_reclaim(void) +{ + // set globals (required for aliased functions) + u3H = (u3v_home*) u3H_v2; + u3R = (u3a_road*) u3R_v2; + + // clear the bytecode cache + u3n_v2_free(); + u3R->byc.har_p = u3h_v2_new(); +} + +/* _cn_v2_prog_free(): free memory retained by program pog_u +*/ +static void +_cn_v2_prog_free(u3n_v2_prog* pog_u) +{ + // fix up pointers for loom portability + pog_u->byc_u.ops_y = (c3_y*) ((void*) pog_u) + sizeof(u3n_v2_prog); + pog_u->lit_u.non = (u3_noun*) (pog_u->byc_u.ops_y + pog_u->byc_u.len_w); + pog_u->mem_u.sot_u = (u3n_v2_memo*) (pog_u->lit_u.non + pog_u->lit_u.len_w); + pog_u->cal_u.sit_u = (u3j_v2_site*) (pog_u->mem_u.sot_u + pog_u->mem_u.len_w); + pog_u->reg_u.rit_u = (u3j_v2_rite*) (pog_u->cal_u.sit_u + pog_u->cal_u.len_w); + + c3_w dex_w; + for (dex_w = 0; dex_w < pog_u->lit_u.len_w; ++dex_w) { + u3a_v2_lose(pog_u->lit_u.non[dex_w]); + } + for (dex_w = 0; dex_w < pog_u->mem_u.len_w; ++dex_w) { + u3a_v2_lose(pog_u->mem_u.sot_u[dex_w].key); + } + for (dex_w = 0; dex_w < pog_u->cal_u.len_w; ++dex_w) { + u3j_v2_site_lose(&(pog_u->cal_u.sit_u[dex_w])); + } + for (dex_w = 0; dex_w < pog_u->reg_u.len_w; ++dex_w) { + u3j_v2_rite_lose(&(pog_u->reg_u.rit_u[dex_w])); + } + u3a_v2_free(pog_u); +} + +/* _n_v2_feb(): u3h_v2_walk helper for u3n_v2_free + */ +static void +_n_v2_feb(u3_noun kev) +{ + u3a_v2_cell *cel_u = (u3a_v2_cell*) u3a_v2_to_ptr(kev); + _cn_v2_prog_free(u3to(u3n_v2_prog, cel_u->tel)); +} + +/* u3n_v2_free(): free bytecode cache + */ +void +u3n_v2_free() +{ + u3p(u3h_v2_root) har_p = u3R_v2->byc.har_p; + u3h_v2_walk(har_p, _n_v2_feb); + u3h_v2_free(har_p); +} + +/* u3n_v2_mig_rewrite_compact(): rewrite the bytecode cache for compaction. + * + * NB: u3R_v2->byc.har_p *must* be cleared (currently via u3n_v2_reclaim above), + * since it contains things that look like nouns but aren't. + * Specifically, it contains "cells" where the tail is a + * pointer to a u3a_v2_malloc'ed block that contains loom pointers. + * + * You should be able to walk this with u3h_v2_walk and rewrite the + * pointers, but you need to be careful to handle that u3a_v2_malloc + * pointers can't be turned into a box by stepping back two words. You + * must step back one word to get the padding, step then step back that + * many more words (plus one?). + */ +void +u3n_v2_mig_rewrite_compact() +{ + u3h_v2_rewrite(u3R_v2->byc.har_p); + u3R_v2->byc.har_p = u3a_v2_rewritten(u3R_v2->byc.har_p); +} diff --git a/pkg/noun/v2/nock.h b/pkg/noun/v2/nock.h new file mode 100644 index 0000000000..e7d44aee08 --- /dev/null +++ b/pkg/noun/v2/nock.h @@ -0,0 +1,64 @@ +/// @file + +#ifndef U3_NOCK_V2_H +#define U3_NOCK_V2_H + +#include "pkg/noun/v3/nock.h" + +#include "pkg/noun/v2/jets.h" + +#include "types.h" + + /** Data structures. + **/ + /* u3n_memo: %memo hint space + */ + typedef struct { + c3_l sip_l; + u3_noun key; + } u3n_v2_memo; + + /* u3n_v2_prog: program compiled from nock + */ + typedef struct _u3n_v2_prog { + struct { + c3_o own_o; // program owns ops_y? + c3_w len_w; // length of bytecode (bytes) + c3_y* ops_y; // actual array of bytes + } byc_u; // bytecode + struct { + c3_w len_w; // number of literals + u3_noun* non; // array of literals + } lit_u; // literals + struct { + c3_w len_w; // number of memo slots + u3n_v2_memo* sot_u; // array of memo slots + } mem_u; // memo slot data + struct { + c3_w len_w; // number of calls sites + u3j_v2_site* sit_u; // array of sites + } cal_u; // call site data + struct { + c3_w len_w; // number of registration sites + u3j_v2_rite* rit_u; // array of sites + } reg_u; // registration site data + } u3n_v2_prog; + + /** Functions. + **/ + /* u3n_v2_reclaim(): clear ad-hoc persistent caches to reclaim memory. + */ + void + u3n_v2_reclaim(void); + + /* u3n_v2_free(): free bytecode cache. + */ + void + u3n_v2_free(void); + + /* u3n_v2_mig_rewrite_compact(): rewrite bytecode cache for compaction. + */ + void + u3n_v2_mig_rewrite_compact(); + +#endif /* ifndef U3_NOCK_V2_H */ diff --git a/pkg/noun/v2/options.h b/pkg/noun/v2/options.h new file mode 100644 index 0000000000..4af573f20d --- /dev/null +++ b/pkg/noun/v2/options.h @@ -0,0 +1,14 @@ +/// @file + +#ifndef U3_OPTIONS_V2_H +#define U3_OPTIONS_V2_H + +#include "pkg/noun/options.h" + + /** Globals. + **/ + /* u3_Config / u3C: global memory control. + */ +# define u3C_v2 u3C + +#endif /* ifndef U3_OPTIONS_H */ diff --git a/pkg/noun/v2/vortex.c b/pkg/noun/v2/vortex.c new file mode 100644 index 0000000000..b22dfaed4d --- /dev/null +++ b/pkg/noun/v2/vortex.c @@ -0,0 +1,25 @@ +/// @file + +#include "pkg/noun/vortex.h" +#include "pkg/noun/v2/vortex.h" + +#include "pkg/noun/v2/allocate.h" + +u3v_v2_home* u3v_v2_Home; + +/* u3v_v2_mig_rewrite_compact(): rewrite arvo kernel for compaction. +*/ +void +u3v_v2_mig_rewrite_compact() +{ + u3v_v2_arvo* arv_u = &(u3H_v2->arv_u); + + u3a_v2_rewrite_noun(arv_u->roc); + u3a_v2_rewrite_noun(arv_u->now); + u3a_v2_rewrite_noun(arv_u->yot); + + arv_u->roc = u3a_v2_rewritten_noun(arv_u->roc); + arv_u->now = u3a_v2_rewritten_noun(arv_u->now); + arv_u->yot = u3a_v2_rewritten_noun(arv_u->yot); +} + diff --git a/pkg/noun/v2/vortex.h b/pkg/noun/v2/vortex.h new file mode 100644 index 0000000000..ee8a2f861e --- /dev/null +++ b/pkg/noun/v2/vortex.h @@ -0,0 +1,40 @@ +/// @file + +#ifndef U3_VORTEX_V2_H +#define U3_VORTEX_V2_H + +#include "pkg/noun/vortex.h" + +#include "pkg/noun/v2/allocate.h" +#include "pkg/noun/version.h" + + /** Aliases. + **/ +# define u3v_v2_arvo u3v_arvo + + /** Data structures. + **/ + /* u3v_v2_home: all internal (within image) state. + ** NB: version must be last for discriminability in north road + */ + typedef struct _u3v_v2_home { + u3a_v2_road rod_u; // storage state + u3v_v2_arvo arv_u; // arvo state + u3v_version ver_w; // version number + } u3v_v2_home; + + /** Globals. + **/ + /// Arvo internal state. + extern u3v_v2_home* u3v_v2_Home; +# define u3H_v2 u3v_v2_Home +# define u3A_v2 (&(u3v_v2_Home->arv_u)) + + /** Functions. + **/ + /* u3v_v2_mig_rewrite_compact(): rewrite arvo kernel for compaction. + */ + void + u3v_v2_mig_rewrite_compact(); + +#endif /* ifndef U3_VORTEX_V2_H */ diff --git a/pkg/noun/v3/allocate.h b/pkg/noun/v3/allocate.h new file mode 100644 index 0000000000..bacc79e174 --- /dev/null +++ b/pkg/noun/v3/allocate.h @@ -0,0 +1,17 @@ +#ifndef U3_ALLOCATE_V3_H +#define U3_ALLOCATE_V3_H + +#include "pkg/noun/allocate.h" + +#include "pkg/noun/v3/manage.h" +#include "options.h" + + /** Aliases. + **/ +# define u3R_v3 u3a_Road +# define u3a_v3_balign u3a_balign +# define u3a_v3_road u3a_road +# define u3a_v3_walign u3a_walign +# define u3a_v3_walloc u3a_walloc + +#endif /* ifndef U3_ALLOCATE_V3_H */ diff --git a/pkg/noun/v3/hashtable.c b/pkg/noun/v3/hashtable.c new file mode 100644 index 0000000000..b17d7b6e78 --- /dev/null +++ b/pkg/noun/v3/hashtable.c @@ -0,0 +1,33 @@ +/// @file + +#include "pkg/noun/v3/hashtable.h" + +#include "pkg/noun/allocate.h" +#include "pkg/noun/vortex.h" + +#include "pkg/noun/v3/allocate.h" +#include "pkg/noun/v3/vortex.h" + +/* u3h_v3_new_cache(): create hashtable with bounded size. +*/ +u3p(u3h_v3_root) +u3h_v3_new_cache(c3_w max_w) +{ + // set globals (required for aliased functions) + u3H = (u3v_home*) u3H_v3; + u3R = (u3a_road*) u3R_v3; + + u3h_v3_root* har_u = u3a_v3_walloc(c3_wiseof(u3h_v3_root)); + u3p(u3h_v3_root) har_p = u3of(u3h_v3_root, har_u); + c3_w i_w; + + har_u->max_w = max_w; + har_u->use_w = 0; + har_u->arm_u.mug_w = 0; + har_u->arm_u.inx_w = 0; + + for ( i_w = 0; i_w < 64; i_w++ ) { + har_u->sot_w[i_w] = 0; + } + return har_p; +} diff --git a/pkg/noun/v3/hashtable.h b/pkg/noun/v3/hashtable.h new file mode 100644 index 0000000000..14e0e47753 --- /dev/null +++ b/pkg/noun/v3/hashtable.h @@ -0,0 +1,18 @@ +#ifndef U3_HASHTABLE_V3_H +#define U3_HASHTABLE_V3_H + +#define u3h_v3_free u3h_free +#define u3h_v3_new u3h_new +#define u3h_v3_root u3h_root +#define u3h_v3_walk u3h_walk + +#include "pkg/noun/hashtable.h" + + /** Functions. + **/ + /* u3h_v3_new_cache(): create hashtable with bounded size. + */ + u3p(u3h_v3_root) + u3h_v3_new_cache(c3_w clk_w); + +#endif /* U3_HASHTABLE_V3_H */ diff --git a/pkg/noun/v3/jets.h b/pkg/noun/v3/jets.h new file mode 100644 index 0000000000..a0fe54f9d7 --- /dev/null +++ b/pkg/noun/v3/jets.h @@ -0,0 +1,12 @@ +/// @file + +#ifndef U3_JETS_V3_H +#define U3_JETS_V3_H + +#include "pkg/noun/jets.h" + + /** Aliases. + **/ +# define u3j_v3_free_hank u3j_free_hank + +#endif /* ifndef U3_JETS_V3_H */ diff --git a/pkg/noun/v3/manage.c b/pkg/noun/v3/manage.c new file mode 100644 index 0000000000..a15d5ad9be --- /dev/null +++ b/pkg/noun/v3/manage.c @@ -0,0 +1,107 @@ +/// @file + +#include "pkg/noun/v3/manage.h" + +#include "pkg/noun/v2/jets.h" +#include "pkg/noun/v2/nock.h" +#include "pkg/noun/v2/vortex.h" + +#include "pkg/noun/v3/allocate.h" +#include "pkg/noun/v3/hashtable.h" +#include "pkg/noun/version.h" +#include "pkg/noun/v3/vortex.h" +#include + +/* u3m_v3_migrate: perform loom migration if necessary. +*/ +void +u3m_v3_migrate() +{ + fprintf(stderr, "loom: memoization migration running...\r\n"); + + + c3_w ver_w = *(u3_Loom + u3C.wor_i - 1); + c3_w *mem_w = u3_Loom + u3a_v3_walign; + c3_w len_w = u3C.wor_i - u3a_v3_walign; + c3_w suz_w = c3_wiseof(u3v_v2_home); + c3_w *mut_w = c3_align(mem_w + len_w - suz_w, u3a_v3_balign, C3_ALGLO); + + // old road + u3v_v2_home* hum_u = (u3v_v2_home*)mut_w; + u3a_v2_road* rud_u = &hum_u->rod_u; + size_t ruz_t = sizeof(u3a_v2_road); + + // set v2 globals + u3H_v2 = (void *)mut_w; + u3R_v2 = &u3H_v2->rod_u; + u3R_v2->cap_p = u3R_v2->mat_p = u3a_v2_outa(u3H_v2); + + // free bytecode caches in old road + u3j_v2_reclaim(); + u3n_v2_reclaim(); + + // new home, new road + u3v_v3_home hom_u = {0}; + u3a_v3_road rod_u = {0}; + + // copy members, one-by-one, from old road to new road + rod_u.par_p = rud_u->par_p; + rod_u.kid_p = rud_u->kid_p; + rod_u.nex_p = rud_u->nex_p; + + rod_u.cap_p = rud_u->cap_p; + rod_u.hat_p = rud_u->hat_p; + rod_u.mat_p = rud_u->mat_p; + rod_u.rut_p = rud_u->rut_p; + rod_u.ear_p = rud_u->ear_p; + + // no need to zero-out fut_w + // no need to do anything with esc + + rod_u.how.fag_w = rud_u->how.fag_w; + + memcpy(rod_u.all.fre_p, rud_u->all.fre_p, sizeof(rud_u->all.fre_p)); + rod_u.all.cel_p = rud_u->all.cel_p; + rod_u.all.fre_w = rud_u->all.fre_w; + rod_u.all.max_w = rud_u->all.max_w; + + rod_u.jed.hot_p = rud_u->jed.hot_p; + rod_u.jed.war_p = rud_u->jed.war_p; + rod_u.jed.cod_p = rud_u->jed.cod_p; + rod_u.jed.han_p = rud_u->jed.han_p; + rod_u.jed.bas_p = rud_u->jed.bas_p; + + rod_u.byc.har_p = rud_u->byc.har_p; + + rod_u.ski.gul = rud_u->ski.gul; + + rod_u.bug.tax = rud_u->bug.tax; + rod_u.bug.mer = rud_u->bug.mer; + + rod_u.pro.nox_d = rud_u->pro.nox_d; + rod_u.pro.cel_d = rud_u->pro.cel_d; + rod_u.pro.don = rud_u->pro.don; + rod_u.pro.trace = rud_u->pro.trace; + rod_u.pro.day = rud_u->pro.day; + + rod_u.cax.har_p = rud_u->cax.har_p; + + // prepare the new home, update the version + hom_u.arv_u = hum_u->arv_u; + hom_u.rod_u = rod_u; + + // place the new home over the old one + c3_w siz_w = c3_wiseof(u3v_v3_home); + c3_w *mat_w = c3_align(mem_w + len_w - siz_w, u3a_v3_balign, C3_ALGLO); + memcpy(mat_w, &hom_u, sizeof(u3v_v3_home)); + + // set globals + u3H_v3 = (void*)mat_w; + u3R_v3 = &u3H_v3->rod_u; + u3H_v3->ver_w = U3V_VER3; + + // initialize persistent cache + u3R_v3->cax.per_p = u3h_v3_new_cache(u3C.per_w); + + fprintf(stderr, "loom: memoization migration done\r\n"); +} diff --git a/pkg/noun/v3/manage.h b/pkg/noun/v3/manage.h new file mode 100644 index 0000000000..5745a6316b --- /dev/null +++ b/pkg/noun/v3/manage.h @@ -0,0 +1,13 @@ +/// @file + +#ifndef U3_MANAGE_V3_H +#define U3_MANAGE_V3_H + + /** System management. + **/ + /* u3m_v3_migrate: perform memoization loom migration if necessary. + */ + void + u3m_v3_migrate(); + +#endif /* ifndef U3_MANAGE_V3_H */ diff --git a/pkg/noun/v3/nock.h b/pkg/noun/v3/nock.h new file mode 100644 index 0000000000..a7b208ba99 --- /dev/null +++ b/pkg/noun/v3/nock.h @@ -0,0 +1,17 @@ +/// @file + +#ifndef U3_NOCK_V3_H +#define U3_NOCK_V3_H + +#include "pkg/noun/nock.h" + +#include "types.h" + + /** Aliases. + **/ +# define u3n_v3_free u3n_free +# define u3n_v3_memo u3n_memo +# define u3n_v3_prog u3n_prog +# define u3n_v3_reclaim u3n_reclaim + +#endif /* ifndef U3_NOCK_V3_H */ diff --git a/pkg/noun/v3/vortex.h b/pkg/noun/v3/vortex.h new file mode 100644 index 0000000000..d47ac19654 --- /dev/null +++ b/pkg/noun/v3/vortex.h @@ -0,0 +1,19 @@ +/// @file + +#ifndef U3_VORTEX_V3_H +#define U3_VORTEX_V3_H + +#include "pkg/noun/vortex.h" + +#include "pkg/noun/v3/allocate.h" +#include "pkg/noun/version.h" + + /** Aliases. + **/ +# define u3v_v3_arvo u3v_arvo +# define u3H_v3 u3v_Home +# define u3A_v3 (&(u3H_v3)->arv_u) +# define u3v_v3_home u3v_home +# define u3v_v3_rewrite_compact u3v_rewrite_compact + +#endif /* ifndef U3_VORTEX_V3_H */ diff --git a/pkg/noun/version.h b/pkg/noun/version.h index f64d0a398e..5cc6acb86d 100644 --- a/pkg/noun/version.h +++ b/pkg/noun/version.h @@ -8,7 +8,8 @@ typedef c3_w u3v_version; #define U3V_VER1 1 #define U3V_VER2 2 -#define U3V_VERLAT U3V_VER2 +#define U3V_VER3 3 +#define U3V_VERLAT U3V_VER3 /* EVENTS */ @@ -18,4 +19,10 @@ typedef c3_w u3e_version; #define U3E_VER1 1 #define U3E_VERLAT U3E_VER1 +/* DISK FORMAT + */ + +#define U3D_VER1 1 +#define U3D_VERLAT U3L_VER1 + #endif /* ifndef U3_VERSION_H */ diff --git a/pkg/noun/vortex.c b/pkg/noun/vortex.c index 5981068b87..0bb9af8c29 100644 --- a/pkg/noun/vortex.c +++ b/pkg/noun/vortex.c @@ -39,18 +39,12 @@ c3_o u3v_boot(u3_noun eve) { c3_d len_d; - { u3_noun len = u3qb_lent(eve); u3_assert( c3y == u3r_safe_chub(len, &len_d) ); u3z(len); } - // ensure zero-initialized kernel - // - u3A->roc = 0; - u3A->eve_d = 0; - { u3_noun pro = u3m_soft(0, u3v_life, eve); @@ -59,6 +53,7 @@ u3v_boot(u3_noun eve) return c3n; } + u3z(u3A->roc); u3A->roc = u3k(u3t(pro)); u3A->eve_d = len_d; u3z(pro); diff --git a/pkg/noun/vortex.h b/pkg/noun/vortex.h index cbd0f295ff..2d202e831c 100644 --- a/pkg/noun/vortex.h +++ b/pkg/noun/vortex.h @@ -37,9 +37,6 @@ # define u3H u3v_Home # define u3A (&(u3v_Home->arv_u)) - /** Constants. - **/ - /** Functions. **/ /* u3v_life(): execute initial lifecycle, producing Arvo core. diff --git a/pkg/noun/xtract.c b/pkg/noun/xtract.c index fc59f463d8..1ce799f9fa 100644 --- a/pkg/noun/xtract.c +++ b/pkg/noun/xtract.c @@ -5,21 +5,10 @@ #include "manage.h" #include "retrieve.h" -/* u3x_good(): test for u3_none. -*/ +u3_atom +u3x_atom(u3_noun a); u3_noun -u3x_good(u3_weak som) -{ - return ( u3_none == som ) ? u3m_bail(c3__exit) : som; -} - -/* u3x_at (u3at): fragment. -*/ -u3_noun -u3x_at(u3_noun axe, u3_noun som) -{ - return u3x_good(u3r_at(axe, som)); -} +u3x_good(u3_weak som); /* u3x_mean(): ** @@ -40,94 +29,3 @@ u3x_mean(u3_noun som, ...) u3m_bail(c3__exit); } } - -/* u3x_bite(): xtract/default $bloq and $step from $bite. -*/ -void -u3x_bite(u3_noun bite, u3_atom* bloq, u3_atom *step) -{ - if ( c3n == u3r_bite(bite, bloq, step) ) { - u3m_bail(c3__exit); - } -} - -/* u3x_cell(): -** -** Divide `a` as a cell `[b c]`. -*/ -void -u3x_cell(u3_noun a, - u3_noun* b, - u3_noun* c) -{ - if ( c3n == u3r_cell(a, b, c) ) { - u3m_bail(c3__exit); - } -} - -/* u3x_trel(): -** -** Divide `a` as a trel `[b c d]`, or bail. -*/ -void -u3x_trel(u3_noun a, - u3_noun* b, - u3_noun* c, - u3_noun* d) -{ - if ( c3n == u3r_trel(a, b, c, d) ) { - u3m_bail(c3__exit); - } -} - -/* u3x_qual(): -** -** Divide `a` as a quadruple `[b c d e]`. -*/ -void -u3x_qual(u3_noun a, - u3_noun* b, - u3_noun* c, - u3_noun* d, - u3_noun* e) -{ - if ( c3n == u3r_qual(a, b, c, d, e) ) { - u3m_bail(c3__exit); - } -} - -/* u3x_quil(): -** -** Divide `a` as a quintuple `[b c d e f]`. -*/ -void -u3x_quil(u3_noun a, - u3_noun* b, - u3_noun* c, - u3_noun* d, - u3_noun* e, - u3_noun* f) -{ - if ( c3n == u3r_quil(a, b, c, d, e, f) ) { - u3m_bail(c3__exit); - } -} - -/* u3x_hext(): -** -** Divide `a` as a hextuple `[b c d e f g]`. -*/ -void -u3x_hext(u3_noun a, - u3_noun* b, - u3_noun* c, - u3_noun* d, - u3_noun* e, - u3_noun* f, - u3_noun* g) -{ - if ( c3n == u3r_hext(a, b, c, d, e, f, g) ) { - u3m_bail(c3__exit); - } -} - diff --git a/pkg/noun/xtract.h b/pkg/noun/xtract.h index cf39011463..343690a8f2 100644 --- a/pkg/noun/xtract.h +++ b/pkg/noun/xtract.h @@ -5,6 +5,8 @@ #include "c3.h" #include "types.h" +#include "allocate.h" +#include "manage.h" /** Constants. **/ @@ -40,6 +42,21 @@ **/ /* Word axis macros. For 31-bit axes only. */ + + /* u3x_at (u3at): fragment. + */ +# define u3x_at(a, b) u3x_good(u3r_at(a, b)) +# define u3at(a, b) u3x_at(a, b) + + /* u3x_bite(): xtract/default $bloq and $step from $bite. + */ +# define u3x_bite(a, b, c) \ + do { \ + if ( c3n == u3r_bite(a, b, c) ) { \ + u3m_bail(c3__exit); \ + } \ + } while (0) + /* u3x_dep(): number of axis bits. */ # define u3x_dep(a_w) (c3_bits_word(a_w) - 1) @@ -61,25 +78,70 @@ # define u3x_peg(a_w, b_w) \ ( (a_w << u3x_dep(b_w)) | (b_w &~ (1 << u3x_dep(b_w))) ) - /* u3x_atom(): atom or exit. + /* u3x_cell(): divide `a` as a cell `[b c]`. + */ +# define u3x_cell(a, b, c) \ + do { \ + if ( c3n == u3r_cell(a, b, c) ) { \ + u3m_bail(c3__exit); \ + } \ + } while (0) + + /* u3x_trel(): divide `a` as a trel `[b c d]`, or bail. + */ +# define u3x_trel(a, b, c, d) \ + do { \ + if ( c3n == u3r_trel(a, b, c, d) ) { \ + u3m_bail(c3__exit); \ + } \ + } while (0) + + /* u3x_qual(): divide `a` as a quadruple `[b c d e]`. */ -# define u3x_atom(a) \ - ( (c3y == u3a_is_cell(a)) ? u3m_bail(c3__exit) : a ) +# define u3x_qual(a, b, c, d, e) \ + do { \ + if ( c3n == u3r_qual(a, b, c, d, e) ) { \ + u3m_bail(c3__exit); \ + } \ + } while (0) + + /* u3x_quil(): divide `a` as a quintuple `[b c d e f]`. + */ +# define u3x_quil(a, b, c, d, e, f) \ + do { \ + if ( c3n == u3r_quil(a, b, c, d, e, f) ) { \ + u3m_bail(c3__exit); \ + } \ + } while (0) + + /* u3x_hext(): divide `a` as a hextuple `[b c d e f g]`. + */ +# define u3x_hext(a, b, c, d, e, f, g) \ + do { \ + if ( c3n == u3r_hext(a, b, c, d, e, f, g) ) { \ + u3m_bail(c3__exit); \ + } \ + } while (0) /** Functions. **/ /** u3x_*: read, but bail with c3__exit on a crash. **/ - /* u3x_good(): test for u3_none. + /* u3x_atom(): atom or exit. */ - u3_noun - u3x_good(u3_weak som); + inline u3_atom + u3x_atom(u3_noun a) + { + return ( c3y == u3a_is_cell(a) ) ? u3m_bail(c3__exit) : a; + } - /* u3x_at (u3at): fragment. + /* u3x_good(): test for u3_none. */ - u3_noun - u3x_at(u3_noun axe, u3_noun som); -# define u3at(axe, som) u3x_at(axe, som) + inline u3_noun + u3x_good(u3_weak som) + { + return ( u3_none == som ) ? u3m_bail(c3__exit) : som; + } /* u3x_mean(): ** @@ -89,64 +151,4 @@ void u3x_mean(u3_noun a, ...); - /* u3x_bite(): xtract/default $bloq and $step from $bite. - */ - void - u3x_bite(u3_noun bite, u3_atom* bloq, u3_atom *step); - - /* u3x_cell(): - ** - ** Divide `a` as a cell `[b c]`. - */ - void - u3x_cell(u3_noun a, - u3_noun* b, - u3_noun* c); - - /* u3x_trel(): - ** - ** Divide `a` as a trel `[b c d]`, or bail. - */ - void - u3x_trel(u3_noun a, - u3_noun* b, - u3_noun* c, - u3_noun* d); - - /* u3x_qual(): - ** - ** Divide `a` as a quadruple `[b c d e]`. - */ - void - u3x_qual(u3_noun a, - u3_noun* b, - u3_noun* c, - u3_noun* d, - u3_noun* e); - - /* u3x_quil(): - ** - ** Divide `a` as a quintuple `[b c d e f]`. - */ - void - u3x_quil(u3_noun a, - u3_noun* b, - u3_noun* c, - u3_noun* d, - u3_noun* e, - u3_noun* f); - - /* u3x_hext(): - ** - ** Divide `a` as a hextuple `[b c d e f g]`. - */ - void - u3x_hext(u3_noun a, - u3_noun* b, - u3_noun* c, - u3_noun* d, - u3_noun* e, - u3_noun* f, - u3_noun* g); - #endif /* ifndef U3_XTRACT_H */ diff --git a/pkg/noun/zave.c b/pkg/noun/zave.c index 0955af906c..924bee8987 100644 --- a/pkg/noun/zave.c +++ b/pkg/noun/zave.c @@ -5,6 +5,7 @@ #include "allocate.h" #include "hashtable.h" #include "imprison.h" +#include "vortex.h" /* u3z_key(): construct a memo cache-key. Arguments retained. */ @@ -34,30 +35,60 @@ u3z_key_5(c3_m fun, u3_noun one, u3_noun two, u3_noun tri, u3_noun qua, u3_noun return u3nc(fun, u3nq(u3k(one), u3k(two), u3k(tri), u3nc(u3k(qua), u3k(qin)))); } +/* _har(): get the memo cache for the given cid. +*/ +static u3p(u3h_root) +_har(u3a_road* rod_u, u3z_cid cid) +{ + switch ( cid ) { + case u3z_memo_toss: + return rod_u->cax.har_p; + case u3z_memo_keep: + return rod_u->cax.per_p; + } + u3_assert(0); +} + /* u3z_find(): find in memo cache. Arguments retained. */ u3_weak -u3z_find(u3_noun key) +u3z_find(u3z_cid cid, u3_noun key) { - return u3h_get(u3R->cax.har_p, key); + if ( u3z_memo_toss == cid ) { + return u3h_get(_har(u3R, cid), key); + } + else { + // XX needs to be benchmarked (up vs. down search) + u3a_road* rod_u = &(u3H->rod_u); + while ( 1 ) { + u3_weak got = u3h_get(_har(rod_u, cid), key); + if ( u3_none != got ) { + return got; + } + if ( 0 == rod_u->kid_p ) { + return u3_none; + } + rod_u = u3to(u3a_road, rod_u->kid_p); + }; + } } u3_weak -u3z_find_m(c3_m fun, u3_noun one) +u3z_find_m(u3z_cid cid, c3_m fun, u3_noun one) { u3_noun key = u3nc(fun, u3k(one)); u3_weak val; - - val = u3h_get(u3R->cax.har_p, key); + val = u3z_find(cid, key); u3z(key); + return val; } /* u3z_save(): save in memo cache. TRANSFER key; RETAIN val */ u3_noun -u3z_save(u3_noun key, u3_noun val) +u3z_save(u3z_cid cid, u3_noun key, u3_noun val) { - u3h_put(u3R->cax.har_p, key, u3k(val)); + u3h_put(_har(u3R, cid), key, u3k(val)); u3z(key); return val; } @@ -65,28 +96,36 @@ u3z_save(u3_noun key, u3_noun val) /* u3z_save_m(): save in memo cache. Arguments retained. */ u3_noun -u3z_save_m(c3_m fun, u3_noun one, u3_noun val) +u3z_save_m(u3z_cid cid, c3_m fun, u3_noun one, u3_noun val) { u3_noun key = u3nc(fun, u3k(one)); - u3h_put(u3R->cax.har_p, key, u3k(val)); + u3h_put(_har(u3R, cid), key, u3k(val)); u3z(key); return val; } -/* u3z_uniq(): uniquify with memo cache. +/* u3z_uniq(): uniquify with memo cache. XX not used. */ u3_noun -u3z_uniq(u3_noun som) +u3z_uniq(u3z_cid cid, u3_noun som) { u3_noun key = u3nc(c3__uniq, u3k(som)); - u3_noun val = u3h_get(u3R->cax.har_p, key); + u3_noun val = u3h_get(_har(u3R, cid), key); if ( u3_none != val ) { u3z(key); u3z(som); return val; } else { - u3h_put(u3R->cax.har_p, key, u3k(som)); + u3h_put(_har(u3R, cid), key, u3k(som)); return som; } } + +/* u3z_reap(): promote memoization cache state. +*/ +void +u3z_reap(u3p(u3h_root) per_p) +{ + u3h_take_uni(u3R->cax.per_p, per_p); +} diff --git a/pkg/noun/zave.h b/pkg/noun/zave.h index 44f272da7c..6857427eb4 100644 --- a/pkg/noun/zave.h +++ b/pkg/noun/zave.h @@ -12,10 +12,18 @@ *** and a noun argument to that (logical) function. Functions *** are predefined by C-level callers, but 0 means nock. *** - *** The memo cache is within its road and dies when it falls. - *** *** Memo functions RETAIN keys and transfer values. **/ + /* u3z_cid: cache id + */ + typedef enum { + u3z_memo_toss = 0, + u3z_memo_keep = 1, + // u3z_memo_ford = 2, + // u3z_memo_ames = 3, + // ... + } u3z_cid; + /* u3z_key*(): construct a memo cache-key. Arguments retained. */ u3_noun u3z_key(c3_m, u3_noun); @@ -26,20 +34,35 @@ /* u3z_find*(): find in memo cache. Arguments retained */ - u3_weak u3z_find(u3_noun key); - u3_weak u3z_find_m(c3_m fun_m, u3_noun one); + u3_weak u3z_find(u3z_cid cid, u3_noun key); + u3_weak u3z_find_m(u3z_cid cid, c3_m fun_m, u3_noun one); /* u3z_save(): save in memo cache. TRANSFER key; RETAIN val; */ - u3_noun u3z_save(u3_noun key, u3_noun val); + u3_noun u3z_save(u3z_cid cid, u3_noun key, u3_noun val); /* u3z_save_m(): save in memo cache. Arguments retained */ - u3_noun u3z_save_m(c3_m fun_m, u3_noun one, u3_noun val); + u3_noun u3z_save_m(u3z_cid cid, c3_m fun_m, u3_noun one, u3_noun val); /* u3z_uniq(): uniquify with memo cache. */ u3_noun - u3z_uniq(u3_noun som); + u3z_uniq(u3z_cid cid, u3_noun som); + + /* u3z_reap(): promote persistent memoization cache. + */ + void + u3z_reap(u3p(u3h_root) per_p); + + /* u3z_free(): free memoization cache. + */ + void + u3z_free(u3z_cid cid); + + /* u3z_ream(): refresh after restoring from checkpoint. + */ + void + u3z_ream(u3z_cid cid); #endif /* ifndef U3_ZAVE_H */ diff --git a/pkg/urcrypt/.gitignore b/pkg/urcrypt/.gitignore deleted file mode 100644 index b464b3f2f1..0000000000 --- a/pkg/urcrypt/.gitignore +++ /dev/null @@ -1,56 +0,0 @@ -*.pc -config.h -config.status -libtool - -# the following was adapted from -# https://github.com/github/gitignore/blob/991e760c1c6d50fdda246e0178b9c58b06770b90/Autotools.gitignore - -# http://www.gnu.org/software/automake - -Makefile.in -build-aux/ar-lib -/mdate-sh -/py-compile -/test-driver -/ylwrap -.deps/ -.dirstamp - -# http://www.gnu.org/software/autoconf - -autom4te.cache -/autoscan.log -/autoscan-*.log -/aclocal.m4 -build-aux/compile -/config.cache -build-aux/config.guess -/config.h.in -build-aux/config.log -build-aux/config.status -build-aux/config.sub -/configure -/configure.scan -build-aux/depcomp -build-aux/install-sh -build-aux/missing -/stamp-h1 - -# https://www.gnu.org/software/libtool/ - -build-aux/ltmain.sh - -# http://www.gnu.org/software/m4/ - -build-aux/m4/libtool.m4 -build-aux/m4/ltoptions.m4 -build-aux/m4/ltsugar.m4 -build-aux/m4/ltversion.m4 -build-aux/m4/lt~obsolete.m4 - -# Generated Makefile -# (meta build system like autotools, -# can automatically generate from config.status script -# (which is called by configure script)) -Makefile diff --git a/pkg/urcrypt/BUILD.bazel b/pkg/urcrypt/BUILD.bazel deleted file mode 100644 index 22108ea72c..0000000000 --- a/pkg/urcrypt/BUILD.bazel +++ /dev/null @@ -1,36 +0,0 @@ -# -# LIBRARIES -# - -load("//bazel:common_settings.bzl", "vere_library") - -vere_library( - name = "urcrypt", - srcs = glob( - [ - "*.c", - "*.h", - ], - exclude = ["urcrypt.h"], - ), - hdrs = ["urcrypt.h"], - copts = [ - "-Wall", - "-g", - "-O3", - ], - include_prefix = "urcrypt", - includes = ["."], - linkstatic = True, - visibility = ["//pkg:__subpackages__"], - deps = [ - "//pkg/urcrypt/ge-additions", - "@aes_siv", - "@argon2", - "@ed25519", - "@keccak_tiny", - "@openssl", - "@scrypt", - "@secp256k1", - ], -) diff --git a/pkg/urcrypt/README.md b/pkg/urcrypt/README.md deleted file mode 100644 index 7170a906b3..0000000000 --- a/pkg/urcrypt/README.md +++ /dev/null @@ -1,33 +0,0 @@ -What is urcrypt? ----------------- -urcrypt is a library of cryptography routines used by urbit jets. - -Why is urcrypt? ---------------- -Urbit's C runtime (long the only urbit runtime) has accumulated a collection of -cryptography dependencies, some with custom additions or patches. These -libraries have different conventions and have been managed by u3 in an ad-hoc -manner. Reproducing that arrangement in other runtimes is tricky and -error-prone. The (sometimes inconsistent) logic must be reproduced and suitable -cryptography primitives must be found (or worse, written) for the new -environment. - -To ease these burdens, urcrypt isolates the quirks behind a consistent calling -convention. Everything is a little-endian byte array, and each jetted operation -has a corresponding function in the library. Jets simply unpack their nouns, -call urcrypt, and pack the results. - -What is a cryptography routine? -------------------------------- -This is more of a subjective question than it might appear. Any of the following -conditions are sufficient, but not necessary, for a function to be included in -urcrypt: - - * The routine is sensitive to side-channel attacks (encryption, etc) - * Some property of the routine is cryptographically useful (SHA, RIPE, etc) - * The routine typically lives in a crypto library, for whatever reason. - -A word on OpenSSL ------------------ -It is the library user's responsibility to initialize openssl, set custom memory -functions, etc. diff --git a/pkg/urcrypt/aes_cbc.c b/pkg/urcrypt/aes_cbc.c deleted file mode 100644 index f163bccd8c..0000000000 --- a/pkg/urcrypt/aes_cbc.c +++ /dev/null @@ -1,181 +0,0 @@ -#include "urcrypt.h" -#include "util.h" -#include -#include - -static int -urcrypt__cbc_pad(uint8_t **message_ptr, - size_t *length_ptr, - urcrypt_realloc_t realloc_ptr) -{ - size_t length = *length_ptr, - remain = length % 16; - - if ( 0 == remain ) { - // no padding needed - return 0; - } - else { - size_t padding = 16 - remain, - padded = length + padding; - - if ( padded < length ) { - // size_t overflow - return -1; - } - else { - uint8_t *out = (*realloc_ptr)(*message_ptr, padded); - if ( NULL == out ) { - return -2; - } - else { - memset(out + length, 0, padding); - *message_ptr = out; - *length_ptr = padded; - return 0; - } - } - } -} - -static int -urcrypt__cbc_help(uint8_t **message_ptr, - size_t *length_ptr, - const AES_KEY *key, - uint8_t ivec[16], - const int enc, - urcrypt_realloc_t realloc_ptr) -{ - if ( 0 != urcrypt__cbc_pad(message_ptr, length_ptr, realloc_ptr) ) { - return -1; - } - else { - uint8_t *out = *message_ptr; - size_t length = *length_ptr; - urcrypt__reverse(16, ivec); - urcrypt__reverse(length, out); - AES_cbc_encrypt(out, out, length, key, ivec, enc); - urcrypt__reverse(length, out); - return 0; - } -} - -int -urcrypt_aes_cbca_en(uint8_t **message_ptr, - size_t *length_ptr, - uint8_t key[16], - uint8_t ivec[16], - urcrypt_realloc_t realloc_ptr) -{ - AES_KEY aes_key; - - urcrypt__reverse(16, key); - - if ( 0 != AES_set_encrypt_key(key, 128, &aes_key) ) { - return -1; - } - else { - return urcrypt__cbc_help(message_ptr, length_ptr, - &aes_key, ivec, AES_ENCRYPT, realloc_ptr); - } -} - -int -urcrypt_aes_cbca_de(uint8_t **message_ptr, - size_t *length_ptr, - uint8_t key[16], - uint8_t ivec[16], - urcrypt_realloc_t realloc_ptr) -{ - AES_KEY aes_key; - - urcrypt__reverse(16, key); - - if ( 0 != AES_set_decrypt_key(key, 128, &aes_key) ) { - return -1; - } - else { - return urcrypt__cbc_help(message_ptr, length_ptr, - &aes_key, ivec, AES_DECRYPT, realloc_ptr); - } -} - -int -urcrypt_aes_cbcb_en(uint8_t **message_ptr, - size_t *length_ptr, - uint8_t key[24], - uint8_t ivec[16], - urcrypt_realloc_t realloc_ptr) -{ - AES_KEY aes_key; - - urcrypt__reverse(24, key); - - if ( 0 != AES_set_encrypt_key(key, 192, &aes_key) ) { - return -1; - } - else { - return urcrypt__cbc_help(message_ptr, length_ptr, - &aes_key, ivec, AES_ENCRYPT, realloc_ptr); - } -} - -int -urcrypt_aes_cbcb_de(uint8_t **message_ptr, - size_t *length_ptr, - uint8_t key[24], - uint8_t ivec[16], - urcrypt_realloc_t realloc_ptr) -{ - AES_KEY aes_key; - - urcrypt__reverse(24, key); - - if ( 0 != AES_set_decrypt_key(key, 192, &aes_key) ) { - return -1; - } - else { - return urcrypt__cbc_help(message_ptr, length_ptr, - &aes_key, ivec, AES_DECRYPT, realloc_ptr); - } -} - -int -urcrypt_aes_cbcc_en(uint8_t **message_ptr, - size_t *length_ptr, - uint8_t key[32], - uint8_t ivec[16], - urcrypt_realloc_t realloc_ptr) -{ - AES_KEY aes_key; - - urcrypt__reverse(32, key); - - if ( 0 != AES_set_encrypt_key(key, 256, &aes_key) ) { - return -1; - } - else { - return urcrypt__cbc_help(message_ptr, length_ptr, - &aes_key, ivec, AES_ENCRYPT, realloc_ptr); - } -} - -int -urcrypt_aes_cbcc_de(uint8_t **message_ptr, - size_t *length_ptr, - uint8_t key[32], - uint8_t ivec[16], - urcrypt_realloc_t realloc_ptr) -{ - AES_KEY aes_key; - - urcrypt__reverse(32, key); - - if ( 0 != AES_set_decrypt_key(key, 256, &aes_key) ) { - return -1; - } - else { - return urcrypt__cbc_help(message_ptr, length_ptr, - &aes_key, ivec, AES_DECRYPT, realloc_ptr); - } -} diff --git a/pkg/urcrypt/aes_ecb.c b/pkg/urcrypt/aes_ecb.c deleted file mode 100644 index 9b39100126..0000000000 --- a/pkg/urcrypt/aes_ecb.c +++ /dev/null @@ -1,111 +0,0 @@ -#include "urcrypt.h" -#include "util.h" -#include - -int -urcrypt_aes_ecba_en(uint8_t key[16], uint8_t block[16], uint8_t out[16]) -{ - AES_KEY aes_key; - - urcrypt__reverse(16, key); - urcrypt__reverse(16, block); - - if ( 0 != AES_set_encrypt_key(key, 128, &aes_key) ) { - return -1; - } - else { - AES_ecb_encrypt(block, out, &aes_key, AES_ENCRYPT); - urcrypt__reverse(16, out); - return 0; - } -} - -int -urcrypt_aes_ecba_de(uint8_t key[16], uint8_t block[16], uint8_t out[16]) -{ - AES_KEY aes_key; - - urcrypt__reverse(16, key); - urcrypt__reverse(16, block); - - if ( 0 != AES_set_decrypt_key(key, 128, &aes_key) ) { - return -1; - } - else { - AES_ecb_encrypt(block, out, &aes_key, AES_DECRYPT); - urcrypt__reverse(16, out); - return 0; - } -} - -int -urcrypt_aes_ecbb_en(uint8_t key[24], uint8_t block[16], uint8_t out[16]) -{ - AES_KEY aes_key; - - urcrypt__reverse(24, key); - urcrypt__reverse(16, block); - - if ( 0 != AES_set_encrypt_key(key, 192, &aes_key) ) { - return -1; - } - else { - AES_ecb_encrypt(block, out, &aes_key, AES_ENCRYPT); - urcrypt__reverse(16, out); - return 0; - } -} - -int -urcrypt_aes_ecbb_de(uint8_t key[24], uint8_t block[16], uint8_t out[16]) -{ - AES_KEY aes_key; - - urcrypt__reverse(24, key); - urcrypt__reverse(16, block); - - if ( 0 != AES_set_decrypt_key(key, 192, &aes_key) ) { - return -1; - } - else { - AES_ecb_encrypt(block, out, &aes_key, AES_DECRYPT); - urcrypt__reverse(16, out); - return 0; - } -} - -int -urcrypt_aes_ecbc_en(uint8_t key[32], uint8_t block[16], uint8_t out[16]) -{ - AES_KEY aes_key; - - urcrypt__reverse(32, key); - urcrypt__reverse(16, block); - - if ( 0 != AES_set_encrypt_key(key, 256, &aes_key) ) { - return -1; - } - else { - AES_ecb_encrypt(block, out, &aes_key, AES_ENCRYPT); - urcrypt__reverse(16, out); - return 0; - } -} - -int -urcrypt_aes_ecbc_de(uint8_t key[32], uint8_t block[16], uint8_t out[16]) -{ - AES_KEY aes_key; - - urcrypt__reverse(32, key); - urcrypt__reverse(16, block); - - if ( 0 != AES_set_decrypt_key(key, 256, &aes_key) ) { - return -1; - } - else { - AES_ecb_encrypt(block, out, &aes_key, AES_DECRYPT); - urcrypt__reverse(16, out); - return 0; - } -} diff --git a/pkg/urcrypt/aes_siv.c b/pkg/urcrypt/aes_siv.c deleted file mode 100644 index 7e06e769f2..0000000000 --- a/pkg/urcrypt/aes_siv.c +++ /dev/null @@ -1,181 +0,0 @@ -#include "urcrypt.h" -#include "util.h" -#include "aes_siv.h" - -static AES_SIV_CTX* -urcrypt__aes_siv_init(uint8_t *key, - size_t key_length, - urcrypt_aes_siv_data *data, - size_t data_length) -{ - AES_SIV_CTX *ctx = AES_SIV_CTX_new(); - if ( NULL == ctx ) { - return NULL; - } - else { - urcrypt__reverse(key_length, key); - if ( 0 == AES_SIV_Init(ctx, key, key_length) ) { - AES_SIV_CTX_free(ctx); - return NULL; - } - else { - size_t i, len; - uint8_t *dat; - - for ( i = 0; i < data_length; ++i ) { - len = data[i].length; - dat = data[i].bytes; - urcrypt__reverse(len, dat); - if ( 0 == AES_SIV_AssociateData(ctx, dat, len) ) { - AES_SIV_CTX_free(ctx); - return NULL; - } - } - - return ctx; - } - } -} - -static int -urcrypt__aes_siv_en(uint8_t *key, - size_t key_length, - uint8_t *message, - size_t message_length, - urcrypt_aes_siv_data *data, - size_t data_length, - uint8_t iv[16], - uint8_t *out) -{ - AES_SIV_CTX *ctx = urcrypt__aes_siv_init(key, key_length, data, data_length); - - if ( NULL == ctx ) { - return -1; - } - else { - int ret; - urcrypt__reverse(message_length, message); - ret = AES_SIV_EncryptFinal(ctx, iv, out, message, message_length); - AES_SIV_CTX_free(ctx); - - if ( 0 == ret ) { - return -2; - } - else { - urcrypt__reverse(16, iv); - urcrypt__reverse(message_length, out); - return 0; - } - } -} - -static int -urcrypt__aes_siv_de(uint8_t *key, - size_t key_length, - uint8_t *message, - size_t message_length, - urcrypt_aes_siv_data *data, - size_t data_length, - uint8_t iv[16], - uint8_t *out) -{ - AES_SIV_CTX *ctx = urcrypt__aes_siv_init(key, key_length, data, data_length); - - if ( NULL == ctx ) { - return -1; - } - else { - int ret; - - urcrypt__reverse(message_length, message); - urcrypt__reverse(16, iv); - ret = AES_SIV_DecryptFinal(ctx, out, iv, message, message_length); - AES_SIV_CTX_free(ctx); - - if ( 0 == ret ) { - return -2; - } - else { - urcrypt__reverse(message_length, out); - return 0; - } - } -} - -int -urcrypt_aes_siva_en(uint8_t *message, - size_t message_length, - urcrypt_aes_siv_data *data, - size_t data_length, - uint8_t key[32], - uint8_t iv[16], - uint8_t *out) -{ - return urcrypt__aes_siv_en(key, 32, - message, message_length, data, data_length, iv, out); -} - -int -urcrypt_aes_siva_de(uint8_t *message, - size_t message_length, - urcrypt_aes_siv_data *data, - size_t data_length, - uint8_t key[32], - uint8_t iv[16], - uint8_t *out) -{ - return urcrypt__aes_siv_de(key, 32, - message, message_length, data, data_length, iv, out); -} - -int -urcrypt_aes_sivb_en(uint8_t *message, - size_t message_length, - urcrypt_aes_siv_data *data, - size_t data_length, - uint8_t key[48], - uint8_t iv[16], - uint8_t *out) -{ - return urcrypt__aes_siv_en(key, 48, - message, message_length, data, data_length, iv, out); -} - -int -urcrypt_aes_sivb_de(uint8_t *message, - size_t message_length, - urcrypt_aes_siv_data *data, - size_t data_length, - uint8_t key[48], - uint8_t iv[16], - uint8_t *out) -{ - return urcrypt__aes_siv_de(key, 48, - message, message_length, data, data_length, iv, out); -} - -int -urcrypt_aes_sivc_en(uint8_t *message, - size_t message_length, - urcrypt_aes_siv_data *data, - size_t data_length, - uint8_t key[64], - uint8_t iv[16], - uint8_t *out) -{ - return urcrypt__aes_siv_en(key, 64, - message, message_length, data, data_length, iv, out); -} - -int -urcrypt_aes_sivc_de(uint8_t *message, - size_t message_length, - urcrypt_aes_siv_data *data, - size_t data_length, - uint8_t key[64], - uint8_t iv[16], - uint8_t *out) -{ - return urcrypt__aes_siv_de(key, 64, - message, message_length, data, data_length, iv, out); -} diff --git a/pkg/urcrypt/argon.c b/pkg/urcrypt/argon.c deleted file mode 100644 index e1090dddec..0000000000 --- a/pkg/urcrypt/argon.c +++ /dev/null @@ -1,120 +0,0 @@ -#include "urcrypt.h" -#include "util.h" -#include -#include - -// library convention is to have sizes in size_t, but argon2 wants them -// in uint32_t, so here's a helper macro for ensuring equivalence. -#define SZ_32(s) ( sizeof(size_t) <= sizeof(uint32_t) || s <= 0xFFFFFFFF ) - -const char* -urcrypt_argon2(uint8_t type, - uint32_t version, - uint32_t threads, - uint32_t memory_cost, - uint32_t time_cost, - size_t secret_length, - uint8_t *secret, - size_t associated_length, - uint8_t *associated, - size_t password_length, - uint8_t *password, - size_t salt_length, - uint8_t *salt, - size_t out_length, - uint8_t *out, - urcrypt_argon2_alloc_t alloc_ptr, - urcrypt_argon2_free_t free_ptr) -{ - if ( !( SZ_32(secret_length) && - SZ_32(associated_length) && - SZ_32(password_length) && - SZ_32(salt_length) && - SZ_32(out_length) ) ) { - return "length > 32 bits"; - } - else { - int (*f)(argon2_context*); - int result; - - switch ( type ) { - default: - return "unknown type"; - case urcrypt_argon2_d: - f = &argon2d_ctx; - break; - case urcrypt_argon2_i: - f = &argon2i_ctx; - break; - case urcrypt_argon2_id: - f = &argon2id_ctx; - break; - case urcrypt_argon2_u: - f = &argon2u_ctx; - break; - } - - urcrypt__reverse(secret_length, secret); - urcrypt__reverse(associated_length, associated); - urcrypt__reverse(password_length, password); - urcrypt__reverse(salt_length, salt); - - argon2_context context = { - out, // output array, at least [digest length] in size - out_length, // digest length - password, // password array - password_length, // password length - salt, // salt array - salt_length, // salt length - secret, // optional secret data - secret_length, - associated, // optional associated data - associated_length, - time_cost, // performance cost configuration - memory_cost, - threads, - threads, - version, // algorithm version - alloc_ptr, // custom memory allocation function - free_ptr, // custom memory deallocation function - ARGON2_DEFAULT_FLAGS // by default only internal memory is cleared - }; - - result = (*f)(&context); - - if ( ARGON2_OK != result ) { - return argon2_error_message(result); - } - else { - urcrypt__reverse(out_length, out); - return NULL; - } - } -} - -int -urcrypt_blake2(size_t message_length, - uint8_t *message, - size_t key_length, - uint8_t key[64], - size_t out_length, - uint8_t *out) -{ - if ( key_length > 64 ) { - return -1; - } - else { - urcrypt__reverse(message_length, message); - urcrypt__reverse(key_length, key); - - if ( 0 != blake2b(out, out_length, - message, message_length, - key, key_length)) { - return -1; - } - else { - urcrypt__reverse(out_length, out); - return 0; - } - } -} diff --git a/pkg/urcrypt/argon2/argon2-specs.pdf b/pkg/urcrypt/argon2/argon2-specs.pdf deleted file mode 100644 index d916af6415..0000000000 Binary files a/pkg/urcrypt/argon2/argon2-specs.pdf and /dev/null differ diff --git a/pkg/urcrypt/argon2/latex/pics/argon2-par.pdf b/pkg/urcrypt/argon2/latex/pics/argon2-par.pdf deleted file mode 100644 index ab2a8438a4..0000000000 Binary files a/pkg/urcrypt/argon2/latex/pics/argon2-par.pdf and /dev/null differ diff --git a/pkg/urcrypt/argon2/latex/pics/compression.pdf b/pkg/urcrypt/argon2/latex/pics/compression.pdf deleted file mode 100644 index 999775825e..0000000000 Binary files a/pkg/urcrypt/argon2/latex/pics/compression.pdf and /dev/null differ diff --git a/pkg/urcrypt/argon2/latex/pics/generic.pdf b/pkg/urcrypt/argon2/latex/pics/generic.pdf deleted file mode 100644 index b58197eb2f..0000000000 Binary files a/pkg/urcrypt/argon2/latex/pics/generic.pdf and /dev/null differ diff --git a/pkg/urcrypt/argon2/latex/pics/power-distribution.jpg b/pkg/urcrypt/argon2/latex/pics/power-distribution.jpg deleted file mode 100644 index 82d006965e..0000000000 Binary files a/pkg/urcrypt/argon2/latex/pics/power-distribution.jpg and /dev/null differ diff --git a/pkg/urcrypt/ed25519.c b/pkg/urcrypt/ed25519.c deleted file mode 100644 index eb25f0b759..0000000000 --- a/pkg/urcrypt/ed25519.c +++ /dev/null @@ -1,53 +0,0 @@ -#include "urcrypt.h" -#include -#include - -void -urcrypt_ed_puck(const uint8_t seed[32], - uint8_t out[32]) -{ - uint8_t secret[64]; - ed25519_create_keypair(out, secret, seed); -} - -void -urcrypt_ed_shar(const uint8_t public[32], - const uint8_t seed[32], - uint8_t out[32]) -{ - uint8_t self[32], exp[64]; - - memset(self, 0, 32); - memset(exp, 0, 64); - memset(out, 0, 32); - - ed25519_create_keypair(self, exp, seed); - ed25519_key_exchange(out, public, exp); -} - -void -urcrypt_ed_sign(const uint8_t *message, - size_t length, - const uint8_t seed[32], - uint8_t out[64]) -{ - uint8_t public[64], secret[64]; - - memset(public, 0, 64); - memset(secret, 0, 64); - memset(out, 0, 64); - - ed25519_create_keypair(public, secret, seed); - ed25519_sign(out, message, length, public, secret); -} - -bool -urcrypt_ed_veri(const uint8_t *message, - size_t length, - const uint8_t public[32], - const uint8_t signature[64]) -{ - return ( ed25519_verify(signature, message, length, public) == 1 ) - ? true - : false; -} diff --git a/pkg/urcrypt/ge-additions/BUILD.bazel b/pkg/urcrypt/ge-additions/BUILD.bazel deleted file mode 100644 index 06ab7c3e5a..0000000000 --- a/pkg/urcrypt/ge-additions/BUILD.bazel +++ /dev/null @@ -1,11 +0,0 @@ -load("//bazel:common_settings.bzl", "vere_library") - -vere_library( - name = "ge-additions", - srcs = ["ge-additions.c"], - hdrs = ["ge-additions.h"], - includes = ["."], - linkstatic = True, - visibility = ["//pkg/urcrypt:__pkg__"], - deps = ["@ed25519"], -) diff --git a/pkg/urcrypt/ge-additions/LICENSE b/pkg/urcrypt/ge-additions/LICENSE deleted file mode 100644 index 6a66aea5ea..0000000000 --- a/pkg/urcrypt/ge-additions/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/pkg/urcrypt/ge-additions/README.md b/pkg/urcrypt/ge-additions/README.md deleted file mode 100644 index 0684ab2656..0000000000 --- a/pkg/urcrypt/ge-additions/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# Group Element Additions - -Urbit uses the ge.h code from the ed25519 library, which was ported from the -ref10 SUPERCOP public domain implementation. That implementation doesn't -contain several functions needed for ring signatures. - -This file does. The providence of this code starts with Adam Langley taking -the SUPERCOP C implementation and producing an ed25519 implementation for it -in golang (https://godoc.org/golang.org/x/crypto/ed25519). (If you look at -the go code, you'll see the comments are the same as the comments in the C -implementation.) - -From there, the DEDIS group from ETH Zurich took that implementation and -added the additional methods to make a generalized ECC point library. While -their project as a whole is MPL, they deliberately left their ed25519 -implementation under the Go BSD-3 license: -(https://github.com/dedis/kyber/blob/master/group/edwards25519/LICENSE) - -This file is a fairly straight translation from Go to C of DEDIS' additions, -so this falls under the same license. diff --git a/pkg/urcrypt/ge-additions/ge-additions.c b/pkg/urcrypt/ge-additions/ge-additions.c deleted file mode 100644 index 95b296dc20..0000000000 --- a/pkg/urcrypt/ge-additions/ge-additions.c +++ /dev/null @@ -1,169 +0,0 @@ -// Group Element Additions -// -// Urbit uses the ge.h code from the ed25519 library, which was ported from the -// ref10 SUPERCOP public domain implementation. That implementation doesn't -// contain several functions needed for ring signatures. -// -// This file does. The providence of this code starts with Adam Langley taking -// the SUPERCOP C implementation and producing an ed25519 implementation for it -// in golang (https://godoc.org/golang.org/x/crypto/ed25519). (If you look at -// the go code, you'll see the comments are the same as the comments in the C -// implementation.) -// -// From there, the DEDIS group from ETH Zurich took that implementation and -// added the additional methods to make a generalized ECC point library. While -// their project as a whole is MPL, they deliberately left their ed25519 -// implementation under the Go BSD-3 license: -// (https://github.com/dedis/kyber/blob/master/group/edwards25519/LICENSE) -// -// This file is a fairly straight translation from Go to C of DEDIS' additions, -// so this falls under the same license. -// -// ------ -// -// Copyright (c) 2009 The Go Authors. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include "ge-additions.h" - -#include - -static unsigned char equal(signed char b, signed char c) { - unsigned char ub = b; - unsigned char uc = c; - unsigned char x = ub ^ uc; /* 0: yes; 1..255: no */ - uint64_t y = x; /* 0: yes; 1..255: no */ - y -= 1; /* large: yes; 0..254: no */ - y >>= 63; /* 1: yes; 0: no */ - return (unsigned char) y; -} - -static unsigned char negative(signed char b) { - uint64_t x = b; /* 18446744073709551361..18446744073709551615: yes; 0..255: no */ - x >>= 63; /* 1: yes; 0: no */ - return (unsigned char) x; -} - -void ge_cached_0(ge_cached* c) { - fe_1(c->YplusX); - fe_1(c->YminusX); - fe_1(c->Z); - fe_0(c->T2d); -} - -void ge_cached_cmov(ge_cached* r, const ge_cached* u, int32_t b) -{ - fe_cmov(r->YplusX, u->YplusX, b); - fe_cmov(r->YminusX, u->YminusX, b); - fe_cmov(r->Z, u->Z, b); - fe_cmov(r->T2d, u->T2d, b); -} - -void ge_cached_neg(ge_cached* r, const ge_cached* t) -{ - fe_copy(r->YplusX, t->YminusX); - fe_copy(r->YminusX, t->YplusX); - fe_copy(r->Z, t->Z); - fe_neg(r->T2d, t->T2d); -} - -void select_cached(ge_cached* c, const ge_cached Ai[8], int32_t b) -{ - int32_t is_negative = negative(b); - int32_t b_abs = b - (((-is_negative) & b) << 1); - - ge_cached_0(c); - for (int32_t i = 0; i < 8; ++i) { - ge_cached_cmov(c, &Ai[i], equal(b_abs, i+1)); - } - - ge_cached minusC; - ge_cached_neg(&minusC, c); - ge_cached_cmov(c, &minusC, is_negative); -} - -// -void ge_scalarmult(ge_p3* h, const unsigned char* a, const ge_p3* A) -{ - signed char e[64]; - int i; - ge_p1p1 t; - ge_p3 u; - - for (i = 0; i < 32; ++i) { - e[2 * i + 0] = (a[i] >> 0) & 15; - e[2 * i + 1] = (a[i] >> 4) & 15; - } - - /* each e[i] is between 0 and 15 */ - /* e[63] is between 0 and 7 */ - signed char carry = 0; - for (i = 0; i < 63; ++i) { - e[i] += carry; - carry = e[i] + 8; - carry >>= 4; - e[i] -= carry << 4; - } - e[63] += carry; - /* each e[i] is between -8 and 8 */ - - // compute cached array of multiples of A from 1A through 8A - ge_cached Ai[8]; - ge_p3_to_cached(&Ai[0], A); - for (i = 0; i < 7; ++i) { - ge_add(&t, A, &Ai[i]); - ge_p1p1_to_p3(&u, &t); - ge_p3_to_cached(&Ai[i+1], &u); - } - - // special case for exponent nybble i == 63 - ge_p3_0(&u); - ge_cached c; - select_cached(&c, Ai, e[63]); - ge_add(&t, &u, &c); - - ge_p2 r; - for (i = 62; i >= 0; i--) { - // t <<= 4 - ge_p1p1_to_p2(&r, &t); - ge_p2_dbl(&t, &r); - ge_p1p1_to_p2(&r, &t); - ge_p2_dbl(&t, &r); - ge_p1p1_to_p2(&r, &t); - ge_p2_dbl(&t, &r); - ge_p1p1_to_p2(&r, &t); - ge_p2_dbl(&t, &r); - - // Add next nyble - ge_p1p1_to_p3(&u, &t); - select_cached(&c, Ai, e[i]); - ge_add(&t, &u, &c); - } - - ge_p1p1_to_p3(h, &t); -} diff --git a/pkg/urcrypt/ge-additions/ge-additions.h b/pkg/urcrypt/ge-additions/ge-additions.h deleted file mode 100644 index 3424ac689b..0000000000 --- a/pkg/urcrypt/ge-additions/ge-additions.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef GE_ADDITIONS_H -#define GE_ADDITIONS_H - -#include - -void ge_scalarmult(ge_p3* h, const unsigned char* a, const ge_p3* A); - -#endif diff --git a/pkg/urcrypt/ge_additions.c b/pkg/urcrypt/ge_additions.c deleted file mode 100644 index f894fc8adb..0000000000 --- a/pkg/urcrypt/ge_additions.c +++ /dev/null @@ -1,126 +0,0 @@ -#include "urcrypt.h" -#include - -int -urcrypt_ed_point_add(const uint8_t a[32], - const uint8_t b[32], - uint8_t out[32]) -{ - ge_p3 A, B; - ge_cached b_cached; - ge_p1p1 sum; - ge_p3 result; - - if ( ge_frombytes_negate_vartime(&A, a) != 0 ) { - return -1; - } - - if ( ge_frombytes_negate_vartime(&B, b) != 0 ) { - return -1; - } - - // Undo the negation from above. See add_scalar.c in the ed25519 distro. - fe_neg(A.X, A.X); - fe_neg(A.T, A.T); - fe_neg(B.X, B.X); - fe_neg(B.T, B.T); - - ge_p3_to_cached(&b_cached, &B); - ge_add(&sum, &A, &b_cached); - ge_p1p1_to_p3(&result, &sum); - - ge_p3_tobytes(out, &result); - - return 0; -} - -int -urcrypt_ed_scalarmult(const uint8_t a[32], - const uint8_t b[32], - uint8_t out[32]) -{ - ge_p3 B, result; - - if ( ge_frombytes_negate_vartime(&B, b) != 0 ) { - return -1; - } - - // Undo the negation from above. See add_scalar.c in the ed25519 distro. - fe_neg(B.X, B.X); - fe_neg(B.T, B.T); - - ge_scalarmult(&result, a, &B); - ge_p3_tobytes(out, &result); - return 0; -} - -void -urcrypt_ed_scalarmult_base(const uint8_t a[32], - uint8_t out[32]) -{ - ge_p3 R; - ge_scalarmult_base(&R, a); - ge_p3_tobytes(out, &R); -} - -int -urcrypt_ed_add_scalarmult_scalarmult_base(const uint8_t a[32], - const uint8_t a_point[32], - const uint8_t b[32], - uint8_t out[32]) -{ - ge_p2 r; - ge_p3 A; - - if (ge_frombytes_negate_vartime(&A, a_point) != 0) { - return -1; - } - - // Undo the negation from above. See add_scalar.c in the ed25519 distro. - fe_neg(A.X, A.X); - fe_neg(A.T, A.T); - - ge_double_scalarmult_vartime(&r, a, &A, b); - ge_tobytes(out, &r); - - return 0; -} - -int -urcrypt_ed_add_double_scalarmult(const uint8_t a[32], - const uint8_t a_point[32], - const uint8_t b[32], - const uint8_t b_point[32], - uint8_t out[32]) -{ - ge_p3 A, B, a_result, b_result, final_result; - ge_cached b_result_cached; - ge_p1p1 sum; - - if ( ge_frombytes_negate_vartime(&A, a_point) != 0 ) { - return -1; - } - - if ( ge_frombytes_negate_vartime(&B, b_point) != 0 ) { - return -1; - } - - // Undo the negation from above. See add_scalar.c in the ed25519 distro. - fe_neg(A.X, A.X); - fe_neg(A.T, A.T); - fe_neg(B.X, B.X); - fe_neg(B.T, B.T); - - // Perform the multiplications of a*A and b*B - ge_scalarmult(&a_result, a, &A); - ge_scalarmult(&b_result, b, &B); - - // Sum those two points - ge_p3_to_cached(&b_result_cached, &b_result); - ge_add(&sum, &a_result, &b_result_cached); - - ge_p1p1_to_p3(&final_result, &sum); - ge_p3_tobytes(out, &final_result); - - return 0; -} diff --git a/pkg/urcrypt/keccak.c b/pkg/urcrypt/keccak.c deleted file mode 100644 index 028b872fbc..0000000000 --- a/pkg/urcrypt/keccak.c +++ /dev/null @@ -1,21 +0,0 @@ -#include "urcrypt.h" -#include "util.h" -#include - -#define defkec(bits,byts) \ - int urcrypt_keccak_##bits(const uint8_t *message, size_t length, \ - uint8_t out[byts]) \ - { \ - if ( 0 == keccak_##bits(out, byts, message, length) ) { \ - urcrypt__reverse(byts, out); \ - return 0; \ - } \ - else { \ - return -1; \ - }\ - } - -defkec(224, 28) -defkec(256, 32) -defkec(384, 48) -defkec(512, 64) diff --git a/pkg/urcrypt/ripemd.c b/pkg/urcrypt/ripemd.c deleted file mode 100644 index 787184090b..0000000000 --- a/pkg/urcrypt/ripemd.c +++ /dev/null @@ -1,20 +0,0 @@ -#include "urcrypt.h" -#include "util.h" -#include - -int -urcrypt_ripemd160(uint8_t *message, size_t length, uint8_t out[20]) -{ - unsigned long n = length; - - if ( length != n ) { - return -1; - } - else { - urcrypt__reverse(length, message); - RIPEMD160(message, n, out); - urcrypt__reverse(20, out); - return 0; - } -} - diff --git a/pkg/urcrypt/scrypt.c b/pkg/urcrypt/scrypt.c deleted file mode 100644 index 830eddc563..0000000000 --- a/pkg/urcrypt/scrypt.c +++ /dev/null @@ -1,31 +0,0 @@ -#include "urcrypt.h" -#include -#include - -void -urcrypt_scrypt_pbkdf_sha256(const uint8_t *passwd, - size_t passwdlen, - const uint8_t *salt, - size_t saltlen, - uint64_t count, - size_t outlen, // must be at most 32*(2^32-1) - uint8_t *out) -{ - libscrypt_PBKDF2_SHA256( - passwd, passwdlen, salt, saltlen, count, out, outlen); -} - -int -urcrypt_scrypt(const uint8_t *passwd, - size_t passwdlen, - const uint8_t *salt, - size_t saltlen, - uint64_t n, - uint32_t r, - uint32_t p, - size_t outlen, - uint8_t *out) -{ - return libscrypt_scrypt( - passwd, passwdlen, salt, saltlen, n, r, p, out, outlen); -} diff --git a/pkg/urcrypt/secp256k1.c b/pkg/urcrypt/secp256k1.c deleted file mode 100644 index 76f16a0505..0000000000 --- a/pkg/urcrypt/secp256k1.c +++ /dev/null @@ -1,245 +0,0 @@ -#include "urcrypt.h" -#include "util.h" -#include -#include -#include -#include -#include -#include - -#define SECP_FLAGS SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN - -struct urcrypt_secp_context_struct { - secp256k1_context* secp; - uint8_t prealloc[]; -}; - -size_t -urcrypt_secp_prealloc_size() -{ - return sizeof(urcrypt_secp_context) + - secp256k1_context_preallocated_size(SECP_FLAGS); -} - -int -urcrypt_secp_init(urcrypt_secp_context *context, - uint8_t entropy[32]) -{ - secp256k1_context* secp = - secp256k1_context_preallocated_create(context->prealloc, SECP_FLAGS); - if ( 1 == secp256k1_context_randomize(secp, entropy) ) { - context->secp = secp; - return 0; - } - else { - secp256k1_context_preallocated_destroy(secp); - return -1; - } -} - -void -urcrypt_secp_destroy(urcrypt_secp_context *context) -{ - secp256k1_context_preallocated_destroy(context->secp); -} - -int -urcrypt_secp_make(uint8_t hash[32], uint8_t key[32], uint8_t out[32]) -{ - urcrypt__reverse(32, hash); - urcrypt__reverse(32, key); - - if ( 1 != secp256k1_nonce_function_rfc6979( - out, // OUT: return arg for nonce - hash, // IN: message / hash */ - key, // IN: key32 - NULL, // IN: algorithm (NULL == ECDSA) - NULL, // IN: arbitrary data pointer (unused) - 0) ) { // IN: attempt number (0 == normal) - return -1; - } - else { - urcrypt__reverse(32, out); - return 0; - } -} - -int -urcrypt_secp_sign(urcrypt_secp_context* context, - uint8_t hash[32], - uint8_t key[32], - uint8_t* out_v, - uint8_t out_r[32], - uint8_t out_s[32]) -{ - secp256k1_ecdsa_recoverable_signature signature; - - urcrypt__reverse(32, hash); - urcrypt__reverse(32, key); - - /* sign - N.B. if we want the 'v' field we can't use default secp256k1_ecdsa_sign(), - but must use secp256k1_ecdsa_sign_recoverable() */ - if ( 1 != secp256k1_ecdsa_sign_recoverable( - context->secp, /* IN: context object */ - &signature, /* OUT: signature */ - hash, /* IN: 32 byte hash to be signed */ - key, /* IN: 32 byte secret key */ - NULL, /* IN: nonce-function ptr ; NULL = default */ - NULL) ) { /* IN: data for nonce function; not used */ - return -1; - } - else { - uint8_t sigbytes[64]; - int recid; - if ( 1 != secp256k1_ecdsa_recoverable_signature_serialize_compact( - context->secp, /* IN: context object */ - sigbytes, /* OUT: 64 byte sig (r,s) */ - &recid, /* OUT: v */ - &signature) ) { /* IN: 65 byte sig */ - return -2; - } - else { - /* read sigbytes into r and s - convert endianness while we're at it */ - uint8_t i, j; - for ( j = 31, i = 0; i < 32; ++i, --j) { - out_r[j] = sigbytes[i]; - } - for ( j = 31; i < 64; ++i, --j ) { - out_s[j] = sigbytes[i]; - } - *out_v = (uint8_t) recid; - return 0; - } - } -} - -int -urcrypt_secp_reco(urcrypt_secp_context* context, - uint8_t hash[32], - uint8_t key_v, - const uint8_t key_r[32], - const uint8_t key_s[32], - uint8_t out_x[32], - uint8_t out_y[32]) -{ - if ( (NULL == hash) || - (NULL == key_r) || - (NULL == key_s) ) { - return -1; - } - else if ( key_v > 3 ) { - return -2; - } - else { - secp256k1_ecdsa_recoverable_signature signature; - uint8_t private[64]; - uint8_t i, j; - // make big private key out of two smaller parts, reversing endianness - for ( j = 31, i = 0; i < 32; ++i, --j) { - private[i] = key_r[j]; - } - for ( j = 31; i < 64; ++i, --j ) { - private[i] = key_s[j]; - } - memset(&signature, 0, sizeof(secp256k1_ecdsa_recoverable_signature)); - if ( 1 != secp256k1_ecdsa_recoverable_signature_parse_compact( - context->secp, /* IN: context */ - &signature, /* OUT: sig */ - private, /* IN: r/s */ - key_v) ) { /* IN: v */ - return -3; - } - else { - secp256k1_pubkey public; - memset(&public, 0, sizeof(secp256k1_pubkey)); - urcrypt__reverse(32, hash); - if ( 1 != secp256k1_ecdsa_recover( - context->secp, /* IN: context */ - &public, /* OUT: pub key */ - &signature, /* IN: signature */ - hash) ) { /* IN: message hash */ - return -4; - } - else { - /* convert pub into serialized form that we can get x, y out of */ - uint8_t serialized[65]; - size_t outputlen = 65; - memset(serialized, 0, outputlen); - if ( 1 != secp256k1_ec_pubkey_serialize( - context->secp, /* IN: context */ - serialized, /* OUT: output */ - &outputlen, /* IN/OUT: outputlen */ - &public, /* IN: pubkey*/ - SECP256K1_EC_UNCOMPRESSED) ) { /* IN: flags */ - return -5; - } - else { - /* in file - subprojects/secp256k1/src/eckey_impl.h - func - secp256k1_eckey_pubkey_parse() - we can see - byte 0: signal bits (???) - bytes 1-32: x - bytes 33-64: y - - convert endianness while we're at it */ - for (j = 32, i = 0; i < 32; ++i, --j) { - out_x[i] = serialized[j]; - } - for (j = 64, i = 0; i < 32; ++i, --j) { - out_y[i] = serialized[j]; - } - return 0; - } - } - } - } -} - -int -urcrypt_secp_schnorr_sign(urcrypt_secp_context* context, - uint8_t key[32], - uint8_t msg[32], - uint8_t aux[32], - uint8_t out_sig[64]) -{ - secp256k1_keypair keypair; - - urcrypt__reverse(32, key); - urcrypt__reverse(32, msg); - urcrypt__reverse(32, aux); - - if ( 1 != secp256k1_keypair_create(context->secp, &keypair, key) ) { - return -1; - } - if ( 1 != secp256k1_schnorrsig_sign32(context->secp, out_sig, msg, &keypair, aux) ) { - return -1; - } - - urcrypt__reverse(64, out_sig); - return 0; -} - -bool -urcrypt_secp_schnorr_veri(urcrypt_secp_context* context, - uint8_t sig[64], - uint8_t msg[32], - uint8_t pub[32]) -{ - secp256k1_xonly_pubkey pubkey; - - urcrypt__reverse(64, sig); - urcrypt__reverse(32, msg); - urcrypt__reverse(32, pub); - - if ( 1 != secp256k1_xonly_pubkey_parse(context->secp, &pubkey, pub) ) { - return false; - } - if ( 1 != secp256k1_schnorrsig_verify(context->secp, sig, msg, 32, &pubkey) ) { - return false; - } - return true; -} diff --git a/pkg/urcrypt/sha.c b/pkg/urcrypt/sha.c deleted file mode 100644 index 9cd318c5ad..0000000000 --- a/pkg/urcrypt/sha.c +++ /dev/null @@ -1,49 +0,0 @@ -#include "urcrypt.h" -#include "util.h" -#include - -void -urcrypt_sha1(uint8_t *message, size_t length, uint8_t out[20]) -{ - urcrypt__reverse(length, message); - SHA1(message, length, out); - urcrypt__reverse(20, out); -} - -void -urcrypt_shay(const uint8_t *message, size_t length, uint8_t out[32]) -{ - SHA256(message, length, out); -} - -void -urcrypt_shal(const uint8_t *message, size_t length, uint8_t out[64]) -{ - SHA512(message, length, out); -} - -void -urcrypt_shas(uint8_t *salt, size_t salt_length, - const uint8_t *message, size_t message_length, - uint8_t out[32]) -{ - size_t i; - uint8_t mid[32]; - - // docs don't say what happens if msg overlaps with out - urcrypt_shay(message, message_length, mid); - - if ( salt_length > 32 ) { - for ( i = 0; i < 32; i++ ) { - salt[i] ^= mid[i]; - } - urcrypt_shay(salt, salt_length, out); - } - else { - for ( i = 0; i < salt_length; i++ ) { - mid[i] ^= salt[i]; - } - urcrypt_shay(mid, 32, out); - } -} - diff --git a/pkg/urcrypt/urcrypt.h b/pkg/urcrypt/urcrypt.h deleted file mode 100644 index 682531b948..0000000000 --- a/pkg/urcrypt/urcrypt.h +++ /dev/null @@ -1,246 +0,0 @@ -#ifndef URCRYPT_H -#define URCRYPT_H -#include -#include -#include -// const arguments are not written to, non-const arguments may be -// all arrays are in little-endian byte order. -// array sizes[64] are purely documentary - -// 0 on success, result in out -int urcrypt_ed_point_add(const uint8_t a[32], - const uint8_t b[32], - uint8_t out[32]); -int urcrypt_ed_scalarmult(const uint8_t a[32], - const uint8_t b[32], - uint8_t out[32]); -// void functions have no failure mode -void urcrypt_ed_scalarmult_base(const uint8_t a[32], - uint8_t out[32]); -int urcrypt_ed_add_scalarmult_scalarmult_base(const uint8_t a[32], - const uint8_t a_point[32], - const uint8_t b[32], - uint8_t out[32]); -int urcrypt_ed_add_double_scalarmult(const uint8_t a[32], - const uint8_t a_point[32], - const uint8_t b[32], - const uint8_t b_point[32], - uint8_t out[32]); - -void urcrypt_ed_puck(const uint8_t seed[32], - uint8_t out[32]); -void urcrypt_ed_shar(const uint8_t public[32], - const uint8_t seed[32], - uint8_t out[32]); -void urcrypt_ed_sign(const uint8_t *message, - size_t length, - const uint8_t seed[32], - uint8_t out[64]); -// return value means the signature was (not) verified -bool urcrypt_ed_veri(const uint8_t *message, - size_t length, - const uint8_t public[32], - const uint8_t signature[64]); - -int urcrypt_aes_ecba_en(uint8_t key[16], uint8_t block[16], uint8_t out[16]); -int urcrypt_aes_ecba_de(uint8_t key[16], uint8_t block[16], uint8_t out[16]); -int urcrypt_aes_ecbb_en(uint8_t key[24], uint8_t block[16], uint8_t out[16]); -int urcrypt_aes_ecbb_de(uint8_t key[24], uint8_t block[16], uint8_t out[16]); -int urcrypt_aes_ecbc_en(uint8_t key[32], uint8_t block[16], uint8_t out[16]); -int urcrypt_aes_ecbc_de(uint8_t key[32], uint8_t block[16], uint8_t out[16]); - -typedef void* (*urcrypt_realloc_t)(void*, size_t); - -// message and length are read/write so -// realloc_ptr can be used as realloc to pad message -int urcrypt_aes_cbca_en(uint8_t **message_ptr, - size_t *length_ptr, - uint8_t key[16], - uint8_t ivec[16], - urcrypt_realloc_t realloc_ptr); -int urcrypt_aes_cbca_de(uint8_t **message_ptr, - size_t *length_ptr, - uint8_t key[16], - uint8_t ivec[16], - urcrypt_realloc_t realloc_ptr); -int urcrypt_aes_cbcb_en(uint8_t **message_ptr, - size_t *length_ptr, - uint8_t key[24], - uint8_t ivec[16], - urcrypt_realloc_t realloc_ptr); -int urcrypt_aes_cbcb_de(uint8_t **message_ptr, - size_t *length_ptr, - uint8_t key[24], - uint8_t ivec[16], - urcrypt_realloc_t realloc_ptr); -int urcrypt_aes_cbcc_en(uint8_t **message_ptr, - size_t *length_ptr, - uint8_t key[32], - uint8_t ivec[16], - urcrypt_realloc_t realloc_ptr); -int urcrypt_aes_cbcc_de(uint8_t **message_ptr, - size_t *length_ptr, - uint8_t key[32], - uint8_t ivec[16], - urcrypt_realloc_t realloc_ptr); - -typedef struct { - size_t length; - uint8_t *bytes; -} urcrypt_aes_siv_data; - -int urcrypt_aes_siva_en(uint8_t *message, - size_t message_length, - urcrypt_aes_siv_data *data, - size_t data_length, - uint8_t key[32], - uint8_t iv[16], - uint8_t *out); -int urcrypt_aes_siva_de(uint8_t *message, - size_t message_length, - urcrypt_aes_siv_data *data, - size_t data_length, - uint8_t key[32], - uint8_t iv[16], - uint8_t *out); -int urcrypt_aes_sivb_en(uint8_t *message, - size_t message_length, - urcrypt_aes_siv_data *data, - size_t data_length, - uint8_t key[48], - uint8_t iv[16], - uint8_t *out); -int urcrypt_aes_sivb_de(uint8_t *message, - size_t message_length, - urcrypt_aes_siv_data *data, - size_t data_length, - uint8_t key[48], - uint8_t iv[16], - uint8_t *out); -int urcrypt_aes_sivc_en(uint8_t *message, - size_t message_length, - urcrypt_aes_siv_data *data, - size_t data_length, - uint8_t key[64], - uint8_t iv[16], - uint8_t *out); -int urcrypt_aes_sivc_de(uint8_t *message, - size_t message_length, - urcrypt_aes_siv_data *data, - size_t data_length, - uint8_t key[64], - uint8_t iv[16], - uint8_t *out); - -int urcrypt_ripemd160(uint8_t *message, size_t length, uint8_t out[20]); - -void urcrypt_sha1(uint8_t *message, size_t length, uint8_t out[20]); -void urcrypt_shay(const uint8_t *message, size_t length, uint8_t out[32]); -void urcrypt_shal(const uint8_t *message, size_t length, uint8_t out[64]); -void urcrypt_shas(uint8_t *salt, size_t salt_length, - const uint8_t *message, size_t message_length, - uint8_t out[32]); - -typedef int (*urcrypt_argon2_alloc_t)(uint8_t**, size_t); -typedef void (*urcrypt_argon2_free_t)(uint8_t*, size_t); - -#define urcrypt_argon2_d 0 -#define urcrypt_argon2_i 1 -#define urcrypt_argon2_id 2 -#define urcrypt_argon2_u 10 - -/* returns a constant error message string or NULL for success */ -const char* urcrypt_argon2(uint8_t type, // one of the urcrpyt_argon2_* - uint32_t version, - uint32_t threads, - uint32_t memory_cost, - uint32_t time_cost, - size_t secret_length, - uint8_t *secret, - size_t associated_length, - uint8_t *associated, - size_t password_length, - uint8_t *password, - size_t salt_length, - uint8_t *salt, - size_t out_length, - uint8_t *out, - urcrypt_argon2_alloc_t alloc_ptr, - urcrypt_argon2_free_t free_ptr); - -int urcrypt_blake2(size_t message_length, - uint8_t *message, - size_t key_length, - uint8_t key[64], - size_t out_length, - uint8_t *out); - -/* there is some long-term context associated with the secp library - * (precomputed tables, etc), so secp functions require a context object - */ -typedef struct urcrypt_secp_context_struct urcrypt_secp_context; - -// size of opaque secp handle, malloc and pass to init -size_t urcrypt_secp_prealloc_size(void); -// call this once at per context with high quality entropy -int urcrypt_secp_init(urcrypt_secp_context *context, - uint8_t entropy[32]); -// call before freeing opaque secp handle -void urcrypt_secp_destroy(urcrypt_secp_context *context); - -int urcrypt_secp_make(uint8_t hash[32], uint8_t key[32], uint8_t out[32]); -int urcrypt_secp_sign(urcrypt_secp_context* context, - uint8_t hash[32], - uint8_t key[32], - uint8_t* out_v, - uint8_t out_r[32], - uint8_t out_s[32]); -int urcrypt_secp_reco(urcrypt_secp_context* context, - uint8_t hash[32], - uint8_t key_v, // 0, 1, 2, 3 - const uint8_t key_r[32], - const uint8_t key_s[32], - uint8_t out_x[32], - uint8_t out_y[32]); -int urcrypt_secp_schnorr_sign(urcrypt_secp_context* context, - uint8_t key[32], - uint8_t msg[32], - uint8_t aux[32], - uint8_t out_sig[64]); -// return value means signature was (not) verified -bool urcrypt_secp_schnorr_veri(urcrypt_secp_context* context, - uint8_t sig[64], - uint8_t msg[32], - uint8_t pub[32]); - -void urcrypt_scrypt_pbkdf_sha256(const uint8_t *passwd, - size_t passwdlen, - const uint8_t *salt, - size_t saltlen, - uint64_t count, - size_t outlen, // must be at most 32*(2^32-1) - uint8_t *out); - -int urcrypt_scrypt(const uint8_t *passwd, - size_t passwdlen, - const uint8_t *salt, - size_t saltlen, - uint64_t n, - uint32_t r, - uint32_t p, - size_t outlen, - uint8_t *out); - -int urcrypt_keccak_224(const uint8_t *message, - size_t length, - uint8_t out[28]); -int urcrypt_keccak_256(const uint8_t *message, - size_t length, - uint8_t out[32]); -int urcrypt_keccak_384(const uint8_t *message, - size_t length, - uint8_t out[48]); -int urcrypt_keccak_512(const uint8_t *message, - size_t length, - uint8_t out[64]); -#endif diff --git a/pkg/urcrypt/util.c b/pkg/urcrypt/util.c deleted file mode 100644 index 8caa519389..0000000000 --- a/pkg/urcrypt/util.c +++ /dev/null @@ -1,14 +0,0 @@ -#include "urcrypt.h" - -void -urcrypt__reverse(size_t size, uint8_t *ptr) { - if ( size > 0 ) { - size_t i, j; - uint8_t tmp; - for ( i = 0, j = size - 1; i < j; i++, j-- ) { - tmp = ptr[i]; - ptr[i] = ptr[j]; - ptr[j] = tmp; - } - } -} diff --git a/pkg/urcrypt/util.h b/pkg/urcrypt/util.h deleted file mode 100644 index 1c6202c6df..0000000000 --- a/pkg/urcrypt/util.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef URCRYPT_UTIL_H -#define URCRYPT_UTIL_H - -void urcrypt__reverse(size_t size, uint8_t *ptr); - -#endif diff --git a/pkg/vere/BUILD.bazel b/pkg/vere/BUILD.bazel index 76f8b47eca..f50642ab89 100644 --- a/pkg/vere/BUILD.bazel +++ b/pkg/vere/BUILD.bazel @@ -119,7 +119,13 @@ vere_library( hdrs = [ "db/lmdb.h", "vere.h", - ], + "mdns.h", + ] + select({ + "@platforms//os:macos": [], + "@platforms//os:linux": [ + "dns_sd.h", + ], + }), includes = ["."], linkstatic = True, visibility = ["//pkg:__subpackages__"], @@ -133,7 +139,12 @@ vere_library( "@lmdb", "@openssl", "@uv", - ], + ] + select({ + "@platforms//os:macos": [], + "@platforms//os:linux": [ + "@avahi", + ], + }) ) # @@ -301,7 +312,7 @@ genrule( if check && sleep 10 && check; then echo "boot success" - lensa hood "+hood/exit" + lensa hood '+hood/exit' while [ -f ./pier/.vere.lock ]; do echo "waiting for pier to shut down" sleep 5 diff --git a/pkg/vere/benchmarks.c b/pkg/vere/benchmarks.c index 91f3bc4ab1..a9215cf864 100644 --- a/pkg/vere/benchmarks.c +++ b/pkg/vere/benchmarks.c @@ -1,6 +1,7 @@ /// @file #include "noun.h" +#include "jets/q.h" #include "ur.h" #include "vere.h" @@ -387,6 +388,63 @@ _cue_soft_bench(void) u3z(vat); } +static void +_edit_bench_impl(c3_w max_w) +{ + u3_assert( max_w && (c3y == u3a_is_cat(max_w)) ); + + c3_w* axe_w = c3_calloc(((max_w + 31) >> 5) << 2); + c3_w bit_w; + u3_noun lit = u3qb_reap(max_w, 1); + u3_noun axe; + + axe_w[0] = bit_w = 2; + + do { + axe = u3i_words((bit_w + 31) >> 5, axe_w); + lit = u3i_edit(lit, axe, 2); + u3z(axe); + + axe_w[bit_w >> 5] |= (c3_w)1 << (bit_w & 31); + bit_w++; + } + while ( bit_w <= max_w ); + + u3z(lit); + c3_free(axe_w); +} + +static void +_edit_bench(void) +{ + struct timeval b4, f2, d0; + c3_w mil_w; + + fprintf(stderr, "\r\nopcode 10 microbenchmark:\r\n"); + + { + gettimeofday(&b4, 0); + + _edit_bench_impl(1000); + + gettimeofday(&f2, 0); + timersub(&f2, &b4, &d0); + mil_w = (d0.tv_sec * 1000) + (d0.tv_usec / 1000); + fprintf(stderr, " opcode 10 1k list: %u ms\r\n", mil_w); + } + + { + gettimeofday(&b4, 0); + + _edit_bench_impl(10000); + + gettimeofday(&f2, 0); + timersub(&f2, &b4, &d0); + mil_w = (d0.tv_sec * 1000) + (d0.tv_usec / 1000); + fprintf(stderr, " opcode 10 10k list: %u ms\r\n", mil_w); + } +} + /* main(): run all benchmarks */ int @@ -397,6 +455,7 @@ main(int argc, char* argv[]) _jam_bench(); _cue_bench(); _cue_soft_bench(); + _edit_bench(); // GC // diff --git a/pkg/vere/db/lmdb.c b/pkg/vere/db/lmdb.c index 8f8864b77f..f7554019a4 100644 --- a/pkg/vere/db/lmdb.c +++ b/pkg/vere/db/lmdb.c @@ -414,7 +414,7 @@ void u3_lmdb_read_meta(MDB_env* env_u, void* ptr_v, const c3_c* key_c, - void (*read_f)(void*, size_t, void*)) + void (*read_f)(void*, ssize_t, void*)) { MDB_txn* txn_u; MDB_dbi mdb_u; @@ -424,7 +424,7 @@ u3_lmdb_read_meta(MDB_env* env_u, // if ( (ret_w = mdb_txn_begin(env_u, 0, MDB_RDONLY, &txn_u)) ) { mdb_logerror(stderr, ret_w, "lmdb: meta read: txn_begin fail"); - return read_f(ptr_v, 0, 0); + return read_f(ptr_v, -1, 0); } // open the database in the transaction @@ -432,7 +432,7 @@ u3_lmdb_read_meta(MDB_env* env_u, if ( (ret_w = mdb_dbi_open(txn_u, "META", 0, &mdb_u)) ) { mdb_logerror(stderr, ret_w, "lmdb: meta read: dbi_open fail"); mdb_txn_abort(txn_u); - return read_f(ptr_v, 0, 0); + return read_f(ptr_v, -1, 0); } // read by string key, invoking callback with result @@ -443,7 +443,7 @@ u3_lmdb_read_meta(MDB_env* env_u, if ( (ret_w = mdb_get(txn_u, mdb_u, &key_u, &val_u)) ) { mdb_logerror(stderr, ret_w, "lmdb: read failed"); mdb_txn_abort(txn_u); - return read_f(ptr_v, 0, 0); + return read_f(ptr_v, -1, 0); } else { read_f(ptr_v, val_u.mv_size, val_u.mv_data); diff --git a/pkg/vere/db/lmdb.h b/pkg/vere/db/lmdb.h index aab4e6c9f7..43c6b433ff 100644 --- a/pkg/vere/db/lmdb.h +++ b/pkg/vere/db/lmdb.h @@ -63,7 +63,7 @@ u3_lmdb_read_meta(MDB_env* env_u, void* ptr_v, const c3_c* key_c, - void (*read_f)(void*, size_t, void*)); + void (*read_f)(void*, ssize_t, void*)); /* u3_lmdb_save_meta(): save by string into the META db. */ diff --git a/pkg/vere/disk.c b/pkg/vere/disk.c index 2cc270a368..de141988bd 100644 --- a/pkg/vere/disk.c +++ b/pkg/vere/disk.c @@ -1,8 +1,11 @@ /// @file #include "noun.h" +#include "events.h" #include "vere.h" +#include "version.h" #include "db/lmdb.h" +#include struct _cd_read { uv_timer_t tim_u; @@ -28,6 +31,15 @@ struct _u3_disk_walk { c3_o liv_o; }; +// for u3_lmdb_init() calls +static const size_t siz_i = +#if (defined(U3_CPU_aarch64) && defined(U3_OS_linux)) + // 500 GiB is as large as musl on aarch64 wants to allow + 0x7d00000000; +#else + 0x10000000000; +#endif + #undef VERBOSE_DISK #undef DISK_TRACE_JAM #undef DISK_TRACE_CUE @@ -650,26 +662,26 @@ u3_disk_save_meta(MDB_env* mdb_u, return c3y; } +typedef struct { + ssize_t hav_i; + c3_y buf_y[16]; +} _mdb_val; + + /* _disk_meta_read_cb(): copy [val_p] to atom [ptr_v] if present. */ static void -_disk_meta_read_cb(void* ptr_v, size_t val_i, void* val_p) +_disk_meta_read_cb(void* ptr_v, ssize_t val_i, void* val_v) { - u3_weak* mat = ptr_v; + _mdb_val* val_u = ptr_v; + c3_y* dat_y = (c3_y*)val_v; - if ( val_p ) { - *mat = u3i_bytes(val_i, val_p); - } -} + memset(val_u->buf_y, 0, sizeof(val_u->buf_y)); + val_u->hav_i = val_i; -/* _disk_read_meta(): read metadata at [key_c], deserialize. -*/ -static u3_weak -_disk_read_meta(MDB_env* mdb_u, const c3_c* key_c) -{ - u3_weak dat = u3_none; - u3_lmdb_read_meta(mdb_u, &dat, key_c, _disk_meta_read_cb); - return dat; + if ( 0 < val_i ) { + memcpy(val_u->buf_y, dat_y, c3_min(val_i, sizeof(val_u->buf_y))); + } } /* u3_disk_read_meta(): read metadata. @@ -680,60 +692,104 @@ u3_disk_read_meta(MDB_env* mdb_u, c3_o* fak_o, c3_w* lif_w) { - u3_weak ver, who, fak, lif; + _mdb_val val_u; - if ( u3_none == (ver = _disk_read_meta(mdb_u, "version")) ) { + // version + // + u3_lmdb_read_meta(mdb_u, &val_u, "version", _disk_meta_read_cb); + + if ( 0 > val_u.hav_i ) { fprintf(stderr, "disk: read meta: no version\r\n"); return c3n; } - if ( u3_none == (who = _disk_read_meta(mdb_u, "who")) ) { - fprintf(stderr, "disk: read meta: no indentity\r\n"); + else if ( (1 != val_u.hav_i) || (1 != *val_u.buf_y) ) { + fprintf(stderr, "disk: read meta: unknown version %u\r\n", *val_u.buf_y); return c3n; } - if ( u3_none == (fak = _disk_read_meta(mdb_u, "fake")) ) { - fprintf(stderr, "disk: read meta: no fake bit\r\n"); + + // identity + // + u3_lmdb_read_meta(mdb_u, &val_u, "who", _disk_meta_read_cb); + + if ( 0 > val_u.hav_i ) { + fprintf(stderr, "disk: read meta: no identity\r\n"); return c3n; } - if ( u3_none == (lif = _disk_read_meta(mdb_u, "life")) ) { - fprintf(stderr, "disk: read meta: no lifecycle length\r\n"); - return c3n; + else if ( 16 < val_u.hav_i ) { + // NB: non-fatal + // + fprintf(stderr, "disk: read meta: strange identity\r\n"); } - { - c3_o val_o = c3y; + if ( who_d ) { + c3_y* byt_y = val_u.buf_y; + + who_d[0] = (c3_d)byt_y[0] + | (c3_d)byt_y[1] << 8 + | (c3_d)byt_y[2] << 16 + | (c3_d)byt_y[3] << 24 + | (c3_d)byt_y[4] << 32 + | (c3_d)byt_y[5] << 40 + | (c3_d)byt_y[6] << 48 + | (c3_d)byt_y[7] << 56; + + byt_y += 8; + who_d[1] = (c3_d)byt_y[0] + | (c3_d)byt_y[1] << 8 + | (c3_d)byt_y[2] << 16 + | (c3_d)byt_y[3] << 24 + | (c3_d)byt_y[4] << 32 + | (c3_d)byt_y[5] << 40 + | (c3_d)byt_y[6] << 48 + | (c3_d)byt_y[7] << 56; + } - if ( 1 != ver ) { - fprintf(stderr, "disk: read meta: unknown version %u\r\n", ver); - val_o = c3n; - } - else if ( !((c3y == fak ) || (c3n == fak )) ) { - fprintf(stderr, "disk: read meta: invalid fake bit\r\n"); - val_o = c3n; - } - else if ( c3n == u3a_is_cat(lif) ) { - fprintf(stderr, "disk: read meta: invalid lifecycle length\r\n"); - val_o = c3n; - } + // fake bit + // + u3_lmdb_read_meta(mdb_u, &val_u, "fake", _disk_meta_read_cb); - if ( c3n == val_o ) { - u3z(ver); u3z(who); u3z(fak); u3z(lif); - return c3n; + if ( 0 > val_u.hav_i ) { + fprintf(stderr, "disk: read meta: no fake bit\r\n"); + return c3n; + } + else if ( 0 == val_u.hav_i ) { + if ( fak_o ) { + *fak_o = 0; } } - - if ( who_d ) { - u3r_chubs(0, 2, who_d, who); + else if ( (1 == val_u.hav_i) || !((*val_u.buf_y) >> 1) ) { + if ( fak_o ) { + *fak_o = (*val_u.buf_y) & 1; + } + } + else { + fprintf(stderr, "disk: read meta: invalid fake bit %u %zd\r\n", + *val_u.buf_y, val_u.hav_i); + return c3n; } - if ( fak_o ) { - *fak_o = fak; + // life + // + u3_lmdb_read_meta(mdb_u, &val_u, "life", _disk_meta_read_cb); + + if ( 0 > val_u.hav_i ) { + fprintf(stderr, "disk: read meta: no lifecycle length\r\n"); + return c3n; + } + else if ( 4 < val_u.hav_i ) { + // NB: non-fatal + // + fprintf(stderr, "disk: read meta: strange life\r\n"); } if ( lif_w ) { - *lif_w = lif; + c3_y* byt_y = val_u.buf_y; + *lif_w = (c3_w)byt_y[0] + | (c3_w)byt_y[1] << 8 + | (c3_w)byt_y[2] << 16 + | (c3_w)byt_y[3] << 24; } - u3z(who); return c3y; } @@ -960,6 +1016,537 @@ u3_disk_slog(u3_disk* log_u) } } +/* _disk_epoc_meta: read metadata from epoch. +*/ +static c3_o +_disk_epoc_meta(u3_disk* log_u, + c3_d epo_d, + const c3_c* met_c, + c3_w max_w, + c3_c* buf_c) +{ + struct stat buf_u; + c3_w red_w, len_w; + c3_i ret_i, fid_i; + c3_c* pat_c; + + ret_i = asprintf(&pat_c, "%s/0i%" PRIc3_d "/%s.txt", + log_u->com_u->pax_c, epo_d, met_c); + u3_assert( ret_i > 0 ); + + fid_i = c3_open(pat_c, O_RDONLY, 0644); + c3_free(pat_c); + + if ( (fid_i < 0) || (fstat(fid_i, &buf_u) < 0) ) { + fprintf(stderr, "disk: failed to open %s.txt in epoch 0i%" PRIc3_d "\r\n", + met_c, epo_d); + return c3n; + } + else if ( buf_u.st_size >= max_w ) { + fprintf(stderr, "disk: %s.txt in epoch 0i%" PRIc3_d " too large " + "(%" PRIc3_z ")\r\n", + met_c, epo_d, (c3_z)buf_u.st_size); + return c3n; + } + + len_w = buf_u.st_size; + red_w = read(fid_i, buf_c, len_w); + close(fid_i); + + if ( len_w != red_w ) { + fprintf(stderr, "disk: failed to read %s.txt in epoch 0i%" PRIc3_d "\r\n", + met_c, epo_d); + return c3n; + } + + // trim trailing whitespace + // + do { + buf_c[len_w] = 0; + } + while ( len_w-- && isspace(buf_c[len_w]) ); + + return c3y; +} + +/* u3_disk_epoc_init: create new epoch. +*/ +c3_o +u3_disk_epoc_init(u3_disk* log_u, c3_d epo_d) +{ + // check if any epoch directories exist + c3_d lat_d; + c3_o eps_o = u3_disk_epoc_last(log_u, &lat_d); + + // create new epoch directory if it doesn't exist + c3_c epo_c[8193]; + c3_i epo_i; + snprintf(epo_c, sizeof(epo_c), "%s/0i%" PRIc3_d, log_u->com_u->pax_c, epo_d); + c3_d ret_d = c3_mkdir(epo_c, 0700); + if ( ( ret_d < 0 ) && ( errno != EEXIST ) ) { + fprintf(stderr, "disk: create epoch dir %" PRIc3_d " failed: %s\r\n", + epo_d, strerror(errno)); + return c3n; + } + + // copy snapshot files (skip if first epoch) + if ( epo_d > 0 ) { + c3_c chk_c[8193]; + snprintf(chk_c, 8192, "%s/.urb/chk", u3_Host.dir_c); + if ( c3n == u3e_backup(chk_c, epo_c, c3y) ) { + fprintf(stderr, "disk: copy epoch snapshot failed\r\n"); + goto fail1; + } + } + + if ( -1 == (epo_i = c3_open(epo_c, O_RDONLY)) ) { + fprintf(stderr, "disk: open epoch dir %" PRIc3_d " failed: %s\r\n", + epo_d, strerror(errno)); + goto fail1; + } + + if ( -1 == c3_sync(epo_i) ) { // XX fdatasync on linux? + fprintf(stderr, "disk: sync epoch dir %" PRIc3_d " failed: %s\r\n", + epo_d, strerror(errno)); + goto fail2; + } + + // create epoch version file, overwriting any existing file + c3_c epv_c[8193]; + snprintf(epv_c, sizeof(epv_c), "%s/epoc.txt", epo_c); + FILE* epv_f = fopen(epv_c, "w"); // XX errors + fprintf(epv_f, "%d", U3D_VER1); + fclose(epv_f); + + // create binary version file, overwriting any existing file + c3_c biv_c[8193]; + snprintf(biv_c, sizeof(biv_c), "%s/vere.txt", epo_c); + FILE* biv_f = fopen(biv_c, "w"); // XX errors + fprintf(biv_f, URBIT_VERSION); + fclose(biv_f); + + // get metadata from old epoch or unmigrated event log's db + c3_d who_d[2]; + c3_o fak_o; + c3_w lif_w; + if ( c3y == eps_o ) { // skip if no epochs yet + if ( c3y != u3_disk_read_meta(log_u->mdb_u, who_d, &fak_o, &lif_w) ) { + fprintf(stderr, "disk: failed to read metadata\r\n"); + goto fail3; + } + + u3_lmdb_exit(log_u->mdb_u); + log_u->mdb_u = 0; + } + + // initialize db of new epoch + if ( c3y == u3_Host.ops_u.nuu || epo_d > 0 ) { + c3_c dat_c[8193]; + snprintf(dat_c, sizeof(dat_c), "%s/data.mdb", epo_c); + + if ( 0 == (log_u->mdb_u = u3_lmdb_init(epo_c, siz_i)) ) { + fprintf(stderr, "disk: failed to initialize database\r\n"); + c3_free(log_u); + goto fail3; + } + } + + // write the metadata to the database + if ( c3y == eps_o ) { + if ( c3n == u3_disk_save_meta(log_u->mdb_u, who_d, fak_o, lif_w) ) { + fprintf(stderr, "disk: failed to save metadata\r\n"); + goto fail3; + } + } + + if ( -1 == c3_sync(epo_i) ) { // XX fdatasync on linux? + fprintf(stderr, "disk: sync epoch dir %" PRIc3_d " failed: %s\r\n", + epo_d, strerror(errno)); + goto fail3; + } + + close(epo_i); + + // load new epoch directory and set it in log_u + log_u->epo_d = epo_d; + + // success + return c3y; + +fail3: + c3_unlink(epv_c); + c3_unlink(biv_c); +fail2: + close(epo_i); +fail1: + c3_rmdir(epo_c); + return c3n; +} + +/* u3_disk_epoc_kill: delete an epoch. +*/ +c3_o +u3_disk_epoc_kill(u3_disk* log_u, c3_d epo_d) +{ + // get epoch directory + c3_c epo_c[8193]; + snprintf(epo_c, sizeof(epo_c), "%s/0i%" PRIc3_d, log_u->com_u->pax_c, epo_d); + + // delete files in epoch directory + u3_dire* dir_u = u3_foil_folder(epo_c); + u3_dent* den_u = dir_u->all_u; + while ( den_u ) { + c3_c fil_c[8193]; + snprintf(fil_c, sizeof(fil_c), "%s/%s", epo_c, den_u->nam_c); + if ( 0 != c3_unlink(fil_c) ) { + fprintf(stderr, "disk: failed to delete file in epoch directory\r\n"); + return c3n; + } + den_u = den_u->nex_u; + } + + // delete epoch directory + if ( 0 != c3_rmdir(epo_c) ) { + fprintf(stderr, "disk: failed to delete epoch directory\r\n"); + return c3n; + } + + // cleanup + u3_dire_free(dir_u); + + // success + return c3y; +} + +/* u3_disk_epoc_last: get latest epoch number. +*/ +c3_o +u3_disk_epoc_last(u3_disk* log_u, c3_d* lat_d) +{ + u3_dire* die_u = u3_foil_folder(log_u->com_u->pax_c); + u3_dent* den_u = die_u->dil_u; + c3_o ret_o = c3n; + + *lat_d = 0; + + while ( den_u ) { + c3_d epo_d = 0; + if ( 1 == sscanf(den_u->nam_c, "0i%" PRIc3_d, &epo_d) ) { + ret_o = c3y; // NB: returns yes if the directory merely exists + *lat_d = c3_max(epo_d, *lat_d); // update the latest epoch number + } + den_u = den_u->nex_u; + } + + u3_dire_free(die_u); + + return ret_o; +} + +/* u3_disk_epoc_list: get descending epoch numbers, "mcut" pattern. +*/ +c3_z +u3_disk_epoc_list(u3_disk* log_u, c3_d* sot_d) +{ + u3_dire* ned_u = u3_foil_folder(log_u->com_u->pax_c); + u3_dent* den_u = ned_u->dil_u; + c3_z len_z = 0; + + while ( den_u ) { // count epochs + len_z++; + den_u = den_u->nex_u; + } + + if ( !sot_d ) { + u3_dire_free(ned_u); + return len_z; + } + + len_z = 0; + den_u = ned_u->dil_u; + + while ( den_u ) { + if ( 1 == sscanf(den_u->nam_c, "0i%" PRIc3_d, (sot_d + len_z)) ) { + len_z++; + } + den_u = den_u->nex_u; + } + + // sort sot_d naively in descending order + // + c3_d tmp_d; + for ( c3_z i_z = 0; i_z < len_z; i_z++ ) { + for ( c3_z j_z = i_z + 1; j_z < len_z; j_z++ ) { + if ( sot_d[i_z] < sot_d[j_z] ) { + tmp_d = sot_d[i_z]; + sot_d[i_z] = sot_d[j_z]; + sot_d[j_z] = tmp_d; + } + } + } + + u3_dire_free(ned_u); + return len_z; +} + +/* _disk_need_migrate: does the disk need to be migrated? +*/ +static c3_o +_disk_need_migrate(u3_disk* log_u) +{ + c3_c dut_c[8193]; + snprintf(dut_c, sizeof(dut_c), "%s/data.mdb", log_u->com_u->pax_c); + + // check if data.mdb is readable in log directory + // + if ( 0 == access(dut_c, F_OK)) { + return c3y; + } + + return c3n; +} + +/* _disk_migrate: migrates disk format. + */ +static c3_o +_disk_migrate(u3_disk* log_u, c3_d eve_d) +{ + /* migration steps: + * 0. detect whether we need to migrate or not + * a. if it's a fresh boot via u3_Host.ops_u.nuu -> skip migration + * b. if data.mdb is readable in log directory -> execute migration + * if not -> skip migration (returns yes) + * 1. initialize epoch 0i0 (first call to u3_disk_epoc_init()) + * a. creates epoch directory + * b. creates epoch version file + * c. creates binary version file + * d. initializes database + * e. reads metadata from old database + * f. writes metadata to new database + * g. loads new epoch directory and sets it in log_u + * 2. create hard links to data.mdb and lock.mdb in 0i0/ + * 3. rollover to new epoch (second call to u3_disk_epoc_init()) + * a. same as 1a-g but also copies current snapshot between c/d steps + * 4. delete backup snapshot (c3_unlink() and c3_rmdir() calls) + * 5. delete old data.mdb and lock.mdb files (c3_unlink() calls) + */ + + // check if lock.mdb is readable in log directory + c3_o luk_o = c3n; + c3_c luk_c[8193]; + snprintf(luk_c, sizeof(luk_c), "%s/lock.mdb", log_u->com_u->pax_c); + if ( 0 == access(luk_c, R_OK) ) { + luk_o = c3y; + } + + fprintf(stderr, "disk: migrating disk to v%d format\r\n", U3D_VER1); + + // migrate existing pier which has either: + // - not started the migration, or + // - crashed before completing the migration + + // get first/last event numbers from pre-migrated lmdb + c3_d fir_d, las_d; + if ( c3n == u3_lmdb_gulf(log_u->mdb_u, &fir_d, &las_d) ) { + fprintf(stderr, "disk: failed to get first/last event numbers\r\n"); + return c3n; + } + + // ensure there's a current snapshot + if ( eve_d != las_d ) { + fprintf(stderr, "disk: snapshot is out of date, please " + "start/shutdown your pier gracefully first\r\n"); + fprintf(stderr, "disk: eve_d (%" PRIc3_d ") != las_d (%" PRIc3_d ")\r\n", + eve_d, las_d); + return c3n; + } + + // initialize first epoch "0i0" + if ( c3n == u3_disk_epoc_init(log_u, 0) ) { + fprintf(stderr, "disk: failed to initialize first epoch\r\n"); + return c3n; + } + + // create hard links to data.mdb and lock.mdb in 0i0/ + c3_c epo_c[8193], dut_c[8193], dat_c[8193], lok_c[8193]; + snprintf(epo_c, sizeof(epo_c), "%s/0i0", log_u->com_u->pax_c); + snprintf(dut_c, sizeof(dut_c), "%s/data.mdb", log_u->com_u->pax_c); + snprintf(dat_c, sizeof(dat_c), "%s/data.mdb", epo_c); + snprintf(lok_c, sizeof(lok_c), "%s/lock.mdb", epo_c); + + if ( 0 < c3_link(dut_c, dat_c) ) { + fprintf(stderr, "disk: failed to create data.mdb hard link\r\n"); + return c3n; + } + if ( c3y == luk_o ) { // only link lock.mdb if it exists + if ( 0 < c3_link(luk_c, lok_c) ) { + fprintf(stderr, "disk: failed to create lock.mdb hard link\r\n"); + return c3n; + } + } + + // rollover to new epoch + if ( c3n == u3_disk_epoc_init(log_u, las_d) ) { + fprintf(stderr, "disk: failed to initialize new epoch\r\n"); + return c3n; + } + + // delete backup snapshot + c3_c bhk_c[8193], nop_c[8193], sop_c[8193]; + snprintf(bhk_c, sizeof(bhk_c), "%s/.urb/bhk", u3_Host.dir_c); + snprintf(nop_c, sizeof(nop_c), "%s/north.bin", bhk_c); + snprintf(sop_c, sizeof(sop_c), "%s/south.bin", bhk_c); + if ( c3_unlink(nop_c) ) { + fprintf(stderr, "disk: failed to delete bhk/north.bin: %s\r\n", + strerror(errno)); + } + else if ( c3_unlink(sop_c) ) { + fprintf(stderr, "disk: failed to delete bhk/south.bin: %s\r\n", + strerror(errno)); + } + else { + if ( c3_rmdir(bhk_c) ) { + fprintf(stderr, "disk: failed to delete bhk/: %s\r\n", + strerror(errno)); + } + } + + // delete old lock.mdb and data.mdb files + if ( c3_unlink(luk_c) ) { + fprintf(stderr, "disk: failed to unlink lock.mdb: %s\r\n", + strerror(errno)); + } + if ( c3_unlink(dut_c) ) { + fprintf(stderr, "disk: failed to unlink data.mdb: %s\r\n", + strerror(errno)); + return c3n; // migration succeeds only if we can unlink data.mdb + } + + // success + fprintf(stderr, "disk: migrated disk to v%d format\r\n", U3D_VER1); + + return c3y; +} + +/* _disk_vere_diff(): checks if vere version mismatches latest epoch's. +*/ +static c3_o +_disk_vere_diff(u3_disk* log_u) +{ + c3_c ver_c[128]; + + if ( c3n == _disk_epoc_meta(log_u, log_u->epo_d, "vere", + sizeof(ver_c) - 1, ver_c) ) + { + return c3y; // assume mismatch if we can't read version + } + + if ( 0 != strcmp(ver_c, URBIT_VERSION) ) { + return c3y; + } + + return c3n; +} + +/* u3_disk_kindly(): do the needful. +*/ +void +u3_disk_kindly(u3_disk* log_u, c3_d eve_d) +{ + if ( c3y == _disk_need_migrate(log_u) ) { + if ( c3n == _disk_migrate(log_u, eve_d) ) { + fprintf(stderr, "disk: failed to migrate event log\r\n"); + exit(1); + } + } + else if ( c3y == _disk_vere_diff(log_u) ) { + if ( c3n == u3_disk_epoc_init(log_u, log_u->dun_d) ) { + fprintf(stderr, "disk: failed to initialize epoch\r\n"); + exit(1); + } + } +} + +typedef enum { + _epoc_good = 0, // load successfully + _epoc_gone = 1, // version missing, total failure + _epoc_fail = 2, // transient failure (?) + _epoc_void = 3, // empty event log (cheaper to recover?) + _epoc_late = 4 // format from the future +} _epoc_kind; + +/* _disk_epoc_load(): load existing epoch, enumerating failures +*/ +static _epoc_kind +_disk_epoc_load(u3_disk* log_u, c3_d lat_d) +{ + // check latest epoc version + // + { + c3_c ver_c[8]; + c3_w ver_w; + if ( c3n == _disk_epoc_meta(log_u, lat_d, "epoc", + sizeof(ver_c) - 1, ver_c) ) + { + fprintf(stderr, "disk: failed to load epoch 0i%" PRIc3_d " version\r\n", + lat_d); + + return _epoc_gone; + } + + if ( 1 != sscanf(ver_c, "%d", &ver_w) ) { + fprintf(stderr, "disk: failed to parse epoch version: '%s'\r\n", ver_c); + return _epoc_fail; + } + + if ( U3D_VER1 != ver_w ) { + fprintf(stderr, "disk: unknown epoch version: '%s', expected '%d'\r\n", + ver_c, U3D_VER1); + return _epoc_late; + } + + log_u->ver_w = ver_w; + } + + // set path to latest epoch + c3_c epo_c[8193]; + snprintf(epo_c, 8192, "%s/0i%" PRIc3_d, log_u->com_u->pax_c, lat_d); + + // initialize latest epoch's db + if ( 0 == (log_u->mdb_u = u3_lmdb_init(epo_c, siz_i)) ) { + fprintf(stderr, "disk: failed to initialize database at %s\r\n", + epo_c); + return _epoc_fail; + } + + fprintf(stderr, "disk: loaded epoch 0i%" PRIc3_d "\r\n", lat_d); + + // get first/last event numbers from lmdb + c3_d fir_d, las_d; + if ( c3n == u3_lmdb_gulf(log_u->mdb_u, &fir_d, &las_d) ) { + fprintf(stderr, "disk: failed to get first/last event numbers\r\n"); + u3_lmdb_exit(log_u->mdb_u); + log_u->mdb_u = 0; + return _epoc_fail; + } + + if ( (c3n == u3_Host.ops_u.nuu ) + && !fir_d + && !las_d + && (c3n == u3_disk_read_meta(log_u->mdb_u, 0, 0, 0)) ) + { + u3_lmdb_exit(log_u->mdb_u); + log_u->mdb_u = 0; + return _epoc_void; + } + + // initialize dun_d/sen_d values + log_u->dun_d = ( 0 != las_d ) ? las_d : lat_d; + log_u->sen_d = log_u->dun_d; + + return _epoc_good; +} + /* u3_disk_init(): load or create pier directories and event log. */ u3_disk* @@ -1019,66 +1606,153 @@ u3_disk_init(c3_c* pax_c, u3_disk_cb cb_u) c3_free(dir_c); } - // create/load $pier/.urb/log, initialize db + // create/load $pier/.urb/log // { - c3_c* log_c = c3_malloc(10 + strlen(pax_c)); - - strcpy(log_c, pax_c); - strcat(log_c, "/.urb/log"); + c3_c log_c[8193]; + snprintf(log_c, sizeof(log_c), "%s/.urb/log", pax_c); if ( 0 == (log_u->com_u = u3_foil_folder(log_c)) ) { fprintf(stderr, "disk: failed to load /.urb/log in %s\r\n", pax_c); - c3_free(log_c); c3_free(log_u); return 0; } - // Arbitrarily choosing 1TB as a "large enough" mapsize - // - // per the LMDB docs: - // "[..] on 64-bit there is no penalty for making this huge (say 1TB)." + // if fresh boot, initialize disk v1 // - { - const size_t siz_i = - // 500 GiB is as large as musl on aarch64 wants to allow - #if (defined(U3_CPU_aarch64) && defined(U3_OS_linux)) - 0x7d00000000; - #else - 0x10000000000; - #endif + if ( c3y == u3_Host.ops_u.nuu ) { + // initialize first epoch "0i0" + if ( c3n == u3_disk_epoc_init(log_u, 0) ) { + fprintf(stderr, "disk: failed to initialize first epoch\r\n"); + return 0; + } + } + else if ( c3y == _disk_need_migrate(log_u) ) { + fprintf(stderr, "disk: loading old format\r\n"); if ( 0 == (log_u->mdb_u = u3_lmdb_init(log_c, siz_i)) ) { - fprintf(stderr, "disk: failed to initialize database\r\n"); - c3_free(log_c); + fprintf(stderr, "disk: failed to initialize lmdb\r\n"); + c3_free(log_u); + } + + c3_d fir_d; + if ( c3n == u3_lmdb_gulf(log_u->mdb_u, &fir_d, &log_u->dun_d) ) { + fprintf(stderr, "disk: failed to load latest event from lmdb\r\n"); + // XX dispose mdb_u c3_free(log_u); return 0; } - } - c3_free(log_c); - } + // if a top-level log exists but is empty it's safe to remove. + // for extra safety, we only do this if we also have epochs. + // (the top-level log was most likely created by vere downgrade) + // + c3_d nop_d; + if ( !fir_d + && !log_u->dun_d + && (c3y == u3_disk_epoc_last(log_u, &nop_d)) ) + { + c3_c luk_c[8193]; c3_c dut_c[8193]; + snprintf(dut_c, sizeof(dut_c), "%s/data.mdb", log_c); + snprintf(luk_c, sizeof(luk_c), "%s/lock.mdb", log_u->com_u->pax_c); + + u3_lmdb_exit(log_u->mdb_u); + log_u->mdb_u = 0; + + if ( c3_unlink(dut_c) + || ( (0 == access(luk_c, R_OK)) + && c3_unlink(luk_c) )) + { + fprintf(stderr, "disk: failed to unlink empty, old log\r\n"); + } - // get the latest event number from the db - // - { - log_u->dun_d = 0; - c3_d fir_d; + // continue to epoc initialization + } - if ( c3n == u3_lmdb_gulf(log_u->mdb_u, &fir_d, &log_u->dun_d) ) { - fprintf(stderr, "disk: failed to load latest event from database\r\n"); + else { + log_u->sen_d = log_u->dun_d; + log_u->ver_w = 0; + + return log_u; + } + } + + // get latest epoch number + c3_d lat_d; + if ( c3n == u3_disk_epoc_last(log_u, &lat_d) ) { + fprintf(stderr, "disk: failed to load epoch number\r\n"); c3_free(log_u); return 0; } - log_u->sen_d = log_u->dun_d; - } + c3_o try_o = c3n; + +try_init: + { + _epoc_kind kin_e = _disk_epoc_load(log_u, lat_d); + + switch ( kin_e ) { + case _epoc_good: { + // mark the latest epoch directory + log_u->epo_d = lat_d; - log_u->liv_o = c3y; + // mark the log as live + log_u->liv_o = c3y; #if defined(DISK_TRACE_JAM) || defined(DISK_TRACE_CUE) - u3t_trace_open(pax_c); + u3t_trace_open(pax_c); #endif - return log_u; + return log_u; + } break; + + case _epoc_void: // XX could handle more efficiently + + case _epoc_gone: { + // XX if there is no version number, the epoc is invalid + // backup and try previous + + if ( c3y == try_o ) { + fprintf(stderr, "multiple bad epochs, bailing out\r\n"); + c3_free(log_u); + return 0; + } + + c3_z len_z = u3_disk_epoc_list(log_u, 0); + + if ( len_z <= 1 ) { + fprintf(stderr, "only epoch is bad, bailing out\r\n"); + c3_free(log_u); + return 0; + } + + c3_d* sot_d = c3_malloc(len_z * sizeof(*sot_d)); + u3_disk_epoc_list(log_u, sot_d); + + fprintf(stderr, "disk: latest epoch is 0i%" PRIc3_d " is bogus; " + "falling back to previous at 0i%" PRIc3_d "\r\n", + lat_d, sot_d[1]); + + u3_disk_epoc_kill(log_u, lat_d); + + lat_d = sot_d[1]; + try_o = c3y; + c3_free(sot_d); + goto try_init; + } break; + + case _epoc_fail: { + fprintf(stderr, "failed to load epoch\r\n"); + c3_free(log_u); + return 0; + } break; + + case _epoc_late: { + fprintf(stderr, "upgrade runtime version\r\n"); + c3_free(log_u); + return 0; + } + } + } + } } diff --git a/pkg/vere/dns_sd.h b/pkg/vere/dns_sd.h new file mode 100644 index 0000000000..66e494e402 --- /dev/null +++ b/pkg/vere/dns_sd.h @@ -0,0 +1,1722 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2003-2004, Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DNS_SD_H +#define _DNS_SD_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* standard calling convention under Win32 is __stdcall */ +/* Note: When compiling Intel EFI (Extensible Firmware Interface) under MS Visual Studio, the */ +/* _WIN32 symbol is defined by the compiler even though it's NOT compiling code for Windows32 */ +#if defined(_WIN32) && !defined(EFI32) && !defined(EFI64) +#define DNSSD_API __stdcall +#else +#define DNSSD_API +#endif + +/* stdint.h does not exist on FreeBSD 4.x; its types are defined in sys/types.h instead */ +#if defined(__FreeBSD__) && (__FreeBSD__ < 5) +#include + +/* Likewise, on Sun, standard integer types are in sys/types.h */ +#elif defined(__sun__) +#include + +/* EFI does not have stdint.h, or anything else equivalent */ +#elif defined(EFI32) || defined(EFI64) +typedef UINT8 uint8_t; +typedef INT8 int8_t; +typedef UINT16 uint16_t; +typedef INT16 int16_t; +typedef UINT32 uint32_t; +typedef INT32 int32_t; + +/* Windows has its own differences */ +#elif defined(_WIN32) +#include +#define _UNUSED +#define bzero(a, b) memset(a, 0, b) +#ifndef _MSL_STDINT_H +typedef UINT8 uint8_t; +typedef INT8 int8_t; +typedef UINT16 uint16_t; +typedef INT16 int16_t; +typedef UINT32 uint32_t; +typedef INT32 int32_t; +#endif + +/* All other Posix platforms use stdint.h */ +#else +#include +#endif + +/* DNSServiceRef, DNSRecordRef + * + * Opaque internal data types. + * Note: client is responsible for serializing access to these structures if + * they are shared between concurrent threads. + */ + +typedef struct _DNSServiceRef_t *DNSServiceRef; +typedef struct _DNSRecordRef_t *DNSRecordRef; + +/* General flags used in functions defined below */ +enum + { + kDNSServiceFlagsMoreComing = 0x1, + /* MoreComing indicates to a callback that at least one more result is + * queued and will be delivered following immediately after this one. + * Applications should not update their UI to display browse + * results when the MoreComing flag is set, because this would + * result in a great deal of ugly flickering on the screen. + * Applications should instead wait until until MoreComing is not set, + * and then update their UI. + * When MoreComing is not set, that doesn't mean there will be no more + * answers EVER, just that there are no more answers immediately + * available right now at this instant. If more answers become available + * in the future they will be delivered as usual. + */ + + kDNSServiceFlagsAdd = 0x2, + kDNSServiceFlagsDefault = 0x4, + /* Flags for domain enumeration and browse/query reply callbacks. + * "Default" applies only to enumeration and is only valid in + * conjuction with "Add". An enumeration callback with the "Add" + * flag NOT set indicates a "Remove", i.e. the domain is no longer + * valid. + */ + + kDNSServiceFlagsNoAutoRename = 0x8, + /* Flag for specifying renaming behavior on name conflict when registering + * non-shared records. By default, name conflicts are automatically handled + * by renaming the service. NoAutoRename overrides this behavior - with this + * flag set, name conflicts will result in a callback. The NoAutorename flag + * is only valid if a name is explicitly specified when registering a service + * (i.e. the default name is not used.) + */ + + kDNSServiceFlagsShared = 0x10, + kDNSServiceFlagsUnique = 0x20, + /* Flag for registering individual records on a connected + * DNSServiceRef. Shared indicates that there may be multiple records + * with this name on the network (e.g. PTR records). Unique indicates that the + * record's name is to be unique on the network (e.g. SRV records). + */ + + kDNSServiceFlagsBrowseDomains = 0x40, + kDNSServiceFlagsRegistrationDomains = 0x80, + /* Flags for specifying domain enumeration type in DNSServiceEnumerateDomains. + * BrowseDomains enumerates domains recommended for browsing, RegistrationDomains + * enumerates domains recommended for registration. + */ + + kDNSServiceFlagsLongLivedQuery = 0x100, + /* Flag for creating a long-lived unicast query for the DNSServiceQueryRecord call. */ + + kDNSServiceFlagsAllowRemoteQuery = 0x200, + /* Flag for creating a record for which we will answer remote queries + * (queries from hosts more than one hop away; hosts not directly connected to the local link). + */ + + kDNSServiceFlagsForceMulticast = 0x400, + /* Flag for signifying that a query or registration should be performed exclusively via multicast DNS, + * even for a name in a domain (e.g. foo.apple.com.) that would normally imply unicast DNS. + */ + + kDNSServiceFlagsReturnCNAME = 0x800 + /* Flag for returning CNAME records in the DNSServiceQueryRecord call. CNAME records are + * normally followed without indicating to the client that there was a CNAME record. + */ + }; + +/* + * The values for DNS Classes and Types are listed in RFC 1035, and are available + * on every OS in its DNS header file. Unfortunately every OS does not have the + * same header file containing DNS Class and Type constants, and the names of + * the constants are not consistent. For example, BIND 8 uses "T_A", + * BIND 9 uses "ns_t_a", Windows uses "DNS_TYPE_A", etc. + * For this reason, these constants are also listed here, so that code using + * the DNS-SD programming APIs can use these constants, so that the same code + * can compile on all our supported platforms. + */ + +enum + { + kDNSServiceClass_IN = 1 /* Internet */ + }; + +enum + { + kDNSServiceType_A = 1, /* Host address. */ + kDNSServiceType_NS = 2, /* Authoritative server. */ + kDNSServiceType_MD = 3, /* Mail destination. */ + kDNSServiceType_MF = 4, /* Mail forwarder. */ + kDNSServiceType_CNAME = 5, /* Canonical name. */ + kDNSServiceType_SOA = 6, /* Start of authority zone. */ + kDNSServiceType_MB = 7, /* Mailbox domain name. */ + kDNSServiceType_MG = 8, /* Mail group member. */ + kDNSServiceType_MR = 9, /* Mail rename name. */ + kDNSServiceType_NULL = 10, /* Null resource record. */ + kDNSServiceType_WKS = 11, /* Well known service. */ + kDNSServiceType_PTR = 12, /* Domain name pointer. */ + kDNSServiceType_HINFO = 13, /* Host information. */ + kDNSServiceType_MINFO = 14, /* Mailbox information. */ + kDNSServiceType_MX = 15, /* Mail routing information. */ + kDNSServiceType_TXT = 16, /* One or more text strings. */ + kDNSServiceType_RP = 17, /* Responsible person. */ + kDNSServiceType_AFSDB = 18, /* AFS cell database. */ + kDNSServiceType_X25 = 19, /* X_25 calling address. */ + kDNSServiceType_ISDN = 20, /* ISDN calling address. */ + kDNSServiceType_RT = 21, /* Router. */ + kDNSServiceType_NSAP = 22, /* NSAP address. */ + kDNSServiceType_NSAP_PTR = 23, /* Reverse NSAP lookup (deprecated). */ + kDNSServiceType_SIG = 24, /* Security signature. */ + kDNSServiceType_KEY = 25, /* Security key. */ + kDNSServiceType_PX = 26, /* X.400 mail mapping. */ + kDNSServiceType_GPOS = 27, /* Geographical position (withdrawn). */ + kDNSServiceType_AAAA = 28, /* IPv6 Address. */ + kDNSServiceType_LOC = 29, /* Location Information. */ + kDNSServiceType_NXT = 30, /* Next domain (security). */ + kDNSServiceType_EID = 31, /* Endpoint identifier. */ + kDNSServiceType_NIMLOC = 32, /* Nimrod Locator. */ + kDNSServiceType_SRV = 33, /* Server Selection. */ + kDNSServiceType_ATMA = 34, /* ATM Address */ + kDNSServiceType_NAPTR = 35, /* Naming Authority PoinTeR */ + kDNSServiceType_KX = 36, /* Key Exchange */ + kDNSServiceType_CERT = 37, /* Certification record */ + kDNSServiceType_A6 = 38, /* IPv6 Address (deprecated) */ + kDNSServiceType_DNAME = 39, /* Non-terminal DNAME (for IPv6) */ + kDNSServiceType_SINK = 40, /* Kitchen sink (experimentatl) */ + kDNSServiceType_OPT = 41, /* EDNS0 option (meta-RR) */ + kDNSServiceType_TKEY = 249, /* Transaction key */ + kDNSServiceType_TSIG = 250, /* Transaction signature. */ + kDNSServiceType_IXFR = 251, /* Incremental zone transfer. */ + kDNSServiceType_AXFR = 252, /* Transfer zone of authority. */ + kDNSServiceType_MAILB = 253, /* Transfer mailbox records. */ + kDNSServiceType_MAILA = 254, /* Transfer mail agent records. */ + kDNSServiceType_ANY = 255 /* Wildcard match. */ + }; + + +/* possible error code values */ +enum + { + kDNSServiceErr_NoError = 0, + kDNSServiceErr_Unknown = -65537, /* 0xFFFE FFFF */ + kDNSServiceErr_NoSuchName = -65538, + kDNSServiceErr_NoMemory = -65539, + kDNSServiceErr_BadParam = -65540, + kDNSServiceErr_BadReference = -65541, + kDNSServiceErr_BadState = -65542, + kDNSServiceErr_BadFlags = -65543, + kDNSServiceErr_Unsupported = -65544, + kDNSServiceErr_NotInitialized = -65545, + kDNSServiceErr_AlreadyRegistered = -65547, + kDNSServiceErr_NameConflict = -65548, + kDNSServiceErr_Invalid = -65549, + kDNSServiceErr_Firewall = -65550, + kDNSServiceErr_Incompatible = -65551, /* client library incompatible with daemon */ + kDNSServiceErr_BadInterfaceIndex = -65552, + kDNSServiceErr_Refused = -65553, + kDNSServiceErr_NoSuchRecord = -65554, + kDNSServiceErr_NoAuth = -65555, + kDNSServiceErr_NoSuchKey = -65556, + kDNSServiceErr_NATTraversal = -65557, + kDNSServiceErr_DoubleNAT = -65558, + kDNSServiceErr_BadTime = -65559 + /* mDNS Error codes are in the range + * FFFE FF00 (-65792) to FFFE FFFF (-65537) */ + }; + + +/* Maximum length, in bytes, of a service name represented as a */ +/* literal C-String, including the terminating NULL at the end. */ + +#define kDNSServiceMaxServiceName 64 + +/* Maximum length, in bytes, of a domain name represented as an *escaped* C-String */ +/* including the final trailing dot, and the C-String terminating NULL at the end. */ + +#define kDNSServiceMaxDomainName 1005 + +/* + * Notes on DNS Name Escaping + * -- or -- + * "Why is kDNSServiceMaxDomainName 1005, when the maximum legal domain name is 255 bytes?" + * + * All strings used in DNS-SD are UTF-8 strings. + * With few exceptions, most are also escaped using standard DNS escaping rules: + * + * '\\' represents a single literal '\' in the name + * '\.' represents a single literal '.' in the name + * '\ddd', where ddd is a three-digit decimal value from 000 to 255, + * represents a single literal byte with that value. + * A bare unescaped '.' is a label separator, marking a boundary between domain and subdomain. + * + * The exceptions, that do not use escaping, are the routines where the full + * DNS name of a resource is broken, for convenience, into servicename/regtype/domain. + * In these routines, the "servicename" is NOT escaped. It does not need to be, since + * it is, by definition, just a single literal string. Any characters in that string + * represent exactly what they are. The "regtype" portion is, technically speaking, + * escaped, but since legal regtypes are only allowed to contain letters, digits, + * and hyphens, there is nothing to escape, so the issue is moot. The "domain" + * portion is also escaped, though most domains in use on the public Internet + * today, like regtypes, don't contain any characters that need to be escaped. + * As DNS-SD becomes more popular, rich-text domains for service discovery will + * become common, so software should be written to cope with domains with escaping. + * + * The servicename may be up to 63 bytes of UTF-8 text (not counting the C-String + * terminating NULL at the end). The regtype is of the form _service._tcp or + * _service._udp, where the "service" part is 1-14 characters, which may be + * letters, digits, or hyphens. The domain part of the three-part name may be + * any legal domain, providing that the resulting servicename+regtype+domain + * name does not exceed 255 bytes. + * + * For most software, these issues are transparent. When browsing, the discovered + * servicenames should simply be displayed as-is. When resolving, the discovered + * servicename/regtype/domain are simply passed unchanged to DNSServiceResolve(). + * When a DNSServiceResolve() succeeds, the returned fullname is already in + * the correct format to pass to standard system DNS APIs such as res_query(). + * For converting from servicename/regtype/domain to a single properly-escaped + * full DNS name, the helper function DNSServiceConstructFullName() is provided. + * + * The following (highly contrived) example illustrates the escaping process. + * Suppose you have an service called "Dr. Smith\Dr. Johnson", of type "_ftp._tcp" + * in subdomain "4th. Floor" of subdomain "Building 2" of domain "apple.com." + * The full (escaped) DNS name of this service's SRV record would be: + * Dr\.\032Smith\\Dr\.\032Johnson._ftp._tcp.4th\.\032Floor.Building\0322.apple.com. + */ + + +/* + * Constants for specifying an interface index + * + * Specific interface indexes are identified via a 32-bit unsigned integer returned + * by the if_nametoindex() family of calls. + * + * If the client passes 0 for interface index, that means "do the right thing", + * which (at present) means, "if the name is in an mDNS local multicast domain + * (e.g. 'local.', '254.169.in-addr.arpa.', '{8,9,A,B}.E.F.ip6.arpa.') then multicast + * on all applicable interfaces, otherwise send via unicast to the appropriate + * DNS server." Normally, most clients will use 0 for interface index to + * automatically get the default sensible behaviour. + * + * If the client passes a positive interface index, then for multicast names that + * indicates to do the operation only on that one interface. For unicast names the + * interface index is ignored unless kDNSServiceFlagsForceMulticast is also set. + * + * If the client passes kDNSServiceInterfaceIndexLocalOnly when registering + * a service, then that service will be found *only* by other local clients + * on the same machine that are browsing using kDNSServiceInterfaceIndexLocalOnly + * or kDNSServiceInterfaceIndexAny. + * If a client has a 'private' service, accessible only to other processes + * running on the same machine, this allows the client to advertise that service + * in a way such that it does not inadvertently appear in service lists on + * all the other machines on the network. + * + * If the client passes kDNSServiceInterfaceIndexLocalOnly when browsing + * then it will find *all* records registered on that same local machine. + * Clients explicitly wishing to discover *only* LocalOnly services can + * accomplish this by inspecting the interfaceIndex of each service reported + * to their DNSServiceBrowseReply() callback function, and discarding those + * where the interface index is not kDNSServiceInterfaceIndexLocalOnly. + */ + +#define kDNSServiceInterfaceIndexAny 0 +#define kDNSServiceInterfaceIndexLocalOnly ( (uint32_t) -1 ) + + +typedef uint32_t DNSServiceFlags; +typedef int32_t DNSServiceErrorType; + + +/********************************************************************************************* + * + * Unix Domain Socket access, DNSServiceRef deallocation, and data processing functions + * + *********************************************************************************************/ + + +/* DNSServiceRefSockFD() + * + * Access underlying Unix domain socket for an initialized DNSServiceRef. + * The DNS Service Discovery implmementation uses this socket to communicate between + * the client and the mDNSResponder daemon. The application MUST NOT directly read from + * or write to this socket. Access to the socket is provided so that it can be used as a + * run loop source, or in a select() loop: when data is available for reading on the socket, + * DNSServiceProcessResult() should be called, which will extract the daemon's reply from + * the socket, and pass it to the appropriate application callback. By using a run loop or + * select(), results from the daemon can be processed asynchronously. Without using these + * constructs, DNSServiceProcessResult() will block until the response from the daemon arrives. + * The client is responsible for ensuring that the data on the socket is processed in a timely + * fashion - the daemon may terminate its connection with a client that does not clear its + * socket buffer. + * + * sdRef: A DNSServiceRef initialized by any of the DNSService calls. + * + * return value: The DNSServiceRef's underlying socket descriptor, or -1 on + * error. + */ + +int DNSSD_API DNSServiceRefSockFD(DNSServiceRef sdRef); + + +/* DNSServiceProcessResult() + * + * Read a reply from the daemon, calling the appropriate application callback. This call will + * block until the daemon's response is received. Use DNSServiceRefSockFD() in + * conjunction with a run loop or select() to determine the presence of a response from the + * server before calling this function to process the reply without blocking. Call this function + * at any point if it is acceptable to block until the daemon's response arrives. Note that the + * client is responsible for ensuring that DNSServiceProcessResult() is called whenever there is + * a reply from the daemon - the daemon may terminate its connection with a client that does not + * process the daemon's responses. + * + * sdRef: A DNSServiceRef initialized by any of the DNSService calls + * that take a callback parameter. + * + * return value: Returns kDNSServiceErr_NoError on success, otherwise returns + * an error code indicating the specific failure that occurred. + */ + +DNSServiceErrorType DNSSD_API DNSServiceProcessResult(DNSServiceRef sdRef); + + +/* DNSServiceRefDeallocate() + * + * Terminate a connection with the daemon and free memory associated with the DNSServiceRef. + * Any services or records registered with this DNSServiceRef will be deregistered. Any + * Browse, Resolve, or Query operations called with this reference will be terminated. + * + * Note: If the reference's underlying socket is used in a run loop or select() call, it should + * be removed BEFORE DNSServiceRefDeallocate() is called, as this function closes the reference's + * socket. + * + * Note: If the reference was initialized with DNSServiceCreateConnection(), any DNSRecordRefs + * created via this reference will be invalidated by this call - the resource records are + * deregistered, and their DNSRecordRefs may not be used in subsequent functions. Similarly, + * if the reference was initialized with DNSServiceRegister, and an extra resource record was + * added to the service via DNSServiceAddRecord(), the DNSRecordRef created by the Add() call + * is invalidated when this function is called - the DNSRecordRef may not be used in subsequent + * functions. + * + * Note: This call is to be used only with the DNSServiceRef defined by this API. It is + * not compatible with dns_service_discovery_ref objects defined in the legacy Mach-based + * DNSServiceDiscovery.h API. + * + * sdRef: A DNSServiceRef initialized by any of the DNSService calls. + * + */ + +void DNSSD_API DNSServiceRefDeallocate(DNSServiceRef sdRef); + + +/********************************************************************************************* + * + * Domain Enumeration + * + *********************************************************************************************/ + +/* DNSServiceEnumerateDomains() + * + * Asynchronously enumerate domains available for browsing and registration. + * + * The enumeration MUST be cancelled via DNSServiceRefDeallocate() when no more domains + * are to be found. + * + * Note that the names returned are (like all of DNS-SD) UTF-8 strings, + * and are escaped using standard DNS escaping rules. + * (See "Notes on DNS Name Escaping" earlier in this file for more details.) + * A graphical browser displaying a hierarchical tree-structured view should cut + * the names at the bare dots to yield individual labels, then de-escape each + * label according to the escaping rules, and then display the resulting UTF-8 text. + * + * DNSServiceDomainEnumReply Callback Parameters: + * + * sdRef: The DNSServiceRef initialized by DNSServiceEnumerateDomains(). + * + * flags: Possible values are: + * kDNSServiceFlagsMoreComing + * kDNSServiceFlagsAdd + * kDNSServiceFlagsDefault + * + * interfaceIndex: Specifies the interface on which the domain exists. (The index for a given + * interface is determined via the if_nametoindex() family of calls.) + * + * errorCode: Will be kDNSServiceErr_NoError (0) on success, otherwise indicates + * the failure that occurred (other parameters are undefined if errorCode is nonzero). + * + * replyDomain: The name of the domain. + * + * context: The context pointer passed to DNSServiceEnumerateDomains. + * + */ + +typedef void (DNSSD_API *DNSServiceDomainEnumReply) + ( + DNSServiceRef sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, + const char *replyDomain, + void *context + ); + + +/* DNSServiceEnumerateDomains() Parameters: + * + * + * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds + * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError, + * and the enumeration operation will run indefinitely until the client + * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate(). + * + * flags: Possible values are: + * kDNSServiceFlagsBrowseDomains to enumerate domains recommended for browsing. + * kDNSServiceFlagsRegistrationDomains to enumerate domains recommended + * for registration. + * + * interfaceIndex: If non-zero, specifies the interface on which to look for domains. + * (the index for a given interface is determined via the if_nametoindex() + * family of calls.) Most applications will pass 0 to enumerate domains on + * all interfaces. See "Constants for specifying an interface index" for more details. + * + * callBack: The function to be called when a domain is found or the call asynchronously + * fails. + * + * context: An application context pointer which is passed to the callback function + * (may be NULL). + * + * return value: Returns kDNSServiceErr_NoError on succeses (any subsequent, asynchronous + * errors are delivered to the callback), otherwise returns an error code indicating + * the error that occurred (the callback is not invoked and the DNSServiceRef + * is not initialized.) + */ + +DNSServiceErrorType DNSSD_API DNSServiceEnumerateDomains + ( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceDomainEnumReply callBack, + void *context /* may be NULL */ + ); + + +/********************************************************************************************* + * + * Service Registration + * + *********************************************************************************************/ + +/* Register a service that is discovered via Browse() and Resolve() calls. + * + * + * DNSServiceRegisterReply() Callback Parameters: + * + * sdRef: The DNSServiceRef initialized by DNSServiceRegister(). + * + * flags: Currently unused, reserved for future use. + * + * errorCode: Will be kDNSServiceErr_NoError on success, otherwise will + * indicate the failure that occurred (including name conflicts, + * if the kDNSServiceFlagsNoAutoRename flag was used when registering.) + * Other parameters are undefined if errorCode is nonzero. + * + * name: The service name registered (if the application did not specify a name in + * DNSServiceRegister(), this indicates what name was automatically chosen). + * + * regtype: The type of service registered, as it was passed to the callout. + * + * domain: The domain on which the service was registered (if the application did not + * specify a domain in DNSServiceRegister(), this indicates the default domain + * on which the service was registered). + * + * context: The context pointer that was passed to the callout. + * + */ + +typedef void (DNSSD_API *DNSServiceRegisterReply) + ( + DNSServiceRef sdRef, + DNSServiceFlags flags, + DNSServiceErrorType errorCode, + const char *name, + const char *regtype, + const char *domain, + void *context + ); + + +/* DNSServiceRegister() Parameters: + * + * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds + * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError, + * and the registration will remain active indefinitely until the client + * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate(). + * + * interfaceIndex: If non-zero, specifies the interface on which to register the service + * (the index for a given interface is determined via the if_nametoindex() + * family of calls.) Most applications will pass 0 to register on all + * available interfaces. See "Constants for specifying an interface index" for more details. + * + * flags: Indicates the renaming behavior on name conflict (most applications + * will pass 0). See flag definitions above for details. + * + * name: If non-NULL, specifies the service name to be registered. + * Most applications will not specify a name, in which case the computer + * name is used (this name is communicated to the client via the callback). + * If a name is specified, it must be 1-63 bytes of UTF-8 text. + * If the name is longer than 63 bytes it will be automatically truncated + * to a legal length, unless the NoAutoRename flag is set, + * in which case kDNSServiceErr_BadParam will be returned. + * + * regtype: The service type followed by the protocol, separated by a dot + * (e.g. "_ftp._tcp"). The service type must be an underscore, followed + * by 1-14 characters, which may be letters, digits, or hyphens. + * The transport protocol must be "_tcp" or "_udp". New service types + * should be registered at . + * + * domain: If non-NULL, specifies the domain on which to advertise the service. + * Most applications will not specify a domain, instead automatically + * registering in the default domain(s). + * + * host: If non-NULL, specifies the SRV target host name. Most applications + * will not specify a host, instead automatically using the machine's + * default host name(s). Note that specifying a non-NULL host does NOT + * create an address record for that host - the application is responsible + * for ensuring that the appropriate address record exists, or creating it + * via DNSServiceRegisterRecord(). + * + * port: The port, in network byte order, on which the service accepts connections. + * Pass 0 for a "placeholder" service (i.e. a service that will not be discovered + * by browsing, but will cause a name conflict if another client tries to + * register that same name). Most clients will not use placeholder services. + * + * txtLen: The length of the txtRecord, in bytes. Must be zero if the txtRecord is NULL. + * + * txtRecord: The TXT record rdata. A non-NULL txtRecord MUST be a properly formatted DNS + * TXT record, i.e. ... + * Passing NULL for the txtRecord is allowed as a synonym for txtLen=1, txtRecord="", + * i.e. it creates a TXT record of length one containing a single empty string. + * RFC 1035 doesn't allow a TXT record to contain *zero* strings, so a single empty + * string is the smallest legal DNS TXT record. + * As with the other parameters, the DNSServiceRegister call copies the txtRecord + * data; e.g. if you allocated the storage for the txtRecord parameter with malloc() + * then you can safely free that memory right after the DNSServiceRegister call returns. + * + * callBack: The function to be called when the registration completes or asynchronously + * fails. The client MAY pass NULL for the callback - The client will NOT be notified + * of the default values picked on its behalf, and the client will NOT be notified of any + * asynchronous errors (e.g. out of memory errors, etc.) that may prevent the registration + * of the service. The client may NOT pass the NoAutoRename flag if the callback is NULL. + * The client may still deregister the service at any time via DNSServiceRefDeallocate(). + * + * context: An application context pointer which is passed to the callback function + * (may be NULL). + * + * return value: Returns kDNSServiceErr_NoError on succeses (any subsequent, asynchronous + * errors are delivered to the callback), otherwise returns an error code indicating + * the error that occurred (the callback is never invoked and the DNSServiceRef + * is not initialized.) + */ + +DNSServiceErrorType DNSSD_API DNSServiceRegister + ( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *name, /* may be NULL */ + const char *regtype, + const char *domain, /* may be NULL */ + const char *host, /* may be NULL */ + uint16_t port, + uint16_t txtLen, + const void *txtRecord, /* may be NULL */ + DNSServiceRegisterReply callBack, /* may be NULL */ + void *context /* may be NULL */ + ); + + +/* DNSServiceAddRecord() + * + * Add a record to a registered service. The name of the record will be the same as the + * registered service's name. + * The record can later be updated or deregistered by passing the RecordRef initialized + * by this function to DNSServiceUpdateRecord() or DNSServiceRemoveRecord(). + * + * Note that the DNSServiceAddRecord/UpdateRecord/RemoveRecord are *NOT* thread-safe + * with respect to a single DNSServiceRef. If you plan to have multiple threads + * in your program simultaneously add, update, or remove records from the same + * DNSServiceRef, then it's the caller's responsibility to use a mutext lock + * or take similar appropriate precautions to serialize those calls. + * + * + * Parameters; + * + * sdRef: A DNSServiceRef initialized by DNSServiceRegister(). + * + * RecordRef: A pointer to an uninitialized DNSRecordRef. Upon succesfull completion of this + * call, this ref may be passed to DNSServiceUpdateRecord() or DNSServiceRemoveRecord(). + * If the above DNSServiceRef is passed to DNSServiceRefDeallocate(), RecordRef is also + * invalidated and may not be used further. + * + * flags: Currently ignored, reserved for future use. + * + * rrtype: The type of the record (e.g. kDNSServiceType_TXT, kDNSServiceType_SRV, etc) + * + * rdlen: The length, in bytes, of the rdata. + * + * rdata: The raw rdata to be contained in the added resource record. + * + * ttl: The time to live of the resource record, in seconds. Pass 0 to use a default value. + * + * return value: Returns kDNSServiceErr_NoError on success, otherwise returns an + * error code indicating the error that occurred (the RecordRef is not initialized). + */ + +DNSServiceErrorType DNSSD_API DNSServiceAddRecord + ( + DNSServiceRef sdRef, + DNSRecordRef *RecordRef, + DNSServiceFlags flags, + uint16_t rrtype, + uint16_t rdlen, + const void *rdata, + uint32_t ttl + ); + + +/* DNSServiceUpdateRecord + * + * Update a registered resource record. The record must either be: + * - The primary txt record of a service registered via DNSServiceRegister() + * - A record added to a registered service via DNSServiceAddRecord() + * - An individual record registered by DNSServiceRegisterRecord() + * + * + * Parameters: + * + * sdRef: A DNSServiceRef that was initialized by DNSServiceRegister() + * or DNSServiceCreateConnection(). + * + * RecordRef: A DNSRecordRef initialized by DNSServiceAddRecord, or NULL to update the + * service's primary txt record. + * + * flags: Currently ignored, reserved for future use. + * + * rdlen: The length, in bytes, of the new rdata. + * + * rdata: The new rdata to be contained in the updated resource record. + * + * ttl: The time to live of the updated resource record, in seconds. + * + * return value: Returns kDNSServiceErr_NoError on success, otherwise returns an + * error code indicating the error that occurred. + */ + +DNSServiceErrorType DNSSD_API DNSServiceUpdateRecord + ( + DNSServiceRef sdRef, + DNSRecordRef RecordRef, /* may be NULL */ + DNSServiceFlags flags, + uint16_t rdlen, + const void *rdata, + uint32_t ttl + ); + + +/* DNSServiceRemoveRecord + * + * Remove a record previously added to a service record set via DNSServiceAddRecord(), or deregister + * an record registered individually via DNSServiceRegisterRecord(). + * + * Parameters: + * + * sdRef: A DNSServiceRef initialized by DNSServiceRegister() (if the + * record being removed was registered via DNSServiceAddRecord()) or by + * DNSServiceCreateConnection() (if the record being removed was registered via + * DNSServiceRegisterRecord()). + * + * recordRef: A DNSRecordRef initialized by a successful call to DNSServiceAddRecord() + * or DNSServiceRegisterRecord(). + * + * flags: Currently ignored, reserved for future use. + * + * return value: Returns kDNSServiceErr_NoError on success, otherwise returns an + * error code indicating the error that occurred. + */ + +DNSServiceErrorType DNSSD_API DNSServiceRemoveRecord + ( + DNSServiceRef sdRef, + DNSRecordRef RecordRef, + DNSServiceFlags flags + ); + + +/********************************************************************************************* + * + * Service Discovery + * + *********************************************************************************************/ + +/* Browse for instances of a service. + * + * + * DNSServiceBrowseReply() Parameters: + * + * sdRef: The DNSServiceRef initialized by DNSServiceBrowse(). + * + * flags: Possible values are kDNSServiceFlagsMoreComing and kDNSServiceFlagsAdd. + * See flag definitions for details. + * + * interfaceIndex: The interface on which the service is advertised. This index should + * be passed to DNSServiceResolve() when resolving the service. + * + * errorCode: Will be kDNSServiceErr_NoError (0) on success, otherwise will + * indicate the failure that occurred. Other parameters are undefined if + * the errorCode is nonzero. + * + * serviceName: The discovered service name. This name should be displayed to the user, + * and stored for subsequent use in the DNSServiceResolve() call. + * + * regtype: The service type, which is usually (but not always) the same as was passed + * to DNSServiceBrowse(). One case where the discovered service type may + * not be the same as the requested service type is when using subtypes: + * The client may want to browse for only those ftp servers that allow + * anonymous connections. The client will pass the string "_ftp._tcp,_anon" + * to DNSServiceBrowse(), but the type of the service that's discovered + * is simply "_ftp._tcp". The regtype for each discovered service instance + * should be stored along with the name, so that it can be passed to + * DNSServiceResolve() when the service is later resolved. + * + * domain: The domain of the discovered service instance. This may or may not be the + * same as the domain that was passed to DNSServiceBrowse(). The domain for each + * discovered service instance should be stored along with the name, so that + * it can be passed to DNSServiceResolve() when the service is later resolved. + * + * context: The context pointer that was passed to the callout. + * + */ + +typedef void (DNSSD_API *DNSServiceBrowseReply) + ( + DNSServiceRef sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, + const char *serviceName, + const char *regtype, + const char *replyDomain, + void *context + ); + + +/* DNSServiceBrowse() Parameters: + * + * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds + * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError, + * and the browse operation will run indefinitely until the client + * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate(). + * + * flags: Currently ignored, reserved for future use. + * + * interfaceIndex: If non-zero, specifies the interface on which to browse for services + * (the index for a given interface is determined via the if_nametoindex() + * family of calls.) Most applications will pass 0 to browse on all available + * interfaces. See "Constants for specifying an interface index" for more details. + * + * regtype: The service type being browsed for followed by the protocol, separated by a + * dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp". + * + * domain: If non-NULL, specifies the domain on which to browse for services. + * Most applications will not specify a domain, instead browsing on the + * default domain(s). + * + * callBack: The function to be called when an instance of the service being browsed for + * is found, or if the call asynchronously fails. + * + * context: An application context pointer which is passed to the callback function + * (may be NULL). + * + * return value: Returns kDNSServiceErr_NoError on succeses (any subsequent, asynchronous + * errors are delivered to the callback), otherwise returns an error code indicating + * the error that occurred (the callback is not invoked and the DNSServiceRef + * is not initialized.) + */ + +DNSServiceErrorType DNSSD_API DNSServiceBrowse + ( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *regtype, + const char *domain, /* may be NULL */ + DNSServiceBrowseReply callBack, + void *context /* may be NULL */ + ); + + +/* DNSServiceResolve() + * + * Resolve a service name discovered via DNSServiceBrowse() to a target host name, port number, and + * txt record. + * + * Note: Applications should NOT use DNSServiceResolve() solely for txt record monitoring - use + * DNSServiceQueryRecord() instead, as it is more efficient for this task. + * + * Note: When the desired results have been returned, the client MUST terminate the resolve by calling + * DNSServiceRefDeallocate(). + * + * Note: DNSServiceResolve() behaves correctly for typical services that have a single SRV record + * and a single TXT record. To resolve non-standard services with multiple SRV or TXT records, + * DNSServiceQueryRecord() should be used. + * + * DNSServiceResolveReply Callback Parameters: + * + * sdRef: The DNSServiceRef initialized by DNSServiceResolve(). + * + * flags: Currently unused, reserved for future use. + * + * interfaceIndex: The interface on which the service was resolved. + * + * errorCode: Will be kDNSServiceErr_NoError (0) on success, otherwise will + * indicate the failure that occurred. Other parameters are undefined if + * the errorCode is nonzero. + * + * fullname: The full service domain name, in the form ... + * (This name is escaped following standard DNS rules, making it suitable for + * passing to standard system DNS APIs such as res_query(), or to the + * special-purpose functions included in this API that take fullname parameters. + * See "Notes on DNS Name Escaping" earlier in this file for more details.) + * + * hosttarget: The target hostname of the machine providing the service. This name can + * be passed to functions like gethostbyname() to identify the host's IP address. + * + * port: The port, in network byte order, on which connections are accepted for this service. + * + * txtLen: The length of the txt record, in bytes. + * + * txtRecord: The service's primary txt record, in standard txt record format. + * + * context: The context pointer that was passed to the callout. + * + * NOTE: In earlier versions of this header file, the txtRecord parameter was declared "const char *" + * This is incorrect, since it contains length bytes which are values in the range 0 to 255, not -128 to +127. + * Depending on your compiler settings, this change may cause signed/unsigned mismatch warnings. + * These should be fixed by updating your own callback function definition to match the corrected + * function signature using "const unsigned char *txtRecord". Making this change may also fix inadvertent + * bugs in your callback function, where it could have incorrectly interpreted a length byte with value 250 + * as being -6 instead, with various bad consequences ranging from incorrect operation to software crashes. + * If you need to maintain portable code that will compile cleanly with both the old and new versions of + * this header file, you should update your callback function definition to use the correct unsigned value, + * and then in the place where you pass your callback function to DNSServiceResolve(), use a cast to eliminate + * the compiler warning, e.g.: + * DNSServiceResolve(sd, flags, index, name, regtype, domain, (DNSServiceResolveReply)MyCallback, context); + * This will ensure that your code compiles cleanly without warnings (and more importantly, works correctly) + * with both the old header and with the new corrected version. + * + */ + +typedef void (DNSSD_API *DNSServiceResolveReply) + ( + DNSServiceRef sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, + const char *fullname, + const char *hosttarget, + uint16_t port, + uint16_t txtLen, + const unsigned char *txtRecord, + void *context + ); + + +/* DNSServiceResolve() Parameters + * + * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds + * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError, + * and the resolve operation will run indefinitely until the client + * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate(). + * + * flags: Currently ignored, reserved for future use. + * + * interfaceIndex: The interface on which to resolve the service. If this resolve call is + * as a result of a currently active DNSServiceBrowse() operation, then the + * interfaceIndex should be the index reported in the DNSServiceBrowseReply + * callback. If this resolve call is using information previously saved + * (e.g. in a preference file) for later use, then use interfaceIndex 0, because + * the desired service may now be reachable via a different physical interface. + * See "Constants for specifying an interface index" for more details. + * + * name: The name of the service instance to be resolved, as reported to the + * DNSServiceBrowseReply() callback. + * + * regtype: The type of the service instance to be resolved, as reported to the + * DNSServiceBrowseReply() callback. + * + * domain: The domain of the service instance to be resolved, as reported to the + * DNSServiceBrowseReply() callback. + * + * callBack: The function to be called when a result is found, or if the call + * asynchronously fails. + * + * context: An application context pointer which is passed to the callback function + * (may be NULL). + * + * return value: Returns kDNSServiceErr_NoError on succeses (any subsequent, asynchronous + * errors are delivered to the callback), otherwise returns an error code indicating + * the error that occurred (the callback is never invoked and the DNSServiceRef + * is not initialized.) + */ + +DNSServiceErrorType DNSSD_API DNSServiceResolve + ( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *name, + const char *regtype, + const char *domain, + DNSServiceResolveReply callBack, + void *context /* may be NULL */ + ); + + +/********************************************************************************************* + * + * Special Purpose Calls (most applications will not use these) + * + *********************************************************************************************/ + +/* DNSServiceCreateConnection() + * + * Create a connection to the daemon allowing efficient registration of + * multiple individual records. + * + * + * Parameters: + * + * sdRef: A pointer to an uninitialized DNSServiceRef. Deallocating + * the reference (via DNSServiceRefDeallocate()) severs the + * connection and deregisters all records registered on this connection. + * + * return value: Returns kDNSServiceErr_NoError on success, otherwise returns + * an error code indicating the specific failure that occurred (in which + * case the DNSServiceRef is not initialized). + */ + +DNSServiceErrorType DNSSD_API DNSServiceCreateConnection(DNSServiceRef *sdRef); + + +/* DNSServiceRegisterRecord + * + * Register an individual resource record on a connected DNSServiceRef. + * + * Note that name conflicts occurring for records registered via this call must be handled + * by the client in the callback. + * + * + * DNSServiceRegisterRecordReply() parameters: + * + * sdRef: The connected DNSServiceRef initialized by + * DNSServiceCreateConnection(). + * + * RecordRef: The DNSRecordRef initialized by DNSServiceRegisterRecord(). If the above + * DNSServiceRef is passed to DNSServiceRefDeallocate(), this DNSRecordRef is + * invalidated, and may not be used further. + * + * flags: Currently unused, reserved for future use. + * + * errorCode: Will be kDNSServiceErr_NoError on success, otherwise will + * indicate the failure that occurred (including name conflicts.) + * Other parameters are undefined if errorCode is nonzero. + * + * context: The context pointer that was passed to the callout. + * + */ + + typedef void (DNSSD_API *DNSServiceRegisterRecordReply) + ( + DNSServiceRef sdRef, + DNSRecordRef RecordRef, + DNSServiceFlags flags, + DNSServiceErrorType errorCode, + void *context + ); + + +/* DNSServiceRegisterRecord() Parameters: + * + * sdRef: A DNSServiceRef initialized by DNSServiceCreateConnection(). + * + * RecordRef: A pointer to an uninitialized DNSRecordRef. Upon succesfull completion of this + * call, this ref may be passed to DNSServiceUpdateRecord() or DNSServiceRemoveRecord(). + * (To deregister ALL records registered on a single connected DNSServiceRef + * and deallocate each of their corresponding DNSServiceRecordRefs, call + * DNSServiceRefDealloocate()). + * + * flags: Possible values are kDNSServiceFlagsShared or kDNSServiceFlagsUnique + * (see flag type definitions for details). + * + * interfaceIndex: If non-zero, specifies the interface on which to register the record + * (the index for a given interface is determined via the if_nametoindex() + * family of calls.) Passing 0 causes the record to be registered on all interfaces. + * See "Constants for specifying an interface index" for more details. + * + * fullname: The full domain name of the resource record. + * + * rrtype: The numerical type of the resource record (e.g. kDNSServiceType_PTR, kDNSServiceType_SRV, etc) + * + * rrclass: The class of the resource record (usually kDNSServiceClass_IN) + * + * rdlen: Length, in bytes, of the rdata. + * + * rdata: A pointer to the raw rdata, as it is to appear in the DNS record. + * + * ttl: The time to live of the resource record, in seconds. Pass 0 to use a default value. + * + * callBack: The function to be called when a result is found, or if the call + * asynchronously fails (e.g. because of a name conflict.) + * + * context: An application context pointer which is passed to the callback function + * (may be NULL). + * + * return value: Returns kDNSServiceErr_NoError on succeses (any subsequent, asynchronous + * errors are delivered to the callback), otherwise returns an error code indicating + * the error that occurred (the callback is never invoked and the DNSRecordRef is + * not initialized.) + */ + +DNSServiceErrorType DNSSD_API DNSServiceRegisterRecord + ( + DNSServiceRef sdRef, + DNSRecordRef *RecordRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *fullname, + uint16_t rrtype, + uint16_t rrclass, + uint16_t rdlen, + const void *rdata, + uint32_t ttl, + DNSServiceRegisterRecordReply callBack, + void *context /* may be NULL */ + ); + + +/* DNSServiceQueryRecord + * + * Query for an arbitrary DNS record. + * + * + * DNSServiceQueryRecordReply() Callback Parameters: + * + * sdRef: The DNSServiceRef initialized by DNSServiceQueryRecord(). + * + * flags: Possible values are kDNSServiceFlagsMoreComing and + * kDNSServiceFlagsAdd. The Add flag is NOT set for PTR records + * with a ttl of 0, i.e. "Remove" events. + * + * interfaceIndex: The interface on which the query was resolved (the index for a given + * interface is determined via the if_nametoindex() family of calls). + * See "Constants for specifying an interface index" for more details. + * + * errorCode: Will be kDNSServiceErr_NoError on success, otherwise will + * indicate the failure that occurred. Other parameters are undefined if + * errorCode is nonzero. + * + * fullname: The resource record's full domain name. + * + * rrtype: The resource record's type (e.g. kDNSServiceType_PTR, kDNSServiceType_SRV, etc) + * + * rrclass: The class of the resource record (usually kDNSServiceClass_IN). + * + * rdlen: The length, in bytes, of the resource record rdata. + * + * rdata: The raw rdata of the resource record. + * + * ttl: The resource record's time to live, in seconds. + * + * context: The context pointer that was passed to the callout. + * + */ + +typedef void (DNSSD_API *DNSServiceQueryRecordReply) + ( + DNSServiceRef DNSServiceRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, + const char *fullname, + uint16_t rrtype, + uint16_t rrclass, + uint16_t rdlen, + const void *rdata, + uint32_t ttl, + void *context + ); + + +/* DNSServiceQueryRecord() Parameters: + * + * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds + * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError, + * and the query operation will run indefinitely until the client + * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate(). + * + * flags: Pass kDNSServiceFlagsLongLivedQuery to create a "long-lived" unicast + * query in a non-local domain. Without setting this flag, unicast queries + * will be one-shot - that is, only answers available at the time of the call + * will be returned. By setting this flag, answers (including Add and Remove + * events) that become available after the initial call is made will generate + * callbacks. This flag has no effect on link-local multicast queries. + * + * interfaceIndex: If non-zero, specifies the interface on which to issue the query + * (the index for a given interface is determined via the if_nametoindex() + * family of calls.) Passing 0 causes the name to be queried for on all + * interfaces. See "Constants for specifying an interface index" for more details. + * + * fullname: The full domain name of the resource record to be queried for. + * + * rrtype: The numerical type of the resource record to be queried for + * (e.g. kDNSServiceType_PTR, kDNSServiceType_SRV, etc) + * + * rrclass: The class of the resource record (usually kDNSServiceClass_IN). + * + * callBack: The function to be called when a result is found, or if the call + * asynchronously fails. + * + * context: An application context pointer which is passed to the callback function + * (may be NULL). + * + * return value: Returns kDNSServiceErr_NoError on succeses (any subsequent, asynchronous + * errors are delivered to the callback), otherwise returns an error code indicating + * the error that occurred (the callback is never invoked and the DNSServiceRef + * is not initialized.) + */ + +DNSServiceErrorType DNSSD_API DNSServiceQueryRecord + ( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *fullname, + uint16_t rrtype, + uint16_t rrclass, + DNSServiceQueryRecordReply callBack, + void *context /* may be NULL */ + ); + + +/* DNSServiceReconfirmRecord + * + * Instruct the daemon to verify the validity of a resource record that appears to + * be out of date (e.g. because tcp connection to a service's target failed.) + * Causes the record to be flushed from the daemon's cache (as well as all other + * daemons' caches on the network) if the record is determined to be invalid. + * + * Parameters: + * + * flags: Currently unused, reserved for future use. + * + * interfaceIndex: If non-zero, specifies the interface of the record in question. + * Passing 0 causes all instances of this record to be reconfirmed. + * + * fullname: The resource record's full domain name. + * + * rrtype: The resource record's type (e.g. kDNSServiceType_PTR, kDNSServiceType_SRV, etc) + * + * rrclass: The class of the resource record (usually kDNSServiceClass_IN). + * + * rdlen: The length, in bytes, of the resource record rdata. + * + * rdata: The raw rdata of the resource record. + * + */ + +DNSServiceErrorType DNSSD_API DNSServiceReconfirmRecord + ( + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *fullname, + uint16_t rrtype, + uint16_t rrclass, + uint16_t rdlen, + const void *rdata + ); + + +/********************************************************************************************* + * + * General Utility Functions + * + *********************************************************************************************/ + +/* DNSServiceConstructFullName() + * + * Concatenate a three-part domain name (as returned by the above callbacks) into a + * properly-escaped full domain name. Note that callbacks in the above functions ALREADY ESCAPE + * strings where necessary. + * + * Parameters: + * + * fullName: A pointer to a buffer that where the resulting full domain name is to be written. + * The buffer must be kDNSServiceMaxDomainName (1005) bytes in length to + * accommodate the longest legal domain name without buffer overrun. + * + * service: The service name - any dots or backslashes must NOT be escaped. + * May be NULL (to construct a PTR record name, e.g. + * "_ftp._tcp.apple.com."). + * + * regtype: The service type followed by the protocol, separated by a dot + * (e.g. "_ftp._tcp"). + * + * domain: The domain name, e.g. "apple.com.". Literal dots or backslashes, + * if any, must be escaped, e.g. "1st\. Floor.apple.com." + * + * return value: Returns 0 on success, -1 on error. + * + */ + +int DNSSD_API DNSServiceConstructFullName + ( + char *fullName, + const char *service, /* may be NULL */ + const char *regtype, + const char *domain + ); + + +/********************************************************************************************* + * + * TXT Record Construction Functions + * + *********************************************************************************************/ + +/* + * A typical calling sequence for TXT record construction is something like: + * + * Client allocates storage for TXTRecord data (e.g. declare buffer on the stack) + * TXTRecordCreate(); + * TXTRecordSetValue(); + * TXTRecordSetValue(); + * TXTRecordSetValue(); + * ... + * DNSServiceRegister( ... TXTRecordGetLength(), TXTRecordGetBytesPtr() ... ); + * TXTRecordDeallocate(); + * Explicitly deallocate storage for TXTRecord data (if not allocated on the stack) + */ + + +/* TXTRecordRef + * + * Opaque internal data type. + * Note: Represents a DNS-SD TXT record. + */ + +typedef union _TXTRecordRef_t { char PrivateData[16]; char *ForceNaturalAlignment; } TXTRecordRef; + + +/* TXTRecordCreate() + * + * Creates a new empty TXTRecordRef referencing the specified storage. + * + * If the buffer parameter is NULL, or the specified storage size is not + * large enough to hold a key subsequently added using TXTRecordSetValue(), + * then additional memory will be added as needed using malloc(). + * + * On some platforms, when memory is low, malloc() may fail. In this + * case, TXTRecordSetValue() will return kDNSServiceErr_NoMemory, and this + * error condition will need to be handled as appropriate by the caller. + * + * You can avoid the need to handle this error condition if you ensure + * that the storage you initially provide is large enough to hold all + * the key/value pairs that are to be added to the record. + * The caller can precompute the exact length required for all of the + * key/value pairs to be added, or simply provide a fixed-sized buffer + * known in advance to be large enough. + * A no-value (key-only) key requires (1 + key length) bytes. + * A key with empty value requires (1 + key length + 1) bytes. + * A key with non-empty value requires (1 + key length + 1 + value length). + * For most applications, DNS-SD TXT records are generally + * less than 100 bytes, so in most cases a simple fixed-sized + * 256-byte buffer will be more than sufficient. + * Recommended size limits for DNS-SD TXT Records are discussed in + * + * + * Note: When passing parameters to and from these TXT record APIs, + * the key name does not include the '=' character. The '=' character + * is the separator between the key and value in the on-the-wire + * packet format; it is not part of either the key or the value. + * + * txtRecord: A pointer to an uninitialized TXTRecordRef. + * + * bufferLen: The size of the storage provided in the "buffer" parameter. + * + * buffer: Optional caller-supplied storage used to hold the TXTRecord data. + * This storage must remain valid for as long as + * the TXTRecordRef. + */ + +void DNSSD_API TXTRecordCreate + ( + TXTRecordRef *txtRecord, + uint16_t bufferLen, + void *buffer + ); + + +/* TXTRecordDeallocate() + * + * Releases any resources allocated in the course of preparing a TXT Record + * using TXTRecordCreate()/TXTRecordSetValue()/TXTRecordRemoveValue(). + * Ownership of the buffer provided in TXTRecordCreate() returns to the client. + * + * txtRecord: A TXTRecordRef initialized by calling TXTRecordCreate(). + * + */ + +void DNSSD_API TXTRecordDeallocate + ( + TXTRecordRef *txtRecord + ); + + +/* TXTRecordSetValue() + * + * Adds a key (optionally with value) to a TXTRecordRef. If the "key" already + * exists in the TXTRecordRef, then the current value will be replaced with + * the new value. + * Keys may exist in four states with respect to a given TXT record: + * - Absent (key does not appear at all) + * - Present with no value ("key" appears alone) + * - Present with empty value ("key=" appears in TXT record) + * - Present with non-empty value ("key=value" appears in TXT record) + * For more details refer to "Data Syntax for DNS-SD TXT Records" in + * + * + * txtRecord: A TXTRecordRef initialized by calling TXTRecordCreate(). + * + * key: A null-terminated string which only contains printable ASCII + * values (0x20-0x7E), excluding '=' (0x3D). Keys should be + * 8 characters or less (not counting the terminating null). + * + * valueSize: The size of the value. + * + * value: Any binary value. For values that represent + * textual data, UTF-8 is STRONGLY recommended. + * For values that represent textual data, valueSize + * should NOT include the terminating null (if any) + * at the end of the string. + * If NULL, then "key" will be added with no value. + * If non-NULL but valueSize is zero, then "key=" will be + * added with empty value. + * + * return value: Returns kDNSServiceErr_NoError on success. + * Returns kDNSServiceErr_Invalid if the "key" string contains + * illegal characters. + * Returns kDNSServiceErr_NoMemory if adding this key would + * exceed the available storage. + */ + +DNSServiceErrorType DNSSD_API TXTRecordSetValue + ( + TXTRecordRef *txtRecord, + const char *key, + uint8_t valueSize, /* may be zero */ + const void *value /* may be NULL */ + ); + + +/* TXTRecordRemoveValue() + * + * Removes a key from a TXTRecordRef. The "key" must be an + * ASCII string which exists in the TXTRecordRef. + * + * txtRecord: A TXTRecordRef initialized by calling TXTRecordCreate(). + * + * key: A key name which exists in the TXTRecordRef. + * + * return value: Returns kDNSServiceErr_NoError on success. + * Returns kDNSServiceErr_NoSuchKey if the "key" does not + * exist in the TXTRecordRef. + */ + +DNSServiceErrorType DNSSD_API TXTRecordRemoveValue + ( + TXTRecordRef *txtRecord, + const char *key + ); + + +/* TXTRecordGetLength() + * + * Allows you to determine the length of the raw bytes within a TXTRecordRef. + * + * txtRecord: A TXTRecordRef initialized by calling TXTRecordCreate(). + * + * return value: Returns the size of the raw bytes inside a TXTRecordRef + * which you can pass directly to DNSServiceRegister() or + * to DNSServiceUpdateRecord(). + * Returns 0 if the TXTRecordRef is empty. + */ + +uint16_t DNSSD_API TXTRecordGetLength + ( + const TXTRecordRef *txtRecord + ); + + +/* TXTRecordGetBytesPtr() + * + * Allows you to retrieve a pointer to the raw bytes within a TXTRecordRef. + * + * txtRecord: A TXTRecordRef initialized by calling TXTRecordCreate(). + * + * return value: Returns a pointer to the raw bytes inside the TXTRecordRef + * which you can pass directly to DNSServiceRegister() or + * to DNSServiceUpdateRecord(). + */ + +const void * DNSSD_API TXTRecordGetBytesPtr + ( + const TXTRecordRef *txtRecord + ); + + +/********************************************************************************************* + * + * TXT Record Parsing Functions + * + *********************************************************************************************/ + +/* + * A typical calling sequence for TXT record parsing is something like: + * + * Receive TXT record data in DNSServiceResolve() callback + * if (TXTRecordContainsKey(txtLen, txtRecord, "key")) then do something + * val1ptr = TXTRecordGetValuePtr(txtLen, txtRecord, "key1", &len1); + * val2ptr = TXTRecordGetValuePtr(txtLen, txtRecord, "key2", &len2); + * ... + * bcopy(val1ptr, myval1, len1); + * bcopy(val2ptr, myval2, len2); + * ... + * return; + * + * If you wish to retain the values after return from the DNSServiceResolve() + * callback, then you need to copy the data to your own storage using bcopy() + * or similar, as shown in the example above. + * + * If for some reason you need to parse a TXT record you built yourself + * using the TXT record construction functions above, then you can do + * that using TXTRecordGetLength and TXTRecordGetBytesPtr calls: + * TXTRecordGetValue(TXTRecordGetLength(x), TXTRecordGetBytesPtr(x), key, &len); + * + * Most applications only fetch keys they know about from a TXT record and + * ignore the rest. + * However, some debugging tools wish to fetch and display all keys. + * To do that, use the TXTRecordGetCount() and TXTRecordGetItemAtIndex() calls. + */ + +/* TXTRecordContainsKey() + * + * Allows you to determine if a given TXT Record contains a specified key. + * + * txtLen: The size of the received TXT Record. + * + * txtRecord: Pointer to the received TXT Record bytes. + * + * key: A null-terminated ASCII string containing the key name. + * + * return value: Returns 1 if the TXT Record contains the specified key. + * Otherwise, it returns 0. + */ + +int DNSSD_API TXTRecordContainsKey + ( + uint16_t txtLen, + const void *txtRecord, + const char *key + ); + + +/* TXTRecordGetValuePtr() + * + * Allows you to retrieve the value for a given key from a TXT Record. + * + * txtLen: The size of the received TXT Record + * + * txtRecord: Pointer to the received TXT Record bytes. + * + * key: A null-terminated ASCII string containing the key name. + * + * valueLen: On output, will be set to the size of the "value" data. + * + * return value: Returns NULL if the key does not exist in this TXT record, + * or exists with no value (to differentiate between + * these two cases use TXTRecordContainsKey()). + * Returns pointer to location within TXT Record bytes + * if the key exists with empty or non-empty value. + * For empty value, valueLen will be zero. + * For non-empty value, valueLen will be length of value data. + */ + +const void * DNSSD_API TXTRecordGetValuePtr + ( + uint16_t txtLen, + const void *txtRecord, + const char *key, + uint8_t *valueLen + ); + + +/* TXTRecordGetCount() + * + * Returns the number of keys stored in the TXT Record. The count + * can be used with TXTRecordGetItemAtIndex() to iterate through the keys. + * + * txtLen: The size of the received TXT Record. + * + * txtRecord: Pointer to the received TXT Record bytes. + * + * return value: Returns the total number of keys in the TXT Record. + * + */ + +uint16_t DNSSD_API TXTRecordGetCount + ( + uint16_t txtLen, + const void *txtRecord + ); + + +/* TXTRecordGetItemAtIndex() + * + * Allows you to retrieve a key name and value pointer, given an index into + * a TXT Record. Legal index values range from zero to TXTRecordGetCount()-1. + * It's also possible to iterate through keys in a TXT record by simply + * calling TXTRecordGetItemAtIndex() repeatedly, beginning with index zero + * and increasing until TXTRecordGetItemAtIndex() returns kDNSServiceErr_Invalid. + * + * On return: + * For keys with no value, *value is set to NULL and *valueLen is zero. + * For keys with empty value, *value is non-NULL and *valueLen is zero. + * For keys with non-empty value, *value is non-NULL and *valueLen is non-zero. + * + * txtLen: The size of the received TXT Record. + * + * txtRecord: Pointer to the received TXT Record bytes. + * + * index: An index into the TXT Record. + * + * keyBufLen: The size of the string buffer being supplied. + * + * key: A string buffer used to store the key name. + * On return, the buffer contains a null-terminated C string + * giving the key name. DNS-SD TXT keys are usually + * 8 characters or less. To hold the maximum possible + * key name, the buffer should be 256 bytes long. + * + * valueLen: On output, will be set to the size of the "value" data. + * + * value: On output, *value is set to point to location within TXT + * Record bytes that holds the value data. + * + * return value: Returns kDNSServiceErr_NoError on success. + * Returns kDNSServiceErr_NoMemory if keyBufLen is too short. + * Returns kDNSServiceErr_Invalid if index is greater than + * TXTRecordGetCount()-1. + */ + +DNSServiceErrorType DNSSD_API TXTRecordGetItemAtIndex + ( + uint16_t txtLen, + const void *txtRecord, + uint16_t index, + uint16_t keyBufLen, + char *key, + uint8_t *valueLen, + const void **value + ); + +#ifdef __APPLE_API_PRIVATE + +/* + * Mac OS X specific functionality + * 3rd party clients of this API should not depend on future support or availability of this routine + */ + +/* DNSServiceSetDefaultDomainForUser() + * + * Set the default domain for the caller's UID. Future browse and registration + * calls by this user that do not specify an explicit domain will browse and + * register in this wide-area domain in addition to .local. In addition, this + * domain will be returned as a Browse domain via domain enumeration calls. + * + * + * Parameters: + * + * flags: Pass kDNSServiceFlagsAdd to add a domain for a user. Call without + * this flag set to clear a previously added domain. + * + * domain: The domain to be used for the caller's UID. + * + * return value: Returns kDNSServiceErr_NoError on succeses, otherwise returns + * an error code indicating the error that occurred + */ + +DNSServiceErrorType DNSSD_API DNSServiceSetDefaultDomainForUser + ( + DNSServiceFlags flags, + const char *domain + ); + +#endif //__APPLE_API_PRIVATE + +// Some C compiler cleverness. We can make the compiler check certain things for us, +// and report errors at compile-time if anything is wrong. The usual way to do this would +// be to use a run-time "if" statement or the conventional run-time "assert" mechanism, but +// then you don't find out what's wrong until you run the software. This way, if the assertion +// condition is false, the array size is negative, and the complier complains immediately. + +struct DNS_SD_CompileTimeAssertionChecks + { + char assert0[(sizeof(union _TXTRecordRef_t) == 16) ? 1 : -1]; + }; + +#ifdef __cplusplus + } +#endif + +#endif /* _DNS_SD_H */ diff --git a/pkg/vere/foil.c b/pkg/vere/foil.c index 26ff8b3d04..9fae86973f 100644 --- a/pkg/vere/foil.c +++ b/pkg/vere/foil.c @@ -106,13 +106,17 @@ u3_foil_folder(const c3_c* pax_c) dir_u = u3_dire_init(pax_c); } - /* create entries for all files + /* create entries for all files and directories */ while ( UV_EOF != uv_fs_scandir_next(&ruq_u, &den_u) ) { if ( UV_DIRENT_FILE == den_u.type ) { u3_dent* det_u = u3_dent_init(den_u.name); det_u->nex_u = dir_u->all_u; dir_u->all_u = det_u; + } else if ( UV_DIRENT_DIR == den_u.type ) { + u3_dent* det_u = u3_dent_init(den_u.name); + det_u->nex_u = dir_u->dil_u; + dir_u->dil_u = det_u; } } diff --git a/pkg/vere/io/ames.c b/pkg/vere/io/ames.c index 24baa6e042..9561d6d208 100644 --- a/pkg/vere/io/ames.c +++ b/pkg/vere/io/ames.c @@ -1,10 +1,17 @@ /// @file #include "vere.h" +#include "mdns.h" #include "noun.h" #include "ur.h" +#include "zlib.h" + +#include + +#include + #define FINE_PAGE 4096 // packets per page #define FINE_FRAG 1024 // bytes per fragment packet #define FINE_PATH_MAX 384 // longest allowed scry path @@ -17,6 +24,12 @@ #define QUEUE_MAX 30 // max number of packets in queue +typedef enum u3_stun_state { + STUN_OFF = 0, + STUN_TRYING = 1, + STUN_KEEPALIVE = 2, +} u3_stun_state; + /* u3_fine: fine networking */ typedef struct _u3_fine { @@ -45,6 +58,20 @@ c3_w imp_w[256]; // imperial IPs time_t imp_t[256]; // imperial IP timestamps c3_o imp_o[256]; // imperial print status + struct { // stun client state: + u3_stun_state sat_y; // formal state + c3_y tid_y[12]; // last transaction id + c3_y dad_y; // sponsoring galaxy + u3_lane lan_u; // sponsoring galaxy IP and port + uv_timer_t tim_u; // keepalive timer handle + uv_timer_t dns_u; // DNS resolution timer handle + c3_c* dns_c; // sponsoring galaxy fqdn + struct timeval las_u; // XX last sent date (not used?) + struct timeval sar_u; // date we started trying to send + u3_lane sef_u; // our lane, if we know it + c3_o wok_o; // STUN worked, set on first success + } sun_u; // + c3_o nal_o; // lane cache backcompat flag struct { // config: c3_o net_o; // can send c3_o see_o; // can scry @@ -112,7 +139,7 @@ typedef struct _u3_meow { c3_y sig_y[64]; // host signature c3_w num_w; // number of fragments - c3_w siz_s; // datum size (actual) + c3_s siz_s; // datum size (actual) c3_y* dat_y; // datum (0 if null response) } u3_meow; @@ -358,12 +385,25 @@ _fine_peep_size(u3_peep* pep_u) pep_u->len_s); } +static inline c3_y +_fine_bytes_word(c3_w num_w) +{ + return (c3_bits_word(num_w) + 7) >> 3; +} + static inline c3_s _fine_meow_size(u3_meow* mew_u) { + c3_y cur_y = 0; + if (mew_u->siz_s != 0) { + cur_y = sizeof(mew_u->num_w); + } + else { + cur_y = _fine_bytes_word(mew_u->num_w); + } return ( sizeof(mew_u->sig_y) + - sizeof(mew_u->num_w) + + cur_y + mew_u->siz_s); } @@ -603,7 +643,9 @@ _fine_sift_meow(u3_meow* mew_u, u3_noun mew) // parse number of fragments // u3r_bytes(cur_w, num_w, (c3_y*)&mew_u->num_w, mew); + num_w = c3_min(num_w, (len_w - cur_w)); cur_w += num_w; + u3_assert(len_w >= cur_w); // parse data payload // @@ -735,10 +777,22 @@ _fine_etch_meow(u3_meow* mew_u, c3_y* buf_y) memcpy(buf_y + cur_w, mew_u->sig_y, sig_w); cur_w += sig_w; - // write number of fragments - // - _ames_etch_word(buf_y + cur_w, mew_u->num_w); - cur_w += sizeof(mew_u->num_w); + { + c3_y num_y[4]; + c3_y len_y = _fine_bytes_word(mew_u->num_w); + + // write number of fragments + // + _ames_etch_word(num_y, mew_u->num_w); + memcpy(buf_y + cur_w, num_y, len_y); + + if (mew_u->siz_s != 0) { + cur_w += sizeof(mew_u->num_w); + } + else { + cur_w += len_y; + } + } // write response fragment data // @@ -920,13 +974,22 @@ _ames_lane_into_cache(u3p(u3h_root) lax_p, u3_noun who, u3_noun las) { u3z(who); } -/* _ames_lane_from_cache(): retrieve lane for who from cache, if any & fresh +/* _ames_lane_from_cache(): retrieve lane for who from cache, if any */ static u3_weak -_ames_lane_from_cache(u3p(u3h_root) lax_p, u3_noun who) { +_ames_lane_from_cache(u3p(u3h_root) lax_p, u3_noun who, c3_o nal_o) { u3_weak lac = u3h_git(lax_p, who); - if ( u3_none != lac ) { + if ( u3_none == lac ) { + u3z(who); + return lac; + } + + if ( nal_o == c3y ) { + lac = u3k(u3h(lac)); + } + + else { struct timeval tim_tv; gettimeofday(&tim_tv, 0); u3_noun now = u3_time_in_tv(&tim_tv); @@ -965,16 +1028,31 @@ _ames_czar_port(c3_y imp_y) } } +static c3_c* +_ames_czar_dns(c3_y imp_y, c3_c* czar_c) +{ + u3_noun nam = u3dc("scot", 'p', imp_y); + c3_c* nam_c = u3r_string(nam); + c3_w len_w = 3 + strlen(nam_c) + strlen(czar_c); + u3_assert(len_w <= 256); + c3_c* dns_c = c3_malloc(len_w); + + c3_i sas_i = snprintf(dns_c, len_w, "%s.%s.", nam_c + 1, czar_c); + u3_assert(sas_i <= 255); + + c3_free(nam_c); + u3z(nam); + + return dns_c; +} + /* _ames_czar_gone(): galaxy address resolution failed. */ static void -_ames_czar_gone(u3_pact* pac_u, time_t now) +_ames_czar_gone(u3_ames* sam_u, time_t now, c3_d imp_y, c3_c* dns_c) { - u3_ames* sam_u = pac_u->sam_u; - c3_d imp_y = pac_u->rut_u.imp_y; - if ( c3y == sam_u->imp_o[imp_y] ) { - u3l_log("ames: czar at %s: not found (b)", pac_u->rut_u.dns_c); + u3l_log("ames: czar at %s: not found (b)", dns_c); sam_u->imp_o[imp_y] = c3n; } @@ -987,8 +1065,6 @@ _ames_czar_gone(u3_pact* pac_u, time_t now) // keep existing ip for 5 more minutes // sam_u->imp_t[imp_y] = now; - - _ames_pact_free(pac_u); } /* _ames_czar_here(): galaxy address resolution succeeded. @@ -1018,6 +1094,33 @@ _ames_czar_here(u3_pact* pac_u, time_t now, struct sockaddr_in* add_u) pac_u->rut_u.lan_u.pip_w = pip_w; } +/* _stun_czar_here(): sponsor galaxy address resolution succeeded. +*/ +static c3_w +_stun_czar_here(u3_ames* sam_u, time_t now, struct sockaddr_in* add_u) +{ + c3_y imp_y = sam_u->sun_u.dad_y; + c3_w old_w = sam_u->imp_w[imp_y]; + c3_w pip_w = ntohl(add_u->sin_addr.s_addr); + + if ( pip_w != old_w ) { + u3_noun nam = u3dc("scot", c3__if, u3i_word(pip_w)); + c3_c* nam_c = u3r_string(nam); + + u3l_log("stun: czar %s: ip %s", sam_u->dns_c, nam_c); + + c3_free(nam_c); + u3z(nam); + } + sam_u->sun_u.lan_u.pip_w = pip_w; + + sam_u->imp_w[imp_y] = pip_w; + sam_u->imp_t[imp_y] = now; + sam_u->imp_o[imp_y] = c3y; + + return pip_w; +} + /* _ames_czar_cb(): galaxy address resolution callback. */ static void @@ -1026,22 +1129,17 @@ _ames_czar_cb(uv_getaddrinfo_t* adr_u, struct addrinfo* aif_u) { { - u3_pact* pac_u = (u3_pact*)adr_u->data; - struct addrinfo* rai_u = aif_u; - time_t now = time(0); + u3_pact* pac_u = (u3_pact*)adr_u->data; + struct addrinfo* rai_u = aif_u; + time_t now = time(0); - while ( rai_u ) { - if ( (AF_INET == rai_u->ai_family) ) { - _ames_czar_here(pac_u, now, (struct sockaddr_in *)rai_u->ai_addr); - break; - } - else { - rai_u = rai_u->ai_next; - } - } - - if ( !rai_u ) { - _ames_czar_gone(pac_u, now); + if ( sas_i == 0 ) { + _ames_czar_here(pac_u, now, (struct sockaddr_in *)rai_u->ai_addr); + } else { + _ames_czar_gone(pac_u->sam_u, now, + pac_u->rut_u.imp_y, + pac_u->rut_u.dns_c); + _ames_pact_free(pac_u); } } @@ -1071,7 +1169,7 @@ _ames_czar(u3_pact* pac_u) if ( !sam_u->dns_c ) { u3_noun nam = u3dc("scot", 'p', pac_u->rut_u.imp_y); c3_c* nam_c = u3r_string(nam); - u3l_log("ames: no galaxy domain for %s, no-op\r", nam_c); + u3l_log("ames: no galaxy domain for %s, no-op", nam_c); c3_free(nam_c); u3z(nam); @@ -1100,40 +1198,26 @@ _ames_czar(u3_pact* pac_u) return; } else { - c3_i sas_i; - - { - u3_noun nam = u3dc("scot", 'p', imp_y); - c3_c* nam_c = u3r_string(nam); - - // NB: . separator not counted, as [nam_c] includes a ~ that we skip - // - pac_u->rut_u.dns_c = - c3_malloc(1 + strlen(nam_c) + strlen(sam_u->dns_c)); - - sas_i = - snprintf(pac_u->rut_u.dns_c, 255, "%s.%s", nam_c + 1, sam_u->dns_c); - - c3_free(nam_c); - u3z(nam); - } - - if ( 255 <= sas_i ) { - u3l_log("ames: czar: galaxy domain %s truncated", sam_u->dns_c); - _ames_pact_free(pac_u); - return; - } + pac_u->rut_u.dns_c = _ames_czar_dns(imp_y, sam_u->dns_c); { uv_getaddrinfo_t* adr_u = c3_malloc(sizeof(*adr_u)); adr_u->data = pac_u; + c3_d imp_y = pac_u->rut_u.imp_y; + c3_c* dns_c = pac_u->rut_u.dns_c; + c3_i sas_i; + + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; // only IPv4 addresses if ( 0 != (sas_i = uv_getaddrinfo(u3L, adr_u, _ames_czar_cb, - pac_u->rut_u.dns_c, 0, 0)) ) + pac_u->rut_u.dns_c, 0, &hints)) ) { u3l_log("ames: %s", uv_strerror(sas_i)); - _ames_czar_gone(pac_u, now); + _ames_czar_gone(pac_u->sam_u, now, imp_y, dns_c); + _ames_pact_free(pac_u); return; } } @@ -1141,7 +1225,7 @@ _ames_czar(u3_pact* pac_u) } } -/* _fine_put_cache(): get packet list or status from cache. RETAIN +/* _fine_get_cache(): get packet list or status from cache. RETAIN */ static u3_weak _fine_get_cache(u3_ames* sam_u, u3_noun pax, c3_w fra_w) @@ -1174,6 +1258,597 @@ _fine_put_cache(u3_ames* sam_u, u3_noun pax, c3_w lop_w, u3_noun lis) } } +static void +_stun_stop(u3_ames* sam_u) +{ + switch ( sam_u->sun_u.sat_y ) { + case STUN_OFF: break; // ignore; already stopped + case STUN_TRYING: + case STUN_KEEPALIVE: { + uv_timer_stop(&sam_u->sun_u.tim_u); + uv_timer_stop(&sam_u->sun_u.dns_u); + } break; + default: u3_assert(!"programmer error"); + } + sam_u->sun_u.sat_y = STUN_OFF; +} + +// XX (code reordering?) forward declarations +static void _stun_send_request(u3_ames*); +static void _stun_on_lost(u3_ames* sam_u); +static void _stun_czar(u3_ames* sam_u, c3_d tim_d); +static void _stun_resolve_dns_cb(uv_timer_t* tim_u); +static void _stun_send_request_cb(uv_udp_send_t *req_u, c3_i sas_i); +static void _stun_on_failure(u3_ames* sam_u); +static void _stun_start(u3_ames* sam_u, c3_o fail); +static c3_y* _stun_add_fingerprint(c3_y *message, c3_w index); +static c3_o _stun_find_xor_mapped_address(c3_y* buf_y, c3_w buf_len, u3_lane* lan_u); + +static c3_d +_stun_time_gap(struct timeval start) +{ + struct timeval tim_tv; + gettimeofday(&tim_tv, 0); + u3_noun now = u3_time_in_tv(&tim_tv); + u3_noun den = u3_time_in_tv(&start); + return u3_time_gap_ms(den, now); +} + +/* _stun_reset(): stun failed. start again using max backoff + */ +static void +_stun_reset(uv_timer_t* tim_u) +{ + u3_ames* sam_u = (u3_ames*)(tim_u->data); + + _stun_start(sam_u, c3y); +} + +static void +_stun_timer_cb(uv_timer_t* tim_u) +{ + c3_w rto = 500; + + u3_ames* sam_u = (u3_ames*)(tim_u->data); + + switch ( sam_u->sun_u.sat_y ) { + case STUN_OFF: { + // ignore; stray timer (although this shouldn't happen) + u3l_log("stun: stray timer STUN_OFF"); + } break; + case STUN_KEEPALIVE: { + sam_u->sun_u.sat_y = STUN_TRYING; + sam_u->sun_u.tim_u.data = sam_u; + gettimeofday(&sam_u->sun_u.sar_u, 0); // set start time to now + uv_timer_start(&sam_u->sun_u.tim_u, _stun_timer_cb, rto, 0); + _stun_send_request(sam_u); + } break; + case STUN_TRYING: { + c3_d gap_d = _stun_time_gap(sam_u->sun_u.sar_u); + c3_d nex_d = (gap_d * 2) + rto - gap_d; + + if ( gap_d >= (39500) ) { + _stun_on_lost(sam_u); + } else if ( gap_d >= (31500) ) { + // wait ~s8 for the last STUN request + uv_timer_start(&sam_u->sun_u.tim_u, _stun_timer_cb, 8000 , 0); + _stun_send_request(sam_u); + } else { + uv_timer_start(&sam_u->sun_u.tim_u, _stun_timer_cb, + (nex_d >= 31500) ? 31500 : nex_d, 0); + _stun_send_request(sam_u); + } + } break; + default: u3_assert(!"programmer error"); + } +} + +typedef struct _u3_stun_send { + uv_udp_send_t req_u; // uv udp request handle + u3_ames* sam_u; // backpointer to driver state + c3_y* hun_y; // buffer + +} u3_stun_send; + +static void +_stun_on_request_fail(u3_ames* sam_u, c3_i sas_i) +{ + u3l_log("stun: send callback fail_async: %s", uv_strerror(sas_i)); + + _stun_on_failure(sam_u); // %kick ping app + + sam_u->sun_u.sat_y = STUN_TRYING; + _stun_timer_cb(&sam_u->sun_u.tim_u); // retry sending the failed request +} + +static void +_stun_send_request_cb(uv_udp_send_t *req_u, c3_i sas_i) +{ + u3_stun_send* snd_u = (u3_stun_send*)req_u; + u3_ames* sam_u = snd_u->sam_u; + + if ( sas_i ) { + _stun_on_request_fail(sam_u, sas_i); + } + else { + // XX curently not used + gettimeofday(&sam_u->sun_u.las_u, 0); // overwrite last sent date + } + c3_free(snd_u->hun_y); + c3_free(snd_u); +} + +static void +_stun_send_response_cb(uv_udp_send_t *rep_u, c3_i sas_i) +{ + u3_stun_send* snd_u = (u3_stun_send*)rep_u; + if ( sas_i != 0 ) { + u3l_log("stun: _stun_send_response_cb fail_sync: %s", uv_strerror(sas_i)); + } + c3_free(snd_u->hun_y); + c3_free(snd_u); +} + +static void _stun_on_request(u3_ames *sam_u, c3_y* buf_r, + const struct sockaddr* adr_u) +{ + struct sockaddr_in* add_u = (struct sockaddr_in*)adr_u; + c3_y *buf_y = c3_calloc(40); + c3_w cookie = 0x2112A442; + + c3_w cur_w = 20; // STUN header is 20 bytes + memcpy(buf_y, buf_r, cur_w); // copy STUN request header + buf_y[0] = 0x01; buf_y[1] = 0x01; // 0x0101 SUCCESS RESPONSE + buf_y[2] = 0x00; buf_y[3] = 0x14; // Length: 20 bytes + + // XOR-MAPPED-ADDRESS + buf_y[cur_w] = 0x00; buf_y[cur_w + 1] = 0x20; // attribute type 0x00020 + buf_y[cur_w + 2] = 0x00; buf_y[cur_w + 3] = 0x08; // STUN attribute length + // extra reserved 0x0 byte + buf_y[cur_w + 5] = 0x01; // family 0x01:IPv4 + + c3_s x_port = htons(ntohs(add_u->sin_port) ^ cookie >> 16); + c3_w x_ip = htonl(ntohl(add_u->sin_addr.s_addr) ^ cookie); + memcpy(buf_y + cur_w + 6, &x_port, 2); // X-Port + memcpy(buf_y + cur_w + 8, &x_ip, 4); // X-IP Addres + + // FINGERPRINT + buf_y = _stun_add_fingerprint(buf_y, cur_w + 12); + + uv_buf_t buf_u = uv_buf_init((c3_c*)buf_y, 40); + u3_stun_send* snd_u = c3_calloc(sizeof(*snd_u)); + + snd_u->sam_u = sam_u; + snd_u->hun_y = buf_y; + c3_i sas_i = uv_udp_send( + (uv_udp_send_t*)snd_u, &sam_u->wax_u, &buf_u, 1, + adr_u, _stun_send_response_cb + ); + + if ( sas_i != 0 ) { + u3l_log("stun: send response fail_sync: %s", uv_strerror(sas_i)); + c3_free(buf_y); + c3_free(snd_u); + } +} + +static void +_stun_on_response(u3_ames* sam_u, c3_y* buf_y, c3_w buf_len) +{ + u3_stun_state old_y = sam_u->sun_u.sat_y; + + u3_lane lan_u; + + // Ignore STUN responses that dont' have the XOR-MAPPED-ADDRESS attribute + if ( c3n == _stun_find_xor_mapped_address(buf_y, buf_len, &lan_u) ) { + return; + } + u3_noun wir = u3nc(c3__ames, u3_nul); + if (sam_u->sun_u.wok_o == c3n) { + // stop %ping app + u3_noun cad = u3nq(c3__stun, c3__stop, sam_u->sun_u.dad_y, + u3nc(c3n, u3_ames_encode_lane(lan_u))); + u3_ovum *ovo_u = u3_ovum_init(0, c3__ames, wir, cad); + u3_auto_plan(&sam_u->car_u, ovo_u); + sam_u->sun_u.wok_o = c3y; + } + else if ( (sam_u->sun_u.sef_u.por_s != lan_u.por_s) || + (sam_u->sun_u.sef_u.pip_w != lan_u.pip_w) ) + { + // lane changed + u3_noun cad = u3nq(c3__stun, c3__once, sam_u->sun_u.dad_y, + u3nc(c3n, u3_ames_encode_lane(lan_u))); + u3_ovum *ovo_u = u3_ovum_init(0, c3__ames, wir, cad); + u3_auto_plan(&sam_u->car_u, ovo_u); + } + else { + u3z(wir); + } + sam_u->sun_u.sef_u = lan_u; + + switch ( sam_u->sun_u.sat_y ) { + case STUN_OFF: break; // ignore; stray response + case STUN_KEEPALIVE: break; // ignore; duplicate response + case STUN_TRYING: { + sam_u->sun_u.sat_y = STUN_KEEPALIVE; + if ( ent_getentropy(sam_u->sun_u.tid_y, 12) ) { + u3l_log("stun: getentropy fail: %s", strerror(errno)); + _stun_on_lost(sam_u); + } + else { + uv_timer_start(&sam_u->sun_u.tim_u, _stun_timer_cb, 25*1000, 0); + } + } break; + default: assert("programmer error"); + } +} + +static void +_stun_on_failure(u3_ames* sam_u) +{ + // only inject event into arvo to %kick ping app on first failure + if (sam_u->sun_u.wok_o == c3y) { + u3_noun wir = u3nc(c3__ames, u3_nul); + u3_noun cad = u3nq(c3__stun, c3__fail, sam_u->sun_u.dad_y, + u3nc(c3n, u3_ames_encode_lane(sam_u->sun_u.sef_u))); + u3_ovum *ovo_u = u3_ovum_init(0, c3__ames, wir, cad); + u3_auto_plan(&sam_u->car_u, ovo_u); + } + sam_u->sun_u.wok_o = c3n; +} + +static void +_stun_on_lost(u3_ames* sam_u) +{ + _stun_stop(sam_u); + _stun_on_failure(sam_u); + // resolve DNS again, and (re)start STUN + // XX call _stun_start(sam_u, c3y) directly? + uv_timer_start(&sam_u->sun_u.dns_u, _stun_reset, 5*1000, 0); +} + +static void +_stun_send_request(u3_ames* sam_u) +{ + u3_assert( STUN_OFF != sam_u->sun_u.sat_y ); + + struct sockaddr_in add_u; + memset(&add_u, 0, sizeof(add_u)); + add_u.sin_family = AF_INET; + add_u.sin_addr.s_addr = htonl(sam_u->sun_u.lan_u.pip_w); + add_u.sin_port = htons(sam_u->sun_u.lan_u.por_s); + + // see STUN RFC 8489 + // https://datatracker.ietf.org/doc/html/rfc8489#section-5 + c3_y *buf_y = c3_calloc(28); + + // STUN message type: "binding request" + buf_y[1] = 0x01; + + // STUN message length: 8 (header and 32-bit FINGERPRINT) + buf_y[2] = 0x00; buf_y[3] = 0x08; + + + // STUN "magic cookie" 0x2112A442 in network byte order + buf_y[4] = 0x21; buf_y[5] = 0x12; buf_y[6] = 0xa4; buf_y[7] = 0x42; + + // STUN "transaction id" + memcpy(buf_y + 8, sam_u->sun_u.tid_y, 12); + + // FINGERPRINT + buf_y = _stun_add_fingerprint(buf_y, 20); + + uv_buf_t buf_u = uv_buf_init((c3_c*)buf_y, 28); + u3_stun_send* snd_u = c3_calloc(sizeof(*snd_u)); + snd_u->sam_u = sam_u; + snd_u->hun_y = buf_y; + + c3_i sas_i = uv_udp_send( + (uv_udp_send_t*)snd_u, &sam_u->wax_u, &buf_u, 1, + (const struct sockaddr*)&add_u, _stun_send_request_cb + ); + + if ( sas_i != 0) { + _stun_on_request_fail(sam_u, sas_i); + c3_free(buf_y); + c3_free(snd_u); + } +} + +static void +_stun_czar_cb(uv_getaddrinfo_t* adr_u, + c3_i sas_i, + struct addrinfo* aif_u) +{ + { + u3_ames* sam_u = (u3_ames*)(adr_u->data); + struct addrinfo* rai_u = aif_u; + time_t now = time(0); + + gettimeofday(&sam_u->sun_u.sar_u, 0); // set start time to now + + if (sas_i == 0) { + _stun_czar_here(sam_u, now, (struct sockaddr_in *)rai_u->ai_addr); + if (sam_u->sun_u.sat_y == STUN_OFF) { + sam_u->sun_u.sat_y = STUN_TRYING; + _stun_send_request(sam_u); + uv_timer_start(&sam_u->sun_u.tim_u, _stun_timer_cb, 500, 0); + } + // resolve DNS again in five minutes + uv_timer_start(&sam_u->sun_u.dns_u, _stun_resolve_dns_cb, 5*60*1000, 0); + } else { + u3l_log("stun: _stun_czar_cb request fail_sync: %s", uv_strerror(sas_i)); + _ames_czar_gone(sam_u, now, sam_u->sun_u.dad_y, sam_u->dns_c); + _stun_on_lost(sam_u); + } + } + c3_free(adr_u); + uv_freeaddrinfo(aif_u); +} + +static void +_stun_czar(u3_ames* sam_u, c3_d tim_d) +{ + c3_d imp_y = sam_u->sun_u.dad_y; + sam_u->sun_u.lan_u.por_s = _ames_czar_port(imp_y); + + // Enable STUN using -L + // XX maybe enabled with a flag, for development? + if (c3n == u3_Host.ops_u.net) { + sam_u->sun_u.lan_u.pip_w = 0x7f000001; + sam_u->sun_u.sat_y = STUN_TRYING; + _stun_send_request(sam_u); + + gettimeofday(&sam_u->sun_u.sar_u, 0); + uv_timer_start(&sam_u->sun_u.tim_u, _stun_timer_cb, tim_d, 0); + + return; + } + + + // if we don't have a galaxy domain, no-op + // + if (!sam_u->dns_c) { + u3_noun nam = u3dc("scot", 'p', imp_y); + c3_c *nam_c = u3r_string(nam); + u3l_log("ames: no galaxy domain for %s, no-op", nam_c); + + c3_free(nam_c); + u3z(nam); + return; + } + + { + c3_w pip_w = sam_u->imp_w[imp_y]; + time_t wen = sam_u->imp_t[imp_y]; + time_t now = time(0); + + // XX keep same as ames? + // backoff for 5 minutes after failed lookup + // + if ((now < wen) // time shenanigans! + || ((0xffffffff == pip_w) // sentinal ip address + && ((now - wen) < 300))) { + return; + } + // cached addresses have a 5 minute TTL + // + else if ((0 != pip_w) && ((now - wen) < 300)) { + sam_u->sun_u.sat_y = STUN_TRYING; + sam_u->sun_u.lan_u.pip_w = pip_w; + + _stun_send_request(sam_u); + + gettimeofday(&sam_u->sun_u.sar_u, 0); + uv_timer_start(&sam_u->sun_u.tim_u, _stun_timer_cb, tim_d, 0); + return; + } else { + // call callback right away first time we resolve the sponsor's DNS + sam_u->sun_u.dns_u.data = sam_u; + uv_timer_start(&sam_u->sun_u.dns_u, _stun_resolve_dns_cb, tim_d, 0); + } + } +} + +static void +_stun_start(u3_ames* sam_u, c3_o fail) +{ + if ( ent_getentropy(sam_u->sun_u.tid_y, 12) ) { + u3l_log("stun: getentropy fail: %s", strerror(errno)); + _stun_on_lost(sam_u); + } else { + _stun_czar(sam_u, (fail == c3n) ? 500 : 39500); + } +} + +static void +_stun_resolve_dns_cb(uv_timer_t* tim_u) +{ + u3_ames* sam_u = (u3_ames*)(tim_u->data); + c3_i sas_i; + + c3_y imp_y = sam_u->sun_u.dad_y; + sam_u->sun_u.lan_u.por_s = _ames_czar_port(imp_y); + + if ( !sam_u->sun_u.dns_c ) { + sam_u->sun_u.dns_c = _ames_czar_dns(imp_y, sam_u->dns_c); + } + + { + uv_getaddrinfo_t* adr_u = c3_malloc(sizeof(*adr_u)); + adr_u->data = sam_u; + + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; // only IPv4 addresses + + if (0 != (sas_i = uv_getaddrinfo(u3L, adr_u, _stun_czar_cb, + sam_u->sun_u.dns_c, 0, &hints))) + { + u3l_log("stun: uv_getaddrinfo failed %s %s", uv_strerror(sas_i), sam_u->sun_u.dns_c); + _ames_czar_gone(sam_u, time(0), sam_u->sun_u.dad_y, sam_u->dns_c); + _stun_on_lost(sam_u); + return; + } + } +} + +static c3_o +_stun_find_xor_mapped_address(c3_y* buf_y, c3_w buf_len, u3_lane* lan_u) +{ + c3_y xor_y[4] = {0x00, 0x20, 0x00, 0x08}; + c3_w cookie = 0x2112A442; + + if (buf_len < 40) { // At least STUN header, XOR-MAPPED-ADDRESS & FINGERPRINT + return c3n; + } + + c3_w i = 20; // start after header + + c3_y* fin_y = memmem(buf_y + i, buf_len - i, xor_y, sizeof(xor_y)); + if ( fin_y != 0 ) { + c3_w cur = (c3_w)(fin_y - buf_y) + sizeof(xor_y); + + if ( (buf_y[cur] != 0x0) && (buf_y[cur+1] != 0x1) ) { + return c3n; + } + + cur += 2; + + c3_s port = htons(_ames_sift_short(buf_y + cur)) ^ cookie >> 16; + c3_w ip = ntohl(htonl(_ames_sift_word(buf_y + cur + 2)) ^ cookie); + + lan_u->por_s = ntohs(port); + lan_u->pip_w = ip; + + if ( u3C.wag_w & u3o_verbose ) { + c3_c ip_str[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &ip, ip_str, INET_ADDRSTRLEN); + u3l_log("stun: hear ip:port %s:%u", ip_str, port); + } + return c3y; + } + return c3n; +} + +static c3_o +_stun_has_fingerprint(c3_y* buf_y, c3_w buf_len) +{ + c3_y ned_y[4] = {0x80, 0x28, 0x00, 0x04}; + if ( buf_len < 28 ) { // At least STUN header and FINGERPRINT + return c3n; + } + + { + c3_y* fin_y = 0; + c3_w i = 20; // start after the header + + fin_y = memmem(buf_y + i, buf_len - i, ned_y, sizeof(ned_y)); + if ( fin_y != 0 ) { + c3_w len_w = fin_y - buf_y; + // Skip attribute type and length + c3_w fingerprint = _ames_sift_word(fin_y + sizeof(ned_y)); + c3_w init = crc32(0L, Z_NULL, 0); + c3_w crc = htonl(crc32(init, buf_y, len_w) ^ 0x5354554e); + if ((fingerprint == crc) && (fin_y - buf_y + 8) == buf_len) { + return c3y; + } + } + + return c3n; + } +} + +static c3_y* +_stun_add_fingerprint(c3_y *message, c3_w index) +{ + // Compute FINGERPRINT value as CRC-32 of the STUN message + // up to (but excluding) the FINGERPRINT attribute itself, + // XOR'ed with the 32-bit value 0x5354554e + c3_w init = crc32(0L, Z_NULL, 0); + c3_w crc = htonl(crc32(init, message, index) ^ 0x5354554e); + + // STUN attribute type: "FINGERPRINT" + message[index] = 0x80; message[index + 1] = 0x28; + // STUN attribute length: 4 bytes + message[index + 2] = 0x00; message[index + 3] = 0x04; + + memcpy(message + index + 4, &crc, 4); + + return message; +} + +static c3_o +_stun_is_our_response(c3_y* buf_y, c3_y tid_y[12], c3_w buf_len) +{ + c3_w cookie = htonl(0x2112A442); + + // Expects at least: + // STUN header, 12 byte XOR-MAPPED-ADDRESS and 8 byte FINGERPRINT + if ( (buf_len == 40) && + (buf_y[0] == 0x01 && buf_y[1] == 0x01) && + (memcmp(&cookie, buf_y + 4, 4) == 0) && + (memcmp(tid_y, buf_y + 8, 12) == 0) && + (c3y == _stun_has_fingerprint(buf_y, buf_len)) ) + { + return c3y; + } + return c3n; +} + +static c3_o +_stun_is_request(c3_y* buf_y, c3_w buf_len) +{ + c3_w cookie = htonl(0x2112A442); + + // Expects at least: + // STUN header and 8 byte FINGERPRINT + if ( (buf_len >= 28) && + (buf_y[0] == 0x0 && buf_y[1] == 0x01) && + (memcmp(&cookie, buf_y + 4, 4) == 0) && + (c3y == _stun_has_fingerprint(buf_y, buf_len)) ) + { + return c3y; + } + return c3n; +} + +static c3_o +_ames_is_czar(u3_noun who) +{ + u3_noun rac = u3do("clan:title", u3k(who)); + c3_o zar = ( c3y == (c3__czar == rac) ); + u3z(rac); + return zar; +} + +/* _ames_ef_saxo(): handle sponsorship chain notification +*/ +static void +_ames_ef_saxo(u3_ames* sam_u, u3_noun zad) +{ + u3_noun daz, dad; + + daz = u3qb_flop(zad); + if ( u3_nul == daz ) { + u3l_log("ames: empty sponsorship chain"); + u3z(zad); u3z(daz); + return; + } + + dad = u3h(daz); + u3_noun our = u3i_chubs(2, sam_u->pir_u->who_d); + + if ( c3y == _ames_is_czar(dad) && c3n == _ames_is_czar(our)) { + // if we are a galaxy, don't STUN + sam_u->sun_u.dad_y = u3r_byte(0, dad); + sam_u->sun_u.wok_o = c3n; + _stun_stop(sam_u); + _stun_start(sam_u, c3n); + } + + u3z(zad); u3z(daz); u3z(our); +} /* _ames_ef_send(): send packet to network (v4). */ @@ -1477,7 +2152,7 @@ _ames_try_send(u3_pact* pac_u, c3_o for_o) // else { u3_noun key = u3i_chubs(2, pac_u->pre_u.rec_d); - lac = _ames_lane_from_cache(sam_u->lax_p, key); + lac = _ames_lane_from_cache(sam_u->lax_p, key, sam_u->nal_o); } // if we know there's no lane, drop the packet @@ -1526,6 +2201,7 @@ _ames_try_send(u3_pact* pac_u, c3_o for_o) // if forwarding, enqueue the packet and scry for the lane // + u3_noun gang = u3nc(u3_nul, u3_nul); if ( c3y == for_o ) { if ( 0 != sam_u->pan_u ) { pan_u->nex_u = sam_u->pan_u; @@ -1534,13 +2210,13 @@ _ames_try_send(u3_pact* pac_u, c3_o for_o) sam_u->pan_u = pan_u; sam_u->sat_u.foq_d++; - u3_pier_peek_last(sam_u->pir_u, u3_nul, c3__ax, + u3_pier_peek_last(sam_u->pir_u, gang, c3__ax, u3_nul, pax, pan_u, _ames_lane_scry_forward_cb); } // otherwise, just scry for the lane // else { - u3_pier_peek_last(sam_u->pir_u, u3_nul, c3__ax, + u3_pier_peek_last(sam_u->pir_u, gang, c3__ax, u3_nul, pax, pan_u, _ames_lane_scry_cb); } } @@ -1659,7 +2335,7 @@ _fine_hunk_scry_cb(void* vod_p, u3_noun nun) u3z(nun); } -/* _fine_hear_request(): hear wail (fine equeust packet packet). +/* _fine_hear_request(): hear wail (fine request packet packet). */ static void _fine_hear_request(u3_pact* req_u, c3_w cur_w) @@ -2031,6 +2707,8 @@ _ames_recv_cb(uv_udp_t* wax_u, const struct sockaddr* adr_u, unsigned flg_i) { + u3_ames* sam_u = wax_u->data; + if ( 0 > nrd_i ) { if ( u3C.wag_w & u3o_verbose ) { u3l_log("ames: recv: fail: %s", uv_strerror(nrd_i)); @@ -2046,7 +2724,21 @@ _ames_recv_cb(uv_udp_t* wax_u, } c3_free(buf_u->base); } - else { + // XX reorg, check if a STUN req/resp can look like an ames packet + // check the mug hash of the body of the packet, if not check if STUN + // otherwise , invalid packet, log failure + // check ames first, assume that STUN could maybe (not likely) overlap with ames + // for next protocol version, have an urbit cookie + // + else if (_stun_is_request((c3_y*)buf_u->base, nrd_i) == c3y) { + _stun_on_request(sam_u, (c3_y *)buf_u->base, adr_u); + c3_free(buf_u->base); + } + else if (_stun_is_our_response((c3_y*)buf_u->base, sam_u->sun_u.tid_y, nrd_i) + == c3y) { + _stun_on_response(sam_u, (c3_y*)buf_u->base, nrd_i); + c3_free(buf_u->base); + } else { u3_ames* sam_u = wax_u->data; struct sockaddr_in* add_u = (struct sockaddr_in*)adr_u; u3_lane lan_u; @@ -2060,6 +2752,50 @@ _ames_recv_cb(uv_udp_t* wax_u, } } +static void +_mdns_dear_bail(u3_ovum* egg_u, u3_noun lud) +{ + u3z(lud); + u3_ovum_free(egg_u); +} + +/* _ames_put_dear(): send lane to arvo after hearing mdns response +*/ +static void +_ames_put_dear(c3_c* ship, c3_w s_addr, c3_s port, void* context) +{ + u3_ames* sam_u = (u3_ames*)context; + + u3_lane lan; + lan.pip_w = ntohl(s_addr); + lan.por_s = ntohs(port); + + u3_noun whu = u3dc("slaw", c3__p, u3i_string(ship)); + + if (u3_nul == whu) { + u3l_log("ames: strange ship from mdns: %s", ship); + return; + } + + u3_noun our = u3i_chubs(2, sam_u->pir_u->who_d); + if (our == u3t(whu)) { + u3z(whu); + u3z(our); + return; + } + + u3z(our); + + u3_noun wir = u3nc(c3__ames, u3_nul); + u3_noun cad = u3nt(c3__dear, u3k(u3t(whu)), u3nc(c3n, u3_ames_encode_lane(lan))); + + u3_auto_peer( + u3_auto_plan(&sam_u->car_u, + u3_ovum_init(0, c3__a, wir, cad)), + 0, 0, _mdns_dear_bail); + u3z(whu); +} + /* _ames_io_start(): initialize ames I/O. */ static void @@ -2067,10 +2803,11 @@ _ames_io_start(u3_ames* sam_u) { c3_s por_s = sam_u->pir_u->por_s; u3_noun who = u3i_chubs(2, sam_u->pir_u->who_d); - u3_noun rac = u3do("clan:title", u3k(who)); + c3_o zar_o = _ames_is_czar(who); c3_i ret_i; - if ( c3__czar == rac ) { + + if ( c3y == zar_o ) { c3_y num_y = (c3_y)sam_u->pir_u->who_d[0]; c3_s zar_s = _ames_czar_port(num_y); @@ -2100,7 +2837,7 @@ _ames_io_start(u3_ames* sam_u) { u3l_log("ames: bind: %s", uv_strerror(ret_i)); - if ( (c3__czar == rac) && + if ( (c3y == zar_o) && (UV_EADDRINUSE == ret_i) ) { u3l_log(" ...perhaps you've got two copies of vere running?"); @@ -2124,10 +2861,18 @@ _ames_io_start(u3_ames* sam_u) u3l_log("ames: live on %d (localhost only)", sam_u->pir_u->por_s); } + { + u3_noun our = u3dc("scot", 'p', u3k(who)); + char* our_s = u3r_string(our); + u3z(our); + + mdns_init(por_s, our_s, _ames_put_dear, (void *)sam_u); + c3_free(our_s); + } + uv_udp_recv_start(&sam_u->wax_u, _ames_alloc, _ames_recv_cb); sam_u->car_u.liv_o = c3y; - u3z(rac); u3z(who); } @@ -2146,8 +2891,15 @@ _ames_ef_turf(u3_ames* sam_u, u3_noun tuf) u3_mcut_host(sam_u->dns_c, 0, hot); sam_u->dns_c[len_w] = 0; + if ( 250 <= len_w ) { + // 3 char for the galaxy (e.g. zod) and two dots + u3l_log("ames: galaxy domain too big %s len=%u", sam_u->dns_c, len_w); + u3_pier_bail(u3_king_stub()); + } + // XX invalidate sam_u->imp_w &c ? - // + c3_free(sam_u->sun_u.dns_c); + sam_u->sun_u.dns_c = 0; u3z(tuf); } @@ -2175,14 +2927,17 @@ _ames_prot_scry_cb(void* vod_p, u3_noun nun) // assume protocol version 0 // sam_u->ver_y = 0; + sam_u->fin_s.ver_y = 0; } else if ( (c3n == u3a_is_cat(ver)) || (7 < ver) ) { u3m_p("ames: strange protocol", nun); sam_u->ver_y = 0; + sam_u->fin_s.ver_y = 0; } else { sam_u->ver_y = ver; + sam_u->fin_s.ver_y = ver; } // XX revise: filtering should probably be disabled if @@ -2192,31 +2947,6 @@ _ames_prot_scry_cb(void* vod_p, u3_noun nun) u3z(nun); } -/* _fine_prot_scry_cb(): receive fine protocol version -*/ -static void -_fine_prot_scry_cb(void* vod_p, u3_noun nun) -{ - u3_ames* sam_u = vod_p; - u3_weak ver = u3r_at(7, nun); - - if ( u3_none == ver ) { - // assume protocol version 0 - // - sam_u->fin_s.ver_y = 0; - } - else if ( (c3n == u3a_is_cat(ver)) - || (7 < ver) ) { - u3m_p("fine: strange protocol", nun); - sam_u->fin_s.ver_y = 0; - } - else { - sam_u->fin_s.ver_y = ver; - } - - u3z(nun); -} - /* _ames_io_talk(): start receiving ames traffic. */ static void @@ -2238,17 +2968,14 @@ _ames_io_talk(u3_auto* car_u) u3_auto_plan(car_u, u3_ovum_init(0, c3__a, wir, cad)); } - u3_pier_peek_last(car_u->pir_u, u3_nul, c3__fx, u3_nul, - u3nt(u3i_string("protocol"), u3i_string("version"), u3_nul), - sam_u, _fine_prot_scry_cb); - // scry the protocol version out of arvo // // XX this should be re-triggered periodically, // or, better yet, %ames should emit a %turf // (or some other reconfig) effect when it is reset. // - u3_pier_peek_last(car_u->pir_u, u3_nul, c3__ax, u3_nul, + u3_noun gang = u3nc(u3_nul, u3_nul); + u3_pier_peek_last(car_u->pir_u, gang, c3__ax, u3_nul, u3nt(u3i_string("protocol"), u3i_string("version"), u3_nul), sam_u, _ames_prot_scry_cb); } @@ -2276,6 +3003,19 @@ _ames_kick_newt(u3_ames* sam_u, u3_noun tag, u3_noun dat) _ames_ef_turf(sam_u, u3k(dat)); ret_o = c3y; } break; + + case c3__saxo: { + _ames_ef_saxo(sam_u, u3k(dat)); + ret_o = c3y; + } + + case c3__nail: { + u3_noun who = u3k(u3h(dat)); + u3_noun las = u3k(u3t(dat)); + _ames_lane_into_cache(sam_u->lax_p, who, las); + sam_u->nal_o = c3y; + ret_o = c3y; + } break; } u3z(tag); u3z(dat); @@ -2366,6 +3106,8 @@ _ames_io_exit(u3_auto* car_u) { u3_ames* sam_u = (u3_ames*)car_u; uv_close(&sam_u->had_u, _ames_exit_cb); + uv_close((uv_handle_t*)&sam_u->sun_u.dns_u, 0); + uv_close((uv_handle_t*)&sam_u->sun_u.tim_u, 0); } /* _ames_io_info(): produce status info. @@ -2457,10 +3199,17 @@ u3_ames_io_init(u3_pier* pir_u) { u3_ames* sam_u = c3_calloc(sizeof(*sam_u)); sam_u->pir_u = pir_u; + sam_u->nal_o = c3n; sam_u->fig_u.net_o = c3y; sam_u->fig_u.see_o = c3y; sam_u->fig_u.fit_o = c3n; + // initialize STUN timers + uv_timer_init(u3L, &sam_u->sun_u.dns_u); + uv_timer_init(u3L, &sam_u->sun_u.tim_u); + sam_u->sun_u.tim_u.data = sam_u; + sam_u->sun_u.dns_u.data = sam_u; + // enable forwarding on galaxies only u3_noun who = u3i_chubs(2, sam_u->pir_u->who_d); u3_noun rac = u3do("clan:title", who); diff --git a/pkg/vere/io/http.c b/pkg/vere/io/http.c index 06a7cff43c..b5cb1d12cb 100644 --- a/pkg/vere/io/http.c +++ b/pkg/vere/io/http.c @@ -742,7 +742,9 @@ _http_req_cache(u3_hreq* req_u) req_u->peq_u->pax = sac; req_u->sat_e = u3_rsat_peek; - u3_pier_peek_last(htd_u->car_u.pir_u, u3_nul, c3__ex, + + u3_noun gang = u3nc(u3_nul, u3_nul); + u3_pier_peek_last(htd_u->car_u.pir_u, gang, c3__ex, u3_nul, sac, req_u->peq_u, _http_cache_scry_cb); return c3y; } diff --git a/pkg/vere/io/lick.c b/pkg/vere/io/lick.c index e420057246..1c0a2d7a21 100644 --- a/pkg/vere/io/lick.c +++ b/pkg/vere/io/lick.c @@ -29,6 +29,7 @@ typedef struct _u3_shan { typedef struct _u3_port { c3_c* nam_c; // name of port c3_o con_o; + c3_o liv_o; struct _u3_shan* san_u; // server reference struct _u3_lick* lic_u; // device backpointer struct _u3_port* nex_u; // next pointer @@ -214,7 +215,7 @@ _lick_close_chan(u3_chan* can_u) } can_u->mor_u.nex_u = NULL; - if ( NULL == san_u->can_u ) { + if ( NULL == san_u->can_u && c3y == gen_u->liv_o ) { // send a close event to arvo and stop reading. // u3_noun wir, cad, dev, dat, mar; @@ -301,6 +302,11 @@ static void _lick_close_sock(u3_shan* san_u) { u3_lick* lic_u = san_u->gen_u->lic_u; + + if ( NULL != san_u->can_u ) { + _lick_close_chan(san_u->can_u); + } + c3_w len_w = strlen(lic_u->fod_c) + strlen(san_u->gen_u->nam_c) + 2; c3_c* paf_c = c3_malloc(len_w); c3_i wit_i; @@ -423,6 +429,7 @@ _lick_ef_shut(u3_lick* lic_u, u3_noun nam) while ( NULL != cur_u ) { if ( 0 == strcmp(cur_u->nam_c, nam_c) ) { + cur_u->liv_o = c3n; _lick_close_sock(cur_u->san_u); if( las_u == NULL ) { lic_u->gen_u = cur_u->nex_u; @@ -462,6 +469,7 @@ _lick_ef_spin(u3_lick* lic_u, u3_noun nam) gen_u->san_u->gen_u = gen_u; gen_u->nam_c = nam_c; gen_u->con_o = c3n; + gen_u->liv_o = c3y; _lick_init_sock(gen_u->san_u); if ( NULL == las_u) { diff --git a/pkg/vere/lord.c b/pkg/vere/lord.c index cf1dfcfba9..a63824ae9b 100644 --- a/pkg/vere/lord.c +++ b/pkg/vere/lord.c @@ -1160,10 +1160,11 @@ u3_lord_init(c3_c* pax_c, c3_w wag_w, c3_d key_d[4], u3_lord_cb cb_u) // spawn new process and connect to it // { - c3_c* arg_c[11]; + c3_c* arg_c[12]; c3_c key_c[256]; c3_c wag_c[11]; c3_c hap_c[11]; + c3_c per_c[11]; c3_c cev_c[11]; c3_c lom_c[11]; c3_c tos_c[11]; @@ -1179,6 +1180,8 @@ u3_lord_init(c3_c* pax_c, c3_w wag_w, c3_d key_d[4], u3_lord_cb cb_u) sprintf(hap_c, "%u", u3_Host.ops_u.hap_w); + sprintf(per_c, "%u", u3_Host.ops_u.per_w); + sprintf(lom_c, "%u", u3_Host.ops_u.lom_y); sprintf(tos_c, "%u", u3C.tos_w); @@ -1208,7 +1211,8 @@ u3_lord_init(c3_c* pax_c, c3_w wag_w, c3_d key_d[4], u3_lord_cb cb_u) } arg_c[9] = tos_c; - arg_c[10] = NULL; + arg_c[10] = per_c; + arg_c[11] = NULL; uv_pipe_init(u3L, &god_u->inn_u.pyp_u, 0); uv_timer_init(u3L, &god_u->out_u.tim_u); diff --git a/pkg/vere/main.c b/pkg/vere/main.c index f83d656e98..930d8981ad 100644 --- a/pkg/vere/main.c +++ b/pkg/vere/main.c @@ -183,6 +183,9 @@ _main_init(void) u3_Host.ops_u.veb = c3n; u3_Host.ops_u.puf_c = "jam"; u3_Host.ops_u.hap_w = 50000; + u3C.hap_w = u3_Host.ops_u.hap_w; + u3_Host.ops_u.per_w = 50000; + u3C.per_w = u3_Host.ops_u.per_w; u3_Host.ops_u.kno_w = DefaultKernel; u3_Host.ops_u.sap_w = 120; /* aka 2 minutes */ @@ -247,6 +250,7 @@ _main_getopt(c3_i argc, c3_c** argv) { "loom", required_argument, NULL, c3__loom }, { "local", no_argument, NULL, 'L' }, { "lite-boot", no_argument, NULL, 'l' }, + { "keep-cache-limit", required_argument, NULL, 'M' }, { "replay-to", required_argument, NULL, 'n' }, { "profile", no_argument, NULL, 'P' }, { "ames-port", required_argument, NULL, 'p' }, @@ -279,7 +283,7 @@ _main_getopt(c3_i argc, c3_c** argv) }; while ( -1 != (ch_i=getopt_long(argc, argv, - "A:B:C:DF:G:H:I:J:K:LPRSX:Y:Z:ab:c:de:gi:jk:ln:p:qr:stu:vw:x", + "A:B:C:DF:G:H:I:J:K:LM:PRSX:Y:Z:ab:c:de:gi:jk:ln:p:qr:stu:vw:x", lop_u, &lid_i)) ) { switch ( ch_i ) { @@ -342,7 +346,7 @@ _main_getopt(c3_i argc, c3_c** argv) return c3n; } else { u3_Host.ops_u.sap_w = arg_w * 60; - if ( 0 == u3_Host.ops_u.sap_w) + if ( 0 == u3_Host.ops_u.sap_w ) return c3n; } break; @@ -365,6 +369,7 @@ _main_getopt(c3_i argc, c3_c** argv) if ( c3n == _main_readw(optarg, 1000000000, &u3_Host.ops_u.hap_w) ) { return c3n; } + u3C.hap_w = u3_Host.ops_u.hap_w; break; } case 'c': { @@ -412,6 +417,13 @@ _main_getopt(c3_i argc, c3_c** argv) u3_Host.ops_u.key_c = _main_repath(optarg); break; } + case 'M': { + if ( c3n == _main_readw(optarg, 1000000000, &u3_Host.ops_u.per_w) ) { + return c3n; + } + u3C.per_w = u3_Host.ops_u.per_w; + break; + } case 'n': { u3_Host.ops_u.til_c = strdup(optarg); break; @@ -712,6 +724,7 @@ _cw_usage(c3_c* bin_c) " %s next %.*s request upgrade:\n", " %s queu %.*s cue state:\n", " %s chop %.*s truncate event log:\n", + " %s roll %.*s rollover to new epoch:\n", " %s vere ARGS download binary:\n", "\n run as a 'serf':\n", " %s serf " @@ -764,6 +777,7 @@ u3_ve_usage(c3_i argc, c3_c** argv) "-L, --local Local networking only\n", " --loom Set loom to binary exponent (31 == 2GB)\n" "-l, --lite-boot Most-minimal startup\n", + "-M, --keep-cache-limit LIMIT Set persistent memo cache max size; 0 means default\n", "-n, --replay-to NUMBER Replay up to event\n", "-P, --profile Profiling\n", "-p, --ames-port PORT Set the ames port to bind to\n", @@ -1075,6 +1089,8 @@ _cw_serf_commence(c3_i argc, c3_c* argv[]) exit(1); } + // XX use named arguments and getopt + c3_d eve_d = 0; uv_loop_t* lup_u = u3_Host.lup_u = uv_default_loop(); c3_c* dir_c = argv[2]; @@ -1086,6 +1102,7 @@ _cw_serf_commence(c3_i argc, c3_c* argv[]) c3_c* eve_c = argv[7]; c3_c* eph_c = argv[8]; c3_c* tos_c = argv[9]; + c3_c* per_c = argv[10]; c3_w tos_w; _cw_init_io(lup_u); @@ -1110,7 +1127,8 @@ _cw_serf_commence(c3_i argc, c3_c* argv[]) // XX check return // sscanf(wag_c, "%" SCNu32, &u3C.wag_w); - sscanf(hap_c, "%" SCNu32, &u3_Host.ops_u.hap_w); + sscanf(hap_c, "%" SCNu32, &u3C.hap_w); + sscanf(per_c, "%" SCNu32, &u3C.per_w); sscanf(lom_c, "%" SCNu32, &lom_w); if ( 1 != sscanf(tos_c, "%" SCNu32, &u3C.tos_w) ) { @@ -1560,11 +1578,11 @@ _cw_info(c3_i argc, c3_c* argv[]) exit(1); } - c3_d eve_d = u3m_boot(u3_Host.dir_c, (size_t)1 << u3_Host.ops_u.lom_y); + u3_Host.eve_d = u3m_boot(u3_Host.dir_c, (size_t)1 << u3_Host.ops_u.lom_y); u3_disk* log_u = _cw_disk_init(u3_Host.dir_c); fprintf(stderr, "\r\nurbit: %s at event %" PRIu64 "\r\n", - u3_Host.dir_c, eve_d); + u3_Host.dir_c, u3_Host.eve_d); u3_disk_slog(log_u); printf("\n"); @@ -1719,17 +1737,17 @@ _cw_cram(c3_i argc, c3_c* argv[]) exit(1); } - c3_d eve_d = u3m_boot(u3_Host.dir_c, (size_t)1 << u3_Host.ops_u.lom_y); + u3_Host.eve_d = u3m_boot(u3_Host.dir_c, (size_t)1 << u3_Host.ops_u.lom_y); u3_disk* log_u = _cw_disk_init(u3_Host.dir_c); // XX s/b try_aquire lock c3_o ret_o; fprintf(stderr, "urbit: cram: preparing\r\n"); - if ( c3n == (ret_o = u3u_cram(u3_Host.dir_c, eve_d)) ) { + if ( c3n == (ret_o = u3u_cram(u3_Host.dir_c, u3_Host.eve_d)) ) { fprintf(stderr, "urbit: cram: unable to jam state\r\n"); } else { - fprintf(stderr, "urbit: cram: rock saved at event %" PRIu64 "\r\n", eve_d); + fprintf(stderr, "urbit: cram: rock saved at event %" PRIu64 "\r\n", u3_Host.eve_d); } // save even on failure, as we just did all the work of deduplication @@ -1831,12 +1849,11 @@ _cw_queu(c3_i argc, c3_c* argv[]) exit(1); } else { + u3_Host.eve_d = u3m_boot(u3_Host.dir_c, (size_t)1 << u3_Host.ops_u.lom_y); u3_disk* log_u = _cw_disk_init(u3_Host.dir_c); // XX s/b try_aquire lock fprintf(stderr, "urbit: queu: preparing\r\n"); - u3m_boot(u3_Host.dir_c, (size_t)1 << u3_Host.ops_u.lom_y); - // XX can spuriously fail do to corrupt memory-image checkpoint, // need a u3m_half_boot equivalent // workaround is to delete/move the checkpoint in case of corruption @@ -1924,10 +1941,10 @@ _cw_meld(c3_i argc, c3_c* argv[]) exit(1); } - u3_disk* log_u = _cw_disk_init(u3_Host.dir_c); // XX s/b try_aquire lock - u3C.wag_w |= u3o_hashless; - u3m_boot(u3_Host.dir_c, (size_t)1 << u3_Host.ops_u.lom_y); + + u3_Host.eve_d = u3m_boot(u3_Host.dir_c, (size_t)1 << u3_Host.ops_u.lom_y); + u3_disk* log_u = _cw_disk_init(u3_Host.dir_c); // XX s/b try_aquire lock u3a_print_memory(stderr, "urbit: meld: gained", u3u_meld()); @@ -2086,9 +2103,9 @@ _cw_pack(c3_i argc, c3_c* argv[]) exit(1); } + u3_Host.eve_d = u3m_boot(u3_Host.dir_c, (size_t)1 << u3_Host.ops_u.lom_y); u3_disk* log_u = _cw_disk_init(u3_Host.dir_c); // XX s/b try_aquire lock - u3m_boot(u3_Host.dir_c, (size_t)1 << u3_Host.ops_u.lom_y); u3a_print_memory(stderr, "urbit: pack: gained", u3m_pack()); u3m_save(); @@ -2105,6 +2122,38 @@ _cw_play_slog(u3_noun hod) u3z(hod); } +/* _cw_play_snap(): prepare snapshot for full replay. +*/ +static void +_cw_play_snap(u3_disk* log_u) +{ + c3_c chk_c[8193], epo_c[8193]; + snprintf(chk_c, 8193, "%s/.urb/chk", u3_Host.dir_c); + snprintf(epo_c, 8192, "%s/0i%" PRIc3_d, log_u->com_u->pax_c, log_u->epo_d); + + if ( 0 == log_u->epo_d ) { + // if epoch 0 is the latest, delete the snapshot files in chk/ + c3_c nor_c[8193], sop_c[8193]; + snprintf(nor_c, 8193, "%s/.urb/chk/north.bin", u3_Host.dir_c); + snprintf(sop_c, 8193, "%s/.urb/chk/south.bin", u3_Host.dir_c); + if ( c3_unlink(nor_c) && (ENOENT != errno) ) { + fprintf(stderr, "mars: failed to unlink %s: %s\r\n", + nor_c, strerror(errno)); + exit(1); + } + if ( c3_unlink(sop_c) && (ENOENT != errno) ) { + fprintf(stderr, "mars: failed to unlink %s: %s\r\n", + sop_c, strerror(errno)); + exit(1); + } + } + else if ( 0 != u3e_backup(epo_c, chk_c, c3y) ) { + // copy the latest epoch's snapshot files into chk/ + fprintf(stderr, "mars: failed to copy snapshot\r\n"); + exit(1); + } +} + /* _cw_play_exit(): exit immediately. */ static void @@ -2116,6 +2165,68 @@ _cw_play_exit(c3_i int_i) raise(SIGINT); } +/* _cw_play_impl(): replay events, but better. +*/ +static c3_d +_cw_play_impl(c3_d eve_d, c3_d sap_d, c3_o mel_o, c3_o sof_o, c3_o ful_o) +{ + c3_d pay_d; + + // XX handle SIGTSTP so that the lockfile is not orphaned? + // + u3_disk* log_u = _cw_disk_init(u3_Host.dir_c); + + // Handle SIGTSTP as if it was SIGINT. + // + // Configured here using signal() so as to be immediately available. + // + signal(SIGTSTP, _cw_play_exit); + + // XX source these from a shared struct ops_u + if ( c3y == mel_o ) { + u3C.wag_w |= u3o_auto_meld; + } + + if ( c3y == sof_o ) { + u3C.wag_w |= u3o_soft_mugs; + } + + u3C.wag_w |= u3o_hashless; + + if ( c3y == ful_o ) { + u3l_log("mars: preparing for full replay"); + _cw_play_snap(log_u); + } + + // XX this should check that snapshot is within epoc, + // and load from the epoc / reboot if it is not + // + u3m_boot(u3_Host.dir_c, (size_t)1 << u3_Host.ops_u.lom_y); + + u3C.slog_f = _cw_play_slog; + + { + u3_mars mar_u = { + .log_u = log_u, + .dir_c = u3_Host.dir_c, + .sen_d = u3A->eve_d, + .dun_d = u3A->eve_d, + }; + + pay_d = u3_mars_play(&mar_u, eve_d, sap_d); + u3_Host.eve_d = mar_u.dun_d; + + // migrate or rollover as needed + // + u3_disk_kindly(log_u, u3_Host.eve_d); + } + + u3_disk_exit(log_u); + u3m_stop(); + + return pay_d; +} + /* _cw_play(): replay events, but better. */ static void @@ -2208,55 +2319,9 @@ _cw_play(c3_i argc, c3_c* argv[]) exit(1); } - // XX handle SIGTSTP so that the lockfile is not orphaned? - // - u3_disk* log_u = _cw_disk_init(u3_Host.dir_c); // XX s/b try_aquire lock - - // Handle SIGTSTP as if it was SIGINT. - // - // Configured here using signal() so as to be immediately available. - // - signal(SIGTSTP, _cw_play_exit); - - if ( c3y == mel_o ) { - u3C.wag_w |= u3o_auto_meld; - } - - if ( c3y == sof_o ) { - u3C.wag_w |= u3o_soft_mugs; - } - - u3C.wag_w |= u3o_hashless; - - // XX this should restore the epoch snapshot and replay that - // - if ( c3y == ful_o ) { - u3l_log("mars: preparing for full replay"); - u3m_init((size_t)1 << u3_Host.ops_u.lom_y); - u3e_live(c3n, u3_Host.dir_c); - u3m_foul(); - u3m_pave(c3y); - u3j_boot(c3y); - } - else { - u3m_boot(u3_Host.dir_c, (size_t)1 << u3_Host.ops_u.lom_y); - } - - u3C.slog_f = _cw_play_slog; - - { - u3_mars mar_u = { - .log_u = log_u, - .dir_c = u3_Host.dir_c, - .sen_d = u3A->eve_d, - .dun_d = u3A->eve_d, - }; - - u3_mars_play(&mar_u, eve_d, sap_d); + if ( !_cw_play_impl(eve_d, sap_d, mel_o, sof_o, ful_o) ) { + fprintf(stderr, "mars: nothing to do!"); } - - u3_disk_exit(log_u); - u3m_stop(); } /* _cw_prep(): prepare for upgrade @@ -2264,6 +2329,8 @@ _cw_play(c3_i argc, c3_c* argv[]) static void _cw_prep(c3_i argc, c3_c* argv[]) { + // XX roll with old binary + // check that new epoch is empty, migrate snapshot in-place c3_i ch_i, lid_i; c3_w arg_w; @@ -2404,123 +2471,138 @@ _cw_chop(c3_i argc, c3_c* argv[]) } // gracefully shutdown the pier if it's running - u3_disk* old_u = _cw_disk_init(u3_Host.dir_c); + u3_Host.eve_d = u3m_boot(u3_Host.dir_c, (size_t)1 << u3_Host.ops_u.lom_y); + u3_disk* log_u = _cw_disk_init(u3_Host.dir_c); - // note: this include patch applications (if any) - u3m_boot(u3_Host.dir_c, (size_t)1 << u3_Host.ops_u.lom_y); + u3_disk_kindly(log_u, u3_Host.eve_d); - // check if there's a *current* snapshot - if ( old_u->dun_d != u3A->eve_d ) { - fprintf(stderr, "chop: error: snapshot is out of date, please " - "start/shutdown your pier gracefully first\r\n"); - fprintf(stderr, "chop: eve_d: %" PRIu64 ", dun_d: %" PRIu64 "\r\n", u3A->eve_d, old_u->dun_d); + // create new epoch + c3_d fir_d, las_d; + if ( c3n == u3_lmdb_gulf(log_u->mdb_u, &fir_d, &las_d) ) { + fprintf(stderr, "chop: failed to get first/last events\r\n"); exit(1); } - if ( c3n == u3m_backup(c3y)) { // backup current snapshot - fprintf(stderr, "chop: error: failed to backup snapshot\r\n"); + // create new epoch if latest isn't empty + if ( (fir_d != las_d) && (c3n == u3_disk_epoc_init(log_u, las_d)) ) { + fprintf(stderr, "chop: failed to create new epoch\r\n"); exit(1); } - // initialize the lmdb environment - // see disk.c:885 - const size_t siz_i = - // 500 GiB is as large as musl on aarch64 wants to allow - #if (defined(U3_CPU_aarch64) && defined(U3_OS_linux)) - 0x7d00000000; - #else - 0x10000000000; - #endif - c3_c log_c[8193]; - snprintf(log_c, sizeof(log_c), "%s/.urb/log", u3_Host.dir_c); + c3_z len_z = u3_disk_epoc_list(log_u, 0); - // get the first/last event numbers from the event log - c3_d fir_d, las_d; - if ( c3n == u3_lmdb_gulf(old_u->mdb_u, &fir_d, &las_d) ) { - fprintf(stderr, "chop: failed to load latest event from database\r\n"); - exit(1); + if ( len_z <= 2 ) { + fprintf(stderr, "chop: nothing to do, have a great day\r\n"); + exit(0); // enjoy } - // get the metadata - c3_d who_d[2]; - c3_o fak_o; - c3_w lif_w; - if ( c3y != u3_disk_read_meta(old_u->mdb_u, who_d, &fak_o, &lif_w) ) { - fprintf(stderr, "chop: failed to read metadata\r\n"); - exit(1); - } + c3_d* sot_d = c3_malloc(len_z * sizeof(c3_d)); + u3_disk_epoc_list(log_u, sot_d); - // get the last event - u3_lmdb_walk itr_u; - size_t len_i; - void* buf_v[1]; - if ( c3n == u3_lmdb_walk_init(old_u->mdb_u, &itr_u, las_d, las_d) ) { - fprintf(stderr, "chop: failed to initialize iterator\r\n"); - exit(1); + // delete all but the last two epochs + // + // XX parameterize the number of epochs to chop + // + for ( c3_z i_z = 2; i_z < len_z; i_z++ ) { + fprintf(stderr, "chop: deleting epoch 0i%" PRIc3_d "\r\n", sot_d[i_z]); + if ( c3y != u3_disk_epoc_kill(log_u, sot_d[i_z]) ) { + fprintf(stderr, "chop: failed to delete epoch 0i%" PRIu64 "\r\n", sot_d[i_z]); + exit(1); + } } - if ( c3n == u3_lmdb_walk_next(&itr_u, &len_i, buf_v) ) { - fprintf(stderr, "chop: failed to read event\r\n"); - exit(1); + + // cleanup + c3_free(sot_d); + u3_disk_exit(log_u); + + // success + fprintf(stderr, "chop: event log truncation complete\r\n"); +} + +/* _cw_roll(): rollover to new epoch + */ +static void +_cw_roll(c3_i argc, c3_c* argv[]) +{ + c3_i ch_i, lid_i; + c3_w arg_w; + + static struct option lop_u[] = { + { "loom", required_argument, NULL, c3__loom }, + { NULL, 0, NULL, 0 } + }; + + u3_Host.dir_c = _main_pier_run(argv[0]); + + while ( -1 != (ch_i=getopt_long(argc, argv, "", lop_u, &lid_i)) ) { + switch ( ch_i ) { + case c3__loom: { + if (_main_readw_loom("loom", &u3_Host.ops_u.lom_y)) { + exit(1); + } + } break; + + case '?': { + fprintf(stderr, "invalid argument\r\n"); + exit(1); + } break; + } } - u3_lmdb_walk_done(&itr_u); - // initialize a fresh lmdb environment in the "chop" subdir - c3_c cho_c[8193]; - snprintf(cho_c, sizeof(cho_c), "%s/chop", log_c); - if ( 0 != access(cho_c, F_OK) ) { - if ( 0 != c3_mkdir(cho_c, 0700) ) { - fprintf(stderr, "chop: failed to create chop directory\r\n"); + // argv[optind] is always "roll" + // + + if ( !u3_Host.dir_c ) { + if ( optind + 1 < argc ) { + u3_Host.dir_c = argv[optind + 1]; + } + else { + fprintf(stderr, "invalid command, pier required\r\n"); exit(1); } - } - MDB_env* new_u = u3_lmdb_init(cho_c, siz_i); - if ( !new_u ) { - fprintf(stderr, "chop: failed to initialize new database\r\n"); - exit(1); + + optind++; } - // write the metadata to the database - if ( c3n == u3_disk_save_meta(new_u, who_d, fak_o, lif_w) ) { - fprintf(stderr, "chop: failed to save metadata\r\n"); + if ( optind + 1 != argc ) { + fprintf(stderr, "invalid command\r\n"); exit(1); } - // write the last event to the database - // warning: this relies on the old database still being open - if ( c3n == u3_lmdb_save(new_u, las_d, 1, buf_v, &len_i) ) { - fprintf(stderr, "chop: failed to write last event\r\n"); + // gracefully shutdown the pier if it's running + u3_Host.eve_d = u3m_boot(u3_Host.dir_c, (size_t)1 << u3_Host.ops_u.lom_y); + u3_disk* log_u = _cw_disk_init(u3_Host.dir_c); + + u3_disk_kindly(log_u, u3_Host.eve_d); + + // check if there's a *current* snapshot + if ( log_u->dun_d != u3A->eve_d ) { + fprintf(stderr, "roll: error: snapshot is out of date, please " + "start/shutdown your pier gracefully first\r\n"); + fprintf(stderr, "roll: eve_d: %" PRIc3_d ", dun_d: %" PRIc3_d "\r\n", \ + u3A->eve_d, log_u->dun_d); exit(1); } - // backup the original database file - c3_c dat_c[8193], bak_c[8193]; - snprintf(dat_c, sizeof(dat_c), "%s/data.mdb", log_c); - // "data_-.mdb.bak" - snprintf(bak_c, sizeof(bak_c), "%s/data_%" PRIu64 "-%" PRIu64 ".mdb.bak", cho_c, fir_d, las_d); - if ( 0 != c3_rename(dat_c, bak_c) ) { - fprintf(stderr, "chop: failed to backup original database file\r\n"); + // create new epoch + c3_d fir_d, las_d; + if ( c3n == u3_lmdb_gulf(log_u->mdb_u, &fir_d, &las_d) ) { + fprintf(stderr, "roll: failed to get first/last events\r\n"); exit(1); } - // rename new database file to be official - c3_c new_c[8193]; - snprintf(new_c, sizeof(new_c), "%s/data.mdb", cho_c); - if ( 0 != c3_rename(new_c, dat_c) ) { - fprintf(stderr, "chop: failed to rename new database file\r\n"); + if ( fir_d == las_d ) { + fprintf(stderr, "roll: latest epoch already empty\r\n"); + exit(0); + } + else if ( c3n == u3_disk_epoc_init(log_u, las_d) ) { + fprintf(stderr, "roll: failed to create new epoch\r\n"); exit(1); } - // cleanup - u3_disk_exit(old_u); - u3_lmdb_exit(new_u); - u3m_stop(); - - // success - fprintf(stderr, "chop: event log truncation complete\r\n"); - fprintf(stderr, " event log backup written to %s\r\n", bak_c); - fprintf(stderr, " WARNING: ENSURE YOU CAN RESTART YOUR SHIP BEFORE DELETING YOUR EVENT LOG BACKUP FILE!\r\n"); - fprintf(stderr, " if you can't, restore your log by running:\r\n"); - fprintf(stderr, " `mv %s %s` then try again\r\n", bak_c, dat_c); + // success + c3_d epo_d = log_u->dun_d + 1; + fprintf(stderr, "roll: epoch rollover complete\r\n"); } /* _cw_vere(): download vere @@ -2806,6 +2888,7 @@ _cw_utils(c3_i argc, c3_c* argv[]) case c3__prep: _cw_prep(argc, argv); return 2; // continue on case c3__queu: _cw_queu(argc, argv); return 1; case c3__chop: _cw_chop(argc, argv); return 1; + case c3__roll: _cw_roll(argc, argv); return 1; case c3__vere: _cw_vere(argc, argv); return 1; case c3__vile: _cw_vile(argc, argv); return 1; @@ -2995,6 +3078,13 @@ main(c3_i argc, } } + // we need the current snapshot's latest event number to + // validate whether we can execute disk migration + if ( u3_Host.ops_u.nuu == c3n ) { + _cw_play_impl(0, 0, c3n, c3n, c3n); + // XX unmap loom, else parts of the snapshot could be left in memory + } + // starting u3m configures OpenSSL memory functions, so we must do it // before any OpenSSL allocations // diff --git a/pkg/vere/mars.c b/pkg/vere/mars.c index 37933bd49e..5e940182dc 100644 --- a/pkg/vere/mars.c +++ b/pkg/vere/mars.c @@ -6,6 +6,8 @@ #include "noun.h" #include "types.h" #include "vere.h" +#include "ivory.h" +#include "ur.h" #include "db/lmdb.h" #include #include @@ -179,6 +181,29 @@ _mars_do_boot(u3_disk* log_u, c3_d eve_d) // eve = u3m_love(u3ke_cue(u3ke_jam(eve))); + // install an ivory pill to support stack traces + // + // XX support -J + // + { + c3_d len_d = u3_Ivory_pill_len; + c3_y* byt_y = u3_Ivory_pill; + u3_cue_xeno* sil_u = u3s_cue_xeno_init_with(ur_fib27, ur_fib28); + u3_weak pil; + + if ( u3_none == (pil = u3s_cue_xeno_with(sil_u, len_d, byt_y)) ) { + u3l_log("lite: unable to cue ivory pill"); + exit(1); + } + + u3s_cue_xeno_done(sil_u); + + if ( c3n == u3v_boot_lite(pil)) { + u3l_log("lite: boot failed"); + exit(1); + } + } + u3l_log("--------------- bootstrap starting ----------------"); u3l_log("boot: 1-%u", u3qb_lent(eve)); @@ -195,10 +220,11 @@ _mars_do_boot(u3_disk* log_u, c3_d eve_d) /* u3_mars_play(): replay up to [eve_d], snapshot every [sap_d]. */ -void +c3_d u3_mars_play(u3_mars* mar_u, c3_d eve_d, c3_d sap_d) { u3_disk* log_u = mar_u->log_u; + c3_d pay_d = 0; if ( !eve_d ) { eve_d = log_u->dun_d; @@ -207,17 +233,18 @@ u3_mars_play(u3_mars* mar_u, c3_d eve_d, c3_d sap_d) u3l_log("mars: already computed %" PRIu64 "", eve_d); u3l_log(" state=%" PRIu64 ", log=%" PRIu64 "", mar_u->dun_d, log_u->dun_d); - return; + return pay_d; } else { eve_d = c3_min(eve_d, log_u->dun_d); } if ( mar_u->dun_d == log_u->dun_d ) { - u3l_log("mars: nothing to do!"); - return; + return pay_d; } + pay_d = eve_d - mar_u->dun_d; + if ( !mar_u->dun_d ) { c3_w lif_w; @@ -351,4 +378,6 @@ u3_mars_play(u3_mars* mar_u, c3_d eve_d, c3_d sap_d) u3l_log("---------------- playback complete ----------------"); u3m_save(); + + return pay_d; } diff --git a/pkg/vere/mars.h b/pkg/vere/mars.h index ba97f63614..552f1a9dea 100644 --- a/pkg/vere/mars.h +++ b/pkg/vere/mars.h @@ -15,7 +15,7 @@ /* u3_mars_play(): replay up to [eve_d], snapshot every [sap_d]. */ - void + c3_d u3_mars_play(u3_mars* mar_u, c3_d eve_d, c3_d sap_d); #endif /* ifndef U3_VERE_MARS_H */ diff --git a/pkg/vere/mdns.c b/pkg/vere/mdns.c new file mode 100644 index 0000000000..e62b7ea767 --- /dev/null +++ b/pkg/vere/mdns.c @@ -0,0 +1,210 @@ +/// @file + +#include "vere.h" +#include "mdns.h" +#include "dns_sd.h" + +typedef struct _mdns_payload { + mdns_cb* cb; + DNSServiceRef sref; + char who[58]; + uint16_t port; + uv_poll_t poll; + void* context; +} mdns_payload; + +static void close_cb(uv_handle_t* poll) { + mdns_payload* payload = (mdns_payload*)poll->data; + DNSServiceRefDeallocate(payload->sref); + c3_free(payload); +} + +static void getaddrinfo_cb(uv_getaddrinfo_t* req, int status, struct addrinfo* res) { + mdns_payload* payload = (mdns_payload*)req->data; + + if (status < 0) { + u3l_log("mdns: getaddrinfo error: %s", uv_strerror(status)); + } else { + struct sockaddr_in* addr = (struct sockaddr_in*)res->ai_addr; + payload->cb(payload->who, addr->sin_addr.s_addr, payload->port, payload->context); + } + + payload->poll.data = payload; + uv_close((uv_handle_t*)&payload->poll, close_cb); + c3_free(req); + uv_freeaddrinfo(res); +} + +static void resolve_cb(DNSServiceRef sref, + DNSServiceFlags f, + uint32_t interface, + DNSServiceErrorType err, + const char *name, + const char *host, + uint16_t port, + uint16_t tl, + const unsigned char *t, + void *context) +{ + mdns_payload* payload = (mdns_payload*)context; + + uv_poll_stop(&payload->poll); + + if (err != kDNSServiceErr_NoError) { + u3l_log("mdns: dns resolve error %d", err); + payload->poll.data = payload; + uv_close((uv_handle_t*)&payload->poll, close_cb); + return; + } + + payload->sref = sref; + payload->port = port; + + int i; + payload->who[0] = '~'; + for (i = 0; name[i] != '\0' && name[i] != '.' && i < 58; ++i) + { + payload->who[i+1] = name[i]; + } + + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; // Request only IPv4 addresses + hints.ai_socktype = SOCK_STREAM; // TCP socket + + uv_getaddrinfo_t* req = (uv_getaddrinfo_t*)c3_malloc(sizeof(uv_getaddrinfo_t)); + req->data = (void*)payload; + + uv_loop_t* loop = uv_default_loop(); + + int error = uv_getaddrinfo(loop, req, getaddrinfo_cb, host, NULL, &hints); + + if (error < 0) { + u3l_log("mdns: getaddrinfo error: %s\n", uv_strerror(error)); + payload->poll.data = payload; + uv_close((uv_handle_t*)&payload->poll, close_cb); + c3_free(req); + } +} + +static void poll_cb(uv_poll_t* handle, int status, int events) { + DNSServiceRef sref = (DNSServiceRef) handle->data; + int err = DNSServiceProcessResult(sref); +} + +static void init_sref_poll(DNSServiceRef sref, mdns_payload* payload) { + int fd = DNSServiceRefSockFD(sref); + uv_loop_t* loop = uv_default_loop(); + payload->poll.data = (void*)sref; + uv_poll_init(loop, &payload->poll, fd); + uv_poll_start(&payload->poll, UV_READABLE, poll_cb); +} + +static void browse_cb(DNSServiceRef s, + DNSServiceFlags f, + uint32_t interface, + DNSServiceErrorType err, + const char* name, + const char* type, + const char* domain, + void* context) +{ + if (err != kDNSServiceErr_NoError) { + u3l_log("mdns: service browse error %i", err); + return; + } + + if (f & kDNSServiceFlagsAdd) { // Add + // we are leaking payload because we don't know when we are done + // browsing, luckily we only browse once + mdns_payload* payload = (mdns_payload*)context; + mdns_payload* payload_copy = c3_malloc(sizeof *payload_copy); + + // copy to prevent asynchronous thrashing of payload + memcpy(payload_copy, payload, sizeof(mdns_payload)); + + + DNSServiceErrorType err = + DNSServiceResolve(&payload_copy->sref, 0, interface, + name, type, domain, resolve_cb, + (void*)payload_copy); + + init_sref_poll(payload_copy->sref, payload_copy); + + if (err != kDNSServiceErr_NoError) { + u3l_log("mdns: dns service resolve error %i", err); + payload_copy->poll.data = payload_copy; + uv_close((uv_handle_t*)&payload_copy->poll, close_cb); + } + } +} + +static void register_close_cb(uv_handle_t* poll) { + // not freeing sref with DNSServiceRefDeallocate since that + // deregisters us from mdns + mdns_payload* payload = (mdns_payload*)poll->data; + c3_free(payload); +} + + +static void register_cb(DNSServiceRef sref, + DNSServiceFlags f, + DNSServiceErrorType err, + const char* name, + const char* type, + const char* domain, + void* context) +{ + mdns_payload* payload = (mdns_payload*)context; + + if (err != kDNSServiceErr_NoError) { + u3l_log("mdns: service register error %i", err); + } else { + u3l_log("mdns: %s registered on all interfaces", name); + } + uv_poll_stop(&payload->poll); + uv_close((uv_handle_t*)&payload->poll, register_close_cb); +} + +void mdns_init(uint16_t port, char* our, mdns_cb* cb, void* context) +{ + #if defined(U3_OS_linux) + setenv("AVAHI_COMPAT_NOWARN", "1", 0); + setenv("DBUS_SYSTEM_BUS_ADDRESS", "unix:path=/var/run/dbus/system_bus_socket", 0); + # endif + + mdns_payload* register_payload = (mdns_payload*)c3_malloc(sizeof(mdns_payload)); + + DNSServiceRef sref; + DNSServiceErrorType err; + char* our_no_sig = our + 1; // certain url parsers don't like the ~ + err = DNSServiceRegister(&sref, 0, 0, our_no_sig, "_ames._udp", + NULL, NULL, htons(port), 0, NULL, register_cb, (void*)register_payload); + + if (err != kDNSServiceErr_NoError) { + u3l_log("mdns: service register error %i", err); + DNSServiceRefDeallocate(sref); + return; + } + + init_sref_poll(sref, register_payload); + + mdns_payload* browse_payload = (mdns_payload*)c3_malloc(sizeof(mdns_payload)); + + browse_payload->cb = cb; + browse_payload->context = context; + + DNSServiceErrorType dnserr; + DNSServiceRef sref2; + + dnserr = DNSServiceBrowse(&sref2, 0, 0, "_ames._udp", NULL, browse_cb, (void *)browse_payload); + + if (dnserr != kDNSServiceErr_NoError) { + u3l_log("mdns: service browse error %i", dnserr); + DNSServiceRefDeallocate(sref2); + return; + } + + init_sref_poll(sref2, browse_payload); + +} diff --git a/pkg/vere/mdns.h b/pkg/vere/mdns.h new file mode 100644 index 0000000000..66c3477179 --- /dev/null +++ b/pkg/vere/mdns.h @@ -0,0 +1,5 @@ +#include "noun.h" + +typedef void mdns_cb(c3_c* ship, c3_w s_addr, c3_s port, void* context); + +void mdns_init(uint16_t port, char* our, mdns_cb* cb, void* context); diff --git a/pkg/vere/pier.c b/pkg/vere/pier.c index 1e39d14ef4..5c5b047f3e 100644 --- a/pkg/vere/pier.c +++ b/pkg/vere/pier.c @@ -675,8 +675,8 @@ _pier_wyrd_fail(u3_pier* pir_u, u3_ovum* egg_u, u3_noun lud) // XX organizing version constants // #define VERE_NAME "vere" -#define VERE_ZUSE 412 -#define VERE_LULL 323 +#define VERE_ZUSE 411 +#define VERE_LULL 322 /* _pier_wyrd_aver(): check for %wend effect and version downgrade. RETAIN */ @@ -783,7 +783,7 @@ _pier_on_lord_wyrd_bail(void* ptr_v, u3_ovum* egg_u, u3_noun lud) #endif } -/* _pier_wyrd_init(): construct %wyrd. +/* _pier_wyrd_card(): construct %wyrd. */ static u3_noun _pier_wyrd_card(u3_pier* pir_u) @@ -814,8 +814,8 @@ _pier_wyrd_card(u3_pier* pir_u) u3_nul); u3_noun kel = u3nl(u3nc(c3__zuse, VERE_ZUSE), // XX from both king and serf? u3nc(c3__lull, VERE_LULL), // XX from both king and serf? - u3nc(c3__arvo, 237), // XX from both king and serf? - u3nc(c3__hoon, 139), // god_u->hon_y + u3nc(c3__arvo, 236), // XX from both king and serf? + u3nc(c3__hoon, 138), // god_u->hon_y u3nc(c3__nock, 4), // god_u->noc_y u3_none); u3_noun wir = u3nc(c3__arvo, u3_nul); @@ -1402,6 +1402,8 @@ _pier_on_lord_live(void* ptr_v) // XX print bootstrap commit complete // XX s/b boot_complete_cb // + // XX this codepath should never be hit due to sync replay + u3l_log("pier: warning: async replay"); _pier_play_init(pir_u, log_u->dun_d); } } @@ -1641,6 +1643,8 @@ _pier_init(c3_w wag_w, c3_c* pax_c) c3_free(pir_u); return 0; } + + u3_assert( U3D_VER1 == pir_u->log_u->ver_w ); } // initialize compute diff --git a/pkg/vere/serf.c b/pkg/vere/serf.c index 3a59005e00..cec8bece55 100644 --- a/pkg/vere/serf.c +++ b/pkg/vere/serf.c @@ -3,6 +3,8 @@ #include "noun.h" #include "vere.h" +#include "ivory.h" +#include "ur.h" /* |% @@ -49,6 +51,25 @@ :: next steps: -- */ +/* serf memory-threshold levels +*/ +enum { + _serf_mas_init = 0, // initial + _serf_mas_hit1 = 1, // past low threshold + _serf_mas_hit0 = 2 // have high threshold +}; + +/* serf post-op flags +*/ +enum { + _serf_fag_none = 0, // nothing to do + _serf_fag_hit1 = 1 << 0, // hit low threshold + _serf_fag_hit0 = 1 << 1, // hit high threshold + _serf_fag_mute = 1 << 2, // mutated state + _serf_fag_much = 1 << 3, // bytecode hack + _serf_fag_vega = 1 << 4 // kernel reset +}; + /* _serf_grab(): garbage collect, checking for profiling. RETAIN. */ static void @@ -172,28 +193,53 @@ u3_serf_grab(void) void u3_serf_post(u3_serf* sef_u) { - if ( c3y == sef_u->rec_o ) { + if ( sef_u->fag_w & _serf_fag_hit1 ) { + if ( u3C.wag_w & u3o_verbose ) { + u3l_log("serf: threshold 1: %u", u3h_wyt(u3R->cax.per_p)); + } + u3h_trim_to(u3R->cax.per_p, u3h_wyt(u3R->cax.per_p) / 2); + u3m_reclaim(); + } + + if ( sef_u->fag_w & _serf_fag_much ) { + u3m_reclaim(); + } + + if ( sef_u->fag_w & _serf_fag_vega ) { + u3h_trim_to(u3R->cax.per_p, u3h_wyt(u3R->cax.per_p) / 2); u3m_reclaim(); - sef_u->rec_o = c3n; } // XX this runs on replay too, |mass s/b elsewhere // - if ( c3y == sef_u->mut_o ) { + if ( sef_u->fag_w & _serf_fag_mute ) { _serf_grab(sef_u->sac); sef_u->sac = u3_nul; - sef_u->mut_o = c3n; } - if ( c3y == sef_u->pac_o ) { + if ( sef_u->fag_w & _serf_fag_hit0 ) { + if ( u3C.wag_w & u3o_verbose ) { + u3l_log("serf: threshold 0: per_p %u", u3h_wyt(u3R->cax.per_p)); + } + u3h_free(u3R->cax.per_p); + u3R->cax.per_p = u3h_new_cache(u3C.per_w); u3a_print_memory(stderr, "serf: pack: gained", u3m_pack()); u3l_log(""); - sef_u->pac_o = c3n; } if ( u3C.wag_w & u3o_toss ) { u3m_toss(); } + + sef_u->fag_w = _serf_fag_none; +} + +/* _serf_curb(): check for memory threshold +*/ +static inline c3_t +_serf_curb(c3_w pre_w, c3_w pos_w, c3_w hes_w) +{ + return (pre_w > hes_w) && (pos_w <= hes_w); } /* _serf_sure_feck(): event succeeded, send effects. @@ -201,9 +247,6 @@ u3_serf_post(u3_serf* sef_u) static u3_noun _serf_sure_feck(u3_serf* sef_u, c3_w pre_w, u3_noun vir) { - c3_o rec_o = c3n; - c3_o pac_o = c3n; - // intercept |mass, observe |reset // { @@ -234,7 +277,7 @@ _serf_sure_feck(u3_serf* sef_u, c3_w pre_w, u3_noun vir) // reclaim memory from persistent caches on |reset // if ( c3__vega == u3h(fec) ) { - rec_o = c3y; + sef_u->fag_w |= _serf_fag_vega; } riv = u3t(riv); @@ -251,39 +294,60 @@ _serf_sure_feck(u3_serf* sef_u, c3_w pre_w, u3_noun vir) // For future flexibility, the urgency of the notification is represented // by a *decreasing* number: 0 is maximally urgent, 1 less so, &c. // - // high-priority: 2^22 contiguous words remaining (~8 MB) + // high-priority: 2^25 contiguous words remaining (~128 MB) // low-priority: 2^27 contiguous words remaining (~536 MB) - // XX maybe use 2^23 (~16 MB) and 2^26 (~268 MB? + // + // once a threshold is hit, it's not a candidate to be hit again + // until memory usage falls below: + // + // high-priority: 2^26 contiguous words remaining (~256 MB) + // low-priority: 2^26 + 2^27 contiguous words remaining (~768 MB) // // XX these thresholds should trigger notifications sent to the king // instead of directly triggering these remedial actions. // { u3_noun pri = u3_none; - c3_w pos_w = u3a_open(u3R); - c3_w low_w = (1 << 27); - c3_w hig_w = (1 << 22); + c3_w pos_w = u3a_open(u3R); - if ( (pre_w > low_w) && !(pos_w > low_w) ) { - // XX set flag(s) in u3V so we don't repeat endlessly? - // - pac_o = c3y; - rec_o = c3y; - pri = 1; + // if contiguous free space shrunk, check thresholds + // (and track state to avoid thrashing) + // + if ( pos_w < pre_w ) { + if ( (_serf_mas_hit0 != sef_u->mas_w) + && _serf_curb(pre_w, pos_w, 1 << 25) ) + { + sef_u->mas_w = _serf_mas_hit0; + sef_u->fag_w |= _serf_fag_hit0; + pri = 0; + } + else if ( (_serf_mas_init == sef_u->mas_w) + && _serf_curb(pre_w, pos_w, 1 << 27) ) + { + sef_u->mas_w = _serf_mas_hit1; + sef_u->fag_w |= _serf_fag_hit1; + pri = 1; + } } - else if ( (pre_w > hig_w) && !(pos_w > hig_w) ) { - pac_o = c3y; - rec_o = c3y; - pri = 0; + else if ( _serf_mas_init != sef_u->mas_w ) { + if ( ((1 << 26) + (1 << 27)) < pos_w ) { + sef_u->mas_w = _serf_mas_init; + } + else if ( (_serf_mas_hit0 == sef_u->mas_w) + && ((1 << 26) < pos_w) ) + { + sef_u->mas_w = _serf_mas_hit1; + } } + // reclaim memory from persistent caches periodically // // XX this is a hack to work two things // - bytecode caches grow rapidly and can't be simply capped // - we don't make very effective use of our free lists // - else if ( 0 == (sef_u->dun_d % 1000ULL) ) { - rec_o = c3y; + if ( !(sef_u->dun_d % 1024ULL) ) { + sef_u->fag_w |= _serf_fag_much; } // notify daemon of memory pressure via "fake" effect @@ -295,9 +359,6 @@ _serf_sure_feck(u3_serf* sef_u, c3_w pre_w, u3_noun vir) } } - sef_u->rec_o = c3o(sef_u->rec_o, rec_o); - sef_u->pac_o = c3o(sef_u->pac_o, pac_o); - return vir; } @@ -312,7 +373,7 @@ _serf_sure_core(u3_serf* sef_u, u3_noun cor) u3A->roc = cor; u3A->eve_d = sef_u->dun_d; sef_u->mug_l = u3r_mug(u3A->roc); - sef_u->mut_o = c3y; + sef_u->fag_w |= _serf_fag_mute; } /* _serf_sure(): event succeeded, save state and process effects. @@ -538,11 +599,28 @@ _serf_play_life(u3_serf* sef_u, u3_noun eve) u3z(len); } - // ensure zero-initialized kernel + // install an ivory pill to support stack traces // - // XX assert? + // XX support -J // - u3A->roc = 0; + { + c3_d len_d = u3_Ivory_pill_len; + c3_y* byt_y = u3_Ivory_pill; + u3_cue_xeno* sil_u = u3s_cue_xeno_init_with(ur_fib27, ur_fib28); + u3_weak pil; + + if ( u3_none == (pil = u3s_cue_xeno_with(sil_u, len_d, byt_y)) ) { + u3l_log("lite: unable to cue ivory pill"); + exit(1); + } + + u3s_cue_xeno_done(sil_u); + + if ( c3n == u3v_boot_lite(pil)) { + u3l_log("lite: boot failed"); + exit(1); + } + } gon = u3m_soft(0, u3v_life, eve); @@ -997,9 +1075,7 @@ u3_serf_init(u3_serf* sef_u) // } // } - sef_u->pac_o = c3n; - sef_u->rec_o = c3n; - sef_u->mut_o = c3n; + sef_u->fag_w = _serf_fag_none; sef_u->sac = u3_nul; return rip; diff --git a/pkg/vere/serf.h b/pkg/vere/serf.h index 5808f362e7..7cd2ca47d0 100644 --- a/pkg/vere/serf.h +++ b/pkg/vere/serf.h @@ -11,9 +11,8 @@ c3_d sen_d; // last event requested c3_d dun_d; // last event processed c3_l mug_l; // hash of state - c3_o pac_o; // pack kernel - c3_o rec_o; // reclaim cache - c3_o mut_o; // mutated kerne + c3_w mas_w; // memory threshold state + c3_w fag_w; // post-op flags u3_noun sac; // space measurementl void (*xit_f)(void); // exit callback } u3_serf; diff --git a/pkg/vere/vere.h b/pkg/vere/vere.h index cef16810b6..9527246e19 100644 --- a/pkg/vere/vere.h +++ b/pkg/vere/vere.h @@ -8,6 +8,7 @@ #include "noun.h" #include "serf.h" #include "uv.h" +#include /** Quasi-tunable parameters. **/ @@ -130,6 +131,7 @@ c3_c* pax_c; // path of directory uv_file fil_u; // file, opened read-only to fsync u3_dent* all_u; // file list + u3_dent* dil_u; // directory list } u3_dire; /* u3_save: checkpoint control. @@ -263,7 +265,7 @@ c3_o abo; // -a, abort aggressively c3_c* pil_c; // -B, bootstrap from c3_c* bin_c; // -b, http server bind ip - c3_w hap_w; // -C, cap memo cache + c3_w hap_w; // -C, cap transient memo cache c3_o dry; // -D, dry compute, no checkpoint c3_o dem; // -d, daemon c3_c* eth_c; // -e, ethereum node url @@ -281,6 +283,7 @@ c3_o lit; // -l, lite mode c3_y lom_y; // loom bex c3_y lut_y; // urth-loom bex + c3_w per_w; // -M, cap persistent memo cache c3_c* til_c; // -n, play till eve_d c3_o pro; // -P, profile c3_s per_s; // http port @@ -313,6 +316,7 @@ typedef struct _u3_host { c3_w kno_w; // current executing stage c3_c* dir_c; // pier path (no trailing /) + c3_d eve_d; // initial current snapshot c3_c* dem_c; // daemon executable path c3_c* wrk_c; // worker executable path c3_d now_d; // event tick @@ -538,9 +542,11 @@ u3_dire* urb_u; // urbit system data u3_dire* com_u; // log directory c3_o liv_o; // live - void* mdb_u; // lmdb environment. + c3_w ver_w; // pier version + void* mdb_u; // lmdb env of current epoch c3_d sen_d; // commit requested c3_d dun_d; // committed + c3_d epo_d; // current epoch number u3_disk_cb cb_u; // callbacks u3_read* red_u; // read requests union { // write thread/request @@ -1002,6 +1008,31 @@ void u3_disk_plan(u3_disk* log_u, u3_fact* tac_u); + /* u3_disk_epoc_init(): create new epoch. + */ + c3_o + u3_disk_epoc_init(u3_disk* log_u, c3_d epo_d); + + /* u3_disk_epoc_kill(): delete an epoch. + */ + c3_o + u3_disk_epoc_kill(u3_disk* log_u, c3_d epo_d); + + /* u3_disk_epoc_last(): get latest epoch number. + */ + c3_o + u3_disk_epoc_last(u3_disk* log_u, c3_d* lat_d); + + /* u3_disk_epoc_list: get descending epoch numbers, "mcut" pattern. + */ + c3_z + u3_disk_epoc_list(u3_disk* log_u, c3_d* sot_d); + + /* u3_disk_kindly(): do the needful. + */ + void + u3_disk_kindly(u3_disk* log_u, c3_d eve_d); + /* u3_disk_read_list(): synchronously read a cons list of events. */ u3_weak diff --git a/pkg/vere/ward.c b/pkg/vere/ward.c index 4c90be7179..9d6c8cbed3 100644 --- a/pkg/vere/ward.c +++ b/pkg/vere/ward.c @@ -39,6 +39,7 @@ u3_dire_init(const c3_c* pax_c) { u3_dire *dir_u = c3_malloc(sizeof *dir_u); dir_u->all_u = 0; + dir_u->dil_u = 0; dir_u->pax_c = c3_malloc(1 + strlen(pax_c)); strcpy(dir_u->pax_c, pax_c); @@ -61,6 +62,17 @@ u3_dire_free(u3_dire *dir_u) } } + { + u3_dent *det_u = dir_u->dil_u; + u3_dent *nex_u; + + while ( det_u ) { + nex_u = det_u->nex_u; + u3_dent_free(det_u); + det_u = nex_u; + } + } + c3_free(dir_u->pax_c); c3_free(dir_u); }